server/database/entities/Emoji.ts

118 lines
3.4 KiB
TypeScript
Raw Normal View History

import { emojiValidator, emojiValidatorWithColons } from "@api";
import { proxyUrl } from "@response";
import { type InferSelectModel, and, eq } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { Emojis, Instances } from "~drizzle/schema";
2024-04-14 12:53:21 +02:00
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
import { addInstanceIfNotExists } from "./Instance";
2023-09-12 22:48:10 +02:00
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
instance: InferSelectModel<typeof Instances> | null;
};
2023-09-12 22:48:10 +02:00
/**
* Used for parsing emojis from local text
* @param text The text to parse
* @returns An array of emojis
*/
export const parseEmojis = async (text: string) => {
const matches = text.match(emojiValidatorWithColons);
2024-04-07 07:30:49 +02:00
if (!matches) return [];
const emojis = await db.query.Emojis.findMany({
where: (emoji, { eq, or }) =>
or(
...matches
.map((match) => match.replace(/:/g, ""))
.map((match) => eq(emoji.shortcode, match)),
),
with: {
2024-04-07 07:30:49 +02:00
instance: true,
},
});
return emojis;
};
2023-09-12 22:48:10 +02:00
/**
* Gets an emoji from the database, and fetches it from the remote instance if it doesn't exist.
* @param emoji Emoji to fetch
* @param host Host to fetch the emoji from if remote
* @returns The emoji
*/
export const fetchEmoji = async (
emojiToFetch: Lysand.Emoji,
host?: string,
): Promise<EmojiWithInstance> => {
const existingEmoji = await db
.select()
.from(Emojis)
.innerJoin(Instances, eq(Emojis.instanceId, Instances.id))
.where(
and(
eq(Emojis.shortcode, emojiToFetch.name),
host ? eq(Instances.baseUrl, host) : undefined,
),
)
.limit(1);
if (existingEmoji[0])
return {
...existingEmoji[0].Emojis,
instance: existingEmoji[0].Instances,
};
const foundInstance = host ? await addInstanceIfNotExists(host) : null;
const result = (
await db
.insert(Emojis)
.values({
shortcode: emojiToFetch.name,
url: Object.entries(emojiToFetch.url)[0][1].content,
alt:
emojiToFetch.alt ||
Object.entries(emojiToFetch.url)[0][1].description ||
undefined,
contentType: Object.keys(emojiToFetch.url)[0],
visibleInPicker: true,
instanceId: foundInstance?.id,
})
.returning()
)[0];
return {
...result,
instance: foundInstance,
};
};
2023-09-12 22:48:10 +02:00
/**
* Converts the emoji to an APIEmoji object.
* @returns The APIEmoji object.
*/
export const emojiToAPI = (emoji: EmojiWithInstance): APIEmoji => {
2024-04-07 07:30:49 +02:00
return {
// @ts-expect-error ID is not in regular Mastodon API
id: emoji.id,
2024-04-07 07:30:49 +02:00
shortcode: emoji.shortcode,
static_url: proxyUrl(emoji.url) ?? "", // TODO: Add static version
url: proxyUrl(emoji.url) ?? "",
visible_in_picker: emoji.visibleInPicker,
2024-04-07 07:30:49 +02:00
category: undefined,
};
};
export const emojiToLysand = (emoji: EmojiWithInstance): Lysand.Emoji => {
2024-04-07 07:30:49 +02:00
return {
name: emoji.shortcode,
url: {
[emoji.contentType]: {
2024-04-07 07:30:49 +02:00
content: emoji.url,
description: emoji.alt || undefined,
2024-04-07 07:30:49 +02:00
},
},
alt: emoji.alt || undefined,
2024-04-07 07:30:49 +02:00
};
2024-03-04 01:45:21 +01:00
};