diff --git a/src/index.ts b/src/index.ts index e33afd30..54590c4b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,8 +7,14 @@ import { PermissionFlagsBits, ChannelType, Events, + Message, } from 'discord.js'; import { reuploadCommands } from './_reupload'; +import { + connect as connectStorage, + isUserPlural, + storeUserPlurality, +} from './storage'; import * as BuildConfig from './constants'; import { parseLog } from './logs'; @@ -26,7 +32,11 @@ import { sayCommand } from './commands/say'; import random from 'just-random'; import { green, bold, yellow, cyan } from 'kleur/colors'; import 'dotenv/config'; -import { proxied } from './utils/pluralKit'; +import { + fetchPluralKitMessage, + isMessageProxied, + pkDelay, +} from './utils/pluralKit'; const client = new Client({ intents: [ @@ -42,6 +52,11 @@ const client = new Client({ partials: [Partials.Channel], }); +const handleWebhookMessage = async (e: Message) => { + const pkMessage = await fetchPluralKitMessage(e); + if (pkMessage !== null) storeUserPlurality(pkMessage.sender); +}; + client.once('ready', async () => { console.log(green('Discord bot ready!')); @@ -89,7 +104,15 @@ client.once('ready', async () => { if (e.author === client.user) return; - if (await proxied(e)) return; + if (e.webhookId !== null) { + // defer PK detection + setTimeout(async () => { + await handleWebhookMessage(e); + }, pkDelay); + } + + if ((await isUserPlural(e.author.id)) && (await isMessageProxied(e))) + return; if (e.cleanContent.match(BuildConfig.ETA_REGEX)) { await e.reply( @@ -196,6 +219,7 @@ client.on(Events.ThreadCreate, async (channel) => { reuploadCommands() .then(() => { + connectStorage(); client.login(process.env.DISCORD_TOKEN); }) .catch((e) => { diff --git a/src/storage.ts b/src/storage.ts new file mode 100644 index 00000000..c3b40c94 --- /dev/null +++ b/src/storage.ts @@ -0,0 +1,22 @@ +import { createClient } from 'redis'; + +export const client = createClient({ + url: process.env.REDIS_URL || 'redis://localhost:6379', +}); + +export const storeUserPlurality = async (userId: string) => { + // Just store some value. We only care about the presence of this key + await client + .multi() + .set(`user:${userId}:pk`, '0') + .expire(`user:${userId}:pk`, 7 * 24 * 60 * 60) + .exec(); +}; + +export const isUserPlural = async (userId: string) => { + return (await client.exists(`user:${userId}:pk`)) > 0; +}; + +export const connect = () => { + client.connect(); +}; diff --git a/src/utils/pluralKit.ts b/src/utils/pluralKit.ts index 7753a955..cb2f28c9 100644 --- a/src/utils/pluralKit.ts +++ b/src/utils/pluralKit.ts @@ -1,10 +1,22 @@ -import { Message } from "discord.js"; +import { Message } from 'discord.js'; -export async function proxied(message: Message): Promise { - if (message.webhookId !== null) - return false; +interface PkMessage { + sender: string; +} + +export const pkDelay = 500; + +export async function fetchPluralKitMessage(message: Message) { + const response = await fetch( + `https://api.pluralkit.me/v2/messages/${message.id}` + ); + + if (!response.ok) return null; + + return (await response.json()) as PkMessage; +} - await new Promise(resolve => setTimeout(resolve, 300)); - const response = await fetch(`https://api.pluralkit.me/v2/messages/${message.id}`); - return response.ok; +export async function isMessageProxied(message: Message) { + await new Promise((resolve) => setTimeout(resolve, pkDelay)); + return (await fetchPluralKitMessage(message)) !== null; }