Skip to content

16 implement workflow activities#19

Merged
Behzad-rabiei merged 3 commits intomainfrom
16-implement-workflow-activities
Jun 11, 2025
Merged

16 implement workflow activities#19
Behzad-rabiei merged 3 commits intomainfrom
16-implement-workflow-activities

Conversation

@Behzad-rabiei
Copy link
Member

@Behzad-rabiei Behzad-rabiei commented Jun 11, 2025

Summary by CodeRabbit

  • New Features

    • Added support for handling Discord thread events, including thread creation, update, and deletion.
    • Extended persistence to store and manage thread-related data.
  • Bug Fixes

    • Improved error handling for unsupported Discord gateway events to prevent workflow failures.
  • Refactor

    • Unified and streamlined error handling and upsert logic for Discord gateway persistence.
    • Simplified and cleaned up message mapping logic, including improved handling of thread messages.
  • Chores

    • Updated a dependency version.
    • Enhanced .gitignore to exclude additional files and directories.
  • Style

    • Reformatted import statements for improved readability.

@coderabbitai
Copy link

coderabbitai bot commented Jun 11, 2025

Walkthrough

This update introduces comprehensive support for Discord thread events by adding new handler logic, persistence functions, and mapping improvements for thread entities. It also refactors database error handling for gateway activities, updates a dependency, modifies import formatting, and adjusts event handling to log unsupported events instead of throwing errors.

Changes

File(s) Change Summary
.gitignore Added .cursor patterns to ignore related files/directories.
package.json Updated @togethercrew.dev/db dependency from ^3.10.0 to ^3.13.2.
src/activities/discord/gateway/mapping.activity.ts Reformatted import statements for conciseness; no logic changes.
src/activities/discord/gateway/persistence.activity.ts Refactored error handling with helper functions; added thread persistence functions; unified upsert logic.
src/workflows/discord/gateway/handlers/index.ts Added thread event handlers for create, update, and delete events.
src/workflows/discord/gateway/handlers/message.handler.ts Reformatted imports; commented out channel selection guard in guardMessage.
src/workflows/discord/gateway/handlers/thread.handler.ts Introduced new ThreadHandler class with static methods for thread event handling.
src/workflows/discord/gateway/index.ts Changed unsupported event handling to log a warning instead of throwing an error.
src/workflows/discord/gateway/mappers/message.mapper.ts Removed commented code; consolidated imports; added thread detection logic in message mapping; loosened typing.

Sequence Diagram(s)

sequenceDiagram
    participant DiscordGateway as Discord Gateway
    participant Workflow as Gateway Workflow
    participant Handler as ThreadHandler
    participant Activity as Persistence Activity

    DiscordGateway->>Workflow: ThreadCreate/Update/Delete Event
    Workflow->>Handler: Call create/update/delete(data)
    Handler->>Activity: createThread/updateThread/deleteThread(guildId, data)
    Activity-->>Handler: (Persistence completed)
    Handler-->>Workflow: (Handler completed)
Loading

Possibly related PRs

  • 16 implement workflow activities #18: Introduced the initial persistence activity module for Discord gateway events, which is directly extended and refactored in this PR, especially regarding thread support and error handling.

Poem

In the warren of code, new threads now appear,
With handlers and helpers, their purpose is clear.
Errors now logged, not thrown in despair,
Mapping and imports are tidied with care.
The gateway now listens to Discord’s thread song—
A hop, a skip, and the workflow moves on!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Behzad-rabiei Behzad-rabiei merged commit 287875e into main Jun 11, 2025
4 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
src/activities/discord/gateway/mapping.activity.ts (1)

2-10: Long import list hurts readability

The combined import now exceeds 120 chars and is hard to diff.
Consider grouping related types or using import type { … } with line-breaks to respect the project’s prettier / eslint max-length rule.

Also applies to: 17-17

src/workflows/discord/gateway/index.ts (1)

12-13: Log should include event name for observability

The warning currently lacks context. Add the unsupported event type so on-call can triage quickly.

-    log.warn('Unsupported gateway event');
+    log.warn(`Unsupported gateway event: ${event as string}`);
src/workflows/discord/gateway/handlers/thread.handler.ts (1)

15-27: Consider refactoring to standalone functions instead of a static-only class.

Static-only classes are an anti-pattern in JavaScript/TypeScript. Consider refactoring to standalone functions for better tree-shaking and cleaner code.

-export class ThreadHandler {
-  static async create(data: GatewayThreadCreateDispatchData): Promise<void> {
-    await activitiesProxy.createThread(data.guild_id!, data);
-  }
-
-  static async update(data: GatewayThreadUpdateDispatchData): Promise<void> {
-    await activitiesProxy.updateThread(data.guild_id!, { id: data.id }, data);
-  }
-
-  static async delete(data: GatewayThreadDeleteDispatchData): Promise<void> {
-    await activitiesProxy.deleteThread(data.guild_id!, data.id);
-  }
-}
+export async function createThread(data: GatewayThreadCreateDispatchData): Promise<void> {
+  await activitiesProxy.createThread(data.guild_id!, data);
+}
+
+export async function updateThread(data: GatewayThreadUpdateDispatchData): Promise<void> {
+  await activitiesProxy.updateThread(data.guild_id!, { id: data.id }, data);
+}
+
+export async function deleteThread(data: GatewayThreadDeleteDispatchData): Promise<void> {
+  await activitiesProxy.deleteThread(data.guild_id!, data.id);
+}

Then update the imports in src/workflows/discord/gateway/handlers/index.ts:

-import { ThreadHandler } from './thread.handler';
+import * as ThreadHandler from './thread.handler';
🧰 Tools
🪛 Biome (1.9.4)

[error] 15-27: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/activities/discord/gateway/persistence.activity.ts (1)

15-30: Replace void with undefined in the return type.

Using void in a union type can be confusing. Use undefined for better clarity.

 async function handleDuplicateKeyError<T>(
   operation: () => Promise<T>,
   entityName: string,
   entityId: string,
   guildId: Snowflake,
-): Promise<T | void> {
+): Promise<T | undefined> {
🧰 Tools
🪛 Biome (1.9.4)

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbb6605 and 07254df.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • .gitignore (1 hunks)
  • package.json (1 hunks)
  • src/activities/discord/gateway/mapping.activity.ts (1 hunks)
  • src/activities/discord/gateway/persistence.activity.ts (3 hunks)
  • src/workflows/discord/gateway/handlers/index.ts (2 hunks)
  • src/workflows/discord/gateway/handlers/message.handler.ts (2 hunks)
  • src/workflows/discord/gateway/handlers/thread.handler.ts (1 hunks)
  • src/workflows/discord/gateway/index.ts (1 hunks)
  • src/workflows/discord/gateway/mappers/message.mapper.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/workflows/discord/gateway/index.ts (1)
src/workflows/discord/gateway/handlers/index.ts (1)
  • eventHandlers (9-39)
src/workflows/discord/gateway/handlers/index.ts (1)
src/workflows/discord/gateway/handlers/thread.handler.ts (1)
  • ThreadHandler (15-27)
src/activities/discord/gateway/persistence.activity.ts (1)
src/config/logger.config.ts (1)
  • logger (4-10)
🪛 Biome (1.9.4)
src/workflows/discord/gateway/handlers/thread.handler.ts

[error] 15-27: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

src/activities/discord/gateway/persistence.activity.ts

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-push
🔇 Additional comments (8)
.gitignore (1)

6-8: Ignore rule additions look good

Adding .cursor/* and .cursor is harmless and shields dev artefacts from VCS. No concerns.

package.json (1)

40-40: Verify @togethercrew.dev/db upgrade before merge

Jumping three minor versions (3.10.x3.13.x) may introduce breaking schema or API tweaks.
Please:

  1. run the unit / integration suite against the new version;
  2. regenerate pnpm-lock.yaml / lock-file and commit it;
  3. skim the changelog for migration notes.
src/workflows/discord/gateway/handlers/message.handler.ts (1)

2-5: Import re-formatting LGTM

Pure style change – no behavioural impact.

src/workflows/discord/gateway/index.ts (1)

3-4: 👍 Using Temporal log is appropriate

Switching from thrown errors to workflow-safe logging is sensible.

src/workflows/discord/gateway/handlers/index.ts (1)

7-7: LGTM! Thread handlers properly integrated.

The thread event handlers follow the established pattern and are correctly mapped to their respective handler methods.

Also applies to: 33-38

src/workflows/discord/gateway/mappers/message.mapper.ts (1)

47-50: Thread detection logic is correctly implemented.

The implementation properly identifies Discord thread channel types (10: news thread, 11: public thread, 12: private thread) and correctly assigns the channel_id to either channelId or threadId fields.

Also applies to: 64-67

src/activities/discord/gateway/persistence.activity.ts (2)

15-59: Excellent refactoring to reduce code duplication.

The introduction of handleDuplicateKeyError and performUpsert helper functions effectively centralizes error handling and upsert logic, making the codebase more maintainable and consistent.

🧰 Tools
🪛 Biome (1.9.4)

[error] 20-20: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


263-281: Thread deletion implementation looks good.

The use of deleteMany is appropriate for thread deletion, and the error handling with proper logging follows the established pattern.

authorId?: string,
): Promise<boolean> {
if (!(await activities.isChannelSelected(guildId, channelId))) return false;
// if (!(await activities.isChannelSelected(guildId, channelId))) return false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Channel-selection guard silently disabled

Commenting out the isChannelSelected check means all messages, including those from un-whitelisted channels, will now be processed. This may:

  • flood the persistence layer,
  • undermine privacy filters,
  • inflate cost of downstream analyses.

Unless this is a deliberate feature toggle, please reinstate the guard or gate it behind a config flag.

-  // if (!(await activities.isChannelSelected(guildId, channelId))) return false;
+  if (!(await activities.isChannelSelected(guildId, channelId))) return false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// if (!(await activities.isChannelSelected(guildId, channelId))) return false;
if (!(await activities.isChannelSelected(guildId, channelId))) return false;
🤖 Prompt for AI Agents
In src/workflows/discord/gateway/handlers/message.handler.ts at line 21, the
channel-selection guard check using isChannelSelected is commented out, causing
all messages to be processed regardless of channel whitelist. To fix this,
reinstate the isChannelSelected check to ensure only messages from selected
channels are processed, or alternatively, wrap this check in a configuration
flag to enable or disable it explicitly. This will prevent processing unwanted
messages and avoid flooding the persistence layer or violating privacy
constraints.


export class ThreadHandler {
static async create(data: GatewayThreadCreateDispatchData): Promise<void> {
await activitiesProxy.createThread(data.guild_id!, data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Validate guild_id instead of using non-null assertions.

The non-null assertions on guild_id could cause runtime errors. Consider adding validation to ensure safer code execution.

 export class ThreadHandler {
   static async create(data: GatewayThreadCreateDispatchData): Promise<void> {
+    if (!data.guild_id) {
+      throw new Error('Thread create event missing guild_id');
+    }
-    await activitiesProxy.createThread(data.guild_id!, data);
+    await activitiesProxy.createThread(data.guild_id, data);
   }

   static async update(data: GatewayThreadUpdateDispatchData): Promise<void> {
+    if (!data.guild_id) {
+      throw new Error('Thread update event missing guild_id');
+    }
-    await activitiesProxy.updateThread(data.guild_id!, { id: data.id }, data);
+    await activitiesProxy.updateThread(data.guild_id, { id: data.id }, data);
   }

   static async delete(data: GatewayThreadDeleteDispatchData): Promise<void> {
+    if (!data.guild_id) {
+      throw new Error('Thread delete event missing guild_id');
+    }
-    await activitiesProxy.deleteThread(data.guild_id!, data.id);
+    await activitiesProxy.deleteThread(data.guild_id, data.id);
   }
 }

Also applies to: 21-21, 25-25

🤖 Prompt for AI Agents
In src/workflows/discord/gateway/handlers/thread.handler.ts at lines 17, 21, and
25, the code uses non-null assertions on guild_id which can cause runtime errors
if guild_id is undefined or null. Replace these assertions by adding explicit
validation checks to confirm guild_id is defined before calling createThread or
other functions using it. Handle the case where guild_id is missing gracefully,
such as by logging an error or returning early, to ensure safer code execution.

Comment on lines +52 to +53
console.log(isThreadMessage, payload.channel_type);
console.log('mama');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove debug console.log statements.

Debug statements should not be committed to production code.

-  console.log(isThreadMessage, payload.channel_type);
-  console.log('mama');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(isThreadMessage, payload.channel_type);
console.log('mama');
🤖 Prompt for AI Agents
In src/workflows/discord/gateway/mappers/message.mapper.ts at lines 52 to 53,
remove the debug console.log statements that output isThreadMessage,
payload.channel_type, and the string 'mama' to clean up the production code and
avoid unnecessary logging.

Comment on lines 43 to 46
export function mapMessageCreate(
payload: GatewayMessageCreateDispatchData,
// payload: GatewayMessageCreateDispatchData,
payload: any,
): IRawInfo {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Restore type safety by using the proper Discord type.

Changing the parameter type to any removes valuable type safety. This could lead to runtime errors and makes the code harder to maintain.

 export function mapMessageCreate(
-  // payload: GatewayMessageCreateDispatchData,
-  payload: any,
+  payload: GatewayMessageCreateDispatchData,
 ): IRawInfo {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function mapMessageCreate(
payload: GatewayMessageCreateDispatchData,
// payload: GatewayMessageCreateDispatchData,
payload: any,
): IRawInfo {
export function mapMessageCreate(
payload: GatewayMessageCreateDispatchData,
): IRawInfo {
🤖 Prompt for AI Agents
In src/workflows/discord/gateway/mappers/message.mapper.ts around lines 43 to
46, the function parameter type was changed to 'any', removing type safety.
Restore the original Discord-specific type (likely
GatewayMessageCreateDispatchData) for the payload parameter to ensure proper
type checking and maintainability. Import the correct type if necessary and
replace 'any' with it.

Comment on lines +232 to +253
export async function createThread(
guildId: Snowflake,
data: IThread,
): Promise<void> {
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;
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor createThread to use the handleDuplicateKeyError helper.

For consistency with other create functions, use the handleDuplicateKeyError helper function.

 export async function createThread(
   guildId: Snowflake,
   data: IThread,
 ): Promise<void> {
-  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;
-    }
-  }
+  const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId);
+  const repository = makeThreadRepository(dbConnection);
+
+  await handleDuplicateKeyError(
+    () => repository.create(data),
+    'Thread',
+    data.id,
+    guildId,
+  );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function createThread(
guildId: Snowflake,
data: IThread,
): Promise<void> {
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 createThread(
guildId: Snowflake,
data: IThread,
): Promise<void> {
const dbConnection = await DatabaseManager.getInstance().getGuildDb(guildId);
const repository = makeThreadRepository(dbConnection);
await handleDuplicateKeyError(
() => repository.create(data),
'Thread',
data.id,
guildId,
);
}
🤖 Prompt for AI Agents
In src/activities/discord/gateway/persistence.activity.ts around lines 232 to
253, refactor the createThread function to replace the manual duplicate key
error check with the handleDuplicateKeyError helper function. Remove the if
(err.code === 11000) block and instead call handleDuplicateKeyError inside the
catch block, passing the error, guildId, data.id, and appropriate context to
maintain consistent error handling across create functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant