From c25b57417593b1e01ea83ac72c75f11bf490a386 Mon Sep 17 00:00:00 2001 From: Behzad-rabiei Date: Tue, 10 Jun 2025 18:06:32 +0200 Subject: [PATCH 1/3] chore: warn log on unspoported gateway events --- src/workflows/discord/gateway/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/workflows/discord/gateway/index.ts b/src/workflows/discord/gateway/index.ts index f00e099..2f0e26a 100644 --- a/src/workflows/discord/gateway/index.ts +++ b/src/workflows/discord/gateway/index.ts @@ -1,5 +1,7 @@ import { GatewayDispatchPayload } from 'discord-api-types/v10'; +import { log } from '@temporalio/workflow'; + import { eventHandlers } from './handlers'; export async function DiscordGatewayEventWorkflow( @@ -7,7 +9,8 @@ export async function DiscordGatewayEventWorkflow( ): Promise { const { t: event, d } = payload; if (!(event in eventHandlers)) { - throw new Error(`Unsupported gateway event: ${event as string}`); + log.warn('Unsupported gateway event'); + return; } await (eventHandlers as any)[event](d); } From 2fe4b54c69d179e038cddc165bff66b3abd0942b Mon Sep 17 00:00:00 2001 From: Behzad-rabiei Date: Tue, 10 Jun 2025 18:18:10 +0200 Subject: [PATCH 2/3] chore: update @togethercrew.dev/db package --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 72b99d7..7b8ca0b 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@temporalio/common": "^1.11.8", "@temporalio/worker": "^1.11.7", "@temporalio/workflow": "^1.11.7", - "@togethercrew.dev/db": "^3.10.0", + "@togethercrew.dev/db": "^3.12.0", "@togethercrew.dev/tc-messagebroker": "^0.0.51", "axios": "^1.8.3", "bottleneck": "^2.19.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4653d71..69fafdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ importers: specifier: ^1.11.7 version: 1.11.7 '@togethercrew.dev/db': - specifier: ^3.10.0 - version: 3.11.0 + specifier: ^3.12.0 + version: 3.12.0 '@togethercrew.dev/tc-messagebroker': specifier: ^0.0.51 version: 0.0.51(@aws-sdk/credential-providers@3.817.0) @@ -1395,8 +1395,8 @@ packages: '@temporalio/workflow@1.11.7': resolution: {integrity: sha512-NGNmGCoV3xPvRifjRhCZKbIbPAup92/OC2xuaTb3hzBb13N9Pbm6HVljgQORzKNyO55rXDRpTnAiJ40en57IgA==} - '@togethercrew.dev/db@3.11.0': - resolution: {integrity: sha512-+3LP85ETxCqJc5ts/fml8VsVIqmeSNmvC6AekswYn4Frz4kc/53UDN8kmTmPzTCNDflk2vN9oNDvI66JtvcjoQ==} + '@togethercrew.dev/db@3.12.0': + resolution: {integrity: sha512-bJP5pQh/H1Zbpq584cSQ4Pu5sibs51++R6wEnvWI8tZE6nvveRh3TLnRcMBPxfFDq17fxJVzPJisE9+mASb7ZA==} '@togethercrew.dev/tc-messagebroker@0.0.51': resolution: {integrity: sha512-V7gPLE/jphVi6XfMMc/LFeg2Ip3P62b45jB7oxRykSmVGblZHUZ8xo7PfuNUcq+m2iX2NbddN9kcv8/CDLINZg==} @@ -5951,7 +5951,7 @@ snapshots: '@temporalio/common': 1.11.7 '@temporalio/proto': 1.11.7 - '@togethercrew.dev/db@3.11.0': + '@togethercrew.dev/db@3.12.0': dependencies: discord.js: 14.19.3 joi: 17.13.3 From 07254df91f4f8fd77f891f98fb3e729771bf5b13 Mon Sep 17 00:00:00 2001 From: Behzad-rabiei Date: Wed, 11 Jun 2025 15:43:27 +0200 Subject: [PATCH 3/3] feat: add thread gateway events --- .gitignore | 4 +- package.json | 2 +- pnpm-lock.yaml | 16 +- .../discord/gateway/mapping.activity.ts | 31 +-- .../discord/gateway/persistence.activity.ts | 230 +++++++++++------- .../discord/gateway/handlers/index.ts | 8 + .../gateway/handlers/message.handler.ts | 14 +- .../gateway/handlers/thread.handler.ts | 27 ++ .../discord/gateway/mappers/message.mapper.ts | 86 ++----- 9 files changed, 221 insertions(+), 197 deletions(-) create mode 100644 src/workflows/discord/gateway/handlers/thread.handler.ts diff --git a/.gitignore b/.gitignore index 630ee69..7090b54 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules !.env.test dist coverage -*.db \ No newline at end of file +*.db +.cursor/* +.cursor \ No newline at end of file diff --git a/package.json b/package.json index 7b8ca0b..88d1970 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@temporalio/common": "^1.11.8", "@temporalio/worker": "^1.11.7", "@temporalio/workflow": "^1.11.7", - "@togethercrew.dev/db": "^3.12.0", + "@togethercrew.dev/db": "^3.13.2", "@togethercrew.dev/tc-messagebroker": "^0.0.51", "axios": "^1.8.3", "bottleneck": "^2.19.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69fafdb..c789046 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ importers: specifier: ^1.11.7 version: 1.11.7 '@togethercrew.dev/db': - specifier: ^3.12.0 - version: 3.12.0 + specifier: ^3.13.2 + version: 3.13.2 '@togethercrew.dev/tc-messagebroker': specifier: ^0.0.51 version: 0.0.51(@aws-sdk/credential-providers@3.817.0) @@ -1395,8 +1395,8 @@ packages: '@temporalio/workflow@1.11.7': resolution: {integrity: sha512-NGNmGCoV3xPvRifjRhCZKbIbPAup92/OC2xuaTb3hzBb13N9Pbm6HVljgQORzKNyO55rXDRpTnAiJ40en57IgA==} - '@togethercrew.dev/db@3.12.0': - resolution: {integrity: sha512-bJP5pQh/H1Zbpq584cSQ4Pu5sibs51++R6wEnvWI8tZE6nvveRh3TLnRcMBPxfFDq17fxJVzPJisE9+mASb7ZA==} + '@togethercrew.dev/db@3.13.2': + resolution: {integrity: sha512-iWa4yYPzCh5evFAUWMxSnJoPqexjYtMvtUYeYkk6bH2GsoZhAC3RbG09AiEb1/QeeUpgX6vW8OpTYcpxZfJrKQ==} '@togethercrew.dev/tc-messagebroker@0.0.51': resolution: {integrity: sha512-V7gPLE/jphVi6XfMMc/LFeg2Ip3P62b45jB7oxRykSmVGblZHUZ8xo7PfuNUcq+m2iX2NbddN9kcv8/CDLINZg==} @@ -1979,6 +1979,9 @@ packages: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} + discord-api-types@0.38.11: + resolution: {integrity: sha512-XN0qhcQpetkyb/49hcDHuoeUPsQqOkb17wbV/t48gUkoEDi4ajhsxqugGcxvcN17BBtI9FPPWEgzv6IhQmCwyw==} + discord-api-types@0.38.8: resolution: {integrity: sha512-xuRXPD44FcbKHrQK15FS1HFlMRNJtsaZou/SVws18vQ7zHqmlxyDktMkZpyvD6gE2ctGOVYC/jUyoMMAyBWfcw==} @@ -5951,8 +5954,9 @@ snapshots: '@temporalio/common': 1.11.7 '@temporalio/proto': 1.11.7 - '@togethercrew.dev/db@3.12.0': + '@togethercrew.dev/db@3.13.2': dependencies: + discord-api-types: 0.38.11 discord.js: 14.19.3 joi: 17.13.3 moment: 2.30.1 @@ -6628,6 +6632,8 @@ snapshots: diff@4.0.2: {} + discord-api-types@0.38.11: {} + discord-api-types@0.38.8: {} discord.js@14.19.3: diff --git a/src/activities/discord/gateway/mapping.activity.ts b/src/activities/discord/gateway/mapping.activity.ts index ef940fe..0896c6b 100644 --- a/src/activities/discord/gateway/mapping.activity.ts +++ b/src/activities/discord/gateway/mapping.activity.ts @@ -1,31 +1,20 @@ import { - GatewayChannelCreateDispatchData, - GatewayChannelDeleteDispatchData, - GatewayChannelUpdateDispatchData, - GatewayGuildMemberAddDispatchData, - GatewayGuildMemberRemoveDispatchData, - GatewayGuildMemberUpdateDispatchData, - GatewayGuildRoleCreateDispatchData, - GatewayGuildRoleDeleteDispatchData, - GatewayGuildRoleUpdateDispatchData, - GatewayMessageCreateDispatchData, - GatewayMessageDeleteBulkDispatchData, - GatewayMessageDeleteDispatchData, - GatewayMessageReactionAddDispatchData, - GatewayMessageReactionRemoveAllDispatchData, - GatewayMessageReactionRemoveDispatchData, - GatewayMessageReactionRemoveEmojiDispatchData, - GatewayMessageUpdateDispatchData, + GatewayChannelCreateDispatchData, GatewayChannelDeleteDispatchData, + GatewayChannelUpdateDispatchData, GatewayGuildMemberAddDispatchData, + GatewayGuildMemberRemoveDispatchData, GatewayGuildMemberUpdateDispatchData, + GatewayGuildRoleCreateDispatchData, GatewayGuildRoleDeleteDispatchData, + GatewayGuildRoleUpdateDispatchData, GatewayMessageCreateDispatchData, + GatewayMessageDeleteBulkDispatchData, GatewayMessageDeleteDispatchData, + GatewayMessageReactionAddDispatchData, GatewayMessageReactionRemoveAllDispatchData, + GatewayMessageReactionRemoveDispatchData, GatewayMessageReactionRemoveEmojiDispatchData, + GatewayMessageUpdateDispatchData } from 'discord-api-types/v10'; import { IRawInfo } from '@togethercrew.dev/db'; import parentLogger from '../../../config/logger.config'; import { - ChannelMappers, - GuildMemberMappers, - MessageMappers, - RoleMappers, + ChannelMappers, GuildMemberMappers, MessageMappers, RoleMappers } from '../../../workflows/discord/gateway/mappers'; const logger = parentLogger.child({ activity: 'discord:event:map' }); diff --git a/src/activities/discord/gateway/persistence.activity.ts b/src/activities/discord/gateway/persistence.activity.ts index 5e1fbbf..5b791bd 100644 --- a/src/activities/discord/gateway/persistence.activity.ts +++ b/src/activities/discord/gateway/persistence.activity.ts @@ -2,56 +2,83 @@ import { Snowflake } from 'discord.js'; import { FilterQuery } from 'mongoose'; import { - DatabaseManager, - IChannel, - IChannelUpdateBody, - IGuildMember, - IGuildMemberUpdateBody, - IRawInfo, - IRawInfoUpdateBody, - IRole, - IRoleUpdateBody, - makeChannelRepository, - makeGuildMemberRepository, - makeRawInfoRepository, - makeRoleRepository, + DatabaseManager, IChannel, IChannelUpdateBody, IGuildMember, IGuildMemberUpdateBody, IRawInfo, + IRawInfoUpdateBody, IRole, IRoleUpdateBody, IThread, makeChannelRepository, + makeGuildMemberRepository, makeRawInfoRepository, makeRoleRepository, makeThreadRepository, + ThreadUpdateBody } from '@togethercrew.dev/db'; import parentLogger from '../../../config/logger.config'; const logger = parentLogger.child({ activity: 'discord:event:persistence' }); -export async function createChannel( +async function handleDuplicateKeyError( + operation: () => Promise, + entityName: string, + entityId: string, guildId: Snowflake, - data: IChannel, -): Promise { - const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeChannelRepository(dbConnection); +): Promise { + try { + return await operation(); + } catch (err: any) { + if (err.code === 11000) { + logger.warn({ guildId, entityId }, `${entityName} already exists`); + } else { + throw err; + } + } +} +async function performUpsert( + guildId: Snowflake, + repositoryFactory: (db: any) => any, + filter: FilterQuery, + updateData: TUpdateData, + entityName: string, +): Promise { try { - await repo.create(data); + const dbConnection = + await DatabaseManager.getInstance().getGuildDb(guildId); + const repository = repositoryFactory(dbConnection); + const result = await repository.updateOne(filter, updateData); + + if (!result.modifiedCount) { + await repository.create(updateData); + } } catch (err: any) { if (err.code === 11000) { - logger.warn( - { guildId, channelId: data.channelId }, - 'Channel already exists', - ); + logger.warn({ guildId, filter }, `${entityName} already exists`); } else { + logger.error( + { err, guildId, filter }, + `Failed to update ${entityName.toLowerCase()}`, + ); throw err; } } } +export async function createChannel( + guildId: Snowflake, + data: IChannel, +): Promise { + const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); + const repository = makeChannelRepository(dbConnection); + + await handleDuplicateKeyError( + () => repository.create(data), + 'Channel', + data.channelId, + guildId, + ); +} + export async function updateChannel( guildId: Snowflake, filter: FilterQuery, data: IChannelUpdateBody, ): Promise { - const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeChannelRepository(dbConnection); - - const res = await repo.updateOne(filter, data); - if (!res.modifiedCount) await repo.create(data); + await performUpsert(guildId, makeChannelRepository, filter, data, 'Channel'); } export async function createMember( @@ -59,20 +86,14 @@ export async function createMember( data: IGuildMember, ): Promise { const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeGuildMemberRepository(dbConnection); + const repository = makeGuildMemberRepository(dbConnection); - try { - await repo.create(data); - } catch (err: any) { - if (err.code === 11000) { - logger.warn( - { guildId, discordId: data.discordId }, - 'Member already exists', - ); - } else { - throw err; - } - } + await handleDuplicateKeyError( + () => repository.create(data), + 'Member', + data.discordId, + guildId, + ); } export async function updateMember( @@ -80,11 +101,13 @@ export async function updateMember( filter: FilterQuery, data: IGuildMemberUpdateBody, ): Promise { - const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeGuildMemberRepository(dbConnection); - - const res = await repo.updateOne(filter, data); - if (!res.modifiedCount) await repo.create(data); + await performUpsert( + guildId, + makeGuildMemberRepository, + filter, + data, + 'Member', + ); } export async function createRole( @@ -92,17 +115,14 @@ export async function createRole( data: IRole, ): Promise { const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeRoleRepository(dbConnection); + const repository = makeRoleRepository(dbConnection); - try { - await repo.create(data); - } catch (err: any) { - if (err.code === 11000) { - logger.warn({ guildId, roleId: data.roleId }, 'Role already exists'); - } else { - throw err; - } - } + await handleDuplicateKeyError( + () => repository.create(data), + 'Role', + data.roleId, + guildId, + ); } export async function updateRole( @@ -110,43 +130,31 @@ export async function updateRole( filter: FilterQuery, data: IRoleUpdateBody, ): Promise { - try { - const dbConnection = - await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeRoleRepository(dbConnection); - const res = await repo.updateOne(filter, data); - if (!res.modifiedCount) await repo.create(data); - } catch (err: any) { - if (err.code === 11000) { - logger.warn({ guildId, data }, 'Role already exists'); - } else { - throw err; - } - } + await performUpsert(guildId, makeRoleRepository, filter, data, 'Role'); } -// Message/RawInfo persistence functions export async function createRawInfo( guildId: Snowflake, - doc: IRawInfo, + data: IRawInfo, ): Promise { try { - const db = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeRawInfoRepository(db); - await repo.create(doc); + const dbConnection = + await DatabaseManager.getInstance().getGuildDb(guildId); + const repository = makeRawInfoRepository(dbConnection); + await repository.create(data); logger.debug( - { guildId, messageId: doc.messageId }, + { guildId, messageId: data.messageId }, 'Created rawinfo document', ); } catch (err: any) { if (err.code === 11000) { logger.warn( - { guildId, messageId: doc.messageId }, + { guildId, messageId: data.messageId }, 'RawInfo already exists', ); } else { logger.error( - { err, guildId, messageId: doc.messageId }, + { err, guildId, messageId: data.messageId }, 'Failed to create rawinfo', ); throw err; @@ -157,22 +165,9 @@ export async function createRawInfo( export async function updateRawInfo( guildId: Snowflake, filter: FilterQuery, - updateData: IRawInfoUpdateBody, + data: IRawInfoUpdateBody, ): Promise { - try { - const db = await DatabaseManager.getInstance().getGuildDb(guildId); - const repo = makeRawInfoRepository(db); - const result = await repo.updateOne(filter, updateData); - - if (result.modifiedCount === 0) { - logger.warn({ guildId, filter }, 'No rawinfo document found to update'); - } else { - logger.debug({ guildId, filter }, 'Updated rawinfo document'); - } - } catch (err: any) { - logger.error({ err, guildId, filter }, 'Failed to update rawinfo'); - throw err; - } + await performUpsert(guildId, makeRawInfoRepository, filter, data, 'RawInfo'); } export async function deleteRawInfo( @@ -233,3 +228,54 @@ export async function getRawInfo( throw err; } } + +export async function createThread( + guildId: Snowflake, + data: IThread, +): Promise { + try { + const dbConnection = + await DatabaseManager.getInstance().getGuildDb(guildId); + const repository = makeThreadRepository(dbConnection); + await repository.create(data); + logger.debug({ guildId, threadId: data.id }, 'Created thread'); + } catch (err: any) { + if (err.code === 11000) { + logger.warn({ guildId, threadId: data.id }, 'Thread already exists'); + } else { + logger.error( + { err, guildId, threadId: data.id }, + 'Failed to create thread', + ); + throw err; + } + } +} + +export async function updateThread( + guildId: Snowflake, + filter: FilterQuery, + data: ThreadUpdateBody, +): Promise { + await performUpsert(guildId, makeThreadRepository, filter, data, 'Thread'); +} + +export async function deleteThread( + guildId: Snowflake, + threadId: Snowflake, +): Promise { + try { + const db = await DatabaseManager.getInstance().getGuildDb(guildId); + const repo = makeThreadRepository(db); + const result = await repo.deleteMany({ id: threadId }); + + if (result.deletedCount === 0) { + logger.warn({ guildId, threadId }, 'No thread found to delete'); + } else { + logger.debug({ guildId, threadId }, 'Deleted thread'); + } + } catch (err: any) { + logger.error({ err, guildId, threadId }, 'Failed to delete thread'); + throw err; + } +} diff --git a/src/workflows/discord/gateway/handlers/index.ts b/src/workflows/discord/gateway/handlers/index.ts index e43db7c..51039dd 100644 --- a/src/workflows/discord/gateway/handlers/index.ts +++ b/src/workflows/discord/gateway/handlers/index.ts @@ -4,6 +4,7 @@ import { ChannelHandler } from './channel.handler'; import { GuildMemberHandler } from './guildMember.handler'; import { MessageHandler } from './message.handler'; import { RoleHandler } from './role.handler'; +import { ThreadHandler } from './thread.handler'; export const eventHandlers = { [GatewayDispatchEvents.ChannelCreate]: ChannelHandler.create, @@ -28,4 +29,11 @@ export const eventHandlers = { MessageHandler.reactionRemoveAll, [GatewayDispatchEvents.MessageReactionRemoveEmoji]: MessageHandler.reactionRemoveEmoji, + + [GatewayDispatchEvents.ThreadCreate]: ThreadHandler.create, + [GatewayDispatchEvents.ThreadUpdate]: ThreadHandler.update, + [GatewayDispatchEvents.ThreadDelete]: ThreadHandler.delete, + // [GatewayDispatchEvents.ThreadListSync]: ThreadHandler.listSync, + // [GatewayDispatchEvents.ThreadMemberUpdate]: ThreadHandler.memberUpdate, + // [GatewayDispatchEvents.ThreadMembersUpdate]: ThreadHandler.membersUpdate, } as const; diff --git a/src/workflows/discord/gateway/handlers/message.handler.ts b/src/workflows/discord/gateway/handlers/message.handler.ts index 3c13c6f..28b5e28 100644 --- a/src/workflows/discord/gateway/handlers/message.handler.ts +++ b/src/workflows/discord/gateway/handlers/message.handler.ts @@ -1,12 +1,8 @@ import { - GatewayMessageCreateDispatchData, - GatewayMessageDeleteBulkDispatchData, - GatewayMessageDeleteDispatchData, - GatewayMessageReactionAddDispatchData, - GatewayMessageReactionRemoveAllDispatchData, - GatewayMessageReactionRemoveDispatchData, - GatewayMessageReactionRemoveEmojiDispatchData, - GatewayMessageUpdateDispatchData, + GatewayMessageCreateDispatchData, GatewayMessageDeleteBulkDispatchData, + GatewayMessageDeleteDispatchData, GatewayMessageReactionAddDispatchData, + GatewayMessageReactionRemoveAllDispatchData, GatewayMessageReactionRemoveDispatchData, + GatewayMessageReactionRemoveEmojiDispatchData, GatewayMessageUpdateDispatchData } from 'discord-api-types/v10'; import { proxyActivities } from '@temporalio/workflow'; @@ -22,7 +18,7 @@ async function guardMessage( channelId: string, authorId?: string, ): Promise { - if (!(await activities.isChannelSelected(guildId, channelId))) return false; + // if (!(await activities.isChannelSelected(guildId, channelId))) return false; if (authorId && (await activities.isUserIgnored(guildId, authorId))) return false; return true; diff --git a/src/workflows/discord/gateway/handlers/thread.handler.ts b/src/workflows/discord/gateway/handlers/thread.handler.ts new file mode 100644 index 0000000..b2b8859 --- /dev/null +++ b/src/workflows/discord/gateway/handlers/thread.handler.ts @@ -0,0 +1,27 @@ +import { + GatewayThreadCreateDispatchData, GatewayThreadDeleteDispatchData, + GatewayThreadUpdateDispatchData +} from 'discord-api-types/v10'; + +import { proxyActivities } from '@temporalio/workflow'; + +import type * as Activities from '../../../../activities'; + +const activitiesProxy = proxyActivities({ + startToCloseTimeout: '1 minute', + retry: { maximumAttempts: 5 }, +}); + +export class ThreadHandler { + static async create(data: GatewayThreadCreateDispatchData): Promise { + await activitiesProxy.createThread(data.guild_id!, data); + } + + static async update(data: GatewayThreadUpdateDispatchData): Promise { + await activitiesProxy.updateThread(data.guild_id!, { id: data.id }, data); + } + + static async delete(data: GatewayThreadDeleteDispatchData): Promise { + await activitiesProxy.deleteThread(data.guild_id!, data.id); + } +} diff --git a/src/workflows/discord/gateway/mappers/message.mapper.ts b/src/workflows/discord/gateway/mappers/message.mapper.ts index be58776..83090be 100644 --- a/src/workflows/discord/gateway/mappers/message.mapper.ts +++ b/src/workflows/discord/gateway/mappers/message.mapper.ts @@ -1,53 +1,15 @@ -// import { Message, Role, TextChannel, User } from 'discord.js'; -// import { GatewayMessageCreateDispatchData } from 'discord-api-types/v10'; - -// import { IRawInfo } from '@togethercrew.dev/db'; - -// export interface ThreadInfo { -// threadId: string | null; -// threadName: string | null; -// channelId: string; -// channelName: string | null; -// } - -// export function toIRawInfo(message: GatewayMessageCreateDispatchData): IRawInfo { -// return { -// type: message.type, -// author: message.author.id, -// content: message.content, -// createdDate: new Date(message.timestamp), -// role_mentions: message.mention_roles, -// user_mentions: message.mentions, -// replied_user: message.type === 19 ? message.referenced_message?.id : null, -// reactions: message.reactions, -// messageId: message.id, -// channelId: thread?.channelId ?? message.channelId, -// channelName: -// thread?.channelName ?? -// (message.channel instanceof TextChannel ? message.channel.name : null), -// threadId: thread?.threadId ?? null, -// threadName: thread?.threadName ?? null, -// isGeneratedByWebhook: Boolean(message.webhookId), -// }; -// } - import { - GatewayMessageCreateDispatchData, - GatewayMessageReactionAddDispatchData, - GatewayMessageReactionRemoveAllDispatchData, - GatewayMessageReactionRemoveDispatchData, - GatewayMessageReactionRemoveEmojiDispatchData, - GatewayMessageUpdateDispatchData, + GatewayMessageReactionAddDispatchData, GatewayMessageReactionRemoveAllDispatchData, + GatewayMessageReactionRemoveDispatchData, GatewayMessageReactionRemoveEmojiDispatchData, + GatewayMessageUpdateDispatchData } from 'discord-api-types/v10'; import { IRawInfo, IRawInfoUpdateBody } from '@togethercrew.dev/db'; -// Helper function to format reaction strings according to the schema const formatReaction = (userIds: string[], emoji: string): string => { return `${userIds.join(',')},${emoji}`; }; -// Helper function to find existing reaction by emoji const findReactionByEmoji = ( reactions: string[], emoji: string, @@ -58,19 +20,16 @@ const findReactionByEmoji = ( }); }; -// Helper function to get user IDs from a reaction string const getUserIdsFromReaction = (reaction: string): string[] => { const parts = reaction.split(','); - return parts.slice(0, -1); // All parts except the last one (emoji) + return parts.slice(0, -1); }; -// Helper function to get emoji from a reaction string const getEmojiFromReaction = (reaction: string): string => { const parts = reaction.split(','); - return parts[parts.length - 1]; // Last part is emoji + return parts[parts.length - 1]; }; -// Helper function to create a full IRawInfo from partial update and original function createUpdatedRawInfo( original: IRawInfo, update: Partial, @@ -81,10 +40,17 @@ function createUpdatedRawInfo( }; } -// Map message create event export function mapMessageCreate( - payload: GatewayMessageCreateDispatchData, + // payload: GatewayMessageCreateDispatchData, + payload: any, ): IRawInfo { + const isThreadMessage = + payload.channel_type === 10 || + payload.channel_type === 11 || + payload.channel_type === 12; + + console.log(isThreadMessage, payload.channel_type); + console.log('mama'); return { type: payload.type, author: payload.author.id, @@ -92,18 +58,17 @@ export function mapMessageCreate( createdDate: new Date(payload.timestamp), user_mentions: payload.mentions?.map((user) => user.id) || [], role_mentions: payload.mention_roles || [], - reactions: [], // New messages don't have reactions yet + reactions: [], replied_user: payload.referenced_message?.author?.id || null, messageId: payload.id, - channelId: payload.channel_id, - channelName: null, // This would need to be populated from channel data - threadId: null, // This would need to be determined from channel type + channelId: isThreadMessage ? null : payload.channel_id, + channelName: null, + threadId: isThreadMessage ? payload.channel_id : null, threadName: null, isGeneratedByWebhook: Boolean(payload.webhook_id), }; } -// Map message update event export function mapMessageUpdate( payload: GatewayMessageUpdateDispatchData, ): IRawInfoUpdateBody { @@ -124,7 +89,6 @@ export function mapMessageUpdate( return updateData; } -// Map reaction add export function mapReactionAdd( payload: GatewayMessageReactionAddDispatchData, existingRawInfo?: IRawInfo, @@ -136,13 +100,11 @@ export function mapReactionAdd( const existingReaction = findReactionByEmoji(existingReactions, emojiStr); if (existingReaction) { - // Emoji already exists, add user to existing reaction const userIds = getUserIdsFromReaction(existingReaction); if (!userIds.includes(payload.user_id)) { userIds.push(payload.user_id); const updatedReaction = formatReaction(userIds, emojiStr); - // Replace the old reaction with the updated one const updatedReactions = existingReactions.map((reaction) => reaction === existingReaction ? updatedReaction : reaction, ); @@ -151,10 +113,8 @@ export function mapReactionAdd( reactions: updatedReactions, }); } - // User already reacted with this emoji, no change needed return existingRawInfo; } else { - // New emoji, create new reaction element const newReaction = formatReaction([payload.user_id], emojiStr); const updatedReactions = [...existingReactions, newReaction]; @@ -184,7 +144,6 @@ export function mapReactionAdd( }; } -// Map reaction remove export function mapReactionRemove( payload: GatewayMessageReactionRemoveDispatchData, existingRawInfo: IRawInfo, @@ -200,7 +159,6 @@ export function mapReactionRemove( ); if (updatedUserIds.length === 0) { - // Remove the entire reaction element if no users left const updatedReactions = existingReactions.filter( (reaction) => reaction !== existingReaction, ); @@ -208,7 +166,6 @@ export function mapReactionRemove( reactions: updatedReactions, }); } else { - // Update the reaction with remaining users const updatedReaction = formatReaction(updatedUserIds, emojiStr); const updatedReactions = existingReactions.map((reaction) => reaction === existingReaction ? updatedReaction : reaction, @@ -219,27 +176,22 @@ export function mapReactionRemove( } } - // Reaction not found, return unchanged return existingRawInfo; } -// Map reaction remove all export function mapReactionRemoveAll( payload: GatewayMessageReactionRemoveAllDispatchData, existingRawInfo: IRawInfo, ): IRawInfo { - // Clear all reactions return createUpdatedRawInfo(existingRawInfo, { reactions: [] }); } -// Map reaction remove emoji export function mapReactionRemoveEmoji( payload: GatewayMessageReactionRemoveEmojiDispatchData, existingRawInfo: IRawInfo, ): IRawInfo { const emojiStr = payload.emoji.id || payload.emoji.name || 'unknown_emoji'; - // Remove all reactions with this emoji const updatedReactions = (existingRawInfo.reactions || []).filter( (reaction) => { return getEmojiFromReaction(reaction) !== emojiStr; @@ -249,8 +201,6 @@ export function mapReactionRemoveEmoji( return createUpdatedRawInfo(existingRawInfo, { reactions: updatedReactions }); } -// Delete operations don't need mappers, as we just use the message ID - export const MessageMappers = { create: mapMessageCreate, update: mapMessageUpdate,