diff options
author | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2022-10-03 23:38:53 +1100 |
---|---|---|
committer | Madeline <46743919+MaddyUnderStars@users.noreply.github.com> | 2022-10-03 23:38:53 +1100 |
commit | e8815f1fd595858e85f1c41df9f4cbe51f968760 (patch) | |
tree | 0900328585d42d12b477ffec22f26f42de068aac | |
parent | Make imagor optional (diff) | |
download | server-e8815f1fd595858e85f1c41df9f4cbe51f968760.tar.xz |
Twitter embeds
-rw-r--r-- | src/api/util/handlers/Message.ts | 12 | ||||
-rw-r--r-- | src/api/util/utility/EmbedHandlers.ts | 95 | ||||
-rw-r--r-- | src/util/entities/Config.ts | 6 |
3 files changed, 102 insertions, 11 deletions
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts index 09b86fc2..d4f0e995 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts @@ -192,19 +192,21 @@ export async function postHandleMessage(message: Message) { const url = new URL(link); // bit gross, but whatever! - const { endpointPublic } = Config.get().cdn; - const handler = url.hostname == new URL(endpointPublic!).hostname ? EmbedHandlers["self"] : EmbedHandlers[url.hostname] || EmbedHandlers["default"]; + const endpointPublic = Config.get().cdn.endpointPublic || "http://127.0.0.1"; // lol + const handler = url.hostname == new URL(endpointPublic).hostname ? EmbedHandlers["self"] : EmbedHandlers[url.hostname] || EmbedHandlers["default"]; try { const res = await handler(url); if (!res) continue; - embed = res; + // tried to use shorthand but types didn't like me L + if (Array.isArray(res)) + data.embeds.push(...res) + else + data.embeds.push(res); } catch (e) { continue; } - - data.embeds.push(embed); } await Promise.all([ diff --git a/src/api/util/utility/EmbedHandlers.ts b/src/api/util/utility/EmbedHandlers.ts index 65fecd4d..831cab9a 100644 --- a/src/api/util/utility/EmbedHandlers.ts +++ b/src/api/util/utility/EmbedHandlers.ts @@ -16,7 +16,7 @@ export const DEFAULT_FETCH_OPTIONS: any = { method: "GET", }; -export const getProxyUrl = (url: URL, width: number, height: number) => { +export const getProxyUrl = (url: URL, width: number, height: number): string => { const { endpointPublic, resizeWidthMax, resizeHeightMax, imagorServerUrl } = Config.get().cdn; const secret = Config.get().security.jwtSecret; // maybe shouldn't use this? width = Math.min(width || 500, resizeWidthMax || width); @@ -116,7 +116,7 @@ const genericImageHandler = async (url: URL): Promise<Embed | null> => { }; }; -export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null>; } = { +export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | Embed[] | null>; } = { // the url does not have a special handler "default": async (url: URL) => { const type = await fetch(url, { @@ -168,6 +168,89 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null> // TODO: twitter, facebook // have to use their APIs or something because they don't send the metas in initial html + "twitter.com": (url: URL) => { return EmbedHandlers["www.twitter.com"](url); }, + "www.twitter.com": async (url: URL) => { + const token = Config.get().external.twitter; + if (!token) return null; // todo move to config + + if (!url.href.includes("/status/")) return null; // TODO; + const id = url.pathname.split("/")[3]; // super bad lol + if (!parseInt(id)) return null; + const endpointUrl = `https://api.twitter.com/2/tweets/${id}` + + `?expansions=author_id,attachments.media_keys` + + `&media.fields=url,width,height` + + `&tweet.fields=created_at,public_metrics` + + `&user.fields=profile_image_url`; + + + const response = await fetch(endpointUrl, { + ...DEFAULT_FETCH_OPTIONS, + headers: { + authorization: `Bearer ${token}`, + } + }); + const json = await response.json(); + if (json.errors) return null; + const author = json.includes.users[0]; + const text = json.data.text; + const created_at = new Date(json.data.created_at); + const metrics = json.data.public_metrics; + let media = json.includes.media?.filter((x: any) => x.type == "photo") as any[]; // TODO: video + + const embed: Embed = { + type: EmbedType.rich, + url: `${url.origin}${url.pathname}`, + description: text, + author: { + url: `https://twitter.com/${author.username}`, + name: `${author.name} (@${author.username})`, + proxy_icon_url: getProxyUrl(new URL(author.profile_image_url), 400, 400), + icon_url: author.profile_image_url, + }, + timestamp: created_at, + fields: [ + { inline: true, name: "Likes", value: metrics.like_count.toString() }, + { inline: true, name: "Retweet", value: metrics.retweet_count.toString() }, + ], + color: 1942002, + footer: { + text: "Twitter", + proxy_icon_url: getProxyUrl(new URL("https://abs.twimg.com/icons/apple-touch-icon-192x192.png"), 192, 192), + icon_url: "https://abs.twimg.com/icons/apple-touch-icon-192x192.png" + }, + // Discord doesn't send this? + // provider: { + // name: "Twitter", + // url: "https://twitter.com" + // }, + }; + + if (media) { + embed.image = { + width: media[0].width, + height: media[0].height, + url: media[0].url, + proxy_url: getProxyUrl(new URL(media[0].url), media[0].width, media[0].height) + }; + media.shift(); + } + + return embed; + + // TODO: Client won't merge these into a single embed, for some reason. + // return [embed, ...media.map((x: any) => ({ + // // generate new embeds for each additional attachment + // type: EmbedType.rich, + // url: url.href, + // image: { + // width: x.width, + // height: x.height, + // url: x.url, + // proxy_url: getProxyUrl(new URL(x.url), x.width, x.height) + // } + // }))]; + }, + "open.spotify.com": async (url: URL) => { const response = await doFetch(url); if (!response) return null; @@ -191,7 +274,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null> }; }, - "pixiv.net": async (url: URL) => { return EmbedHandlers["www.pixiv.net"](url); }, + "pixiv.net": (url: URL) => { return EmbedHandlers["www.pixiv.net"](url); }, "www.pixiv.net": async (url: URL) => { const response = await doFetch(url); if (!response) return null; @@ -242,7 +325,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null> }; }, - "reddit.com": async (url: URL) => { return EmbedHandlers["www.reddit.com"](url); }, + "reddit.com": (url: URL) => { return EmbedHandlers["www.reddit.com"](url); }, "www.reddit.com": async (url: URL) => { const res = await EmbedHandlers["default"](url); return { @@ -254,7 +337,7 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null> }; }, - "youtube.com": async (url: URL) => { return EmbedHandlers["www.youtube.com"](url); }, + "youtube.com": (url: URL) => { return EmbedHandlers["www.youtube.com"](url); }, "www.youtube.com": async (url: URL): Promise<Embed | null> => { const response = await doFetch(url); if (!response) return null; @@ -304,4 +387,4 @@ export const EmbedHandlers: { [key: string]: (url: URL) => Promise<Embed | null> } }; }, -}; \ No newline at end of file +};; \ No newline at end of file diff --git a/src/util/entities/Config.ts b/src/util/entities/Config.ts index 42006fc9..2b75a049 100644 --- a/src/util/entities/Config.ts +++ b/src/util/entities/Config.ts @@ -209,6 +209,9 @@ export interface ConfigValue { traceSampleRate: number; environment: string; }; + external: { + twitter: string | null; + } } export const DefaultConfigOptions: ConfigValue = { @@ -418,4 +421,7 @@ export const DefaultConfigOptions: ConfigValue = { traceSampleRate: 1.0, environment: hostname(), }, + external: { + twitter: null, + } }; |