mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
Implement federation of statuses
This commit is contained in:
parent
8563c97403
commit
a58c81c8e9
11 changed files with 788 additions and 411 deletions
|
|
@ -59,8 +59,7 @@ export const isViewableByUser = (status: Status, user: User | null) => {
|
|||
|
||||
export const fetchFromRemote = async (uri: string): Promise<Status | null> => {
|
||||
// Check if already in database
|
||||
|
||||
const existingStatus: StatusWithRelations | null =
|
||||
/* const existingStatus: StatusWithRelations | null =
|
||||
await client.status.findFirst({
|
||||
where: {
|
||||
uri: uri,
|
||||
|
|
@ -112,7 +111,7 @@ export const fetchFromRemote = async (uri: string): Promise<Status | null> => {
|
|||
}
|
||||
: undefined,
|
||||
quote: quotingStatus || undefined,
|
||||
});
|
||||
}); */
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -192,12 +191,127 @@ export const getDescendants = async (
|
|||
return viewableDescendants;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get people mentioned in the content (match @username or @username@domain.com mentions)
|
||||
* @param text The text to parse mentions from.
|
||||
* @returns An array of users mentioned in the text.
|
||||
*/
|
||||
export const parseTextMentions = async (text: string) => {
|
||||
const mentionedPeople =
|
||||
text.match(/@[a-zA-Z0-9_]+(@[a-zA-Z0-9_]+)?/g) ?? [];
|
||||
|
||||
return await client.user.findMany({
|
||||
where: {
|
||||
OR: mentionedPeople.map((person) => ({
|
||||
username: person.split("@")[1],
|
||||
instance: {
|
||||
base_url: person.split("@")[2],
|
||||
},
|
||||
})),
|
||||
},
|
||||
include: userRelations,
|
||||
});
|
||||
};
|
||||
|
||||
export const createNewStatus = async (
|
||||
author: User,
|
||||
content: Lysand.ContentFormat,
|
||||
visibility: APIStatus["visibility"],
|
||||
is_sensitive: boolean,
|
||||
spoiler_text: string,
|
||||
emojis: Emoji[],
|
||||
uri?: string,
|
||||
mentions?: UserWithRelations[],
|
||||
/** List of IDs of database Attachment objects */
|
||||
media_attachments?: string[],
|
||||
inReplyTo?: StatusWithRelations,
|
||||
quoting?: StatusWithRelations,
|
||||
) => {
|
||||
let htmlContent: string;
|
||||
|
||||
if (content["text/html"]) {
|
||||
htmlContent = content["text/html"].content;
|
||||
} else if (content["text/markdown"]) {
|
||||
htmlContent = linkifyHtml(
|
||||
await sanitizeHtml(await parse(content["text/markdown"].content)),
|
||||
);
|
||||
} else if (content["text/plain"]) {
|
||||
htmlContent = linkifyStr(content["text/plain"].content);
|
||||
|
||||
// Split by newline and add <p> tags
|
||||
htmlContent = htmlContent
|
||||
.split("\n")
|
||||
.map((line) => `<p>${line}</p>`)
|
||||
.join("\n");
|
||||
} else {
|
||||
htmlContent = "";
|
||||
}
|
||||
|
||||
// Parse emojis and fuse with existing emojis
|
||||
let foundEmojis = emojis;
|
||||
|
||||
if (author.instanceId === null) {
|
||||
const parsedEmojis = await parseEmojis(htmlContent);
|
||||
// Fuse and deduplicate
|
||||
foundEmojis = [...emojis, ...parsedEmojis].filter(
|
||||
(emoji, index, self) =>
|
||||
index === self.findIndex((t) => t.id === emoji.id),
|
||||
);
|
||||
}
|
||||
|
||||
const status = await client.status.create({
|
||||
data: {
|
||||
authorId: author.id,
|
||||
content: htmlContent,
|
||||
contentSource:
|
||||
content["text/plain"]?.content ||
|
||||
content["text/markdown"]?.content ||
|
||||
"",
|
||||
contentType: "text/html",
|
||||
visibility: visibility,
|
||||
sensitive: is_sensitive,
|
||||
spoilerText: spoiler_text,
|
||||
isReblog: false, // DEPRECATED FIELD
|
||||
emojis: {
|
||||
connect: foundEmojis.map((emoji) => {
|
||||
return {
|
||||
id: emoji.id,
|
||||
};
|
||||
}),
|
||||
},
|
||||
attachments: media_attachments
|
||||
? {
|
||||
connect: media_attachments.map((attachment) => {
|
||||
return {
|
||||
id: attachment,
|
||||
};
|
||||
}),
|
||||
}
|
||||
: undefined,
|
||||
inReplyToPostId: inReplyTo?.id,
|
||||
quotingPostId: quoting?.id,
|
||||
instanceId: author.instanceId || undefined,
|
||||
uri: uri || null,
|
||||
mentions: {
|
||||
connect: mentions?.map((mention) => {
|
||||
return {
|
||||
id: mention.id,
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
include: statusAndUserRelations,
|
||||
});
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new status and saves it to the database.
|
||||
* @param data The data for the new status.
|
||||
* @returns A promise that resolves with the new status.
|
||||
*/
|
||||
export const createNewStatus = async (data: {
|
||||
export const createNewStatus2 = async (data: {
|
||||
account: User;
|
||||
application: Application | null;
|
||||
content: string;
|
||||
|
|
@ -539,11 +653,18 @@ export const statusToLysand = (status: StatusWithRelations): Lysand.Note => {
|
|||
type: "Note",
|
||||
created_at: new Date(status.createdAt).toISOString(),
|
||||
id: status.id,
|
||||
author: status.authorId,
|
||||
uri: new URL(
|
||||
`/objects/note/${status.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
author:
|
||||
status.author.uri ||
|
||||
new URL(
|
||||
`/users/${status.author.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
uri:
|
||||
status.uri ||
|
||||
new URL(
|
||||
`/objects/note/${status.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
content: {
|
||||
"text/html": {
|
||||
content: status.content,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { addEmojiIfNotExists, emojiToAPI, emojiToLysand } from "./Emoji";
|
|||
import { addInstanceIfNotExists } from "./Instance";
|
||||
import { userRelations } from "./relations";
|
||||
import { createNewRelationship } from "./Relationship";
|
||||
import { urlToContentFormat } from "@content_types";
|
||||
|
||||
export interface AuthData {
|
||||
user: UserWithRelations | null;
|
||||
|
|
@ -493,18 +494,9 @@ export const userToLysand = (user: UserWithRelations): Lysand.User => {
|
|||
).toString(),
|
||||
indexable: false,
|
||||
username: user.username,
|
||||
avatar: {
|
||||
[user.avatar.split(".")[1]]: {
|
||||
content: getAvatarUrl(user, config),
|
||||
},
|
||||
},
|
||||
header: {
|
||||
[user.header.split(".")[1]]: {
|
||||
content: getHeaderUrl(user, config),
|
||||
},
|
||||
},
|
||||
avatar: urlToContentFormat(getAvatarUrl(user, config)) ?? undefined,
|
||||
header: urlToContentFormat(getHeaderUrl(user, config)) ?? undefined,
|
||||
display_name: user.displayName,
|
||||
|
||||
fields: (user.source as APISource).fields.map((field) => ({
|
||||
key: {
|
||||
"text/html": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue