diff options
Diffstat (limited to 'packages/gateway/src')
| -rw-r--r-- | packages/gateway/src/database/emojiUsageTracker.ts | 178 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/emojiUsageTracking.ts | 16 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/index.ts | 2 | ||||
| -rw-r--r-- | packages/gateway/src/listeners/memberLeave.ts | 4 |
4 files changed, 197 insertions, 3 deletions
diff --git a/packages/gateway/src/database/emojiUsageTracker.ts b/packages/gateway/src/database/emojiUsageTracker.ts new file mode 100644 index 0000000..604e8a6 --- /dev/null +++ b/packages/gateway/src/database/emojiUsageTracker.ts @@ -0,0 +1,178 @@ +import { Message } from "discord.js"; +import prisma from "./prisma"; +import { logUnexpectedDiscordAPIError } from "../utilities"; + +export class EmojiUsageTracker { + static async trackEmojiUsage(message: Message): Promise<void> { + if (!message.guild) return; + + try { + const guildId = message.guild.id; + const customEmojiRegex = /<a?:(\w+):(\d+)>/g; + const matches = message.content.matchAll(customEmojiRegex); + + for (const match of matches) { + const emojiName = match[1]; + const emojiId = match[2]; + const emoji = message.guild.emojis.cache.get(emojiId); + + if (!emoji) continue; + + await this.recordEmojiUsage(guildId, emojiId, emojiName); + } + + if (message.reactions.cache.size > 0) + for (const reaction of message.reactions.cache.values()) { + const emoji = reaction.emoji; + + if (emoji.id && message.guild.emojis.cache.has(emoji.id)) + await this.recordEmojiUsage( + guildId, + emoji.id, + emoji.name || "unknown", + ); + } + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + static async trackStickerUsage(message: Message): Promise<void> { + if (!message.guild) return; + + try { + const guildId = message.guild.id; + + for (const sticker of message.stickers.values()) + if (sticker.guildId === guildId) + await this.recordStickerUsage(guildId, sticker.id, sticker.name); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + private static async recordEmojiUsage( + guildId: string, + emojiId: string, + emojiName: string, + ): Promise<void> { + try { + const existing = await prisma.emojiUsage.findUnique({ + where: { + guildId_emojiId: { + guildId, + emojiId, + }, + }, + }); + + if (existing) { + await prisma.emojiUsage.update({ + where: { + guildId_emojiId: { + guildId, + emojiId, + }, + }, + data: { + usageCount: existing.usageCount + 1, + lastUsed: new Date(), + }, + }); + } else { + await prisma.emojiUsage.create({ + data: { + guildId, + emojiId, + emojiName, + usageCount: 1, + firstUsed: new Date(), + lastUsed: new Date(), + }, + }); + } + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + private static async recordStickerUsage( + guildId: string, + stickerId: string, + stickerName: string, + ): Promise<void> { + try { + const existing = await prisma.stickerUsage.findUnique({ + where: { + guildId_stickerId: { + guildId, + stickerId, + }, + }, + }); + + if (existing) { + await prisma.stickerUsage.update({ + where: { + guildId_stickerId: { + guildId, + stickerId, + }, + }, + data: { + usageCount: existing.usageCount + 1, + lastUsed: new Date(), + }, + }); + } else { + await prisma.stickerUsage.create({ + data: { + guildId, + stickerId, + stickerName, + usageCount: 1, + firstUsed: new Date(), + lastUsed: new Date(), + }, + }); + } + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + } + + static async getEmojiStats(guildId: string, limit: number = 50) { + try { + return await prisma.emojiUsage.findMany({ + where: { guildId }, + orderBy: { usageCount: "desc" }, + take: limit, + }); + } catch (error) { + logUnexpectedDiscordAPIError(error); + + return []; + } + } + + static async getStickerStats(guildId: string, limit: number = 50) { + try { + return await prisma.stickerUsage.findMany({ + where: { guildId }, + orderBy: { usageCount: "desc" }, + take: limit, + }); + } catch (error) { + logUnexpectedDiscordAPIError(error); + + return []; + } + } + + // static async cleanupDeletedAssets(guildId: string): Promise<void> { + // try { + // } catch (error) { + // logUnexpectedDiscordAPIError(error); + // } + // } +} diff --git a/packages/gateway/src/listeners/emojiUsageTracking.ts b/packages/gateway/src/listeners/emojiUsageTracking.ts new file mode 100644 index 0000000..a6b01a1 --- /dev/null +++ b/packages/gateway/src/listeners/emojiUsageTracking.ts @@ -0,0 +1,16 @@ +import { Client, Events, Message } from "discord.js"; +import { EmojiUsageTracker } from "../database/emojiUsageTracker"; +import { logUnexpectedDiscordAPIError } from "../utilities"; + +export const handleEmojiUsageTracking = (client: Client) => { + client.on(Events.MessageCreate, async (message: Message) => { + try { + if (message.author.bot || !message.guild) return; + + await EmojiUsageTracker.trackEmojiUsage(message); + await EmojiUsageTracker.trackStickerUsage(message); + } catch (error) { + logUnexpectedDiscordAPIError(error); + } + }); +}; diff --git a/packages/gateway/src/listeners/index.ts b/packages/gateway/src/listeners/index.ts index 21da227..15b617b 100644 --- a/packages/gateway/src/listeners/index.ts +++ b/packages/gateway/src/listeners/index.ts @@ -9,6 +9,7 @@ import { handleMemberJoin } from "./memberJoin"; import { handleMemberLeave } from "./memberLeave"; import { handleTimeoutMirroring } from "./timeoutMirroring"; import { handleAutoDeletion } from "./autoDeletion"; +import { handleEmojiUsageTracking } from "./emojiUsageTracking"; // import { handleMediaModeration } from "./mediaModeration"; export const handleListeners = (client: Client) => { @@ -22,5 +23,6 @@ export const handleListeners = (client: Client) => { handleMemberLeave(client); handleTimeoutMirroring(client); handleAutoDeletion(client); + handleEmojiUsageTracking(client); // handleMediaModeration(client); }; diff --git a/packages/gateway/src/listeners/memberLeave.ts b/packages/gateway/src/listeners/memberLeave.ts index e348917..3fdef88 100644 --- a/packages/gateway/src/listeners/memberLeave.ts +++ b/packages/gateway/src/listeners/memberLeave.ts @@ -1,8 +1,6 @@ -/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */ - import { Client, Events, GuildMember } from "discord.js"; import { RolePersistenceService } from "../database/rolePersistence"; -import { log, LogLevel, logUnexpectedDiscordAPIError } from "../utilities"; +import { logUnexpectedDiscordAPIError } from "../utilities"; export const handleMemberLeave = (client: Client) => { client.on(Events.GuildMemberRemove, async (member) => { |