-
-
Notifications
You must be signed in to change notification settings - Fork 1
🚀 release: v1.0.0-rc1 #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…iles Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
…e documentation Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
…cript files Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: Copilot <[email protected]>
…-b04c-ce1160f405df Convert mixed CommonJS require statements to consistent ES6 import syntax
…o export default Co-authored-by: warengonzaga <[email protected]>
…-a94c-bc72daeb9154 Convert Discord bot project to TypeScript for enhanced maintainability
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This release candidate introduces a comprehensive TypeScript migration and architectural upgrade for the Unthread Discord Bot, transforming it from JavaScript to a fully typed TypeScript codebase with improved maintainability and developer experience.
- Complete migration from JavaScript to TypeScript with comprehensive type definitions
- Enhanced build system with Yarn PnP and TypeScript SDK integration
- Modular utility system with retry mechanisms, message processing, and caching functionality
Reviewed Changes
Copilot reviewed 30 out of 41 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
package.json | Updated build scripts, TypeScript dependencies, and development tooling |
src/index.ts | Main TypeScript entry point with improved type safety and module loading |
src/types/*.ts | Comprehensive type definitions for Discord, Unthread API, and utilities |
src/utils/*.ts | Modular utility functions for retry logic, message processing, and caching |
src/services/webhook.ts | TypeScript webhook service with improved type safety |
src/events/ready.ts | TypeScript event handler with enhanced validation |
src/commands/support/support.ts | TypeScript command implementation with proper typing |
src/deploy_commands.ts | TypeScript command deployment script |
.yarn/sdks/ | Yarn SDK integration files for TypeScript and ESLint |
.eslintrc.json | Enhanced ESLint configuration for TypeScript support |
README.md | Updated documentation reflecting TypeScript architecture |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughInitiates a comprehensive migration from JavaScript to TypeScript, adds a new TypeScript build and lint pipeline, restructures the Discord bot runtime and event/command handlers, introduces Redis-backed persistence, replaces the logging system, and updates documentation, environment variables, and VS Code recommendations. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
✨ Finishing Touches🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 20
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
.eslintrc.json (1)
17-60
: Avoid rule duplication and prefer TS-aware variants.To prevent duplicate or incomplete linting:
- Disable base no-unused-vars and (optionally) no-empty-function; enable TS variants.
"rules": { + "no-unused-vars": "off", + "no-empty-function": "off", "arrow-spacing": ["warn", { "before": true, "after": true }], ... - "@typescript-eslint/no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], + "@typescript-eslint/no-shadow": ["error", { "allow": ["err", "resolve", "reject"] }], ... - "yoda": "error", + "yoda": "error", + "@typescript-eslint/no-empty-function": "error", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],package.json (1)
35-41
: Remove the npm “crypto” package; rely on Node’s built-in crypto
"crypto": "^1.0.1"
pulls a third‑party package that can shadow Node’s nativecrypto
module. This is unnecessary on Node 18+ and can create security and behavioral surprises."dependencies": { "@keyv/redis": "^4.4.0", "cacheable": "^1.10.0", - "crypto": "^1.0.1", "discord.js": "^14.20.0", "dotenv": "^16.5.0", "express": "^4.21.2",
If you need the native module, simply
import crypto from 'node:crypto'
orimport { randomUUID } from 'node:crypto'
.
🧹 Nitpick comments (51)
.vscode/extensions.json (1)
3-11
: Action: Remove redundant ZipFS extensionAt your service: I’ve confirmed there’s no Yarn Plug’n’Play setup in this repository (no
.yarnrc.yml
withnodeLinker: pnp
and no.pnp.*
files). Thearcanis.vscode-zipfs
extension is therefore unnecessary and may introduce confusion in file navigation.• Keep the newly added
dbaeumer.vscode-eslint
—it’s a strong choice for maintaining code quality.
• Optionally consider addingesbenp.prettier-vscode
if you’d like to standardize formatting via Prettier.Suggested diff for
.vscode/extensions.json
:.vscode/extensions.json - "arcanis.vscode-zipfs",
.gitignore (1)
51-53
: Duplicate dist ignore; consolidate for clarity.You’re adding dist/ here while another dist entry appears at Line 95. It’s harmless but redundant and can cause head-scratching later. Keep a single authoritative ignore (prefer the scoped “dist/” near the TypeScript section) and remove the other.
tsconfig.json (2)
2-18
: Tighten Node typing and speed up compiles.Consider:
- Declaring Node types explicitly to avoid ambient resolution variance.
- Enabling incremental builds for faster local compiles (you already ignore *.tsbuildinfo).
Proposed patch:
"compilerOptions": { "target": "ES2021", "module": "CommonJS", "lib": ["ES2021"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "declaration": false, "removeComments": false, "noEmitOnError": true, "sourceMap": true, - "moduleResolution": "node" + "moduleResolution": "node", + "types": ["node"], + "incremental": true },
3-7
: Alignment with Node ≥18.16.0 – bump TS target to ES2022Analysis:
I’ve confirmed your package.json specifiesengines.node: ">=18.16.0"
. Node 18’s V8 fully supports ES2022 features, so you can safely target ES2022 for smaller, faster output and take advantage of newer built-ins. If you later upgrade to Node 20+, consider ES2023.Recommended update in tsconfig.json (lines 3–7):
"target": "ES2021", - "module": "CommonJS", + "target": "ES2022", "module": "CommonJS", - "lib": ["ES2021"], + "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src",– JARVIS 🤖
src/utils/decodeHtmlEntities.ts (2)
31-36
: Broaden entity coverage or document scope.You currently handle amp, gt, lt only. If inputs may contain quotes/apos or numeric entities (", '), consider expanding or explicitly documenting the limitation. A lightweight alternative is the entities or he package for comprehensive decoding.
Happy to wire in a zero-dependency fallback that adds &#\d+; and &#x[a-f0-9]+; support if you prefer to avoid new packages.
38-45
: Export shape simplification (optional).Given the single utility, consider exporting the function and defaulting the object to reduce cognitive overhead:
-const htmlEntityDecoder = { - decodeHtmlEntities, -}; - -export default htmlEntityDecoder; +export default { decodeHtmlEntities };.eslintrc.json (2)
12-16
: Stabilize ts-parser project resolution.Without tsconfigRootDir, running ESLint from different CWDs can yield “Parsing error: Cannot read file ... tsconfig.json”. Anchor to the config directory.
"parserOptions": { "ecmaVersion": 2021, "sourceType": "module", - "project": "./tsconfig.json" + "project": "./tsconfig.json", + "tsconfigRootDir": __dirname },
1-8
: Prevent linting build artifacts.ESLint doesn’t ignore dist by default. Add an ignore pattern to keep CI fast and noise-free.
{ + "ignorePatterns": ["dist/**"], "extends": [ "eslint:recommended", "@typescript-eslint/recommended" ],
README.md (2)
195-203
: Pin the Yarn version via Corepack for deterministic installsYou’ve pinned
"packageManager": "[email protected]"
in package.json (excellent), but the docs only runcorepack enable
. To actually activate the pinned Yarn on a machine, it’s safer to runcorepack use [email protected]
(orcorepack prepare [email protected] --activate
) beforeyarn install
. This prevents subtle PnP/SDK mismatches during RC cutovers.Apply this tweak to the snippet:
- corepack enable - yarn install + corepack enable + corepack use [email protected] + yarn install
88-118
: Small documentation polish: reduce ambiguity and anchor reliability
- In “Build System”, consider clarifying that “Deploy commands only” is optional if your start script doesn’t auto-deploy. Once you update the start script per package.json suggestion, this section will be perfectly aligned.
- The internal link “one-click deployment” may be brittle with emoji anchors on GitHub. Prefer a literal anchor or an HTML anchor for reliability.
Example optional anchor stabilization:
- Ready to transform your Discord support experience? Get started in minutes with our [one-click deployment](#-easy-deployment)! + Ready to transform your Discord support experience? Get started in minutes with our <a href="#-easy-deployment">one-click deployment</a>!Also applies to: 228-229
package.json (1)
8-8
: Portability of the build commandCalling the SDK binary via a repo path works, but it ties scripts to the SDK location. With Yarn 4 + PnP and
typescript
in devDependencies,yarn tsc -p tsconfig.json
is portable and uses the pinned TypeScript automatically.- "build": ".yarn/sdks/typescript/bin/tsc", + "build": "tsc -p tsconfig.json",src/types/utils.ts (3)
21-27
: Loosen headers/body typing for HTTP versatilitySome HTTP clients supply multi‑value headers;
body
is not always a string. Consider slightly broader types.export interface ApiRequestConfig { method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; url: string; - headers?: Record<string, string>; - body?: any; + headers?: Record<string, string | string[]>; + body?: unknown; timeout?: number; }Optionally include
'HEAD' | 'OPTIONS'
if you anticipate those calls.
62-68
: Type attachment buffer preciselyNode types are already in devDependencies; you can safely type this as
Buffer
and remove the TODO comment.export interface FileAttachment { filename: string; url: string; size: number; contentType: string; - data?: any; // Buffer type will be available once Node.js types are properly loaded + data?: Buffer; }
91-101
: Optional: freeze pagination shape for stabilityIf you don’t intend mutation, you can mark nested pagination properties as
readonly
.export interface PaginatedResponse<T> { - data: T[]; - pagination: { - page: number; - limit: number; - total: number; - totalPages: number; - hasNext: boolean; - hasPrev: boolean; - }; + readonly data: readonly T[]; + readonly pagination: { + readonly page: number; + readonly limit: number; + readonly total: number; + readonly totalPages: number; + readonly hasNext: boolean; + readonly hasPrev: boolean; + }; }src/types/discord.ts (3)
33-44
: Separate raw env types from runtime configThese are raw strings from
process.env
. Downstream usage (booleans, numbers, arrays) benefits from a refined runtime config type. Consider introducing aRuntimeConfig
with parsed/validated types.Example:
export interface RuntimeConfig { discordToken: string; clientId: string; guildId: string; unthreadApiKey: string; triageChannelId: string; emailInboxId: string; webhookSecret: string; redisUrl?: string; forumChannelIds?: string[]; // parsed debug: boolean; // parsed port: number; // parsed }Pair with a small parser/validator to map
BotConfig
->RuntimeConfig
.
23-27
: Timestamp fields: clarify the expected formatIf these are ISO strings (likely for JSON persistence), note it explicitly in the type docs or use a branded string type to reduce confusion with human‑readable formats.
export interface ThreadTicketMapping { discordThreadId: string; unthreadTicketId: string; - createdAt: string; - lastSyncAt?: string; + createdAt: string; // ISO 8601 UTC + lastSyncAt?: string; // ISO 8601 UTC }
68-72
: Tighten CustomerOperations typing (optional)If you’re already depending on discord.js types at call sites, annotate
user: User
and concrete return shapes. Otherwise, a generic return type parameter can help.Option A (discord.js aware):
-import type { User } from 'discord.js'; -// ... -getOrCreateCustomer: (user: any, email?: string) => Promise<any>; +import type { User } from 'discord.js'; +// ... +getOrCreateCustomer: (user: User, email?: string) => Promise<unknown>;Option B (library-agnostic):
export interface CustomerOperations<TUser = unknown, TCustomer = unknown> { getOrCreateCustomer: (user: TUser, email?: string) => Promise<TCustomer>; getCustomerByDiscordId: (discordId: string) => Promise<TCustomer | null>; updateCustomer: (customerId: string, updates: Partial<TCustomer>) => Promise<TCustomer>; }src/types/unthread.ts (6)
13-20
: Promote timestamp clarity with a dedicated ISO8601 string aliasCurrent timestamps are plain strings, which makes accidental non-ISO values easy to slip in. Introduce an ISO8601String alias and use it for created_at/updated_at here.
+/** + * RFC 3339/ISO 8601 timestamp string, e.g., "2025-08-21T14:26:00Z" + */ +export type ISO8601String = string; + export interface UnthreadCustomer { id: string; name: string; email: string; discord_user_id?: string; - created_at: string; - updated_at: string; + created_at: ISO8601String; + updated_at: ISO8601String; }
41-44
: Apply ISO8601 alias consistently on ticket timestampsExtend the alias to tickets for consistency and self-documentation. Including last_message_at is especially helpful for downstream consumers.
export interface UnthreadTicket { id: string; title: string; status: TicketStatus; priority: TicketPriority; customer_id: string; channel_id: string; - created_at: string; - updated_at: string; - last_message_at?: string; + created_at: ISO8601String; + updated_at: ISO8601String; + last_message_at?: ISO8601String; }
73-78
: Confirm whether CreateMessageRequest requires ticket contextMany APIs require a ticket_id to post a message (unless the route encodes it). If the endpoint uses a path like POST /tickets/:ticket_id/messages, we’re fine; otherwise, add ticket_id here.
If the API expects it in the body, I can patch in the field and cascade the type changes wherever this request is constructed. Shall I proceed?
111-118
: Generic webhook payload to prevent unsafe narrowingDefine WebhookPayload as generic so event/data pairs are enforced by the type system (and upgrade timestamp typing while we’re here).
-export interface WebhookPayload { - event: WebhookEventType; - timestamp: string; - data: UnthreadTicket | UnthreadMessage; -} +export interface WebhookPayload<T = UnthreadTicket | UnthreadMessage, E extends WebhookEventType = WebhookEventType> { + event: E; + timestamp: ISO8601String; + data: T; +}
123-134
: Tighten ticket/message webhook specializations via genericsThis removes the need for casts in consumers and prevents accidental event/data mismatches at compile time.
-export interface TicketWebhookPayload extends WebhookPayload { - event: 'ticket.created' | 'ticket.updated' | 'ticket.solved' | 'ticket.closed'; - data: UnthreadTicket; -} +export interface TicketWebhookPayload + extends WebhookPayload<UnthreadTicket, 'ticket.created' | 'ticket.updated' | 'ticket.solved' | 'ticket.closed'> {} -export interface MessageWebhookPayload extends WebhookPayload { - event: 'message.created' | 'message.updated'; - data: UnthreadMessage; -} +export interface MessageWebhookPayload + extends WebhookPayload<UnthreadMessage, 'message.created' | 'message.updated'> {}
136-144
: Export the discriminated union for ergonomic consumptionA union alias simplifies import sites that accept “any Unthread webhook.”
export interface UnthreadApiResponse<T> { success: boolean; data?: T; error?: string; message?: string; } + +export type UnthreadWebhook = TicketWebhookPayload | MessageWebhookPayload;src/types/global.ts (2)
15-19
: Strengthen channel fetch typing to avoid any-escapeReturning Promise throws away helpful Discord.js types. Prefer GuildBasedChannel | null (or Channel | null if you truly expect DMs).
-import { Client } from 'discord.js'; +import { Client, GuildBasedChannel } from 'discord.js'; ... interface GlobalDiscordClient { channels: { - fetch: (channelId: string) => Promise<any>; + fetch: (channelId: string) => Promise<GuildBasedChannel | null>; }; }
25-25
: Global is pragmatic; consider a typed accessor laterThe global variable is convenient here. As the bot grows or shards, consider a small accessor module (getDiscordClient/setDiscordClient) to avoid accidental undefined access and simplify testing.
src/utils/logger.ts (2)
33-44
: Timestamp helper is unused—either wire it in or drop itLet’s put it to work so logs are readily traceable. Adds minimal noise, maximal value.
function debug(...args: any[]): void { if (debugMode) { + const ts = getTimestamp(); if (args.length > 0) { if (typeof args[0] === 'string') { - args[0] = `[DEBUG] ${args[0]}`; + args[0] = `[DEBUG ${ts}] ${args[0]}`; } else { - args.unshift('[DEBUG]'); + args.unshift(`[DEBUG ${ts}]`); } } console.log(...args); } } function info(...args: any[]): void { // Always show info-level logs regardless of debug mode + const ts = getTimestamp(); if (args.length > 0) { if (typeof args[0] === 'string') { - args[0] = `[INFO] ${args[0]}`; + args[0] = `[INFO ${ts}] ${args[0]}`; } else { - args.unshift('[INFO]'); + args.unshift(`[INFO ${ts}]`); } } console.log(...args); } function warn(...args: any[]): void { + const ts = getTimestamp(); if (args.length > 0) { if (typeof args[0] === 'string') { - args[0] = `[WARN] ${args[0]}`; + args[0] = `[WARN ${ts}] ${args[0]}`; } else { - args.unshift('[WARN]'); + args.unshift(`[WARN ${ts}]`); } } console.warn(...args); // Uses console.warn for proper error stream routing } function error(...args: any[]): void { + const ts = getTimestamp(); if (args.length > 0) { if (typeof args[0] === 'string') { - args[0] = `[ERROR] ${args[0]}`; + args[0] = `[ERROR ${ts}] ${args[0]}`; } else { - args.unshift('[ERROR]'); + args.unshift(`[ERROR ${ts}]`); } } console.error(...args); // Uses console.error for proper error stream routing }
23-26
: Optional: promote LOG_LEVEL for finer controlIf you foresee info/warn noise in some environments, a LOG_LEVEL (debug|info|warn|error) can be helpful. I can wire a tiny level gate around each writer without altering callers.
src/commands/support/support.ts (3)
57-65
: Thread guard: add optional chaining on isThread for stricter safetyOn some channel unions the method may be undefined at compile time. This tiny tweak avoids type friction and remains functionally identical.
- if (interaction.channel?.isThread()) { + if (interaction.channel?.isThread?.()) {
76-97
: Report precisely which permissions are missingComputing the missing set improves UX and reduces guesswork for admins.
// Check if bot has necessary permissions to create threads in this channel const botMember = interaction.guild?.members.me as GuildMember; const requiredPermissions = [ PermissionFlagsBits.ManageThreads, PermissionFlagsBits.CreatePrivateThreads, PermissionFlagsBits.SendMessages, PermissionFlagsBits.SendMessagesInThreads, PermissionFlagsBits.ViewChannel, ]; - if (!interaction.guild || !interaction.channel || !botMember?.permissionsIn(interaction.channel as TextChannel).has(requiredPermissions)) { + if (!interaction.guild || !interaction.channel || !botMember) { + await interaction.reply({ + content: '❌ **Cannot determine permissions in this channel.**', + ephemeral: true, + }); + return; + } + + const perms = botMember.permissionsIn(interaction.channel); + const labelByBit: Record<bigint, string> = { + [PermissionFlagsBits.ManageThreads]: 'Manage Threads', + [PermissionFlagsBits.CreatePrivateThreads]: 'Create Private Threads', + [PermissionFlagsBits.SendMessages]: 'Send Messages', + [PermissionFlagsBits.SendMessagesInThreads]: 'Send Messages in Threads', + [PermissionFlagsBits.ViewChannel]: 'View Channel', + }; + const missing = requiredPermissions.filter(p => !perms.has(p)); + if (missing.length > 0) { await interaction.reply({ - content: `❌ **Cannot create support tickets here** - -Missing permissions: **Manage Threads**, **Create Private Threads**, **Send Messages**, **Send Messages in Threads**, **View Channel** - -Ask an admin to grant these permissions or use \`/support\` in an authorized channel.`, + content: `❌ **Cannot create support tickets here**\n\nMissing permissions: ${missing.map(p => `**${labelByBit[p as unknown as bigint]}**`).join(', ')}\n\nAsk an admin to grant these permissions or use \`/support\` in an authorized channel.`, ephemeral: true, }); return; }
103-124
: Constrain input lengths to keep tickets tidySet sensible max lengths and a tiny min for the title; helps both UX and downstream storage.
// Add input fields const titleInput = new TextInputBuilder() .setCustomId('titleInput') .setLabel('Ticket Title') .setPlaceholder('Title of your issue...') .setStyle(TextInputStyle.Short) - .setRequired(true); + .setRequired(true) + .setMinLength(5) + .setMaxLength(100); const issueInput = new TextInputBuilder() .setCustomId('issueInput') .setLabel('Summary') .setPlaceholder('Please describe your issue...') .setStyle(TextInputStyle.Paragraph) - .setRequired(true); + .setRequired(true) + .setMaxLength(2000); const emailInput = new TextInputBuilder() .setCustomId('emailInput') .setLabel('Contact Email (Optional)') .setPlaceholder('Your email address or leave blank...') .setStyle(TextInputStyle.Short) - .setRequired(false); + .setRequired(false) + .setMaxLength(254);src/events/ready.ts (2)
56-71
: Also surface which channel IDs are invalid, not just the countSmall win for operators: name the culprits so env hygiene is effortless.
const validForumChannels = await getValidatedForumChannelIds(); if (process.env.FORUM_CHANNEL_IDS) { - const allChannelIds = process.env.FORUM_CHANNEL_IDS.split(',').map(id => id.trim()).filter(id => id); - const invalidCount = allChannelIds.length - validForumChannels.length; + const allChannelIds = process.env.FORUM_CHANNEL_IDS.split(',').map(id => id.trim()).filter(id => id); + const validSet = new Set(validForumChannels); + const invalidIds = allChannelIds.filter(id => !validSet.has(id)); + const invalidCount = invalidIds.length; if (invalidCount > 0) { - logger.warn(`⚠️ ${invalidCount} channel(s) in FORUM_CHANNEL_IDS are not forum channels and will be ignored`); + logger.warn(`⚠️ ${invalidCount} channel(s) in FORUM_CHANNEL_IDS are not forum channels and will be ignored: ${invalidIds.join(', ')}`); } if (validForumChannels.length > 0) { logger.info(`✅ Monitoring ${validForumChannels.length} forum channel(s) for ticket creation`); } }
23-26
: JSON import style: default import recommendationSir, I have confirmed that your
tsconfig.json
includes"resolveJsonModule": true
and"esModuleInterop": true
, enabling robust default imports from JSON. For a cleaner and more idiomatic TypeScript experience, you may optionally update the import.• File:
src/events/ready.ts
(lines 23–26)-import * as packageJSON from '../../package.json'; +import packageJSON from '../../package.json';This small refactor promotes consistency and aligns with modern TypeScript module semantics.
src/index.ts (2)
116-129
: Include Thread partials to match prior JS behavior and ensure thread events resolve.The previous JS version loaded
Partials.Thread
as well. If you handle thread-based events/messages, add it back to prevent partials-related misses.partials: [ Partials.Channel, Partials.Message, Partials.Reaction, Partials.ThreadMember, + Partials.Thread, ],
84-85
: Avoid logging raw webhook payloads; potential PII leakage.Dumping the entire raw body into logs at debug can expose user data and secrets. Log metadata or truncate.
-logger.debug('Webhook received:', webhookReq.rawBody); +logger.debug(`Webhook received: size=${Buffer.byteLength(webhookReq.rawBody, 'utf8')}B`);If you want payload visibility in local dev only, guard with an env flag.
src/utils/retry.ts (5)
10-11
: Remove unused import to keep lints and tree-shaking clean.
RetryConfig
is imported but unused.-import { RetryConfig } from '../types/utils'; import logger from './logger';
16-21
: Export RetryOptions for reuse and future-proofing.This is a useful shape; exporting it enables consumers to type their configs.
-interface RetryOptions { +export interface RetryOptions { maxAttempts?: number; baseDelayMs?: number; operationName?: string; }
54-66
: Tune log levels; info per-attempt can be noisy in production.Downgrade attempt logs to debug, keep success/final failure at info/error.
-logger.info(`Attempt ${attempt + 1}/${maxAttempts} for ${operationName}...`); +logger.debug(`Attempt ${attempt + 1}/${maxAttempts} for ${operationName}...`); @@ -logger.info(`${operationName} succeeded on attempt ${attempt + 1}`); +logger.info(`${operationName} succeeded on attempt ${attempt + 1}`);
72-77
: Adopt exponential backoff with jitter to reduce synchronized retries.Linear backoff can herd retries. Exponential + jitter is a safer default.
-// Calculate delay with linear backoff -const delayMs = baseDelayMs * (attempt + 1); -logger.info(`Retrying in ${delayMs / 1000}s... (attempt ${attempt + 1}/${maxAttempts})`); +// Exponential backoff with jitter +const exp = Math.pow(2, attempt); +const base = baseDelayMs * exp; +const jitter = Math.random() * 0.3 * base; // 0–30% jitter +const delayMs = Math.min(base + jitter, 60_000); // cap at 60s +logger.debug(`Retrying in ${(delayMs / 1000).toFixed(2)}s... (next attempt ${attempt + 2}/${maxAttempts})`);If you’d like, I can add an
isRetryable(error)
predicate andsignal?: AbortSignal
.
83-86
: Preserve original error viacause
for better observability.Attach the last error as the cause to maintain stack provenance.
-throw new Error(`${operationName} failed after ${maxAttempts} attempts: ${lastError?.message || 'Unknown error'}`); +throw new Error(`${operationName} failed after ${maxAttempts} attempts: ${lastError?.message || 'Unknown error'}`, { cause: lastError ?? undefined });src/deploy_commands.ts (2)
66-66
: Tighten type for commands payload.Use Discord API types for better compile-time safety.
-const commands: any[] = []; +import { RESTPostAPIApplicationCommandsJSONBody } from 'discord-api-types/v10'; +const commands: RESTPostAPIApplicationCommandsJSONBody[] = [];
119-133
: Optional: Support global deployment via an env switch.Guild deploys are fast; global deploys may be desired in production.
-const data = await rest.put( - Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID), - { body: commands } -) as any[]; +const target = process.env.DEPLOY_GLOBAL === 'true' + ? Routes.applicationCommands(CLIENT_ID!) + : Routes.applicationGuildCommands(CLIENT_ID!, GUILD_ID!); +const data = await rest.put(target, { body: commands }) as unknown as { length: number };src/services/webhook.ts (2)
95-100
: Confirm Unthread’s URL verification contract; echochallenge
if required.Some providers expect the
challenge
to be echoed back. You currently return 200 with no body.If echo is required:
-if (event === 'url_verification') { - logger.info('URL verification webhook received'); - res.sendStatus(200); - return; -} +if (event === 'url_verification') { + logger.info('URL verification webhook received'); + const { challenge } = req.body as UrlVerificationPayload; + if (challenge) return void res.status(200).send({ challenge }); + return void res.sendStatus(200); +}I can adjust once we confirm Unthread’s spec.
84-85
: Trim sensitive logging for webhook payloads.Logging full raw bodies can leak PII/secrets. Prefer size-only or redact.
-logger.debug('Webhook received:', webhookReq.rawBody); +logger.debug(`Webhook received: size=${Buffer.byteLength(webhookReq.rawBody ?? '', 'utf8')}B`);src/utils/channelUtils.ts (3)
17-21
: Remove unused localGlobalDiscordClient
interface; rely on shared global typing.You already import
../types/global
which augmentsglobal
. This local interface is unused.-interface GlobalDiscordClient { - channels: { - fetch: (channelId: string) => Promise<any>; - }; -} +// Global typing supplied by ../types/global
62-74
: Validate forum channels concurrently to reduce startup latency.The current sequential loop can be slow with many IDs. Use
Promise.allSettled
.-const validForumChannels: string[] = []; - -for (const channelId of channelIds) { - const isValid = await isForumChannel(channelId); - if (isValid) { - validForumChannels.push(channelId); - logger.debug(`Validated forum channel: ${channelId}`); - } - else { - logger.warn(`Channel ${channelId} in FORUM_CHANNEL_IDS is not a forum channel - skipping`); - } -} - -return validForumChannels; +const results = await Promise.allSettled( + channelIds.map(async (channelId) => ({ channelId, ok: await isForumChannel(channelId) })) +); +const valid = results + .filter((r): r is PromiseFulfilledResult<{ channelId: string; ok: boolean }> => r.status === 'fulfilled') + .filter(r => r.value.ok) + .map(r => r.value.channelId); +for (const r of results) { + if (r.status === 'fulfilled' && !r.value.ok) { + logger.warn(`Channel ${r.value.channelId} in FORUM_CHANNEL_IDS is not a forum channel - skipping`); + } else if (r.status === 'rejected') { + logger.warn(`Error validating a channel from FORUM_CHANNEL_IDS: ${r.reason}`); + } +} +return valid;
83-101
: Cache design is pragmatic; approved.A simple in-memory TTL is sufficient here. Consider exposing TTL as an env or constant if you foresee tuning.
src/utils/messageUtils.ts (5)
41-44
: Docstring and signature are out of sync—calibrate the contract.The JSDoc says “Array or Discord.js Collection,” but the signature enforces a plain array. Recommend aligning the docs to avoid misuse upstream.
- * @param messages - Array or Discord.js Collection of messages + * @param messages - Array of ProcessableMessage to compare against
45-96
: Normalize before comparing—strip attachments and whitespace to reduce false negatives.At present duplicates are compared against raw content, which can diverge due to trailing “Attachments:” sections and formatting. Normalizing both sides improves precision without adding complexity.
function isDuplicateMessage(messages: ProcessableMessage[], newContent: string): boolean { @@ - const trimmedContent = newContent.trim(); + const trimmedContent = newContent.trim(); + const normalizedNew = removeAttachmentSection(trimmedContent).replace(/\s+/g, ' ').trim(); @@ - const exactDuplicate = messages.some(msg => msg.content === trimmedContent); + const exactDuplicate = messages.some( + msg => removeAttachmentSection(msg.content).replace(/\s+/g, ' ').trim() === normalizedNew + ); @@ - if (trimmedContent.length >= 10) { + if (normalizedNew.length >= 10) { const contentDuplicate = messages.some(msg => { - // Normalize whitespace for comparison - const strippedMsg = msg.content.replace(/\s+/g, ' ').trim(); - const strippedNewContent = trimmedContent.replace(/\s+/g, ' ').trim(); + // Normalize attachments and whitespace for comparison + const strippedMsg = removeAttachmentSection(msg.content).replace(/\s+/g, ' ').trim(); + const strippedNewContent = normalizedNew;
206-207
: Broaden the “quoted attachments” guard to cover angle-bracket variants.Attachments may also arrive as “Attachments: <…>”. The current check only guards “[”. Let’s generalize.
- if (quotedMessage.startsWith('Attachments: [')) { + if (/^Attachments:\s*[\[<]/.test(quotedMessage)) { return result; }
214-221
: Normalize remaining text before duplicate check—and send the normalized content.If the remaining text is “attachments only,” duplicate detection and the content you send will diverge. Normalize once, use it for both the outgoing payload and the duplicate comparison.
- result.replyReference = matchingMsg.id; - result.contentToSend = remainingText || ' '; // Empty message fallback - - // Check if the remaining text is a duplicate in any message - // (Prevents duplicate replies) - const isDuplicate = existingMessages.some(msg => { - const cleanContent = removeAttachmentSection(msg.content); - return cleanContent === remainingText; - }); + result.replyReference = matchingMsg.id; + const normalizedRemaining = removeAttachmentSection(remainingText).trim(); + result.contentToSend = normalizedRemaining || ' '; // Empty message fallback + + // Check if the remaining text is a duplicate in any message + // (Prevents duplicate replies) + const isDuplicate = existingMessages.some(msg => { + const cleanContent = removeAttachmentSection(msg.content).trim(); + return cleanContent === normalizedRemaining; + });
1-11
: Add targeted unit tests for edge cases—shall I draft them?Given the regex-heavy logic, tests will pay dividends. Suggested cases:
- containsDiscordAttachments: Slack-style, Markdown, plain URL; negative controls.
- removeAttachmentSection: multiple attachments, single newline vs double, message with only attachments.
- processQuotedContent: quoted-only, quoted+attachments, unmatched quote, matched quote with duplicate remaining text.
- isDuplicateMessage: exact, whitespace-only differences, attachment-only differences, fuzzy near-length threshold.
Happy to scaffold a Jest/Vitest suite, Sir/Ma’am.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (20)
.yarn/sdks/eslint/bin/eslint.js
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/api.js
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/config-api.js
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/types/config-api.d.ts
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/types/index.d.ts
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/types/rules.d.ts
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/types/universal.d.ts
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/universal.js
is excluded by!**/.yarn/**
.yarn/sdks/eslint/lib/unsupported-api.js
is excluded by!**/.yarn/**
.yarn/sdks/eslint/package.json
is excluded by!**/.yarn/**
.yarn/sdks/integrations.yml
is excluded by!**/.yarn/**
.yarn/sdks/typescript/bin/tsc
is excluded by!**/.yarn/**
.yarn/sdks/typescript/bin/tsserver
is excluded by!**/.yarn/**
.yarn/sdks/typescript/lib/tsc.js
is excluded by!**/.yarn/**
.yarn/sdks/typescript/lib/tsserver.js
is excluded by!**/.yarn/**
.yarn/sdks/typescript/lib/tsserverlibrary.js
is excluded by!**/.yarn/**
.yarn/sdks/typescript/lib/typescript.js
is excluded by!**/.yarn/**
.yarn/sdks/typescript/package.json
is excluded by!**/.yarn/**
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (21)
.eslintrc.json
(3 hunks).gitignore
(1 hunks).vscode/extensions.json
(1 hunks)README.md
(3 hunks)package.json
(2 hunks)src/commands/support/support.ts
(1 hunks)src/deploy_commands.ts
(1 hunks)src/events/ready.ts
(1 hunks)src/index.ts
(1 hunks)src/services/webhook.ts
(1 hunks)src/types/discord.ts
(1 hunks)src/types/global.ts
(1 hunks)src/types/unthread.ts
(1 hunks)src/types/utils.ts
(1 hunks)src/utils/channelUtils.ts
(1 hunks)src/utils/decodeHtmlEntities.ts
(1 hunks)src/utils/logger.ts
(1 hunks)src/utils/memory.ts
(1 hunks)src/utils/messageUtils.ts
(1 hunks)src/utils/retry.ts
(1 hunks)tsconfig.json
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
src/types/discord.ts (1)
src/utils/memory.js (1)
ttl
(36-36)
src/events/ready.ts (1)
src/events/ready.js (3)
packageJSON
(2-2)allChannelIds
(46-46)invalidCount
(47-47)
src/index.ts (4)
src/types/discord.ts (1)
BotConfig
(32-44)src/index.js (7)
port
(24-24)express
(22-22)client
(46-60)app
(23-23)eventsPath
(131-131)eventFiles
(132-134)event
(138-138)src/services/webhook.js (1)
req
(60-60)src/services/webhook.ts (1)
webhookHandler
(118-118)
src/utils/logger.ts (1)
src/types/discord.ts (1)
Logger
(49-54)
src/services/webhook.ts (1)
src/types/unthread.ts (1)
WebhookPayload
(114-118)
src/utils/messageUtils.ts (1)
src/services/unthread.js (2)
messages
(358-358)processQuotedContent
(395-395)
src/utils/memory.ts (2)
src/utils/memory.js (2)
ttl
(36-36)cachedData
(11-11)src/types/discord.ts (1)
CacheOperations
(59-63)
src/utils/channelUtils.ts (1)
src/utils/channelUtils.js (7)
channel
(24-24)forumChannelIds
(82-82)channelIds
(49-49)isValid
(53-53)cachedForumChannelIds
(69-69)lastValidationTime
(70-70)CACHE_DURATION
(71-71)
🪛 LanguageTool
README.md
[grammar] ~88-~88: There might be a mistake here.
Context: ...urrent bot version. ## 🏗️ Architecture This bot is built with TypeScript for...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...e safety and better code maintainability - Discord.js v14: Modern Discord API int...
(QB_NEW_EN)
[grammar] ~95-~95: There might be a mistake here.
Context: ...s v14**: Modern Discord API interactions - Express.js: Webhook server for Unthrea...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... Webhook server for Unthread integration - Node.js 18+: Runtime environment - **Y...
(QB_NEW_EN)
[grammar] ~97-~97: There might be a mistake here.
Context: ...n - Node.js 18+: Runtime environment - Yarn with PnP: Package management and ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...age management and dependency resolution - ESLint: Code quality and consistent fo...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...: Code quality and consistent formatting - Redis (optional): Caching for improved...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ...quired for proper dependency management) - TypeScript: Automatically managed via ...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...pt**: Automatically managed via Yarn SDK - Discord Application: Bot token and pro...
(QB_NEW_EN)
🔇 Additional comments (13)
tsconfig.json (1)
1-28
: Overall tsconfig looks clean and production-ready.Strict mode, source maps, noEmitOnError, and clear include/exclude are well chosen. Proceeding as configured is entirely reasonable.
src/utils/decodeHtmlEntities.ts (1)
31-36
: Double-decoding semantics: intentional or accidental?Replacing & first means strings like ">" become ">" after the chain, i.e., a double decode. That may be desired for text, but can be risky if output ever hits HTML surfaces.
If you want single-pass decoding only, either:
- Reorder to decode >/< before &, or
- Use a mapped replace without introducing new entities mid-pass:
-export function decodeHtmlEntities(text: string): string { - return text - .replace(/&/g, '&') - .replace(/>/g, '>') - .replace(/</g, '<'); -} +export function decodeHtmlEntities(text: string): string { + const map: Record<string, string> = { '>': '>', '<': '<', '&': '&' }; + return text.replace(/&(gt|lt|amp);/g, (m) => map[m]); +}Please confirm intended behavior across your inputs (webhook payloads, etc.).
.eslintrc.json (1)
2-8
: Config baseline is solid.TypeScript parser, plugin, and recommended presets are correctly enabled. This will serve the TS migration well.
src/types/global.ts (1)
21-26
: Global TypeScript augmentation confirmed included
Your tsconfig.json’s"include": ["src/**/*"]
pattern coverssrc/types/global.ts
, so thedeclare global
in that file will be picked up by the compiler. No further changes needed.src/utils/logger.ts (1)
125-133
: Implementation aligns with Logger contractThe shape matches src/types/discord.ts, and level-specific console routing looks correct. Carry on.
src/commands/support/support.ts (1)
39-43
: Command scaffold is clean and idiomaticSlashCommandBuilder usage and modular export look excellent. Modal UX is a nice touch.
src/events/ready.ts (2)
53-55
: Presence and startup log look goodStatus + activity set on ready, with versioned login for traceability. Chef’s kiss.
72-74
: Error handling is appropriateCentralized logger usage keeps failures visible without halting startup.
src/index.ts (1)
259-267
: Global client publication and failure handling look solid.Logging in, setting
global.discordClient
, and failing fast on login errors are appropriately handled. Proceed.src/deploy_commands.ts (1)
20-28
: dotenv usage is correctly positioned; approved.Loading env before reading variables is well done.
src/services/webhook.ts (1)
22-23
: Secret presence check is good; approved.Failing closed on missing
UNTHREAD_WEBHOOK_SECRET
is the correct behavior.src/utils/channelUtils.ts (1)
36-43
: Type guard on fetched channel; approved logic.Gracefully handling missing channels and verifying
ChannelType.GuildForum
is correct.src/utils/messageUtils.ts (1)
234-241
: Neatly packaged utilities—export shape looks sound.The public surface is cohesive and discoverable. Once the above regex/normalization tweaks land, this module will be battle-ready.
Co-authored-by: warengonzaga <[email protected]>
…bs/log-engine Co-authored-by: warengonzaga <[email protected]>
…-a998-23d8cbacdd83 Migrate console logging to @wgtechlabs/log-engine and remove emojis from logs
Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
…-8008-74ae4bd05ca4 Complete TypeScript conversion - migrate all JavaScript files to TypeScript
Co-authored-by: warengonzaga <[email protected]>
Co-authored-by: warengonzaga <[email protected]>
…-b258-e1be0163c166 🔧 security: upgrade ESLint to v9 and migrate to flat config
Co-authored-by: Copilot <[email protected]>
…-9104-bc3dc94c6d62 📚 docs: Comprehensive JSDoc cleanup and standardization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
♻️ Duplicate comments (8)
package.json (2)
8-13
: Scripts consistency: nicely standardized to Yarn.Build/start/deploy/lint now route through Yarn. This closes the mixed npm/yarn concern.
29-29
: Raise engines.node to ≥18.18.0 to match ESLint 9 / TS-ESLint 8 requirements.Prevents install/runtime friction with the current tooling.
Apply:
"engines": { - "node": ">=18.17.0" + "node": ">=18.18.0" },src/types/discord.ts (1)
71-80
: Align cache contract with runtime: allow null on getKey.Keyv returns null/undefined when missing; reflect that in the type.
export interface CacheOperations { /** Retrieve a value by key */ - getKey: (key: string) => Promise<unknown>; + getKey: (key: string) => Promise<unknown | null>; /** Store a value with optional TTL in seconds */ setKey: (key: string, value: unknown, ttl?: number) => Promise<void>; /** Store a value with long-term persistence (3 years TTL) */ setPersistentKey: (key: string, value: unknown) => Promise<void>; /** Remove a key from cache */ deleteKey: (key: string) => Promise<void>; }src/index.ts (1)
149-160
: Remove redundant env checks; rely on the aggregated validator.You already compute
missingVars
from the full required list; the extra DISCORD_BOT_TOKEN/REDIS_URL checks duplicate the failure path and double-log. Let’s streamline.- // Additional specific validation for critical variables - if (!DISCORD_BOT_TOKEN) { - LogEngine.error('DISCORD_BOT_TOKEN is required but not set in environment variables'); - process.exit(1); - } - - if (!REDIS_URL) { - LogEngine.error('REDIS_URL is required but not set in environment variables'); - LogEngine.error('Redis is now required for proper caching and data persistence'); - LogEngine.error('Please provide a valid Redis connection URL (e.g., redis://localhost:6379)'); - process.exit(1); - } + // Redundant with the aggregated check above; removed to avoid double-loggingsrc/utils/threadUtils.ts (1)
260-266
: Prefer injected client or ensured ambient typing for globalThis.discordClient.Current cast is brittle. Either accept a
Client<true>
param or rely on a proper ambient decl (e.g., src/types/global.d.ts). If the ambient exists, import it once to make intent explicit.-const discordClient = (global as typeof globalThis).discordClient; +// Option A: Inject the client via parameter (preferred for testability) +// Option B: Ensure ambient global typing is loaded and just use globalThis.discordClient +const discordClient = globalThis.discordClient;src/services/unthread.ts (3)
284-297
: Normalize event names (dot vs underscore) before switching.Prevents misses when Unthread sends dotted variants.
- try { - switch (event) { + try { + const normalized = String(event).trim().toLowerCase().replace(/\./g, '_'); + switch (normalized) { case 'message_created': await handleMessageCreated(data); break; case 'conversation_updated': await handleStatusUpdated(data); break; - case 'conversation.created': + case 'conversation_created': LogEngine.debug('Conversation created event received - no action needed for Discord integration'); break; default: - LogEngine.debug(`Unhandled webhook event type: ${event}`); + LogEngine.debug(`Unhandled webhook event type: ${event} (normalized: ${normalized})`); } }
336-377
: Harden message parsing and duplicate suppression; drop Slack-ID heuristic.Use provided timestamps and markdown/text fields; the split-based timestamp is brittle.
- const conversationId = data.conversationId || data.id; - const messageText = data.text; + const conversationId = + data.conversationId ?? + data.conversation?.id ?? + data.id; + const messageText = + data.text ?? + data.message?.markdown ?? + data.markdown ?? + data.content ?? + ''; @@ - // Extract timestamp from Slack-formatted message ID for duplicate detection - const messageId = data.id; - const slackTimestamp = messageId ? messageId.split('-').pop()?.split('.')[0] : null; - - if (slackTimestamp) { - // Check if we have any records of a message deleted within a short window - const currentTime = Date.now(); - // Convert to milliseconds - const messageTimestamp = parseInt(slackTimestamp) * 1000; - - // Only process if the message isn't too old (prevents processing old messages) - // Within 10 seconds - if (currentTime - messageTimestamp < 10000) { + // Prefer createdAt-based gating to suppress near-simultaneous echoes + const createdAtIso = + data.createdAt ?? + data.message?.createdAt ?? + data.created_at ?? + data.message?.created_at; + if (createdAtIso) { + const currentTime = Date.now(); + const messageTimestamp = Date.parse(createdAtIso); + if (!Number.isNaN(messageTimestamp) && currentTime - messageTimestamp < 10_000) { // Check recent deleted messages in this channel const ticketMapping = await getTicketByUnthreadTicketId(conversationId); @@ - } - } + } + }
583-584
: Redact payload logging to avoid PII leakage.Logging the full
requestData
includes user emails. Keep it minimal.-LogEngine.debug(`Sending message to Unthread conversation ${conversationId}:`, requestData); +LogEngine.debug(`Sending message to Unthread conversation ${conversationId} (chars=${message.length})`);
🧹 Nitpick comments (9)
package.json (1)
41-50
: Verify dev-time toolchain under Yarn PnP (ts-node, nodemon, ESLint).
- [email protected] + TS 5.9 may be iffy; tsx is generally smoother.
- Ensure PnP resolves binaries; if not, prefer “yarn …” or SDK paths.
Suggested changes:
- "dev": "nodemon --exec ts-node src/index.ts", + "dev": "yarn nodemon --exec ts-node src/index.ts",Optionally switch to tsx:
- "dev": "yarn nodemon --exec ts-node src/index.ts", + "dev": "yarn tsx watch src/index.ts", + "start": "yarn build && yarn node dist/index.js"Run locally: confirm “yarn dev” hot-reloads and “yarn lint”/“yarn build” succeed under PnP.
src/utils/botUtils.ts (1)
28-41
: Tiny simplification: coalesce displayName/username in one return.Keeps intent crisp with identical behavior.
export function getBotName(): string { - const client = global.discordClient; - - if (client?.user?.displayName) { - return client.user.displayName; - } - - if (client?.user?.username) { - return client.user.username; - } - - // Fallback to package name if client is not available - return 'Unthread Discord Bot'; + const user = global.discordClient?.user; + return user?.displayName ?? user?.username ?? 'Unthread Discord Bot'; }src/utils/database.ts (2)
24-29
: Fail-fast at import time: confirm CI/test envs set REDIS_URL.If tests import this module without REDIS_URL, they’ll crash. Validate your pipelines or gate on NODE_ENV.
Option (test-friendly):
-if (!process.env.REDIS_URL) { +if (!process.env.REDIS_URL) { LogEngine.error('REDIS_URL environment variable is required but not set'); LogEngine.error('Please provide a valid Redis connection URL (e.g., redis://localhost:6379)'); - throw new Error('Missing required REDIS_URL environment variable'); + if (process.env.NODE_ENV !== 'test') { + throw new Error('Missing required REDIS_URL environment variable'); + } }
72-81
: Module-level async IIFE rethrows: acceptable; consider explicit process exit on boot.If this guards critical startup, exiting with non-zero may be clearer than bubbling import errors.
(async () => { try { await testRedisConnection(); } catch (error) { LogEngine.error('Redis health check failed during module initialization:', error); - throw error; + process.exitCode = 1; + throw error; } })();src/types/discord.ts (1)
87-94
: getCustomerByDiscordId should allow absence (null).Matches usual lookup semantics and avoids sentinel values.
- getCustomerByDiscordId: (discordId: string) => Promise<unknown>; + getCustomerByDiscordId: (discordId: string) => Promise<unknown | null>;src/utils/memory.ts (3)
16-18
: CommonJS interop: ensure ./cache default import is valid under PnP.If ./cache is CJS, prefer “import = require” or add a local d.ts to avoid runtime surprises.
Option A:
-import cachedData from './cache'; +import cachedData = require('./cache');Option B (types): add src/utils/cache.d.ts with the module shape and keep the default import.
66-71
: Promote 3-year TTL to a constant for clarity.Avoid recalculating; self-document semantics.
+const THREE_YEARS_TTL_MS = 3 * 365 * 24 * 60 * 60 * 1000; @@ - const threeYearsTtl = 3 * 365 * 24 * 60 * 60 * 1000; - await cachedData.set(key, value, threeYearsTtl); + await cachedData.set(key, value, THREE_YEARS_TTL_MS);
93-97
: Doc fix: default TTL is 7 days, not 24 hours.Synchronize the debug note with implementation.
- * 2. Verify the TTL hasn't expired (default is 24 hours) + * 2. Verify the TTL hasn't expired (default is 7 days)src/index.ts (1)
269-273
: Set an explicit body size limit for the raw-body JSON middleware.Being explicit avoids surprises across Express versions and guards memory.
-const rawBodyJsonMiddleware = express.json({ +const rawBodyJsonMiddleware = express.json({ + limit: '100kb', verify: (req: WebhookRequest, _res: express.Response, buf: Buffer) => { req.rawBody = buf.toString(); }, });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (18)
.github/chatmodes/WG Code Builder.chatmode.md
(1 hunks)package.json
(2 hunks)src/commands/utilities/version.ts
(1 hunks)src/events/interactionCreate.ts
(1 hunks)src/events/messageCreate.ts
(1 hunks)src/events/threadCreate.ts
(1 hunks)src/index.ts
(1 hunks)src/services/unthread.ts
(1 hunks)src/types/discord.ts
(1 hunks)src/types/global.ts
(1 hunks)src/types/unthread.ts
(1 hunks)src/utils/botUtils.ts
(1 hunks)src/utils/channelUtils.ts
(1 hunks)src/utils/customerUtils.ts
(1 hunks)src/utils/database.ts
(1 hunks)src/utils/memory.ts
(1 hunks)src/utils/messageUtils.ts
(1 hunks)src/utils/threadUtils.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- src/types/global.ts
- .github/chatmodes/WG Code Builder.chatmode.md
🚧 Files skipped from review as they are similar to previous changes (7)
- src/utils/messageUtils.ts
- src/events/messageCreate.ts
- src/commands/utilities/version.ts
- src/events/interactionCreate.ts
- src/utils/customerUtils.ts
- src/events/threadCreate.ts
- src/utils/channelUtils.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
src/index.ts
package.json
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Applied to files:
src/services/unthread.ts
package.json
🧬 Code graph analysis (3)
src/index.ts (1)
src/types/discord.ts (1)
BotConfig
(43-64)
src/utils/threadUtils.ts (1)
src/config/logger.ts (1)
LogEngine
(35-35)
src/services/unthread.ts (2)
src/types/discord.ts (1)
ThreadTicketMapping
(27-36)src/utils/threadUtils.ts (2)
findDiscordThreadByTicketIdWithRetry
(129-211)findDiscordThreadByTicketId
(245-292)
🔇 Additional comments (2)
src/utils/botUtils.ts (1)
11-12
: Ensure resolveJsonModule is enabled for the package.json import.Without it, tsc will complain or emit awkward CJS interop.
Add to tsconfig.json (compilerOptions):
{ "compilerOptions": { "resolveJsonModule": true, "esModuleInterop": true } }src/utils/threadUtils.ts (1)
189-191
: Resolved: TS lib includes ES2022, ErrorOptions.cause supported
I’ve confirmedtsconfig.json
lists"ES2022"
, sonew Error(message, { cause })
will compile as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
src/index.ts (5)
75-87
: Centralize termination: rethrow in validateStartupRequirements().Let main() own process exit; avoid helper-level exits to keep a single shutdown path.
Apply:
catch (error) { - LogEngine.error('Startup validation failed:', error); - process.exit(1); + LogEngine.error('Startup validation failed:', error); + throw (error instanceof Error ? error : new Error(String(error))); }
128-149
: Eliminate redundant env checks; one gate is sufficient.You already compute missingVars; the extra DISCORD_BOT_TOKEN/REDIS_URL checks duplicate logic.
Apply:
// Step 1: Load and validate environment variables const requiredEnvVars = ['DISCORD_BOT_TOKEN', 'REDIS_URL', 'CLIENT_ID', 'GUILD_ID', 'UNTHREAD_API_KEY', 'UNTHREAD_SLACK_CHANNEL_ID', 'UNTHREAD_WEBHOOK_SECRET']; - const { DISCORD_BOT_TOKEN, REDIS_URL } = process.env as Partial<BotConfig>; - - const missingVars: string[] = []; - - for (const envVar of requiredEnvVars) { - // eslint-disable-next-line security/detect-object-injection - const envValue = process.env[envVar]; - if (!envValue) { - missingVars.push(envVar); - } - } + const env = process.env as Partial<BotConfig>; + const missingVars = requiredEnvVars.filter((k) => { + const v = (env as Record<string, unknown>)[k]; + return typeof v !== 'string' || v.trim() === ''; + }); @@ - // Step 2: Validate Unthread-specific environment variables + // Validate Unthread-specific environment variables validateEnvironment(); - - // Additional specific validation for critical variables - if (!DISCORD_BOT_TOKEN) { - LogEngine.error('DISCORD_BOT_TOKEN is required but not set in environment variables'); - process.exit(1); - } - - if (!REDIS_URL) { - LogEngine.error('REDIS_URL is required but not set in environment variables'); - LogEngine.error('Redis is now required for proper caching and data persistence'); - LogEngine.error('Please provide a valid Redis connection URL (e.g., redis://localhost:6379)'); - process.exit(1); - } + // All required environment variables are present at this point.Also applies to: 153-165
294-332
: Health endpoint: resilient version fallback.If JSON import is altered during packaging, fall back to npm-provided env.
Apply:
- version: version, + version: version ?? process.env.npm_package_version ?? 'unknown',
351-358
: Tighten command module validation: require data.toJSON.Slash command deployment relies on toJSON; validate it at load time.
Apply:
- if (command && + if (command && typeof command === 'object' && command.data && typeof command.data === 'object' && typeof command.data.name === 'string' && - command.data.name.trim() !== '' && + command.data.name.trim() !== '' && + typeof command.data.toJSON === 'function' && typeof command.execute === 'function') { @@ - else if (typeof command.data.name !== 'string' || command.data.name.trim() === '') { + else if (typeof command.data.name !== 'string' || command.data.name.trim() === '') { issues.push('command.data.name is missing or not a non-empty string'); } + if (typeof (command as any).data?.toJSON !== 'function') { + issues.push('command.data.toJSON is missing or not a function'); + } if (typeof command.execute !== 'function') { issues.push('command.execute is missing or not a function'); }Also applies to: 370-401
166-177
: Step annotations are out of sequence (nit).“Step 2” repeats; renumber for clarity.
Apply:
- // Step 2: Validate all dependencies before proceeding + // Step 3: Validate all dependencies before proceeding @@ - // Step 3: Start Discord login after validation succeeds + // Step 4: Start Discord login after validation succeeds @@ - // Step 4: Start Express server after all validation and setup completes + // Step 5: Start Express server after all validation and setup completes
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
src/deploy_commands.ts
(1 hunks)src/index.ts
(1 hunks)src/services/unthread.ts
(1 hunks)src/utils/memory.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/utils/memory.ts
- src/deploy_commands.ts
- src/services/unthread.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
src/index.ts
🧬 Code graph analysis (1)
src/index.ts (5)
src/config/logger.ts (1)
LogEngine
(35-35)src/utils/database.ts (1)
testRedisConnection
(97-97)src/types/discord.ts (1)
BotConfig
(43-64)src/services/unthread.ts (1)
validateEnvironment
(46-62)src/services/webhook.ts (1)
webhookHandler
(129-129)
🔇 Additional comments (4)
src/index.ts (4)
48-51
: dotenv-first import order is correct.Environment variables are loaded before any dependent imports. Proceeding as designed.
186-197
: Numeric port parsing and bounds check: LGTM.Prevents accidental named pipe binding and guards invalid ranges.
426-467
: Event loader validation and ESM default handling: well executed.Graceful skipping of malformed modules avoids runtime crashes; ESM/CJS interop is handled.
246-252
: Confirm necessity of MessageContent intent (privileged).If not strictly required, drop it to minimize privileged scopes.
Would you like me to scan command/event handlers to confirm any reliance on message content and produce a list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (11)
src/index.ts (11)
129-165
: Deduplicate env-var checks; keep a single authoritative gate.You validate required vars via
requiredEnvVars
and then re-checkDISCORD_BOT_TOKEN
andREDIS_URL
. Collapse into one block and keep the Redis hint.Apply:
- if (missingVars.length > 0) { - LogEngine.error(`Missing required environment variables: ${missingVars.join(', ')}`); - LogEngine.error('Please ensure all required environment variables are set before starting the bot'); - process.exit(1); - } - - // Step 2: Validate Unthread-specific environment variables - validateEnvironment(); - - // Additional specific validation for critical variables - if (!DISCORD_BOT_TOKEN) { - LogEngine.error('DISCORD_BOT_TOKEN is required but not set in environment variables'); - process.exit(1); - } - - if (!REDIS_URL) { - LogEngine.error('REDIS_URL is required but not set in environment variables'); - LogEngine.error('Redis is now required for proper caching and data persistence'); - LogEngine.error('Please provide a valid Redis connection URL (e.g., redis://localhost:6379)'); - process.exit(1); - } + if (missingVars.length > 0) { + const missing = new Set(missingVars); + LogEngine.error(`Missing required environment variables: ${[...missing].join(', ')}`); + if (missing.has('REDIS_URL')) { + LogEngine.error('Redis is required for caching and persistence. Example: redis://localhost:6379'); + } + process.exit(1); + } + + // Step 2: Validate Unthread-specific environment variables (domain-specific checks) + validateEnvironment();
75-87
: Prefer throwing over process.exit() inside validators.Let
main()
own the exit. Improves testability and keeps one shutdown path.- catch (error) { - LogEngine.error('Startup validation failed:', error); - process.exit(1); - } + catch (error) { + LogEngine.error('Startup validation failed:', error); + throw error; + }
95-116
: Health check: avoid write-amplification; probe with the existing validator.Using Keyv set/get on every probe performs writes. Reuse
testRedisConnection()
and map the result.async function checkRedisHealth(): Promise<{ status: 'connected' | 'disconnected'; error?: string }> { - try { - // Use the keyv instance to perform a quick health check - const testKey = `health:check:${Date.now()}`; - const testValue = 'ping'; - - // Set and immediately get a test value with short TTL - await keyv.set(testKey, testValue, 1000); - const result = await keyv.get(testKey); - - if (result === testValue) { - return { status: 'connected' }; - } - else { - return { status: 'disconnected', error: 'Redis ping test failed - value mismatch' }; - } - } + try { + await testRedisConnection(); + return { status: 'connected' }; + } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown Redis error'; return { status: 'disconnected', error: errorMessage }; } }
316-319
: Minor: redundant status assignment.Default is already “healthy”; no need to reassign.
- if (isHealthy) { - healthStatus.status = 'healthy'; - res.status(200).json(healthStatus); - } + if (isHealthy) { + res.status(200).json(healthStatus); + }
351-359
: Guard against non-directory entries in commands root.If a file exists under
commands/
,readdirSync(commandsPath)
will throw. Skip non-directories.for (const folder of commandFolders) { const commandsPath = path.join(foldersPath, folder); + if (!fs.statSync(commandsPath).isDirectory()) continue;
214-215
: Allow sync handlers for commands.Some modules won’t be async; widen the type.
- execute: (...args: unknown[]) => Promise<void>; + execute: (...args: unknown[]) => void | Promise<void>;
223-224
: Allow sync handlers for events.Same rationale as commands.
- execute: (...args: unknown[]) => Promise<void>; + execute: (...args: unknown[]) => void | Promise<void>;
246-260
: Create an ExtendedClient instance without a cast.Initialize
commands
at construction to avoid a broad type assertion.-const client = new Client({ +const client = Object.assign(new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages, GatewayIntentBits.GuildMessageReactions, ], partials: [ Partials.Channel, Partials.Message, Partials.Reaction, Partials.ThreadMember, ], -}) as ExtendedClient; +}), { commands: new Collection<string, CommandModule>() }) as ExtendedClient;
344-345
: Follow-through: commands already initialized.Remove the late assignment if adopting the previous change.
-client.commands = new Collection(); const foldersPath = path.join(__dirname, 'commands');
229-231
: Optional: store the raw body as Buffer to avoid encoding pitfalls.Storing a Buffer can prevent signature mismatches from implicit UTF-8 decoding.
If you choose this route, adjust both the interface and the assign:
interface WebhookRequest extends express.Request { - rawBody: string; + rawBody: Buffer; }and in the verify callback (Line 274):
- (req as WebhookRequest).rawBody = buf.toString('utf8'); + (req as WebhookRequest).rawBody = Buffer.from(buf);
474-475
: Graceful shutdown hooks (SIGINT/SIGTERM).Add signal handlers to close the HTTP server and destroy the Discord client for clean exits and quicker restarts.
Happy to draft a small helper that wires:
- process.on('SIGINT'|'SIGTERM')
- server.close(() => client.destroy())
- a timeout fallback with force-exit
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/index.ts
(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
src/index.ts
🧬 Code graph analysis (1)
src/index.ts (5)
src/config/logger.ts (1)
LogEngine
(35-35)src/utils/database.ts (1)
testRedisConnection
(97-97)src/types/discord.ts (1)
BotConfig
(43-64)src/services/unthread.ts (1)
validateEnvironment
(46-62)src/services/webhook.ts (1)
webhookHandler
(129-129)
🔇 Additional comments (3)
src/index.ts (3)
166-178
: Startup gating and ordered boot: nicely done.Discord login and server listen now occur only after validations succeed. This eliminates early-traffic and half-boot states.
288-289
: Scoped raw-body parser on the webhook route: elegant.This keeps global middleware lean and signature verification safe. Well executed.
353-371
: Default-export tolerant loader with dev/prod extension switch: spot on.Handles ESM/CJS interop and ts-node vs compiled runtime cleanly.
Co-authored-by: warengonzaga <[email protected]>
…-9b07-83631f2f49c2 Add Dockerfile and Docker Compose configuration for containerized deployment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
470-473
: Match security policy link filename to existing fileSir, the security policy file is named
SECURITY.md
; update the link casing to avoid a dead link:-For security vulnerabilities, please do not report them publicly. Follow the guidelines in our [security policy](./security.md) to responsibly disclose security issues. +For security vulnerabilities, please do not report them publicly. Follow the guidelines in our [security policy](./SECURITY.md) to responsibly disclose security issues.
🧹 Nitpick comments (10)
.dockerignore (2)
21-25
: Prebuild-only flow: verify CI pipeline or switch to multi-stage buildExcluding src and tsconfig.json means the Docker image can only be built after a local yarn build. Please confirm your CI/CD runs the build before docker build. Alternatively, remove these ignores and compile inside the image with a multi-stage Dockerfile.
Apply one of the following diffs:
Option A — keep prebuild, reduce context noise:
+# Yarn offline cache (large; not needed if you copy only PnP runtime files) +.yarn/cacheOption B — enable in-image build (multi-stage), so keep sources:
-# TypeScript source files (we only need dist) -src -tsconfig.json -eslint.config.js +# Keep sources for multi-stage Docker builds +# src +# tsconfig.json +# eslint.config.js
1-4
: Consider retaining LICENSE in image for complianceIgnoring LICENSE and copying none of it into the image is common, but some consumers expect licenses present. If needed, stop ignoring LICENSE or copy it to /licenses during build.
Also applies to: 32-33
Dockerfile (2)
15-18
: Pin base image to digest for reproducibilityTo avoid surprise updates, pin FROM to an immutable digest.
46-49
: Healthcheck: tolerate slow cold startsIf the bot warms up slowly, consider increasing start-period or retries to avoid premature unhealthy status.
.env.docker (1)
16-18
: Nit: satisfy dotenv-linter key orderingReorder CLIENT_ID before DISCORD_BOT_TOKEN to appease dotenv-linter.
-DISCORD_BOT_TOKEN=your_discord_bot_token_here -CLIENT_ID=your_client_id_here +CLIENT_ID=your_client_id_here +DISCORD_BOT_TOKEN=your_discord_bot_token_here GUILD_ID=your_guild_id_heredocker-compose.yml (1)
15-15
: YAML lint nits: trailing spaces and EOF newlineRemove trailing spaces and add a newline at EOF to satisfy linters.
-# Prerequisites: +# Prerequisites: @@ - redis_platform_data: # Redis platform cache data + redis_platform_data: # Redis platform cache data @@ - external: true + external: true +Also applies to: 71-71, 83-83
README.md (4)
3-3
: Fix LICENSE badge link casingThe badge points to /license, but the file is typically LICENSE. Align the casing to avoid a broken link on case-sensitive systems.
-[](https://github.com/wgtechlabs/unthread-discord-bot/blob/main/license) +[](https://github.com/wgtechlabs/unthread-discord-bot/blob/main/LICENSE)
194-202
: Improve Corepack guidance for reproducibilityActivate the exact Yarn version via Corepack to eliminate host drift.
- corepack enable + corepack enable + # Pin the exact Yarn version used by this repo + corepack prepare [email protected] --activateIf you decide to keep 4.9.2 instead, adjust the version above accordingly.
106-118
: Note PnP editor SDKs for a smoother DXWith Yarn PnP, suggest hinting how to generate editor SDKs (TS/ESLint) so IDEs resolve modules without node_modules.
# Development with live reload yarn dev + +# Tip: generate editor SDKs for PnP +yarn dlx @yarnpkg/sdks
248-294
: Optional: streamline Docker build pathNeeding local yarn build before docker compose suggests the image relies on a prebuilt dist. Consider a multi-stage Docker build that runs yarn install/build inside the image, so users only run docker compose up -d.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (6)
.dockerignore
(1 hunks).env.docker
(1 hunks)Dockerfile
(1 hunks)README.md
(5 hunks)docker-compose.yml
(1 hunks)package.json
(2 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Applied to files:
Dockerfile
README.md
package.json
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
Dockerfile
README.md
package.json
📚 Learning: 2025-08-30T09:26:39.331Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#45
File: src/services/unthread.ts:184-193
Timestamp: 2025-08-30T09:26:39.331Z
Learning: In the wgtechlabs/unthread-discord-bot repository, the current custom Unthread service implementation in src/services/unthread.ts is planned to be replaced with an official SDK in the future, as mentioned by warengonzaga.
Applied to files:
README.md
package.json
📚 Learning: 2025-08-30T09:05:06.895Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#45
File: src/services/unthread.ts:112-137
Timestamp: 2025-08-30T09:05:06.895Z
Learning: In the wgtechlabs/unthread-discord-bot repository, LogEngine (from wgtechlabs/log-engine package) has built-in PII protection capabilities, so manual redaction of log payloads is not necessary when using LogEngine for logging.
Applied to files:
package.json
🪛 YAMLlint (1.37.1)
docker-compose.yml
[error] 15-15: trailing spaces
(trailing-spaces)
[error] 71-71: trailing spaces
(trailing-spaces)
[error] 83-83: no new line character at the end of file
(new-line-at-end-of-file)
🪛 LanguageTool
README.md
[grammar] ~88-~88: There might be a mistake here.
Context: ...urrent bot version. ## 🏗️ Architecture This bot is built with TypeScript fo...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...e safety and better code maintainability - Discord.js v14: Modern Discord API int...
(QB_NEW_EN)
[grammar] ~95-~95: There might be a mistake here.
Context: ...s v14**: Modern Discord API interactions - Express.js: Webhook server for Unthrea...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... Webhook server for Unthread integration - Node.js 18+: Runtime environment - **Y...
(QB_NEW_EN)
[grammar] ~97-~97: There might be a mistake here.
Context: ...n - Node.js 18+: Runtime environment - Yarn with PnP: Package management and ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...age management and dependency resolution - ESLint: Code quality and consistent fo...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...: Code quality and consistent formatting - Redis: Required for caching and data p...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ...quired for proper dependency management) - TypeScript: Automatically managed via ...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...pt**: Automatically managed via Yarn SDK - Discord Application: Bot token and pro...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...mmand:dev ``` ## 📦 Docker Installation For production deployments or containeri...
(QB_NEW_EN)
[grammar] ~254-~254: There might be a mistake here.
Context: ...s - Docker: Version 20.10 or higher - Docker Compose: Version 2.0 or higher ...
(QB_NEW_EN)
[grammar] ~301-~301: There might be a mistake here.
Context: ...ompose up -d ``` This will start: - Discord bot application with webhook ser...
(QB_NEW_EN)
[grammar] ~302-~302: There might be a mistake here.
Context: ...cord bot application with webhook server - Redis for caching and data persistence ...
(QB_NEW_EN)
🪛 dotenv-linter (3.3.0)
.env.docker
[warning] 17-17: [UnorderedKey] The CLIENT_ID key should go before the DISCORD_BOT_TOKEN key
(UnorderedKey)
🔇 Additional comments (9)
.env.docker (1)
26-28
: Good: Redis URL targets in-network hostnameUsing redis://redis-platform:6379 aligns with docker-compose service DNS.
package.json (3)
8-13
: Scripts now consistently use Yarn — aligned with repo standardizationBuild/start/deploycommand look correct for Yarn PnP workflows.
29-31
: Engines match Docker basenode >=22.16.0 aligns with Dockerfile’s Node 22.16 image.
42-50
: Toolchain versions are coherent with Node 22 and ESLint 9Types, TS, ESLint, and ts-eslint versions are compatible. No action required.
docker-compose.yml (2)
29-31
: Use the provided .env.docker for Compose, or confirm .env usageYour repo includes .env.docker; compose references .env. Pick one to avoid drift. If this stack is for Docker-only, point to .env.docker.
- env_file: - - .env # Contains bot tokens, API keys, and Redis URL + env_file: + - .env.docker # Minimal Docker-oriented env file
38-43
: Healthcheck parity with Dockerfile — goodwget-based /health check matches the Dockerfile. Keep as-is.
README.md (3)
295-324
: Docker service namesdiscord-bot
andredis-platform
match docker-compose.yml. No changes required.
371-379
: Webhook route verified Implementation atsrc/index.ts:289
definesapp.post('/webhook/unthread')
, matching the README.
176-181
: Verification complete: deprecated env vars removed
Certainly, Sir. UNTHREAD_TRIAGE_CHANNEL_ID and UNTHREAD_EMAIL_INBOX_ID no longer appear in .env.example or source code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
README.md (1)
44-49
: Add missing deploycommand:dev script or update README
I have detected that package.json definesdeploycommand
but nodeploycommand:dev
as referenced in the README (44–49, 116–118). Add the missing script or adjust the documentation to match the actual commands.
♻️ Duplicate comments (1)
Dockerfile (1)
24-26
: Corepack enabled at build-time — good catch.This ensures Yarn is available in the container. Matches the repo’s Yarn 4 + PnP posture.
🧹 Nitpick comments (4)
Dockerfile (2)
50-51
: Parameterize healthcheck port via $PORT.Keeps HEALTHCHECK aligned with configurable PORT while retaining 3000 as default.
-HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD sh -lc 'wget --no-verbose --tries=1 --spider "http://127.0.0.1:${PORT:-3000}/health" || exit 1'
19-23
: Optional: move OS package installation before switching users and pin packages.For reproducibility and slimmer layers, consider pinning versions or using --no-cache per command block. Low priority.
README.md (2)
282-293
: Build-inside-image (multi-stage) for reproducibility.Local “prebuild then copy dist/” works, but a multi-stage build ensures clean, repeatable releases.
I can provide a Yarn 4 + PnP multi-stage Dockerfile if you want to switch.
88-101
: Minor editorial polish.Consider tightening a couple bullet phrasings for crispness; content is solid, impact is cosmetic.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
Dockerfile
(1 hunks)README.md
(5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Applied to files:
Dockerfile
README.md
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
Dockerfile
README.md
📚 Learning: 2025-08-30T09:26:39.331Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#45
File: src/services/unthread.ts:184-193
Timestamp: 2025-08-30T09:26:39.331Z
Learning: In the wgtechlabs/unthread-discord-bot repository, the current custom Unthread service implementation in src/services/unthread.ts is planned to be replaced with an official SDK in the future, as mentioned by warengonzaga.
Applied to files:
README.md
🪛 LanguageTool
README.md
[grammar] ~88-~88: There might be a mistake here.
Context: ...urrent bot version. ## 🏗️ Architecture This bot is built with TypeScript fo...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...e safety and better code maintainability - Discord.js v14: Modern Discord API int...
(QB_NEW_EN)
[grammar] ~95-~95: There might be a mistake here.
Context: ...s v14**: Modern Discord API interactions - Express.js: Webhook server for Unthrea...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... Webhook server for Unthread integration - Node.js 18+: Runtime environment - **Y...
(QB_NEW_EN)
[grammar] ~97-~97: There might be a mistake here.
Context: ...n - Node.js 18+: Runtime environment - Yarn with PnP: Package management and ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...age management and dependency resolution - ESLint: Code quality and consistent fo...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...: Code quality and consistent formatting - Redis: Required for caching and data p...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ...quired for proper dependency management) - TypeScript: Automatically managed via ...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...pt**: Automatically managed via Yarn SDK - Discord Application: Bot token and pro...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...mmand:dev ``` ## 📦 Docker Installation For production deployments or containeri...
(QB_NEW_EN)
[grammar] ~254-~254: There might be a mistake here.
Context: ...s - Docker: Version 20.10 or higher - Docker Compose: Version 2.0 or higher ...
(QB_NEW_EN)
[grammar] ~301-~301: There might be a mistake here.
Context: ...ompose up -d ``` This will start: - Discord bot application with webhook ser...
(QB_NEW_EN)
[grammar] ~302-~302: There might be a mistake here.
Context: ...cord bot application with webhook server - Redis for caching and data persistence ...
(QB_NEW_EN)
🔇 Additional comments (4)
Dockerfile (1)
15-18
: Base image tag verified as available. Affirmative, Sir. The Docker tag node:22.16-alpine3.21 is confirmed pullable from Docker Hub.README.md (3)
128-129
: Yarn version synchronized to 4.9.4 — aligned with packageManager.Documentation now matches the toolchain. Nicely done.
Also applies to: 201-201
170-183
: Confirmed:UNTHREAD_SLACK_CHANNEL_ID
andREDIS_URL
in README.md match the code’s references; no updates required.
49-51
: Health endpoint verified
Affirmative: the/health
route is implemented in src/index.ts (line 297), matching the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
Dockerfile (2)
29-31
: Corepack enablement is spot-on.This ensures Yarn is available in the container prior to runtime. Nicely aligned with the PnP flow.
43-49
: Ship all PnP runtime artifacts (.yarnrc.yml and optional loader) to avoid surprises.Without .yarnrc.yml, Yarn may not honor repo-wide settings; copying .pnp.loader.mjs future-proofs ESM loader use.
Apply this minimal diff:
COPY --chown=discordbot:nodejs package.json ./ +COPY --chown=discordbot:nodejs .yarnrc.yml ./ COPY --chown=discordbot:nodejs yarn.lock ./ COPY --chown=discordbot:nodejs .pnp.cjs ./ +COPY --chown=discordbot:nodejs .pnp.loader.mjs ./ COPY --chown=discordbot:nodejs .yarn ./.yarn COPY --chown=discordbot:nodejs dist ./distREADME.md (2)
127-130
: Yarn version synced to 4.9.4 — good consistency with package.json.This resolves the earlier mismatch.
248-258
: PnP runtime expectations are now documented.This directly prevents “module not found” reports in containers. Nicely done.
🧹 Nitpick comments (4)
Dockerfile (1)
24-28
: Consider a multi-stage build for reproducible, supply-chain-friendly images.Build inside the image (immutable install, no host toolchain dependency), then copy only runtime bits.
Illustrative skeleton:
+FROM node:${NODE_VERSION} AS build +RUN apk add --no-cache dumb-init && corepack enable +WORKDIR /usr/src/app +COPY package.json yarn.lock .yarnrc.yml ./ +COPY .yarn ./.yarn +RUN yarn install --immutable +COPY . . +RUN yarn build + FROM node:${NODE_VERSION} RUN apk update && apk upgrade && \ apk add --no-cache wget dumb-init && \ rm -rf /var/cache/apk/* RUN corepack enable WORKDIR /usr/src/app RUN addgroup -g 1001 -S nodejs && \ adduser -S discordbot -u 1001 -G nodejs -COPY --chown=discordbot:nodejs package.json ./ -COPY --chown=discordbot:nodejs yarn.lock ./ -COPY --chown=discordbot:nodejs .pnp.cjs ./ -COPY --chown=discordbot:nodejs .yarn ./.yarn -COPY --chown=discordbot:nodejs dist ./dist +COPY --chown=discordbot:nodejs package.json yarn.lock .yarnrc.yml ./ +COPY --chown=discordbot:nodejs .yarn ./.yarn +COPY --from=build --chown=discordbot:nodejs /usr/src/app/.pnp.cjs ./ +COPY --from=build --chown=discordbot:nodejs /usr/src/app/.pnp.loader.mjs ./ +COPY --from=build --chown=discordbot:nodejs /usr/src/app/dist ./dist USER discordbot ENTRYPOINT ["dumb-init","--"] CMD ["yarn","node","dist/index.js"]Also applies to: 43-49
README.md (3)
276-296
: Local prebuild before Compose: call out rebuild trigger as a tip.Minor doc nicety: advise rerunning
yarn build
beforedocker compose up --build
when code changes.Suggested addition after the build block:
+> Tip: After code changes, run `yarn build` before `docker compose up --build` so the image includes the latest dist/.
314-343
: Consider documenting durable Redis storage.If users rely on data persistence, recommend a named Docker volume for Redis.
Add under “Production Deployment”:
+5. Use a named volume for Redis (e.g., `redis-data`) and avoid `down -v` in prod to prevent data loss.
331-332
: Warn thatdocker compose down -v
deletes Redis data.A small caution saves tears.
Proposed tweak:
-# Remove all data (including Redis data) +# Remove all data (including Redis data) — irreversible
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
Dockerfile
(1 hunks)README.md
(5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Applied to files:
Dockerfile
README.md
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
Dockerfile
README.md
📚 Learning: 2025-08-30T09:26:39.331Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#45
File: src/services/unthread.ts:184-193
Timestamp: 2025-08-30T09:26:39.331Z
Learning: In the wgtechlabs/unthread-discord-bot repository, the current custom Unthread service implementation in src/services/unthread.ts is planned to be replaced with an official SDK in the future, as mentioned by warengonzaga.
Applied to files:
README.md
🪛 LanguageTool
README.md
[grammar] ~88-~88: There might be a mistake here.
Context: ...urrent bot version. ## 🏗️ Architecture This bot is built with TypeScript fo...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...e safety and better code maintainability - Discord.js v14: Modern Discord API int...
(QB_NEW_EN)
[grammar] ~95-~95: There might be a mistake here.
Context: ...s v14**: Modern Discord API interactions - Express.js: Webhook server for Unthrea...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... Webhook server for Unthread integration - Node.js 18+: Runtime environment - **Y...
(QB_NEW_EN)
[grammar] ~97-~97: There might be a mistake here.
Context: ...n - Node.js 18+: Runtime environment - Yarn with PnP: Package management and ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...age management and dependency resolution - ESLint: Code quality and consistent fo...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...: Code quality and consistent formatting - Redis: Required for caching and data p...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ...quired for proper dependency management) - TypeScript: Automatically managed via ...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...pt**: Automatically managed via Yarn SDK - Discord Application: Bot token and pro...
(QB_NEW_EN)
[grammar] ~248-~248: There might be a mistake here.
Context: ...mmand:dev ``` ## 📦 Docker Installation For production deployments or containeri...
(QB_NEW_EN)
[grammar] ~256-~256: There might be a mistake here.
Context: ...s - Docker: Version 20.10 or higher - Docker Compose: Version 2.0 or higher ...
(QB_NEW_EN)
[grammar] ~303-~303: There might be a mistake here.
Context: ...ompose up -d ``` This will start: - Discord bot application with webhook ser...
(QB_NEW_EN)
[grammar] ~304-~304: There might be a mistake here.
Context: ...cord bot application with webhook server - Redis for caching and data persistence ...
(QB_NEW_EN)
🔇 Additional comments (6)
Dockerfile (2)
56-59
: Verify the /health endpoint exists and returns 200 reliably.If the route is missing or gated behind auth, health checks will flap.
Run-time check suggestion: confirm Express exposes GET /health with 200 OK before merge.
60-62
: Running viayarn node
is PnP-correct.This guarantees dependency resolution under PnP at runtime.
README.md (4)
88-118
: Architecture and build system section reads clean and matches the new TS/Yarn PnP stack.Clear, actionable commands; no objections.
194-202
: Corepack step documented — excellent developer ergonomics.This mirrors the container flow and reduces “wrong Yarn” issues.
248-258
: Resolved: external network declared
Theunthread-integration-network
is already defined asexternal: true
indocker-compose.yml
, so no further changes are needed.
178-181
: Proceed to merge, sir: env vars and Redis checks alignedProcess.env Slack references exclusively use
UNTHREAD_SLACK_CHANNEL_ID
;REDIS_URL
is enforced in src/utils/database.ts and throws an error if unset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
README.md (1)
128-129
: Yarn version synchronized—nicely done.README now reflects Yarn 4.9.4, aligning with package.json and prior review. Carry on.
🧹 Nitpick comments (4)
README.md (4)
248-264
: Present Developer Mode method first for brevity.Most users just need “Enable Developer Mode → Right‑click server → Copy ID.” Consider listing that as Step 1 and keeping the Widget method as an alternative.
367-367
: Make Redis requirement and failure mode explicit.Add one line stating the app will refuse to start (or degrade) if
REDIS_URL
is missing, and recommend a local default for development.
178-181
: Refine REDIS_URL guidance in README.mdSir, the
.env.example
already includesUNTHREAD_SLACK_CHANNEL_ID
andREDIS_URL
, and both are consumed at runtime insrc/services/unthread.ts
andsrc/utils/database.ts
. I recommend enhancing the README entry forREDIS_URL
with a concrete URI example—e.g.,redis://localhost:6379
—and a note on TLS schemes (such asrediss://
for hosted providers).
102-119
: Scripts verified — adjust missing entries and Yarn PnP pin
Thedev
,build
,deploycommand
, andstart
scripts in package.json align with the README. There is nodeploycommand:dev
script, nor a"packageManager": "yarn@<version>"
field to pin Plug’n’Play—add or remove the former as needed and includepackageManager
if you rely on Yarn PnP.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
README.md
(8 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository, all package.json scripts have been standardized to use yarn consistently, eliminating the previous mixing of npm and yarn commands. This was part of the v1.0.0-rc1 release migration to a Yarn PnP architecture.
Applied to files:
README.md
📚 Learning: 2025-08-28T11:18:55.746Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#0
File: :0-0
Timestamp: 2025-08-28T11:18:55.746Z
Learning: In the wgtechlabs/unthread-discord-bot repository's package.json, the build and deployment scripts (start, deploycommand, build) consistently use yarn, while development scripts (dev, lint, lint:fix) invoke tools directly. This represents a clean separation between orchestration scripts and development tool invocation.
Applied to files:
README.md
📚 Learning: 2025-08-30T09:26:39.331Z
Learnt from: warengonzaga
PR: wgtechlabs/unthread-discord-bot#45
File: src/services/unthread.ts:184-193
Timestamp: 2025-08-30T09:26:39.331Z
Learning: In the wgtechlabs/unthread-discord-bot repository, the current custom Unthread service implementation in src/services/unthread.ts is planned to be replaced with an official SDK in the future, as mentioned by warengonzaga.
Applied to files:
README.md
🪛 LanguageTool
README.md
[grammar] ~88-~88: There might be a mistake here.
Context: ...urrent bot version. ## 🏗️ Architecture This bot is built with TypeScript fo...
(QB_NEW_EN)
[grammar] ~94-~94: There might be a mistake here.
Context: ...e safety and better code maintainability - Discord.js v14: Modern Discord API int...
(QB_NEW_EN)
[grammar] ~95-~95: There might be a mistake here.
Context: ...s v14**: Modern Discord API interactions - Express.js: Webhook server for Unthrea...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ... Webhook server for Unthread integration - Node.js 18+: Runtime environment - **Y...
(QB_NEW_EN)
[grammar] ~97-~97: There might be a mistake here.
Context: ...n - Node.js 18+: Runtime environment - Yarn with PnP: Package management and ...
(QB_NEW_EN)
[grammar] ~98-~98: There might be a mistake here.
Context: ...age management and dependency resolution - ESLint: Code quality and consistent fo...
(QB_NEW_EN)
[grammar] ~99-~99: There might be a mistake here.
Context: ...: Code quality and consistent formatting - Redis: Required for caching and data p...
(QB_NEW_EN)
[grammar] ~128-~128: There might be a mistake here.
Context: ...quired for proper dependency management) - TypeScript: Automatically managed via ...
(QB_NEW_EN)
[grammar] ~129-~129: There might be a mistake here.
Context: ...pt**: Automatically managed via Yarn SDK - Discord Application: Bot token and pro...
(QB_NEW_EN)
[grammar] ~265-~265: There might be a mistake here.
Context: ...# 🌐 Webhook Configuration (Development) For local development, you'll need to ex...
(QB_NEW_EN)
🔇 Additional comments (5)
README.md (5)
88-101
: Architecture section reads well; nothing blocking.The tech stack and positioning are coherent and match the codebase direction. No action needed.
194-201
: Corepack + Yarn PnP note is spot-on.Clear, prevents version drift. No further action.
229-246
: Dev workflow section is crisp and aligned.Commands reflect the intended TS + ESLint flow. LGTM.
302-320
: Forum channel configuration reads correctly.Validation note is helpful; no changes required.
269-279
: Route & secret wiring confirmed: POST /webhook/unthread is registered with rawBodyJsonMiddleware and webhookHandler, and HMAC signature validation using UNTHREAD_WEBHOOK_SECRET (via x-unthread-signature) is correctly implemented.
Summary by CodeRabbit
New Features
Refactor
Documentation
Chores