Files
GMW/services/discord-gateway/src/modules/message-capture/messages.search.ts
T
MythEclipseandClaude Opus 4.8 07032ab521 refactor: atomic, DRY, and logging improvements across codebase
- Split llmModerationClient.ts (2170 lines) into 5 focused sub-modules
- Split aiAnalyzer.ts (1282 lines) into 4 modular pipelines
- Split messages.db.ts (826 lines) into 5 domain-specific modules
- Moved shared schema to @bete/shared, eliminated backend duplication
- Added createChildLogger to all voice-recording and AI moderation modules
- Extracted tryCommandThenFallback, normalizeMediaState, DEFAULT_VOICE_STATUS
- Created shared pagination.ts utility, eliminated 5+ cursor-pagination duplications
- Created shared messageMapper.ts for row mapping
- Standardized backend error handling with asyncHandler
- Added frontend createLogger utility and useAsyncAction hook
- Added structured logging to frontend hooks, socket, and API client

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 19:46:08 +07:00

77 lines
2.3 KiB
TypeScript

import { createChildLogger, type Logger } from "@bete/shared/logger";
import { and, desc, eq, isNull, or, type SQL, sql } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import type * as schema from "../../shared/database/schema.js";
import { messagesTable } from "../../shared/database/schema.js";
import type { MessageRecord } from "../message-capture/types.js";
import { channelOrThreadCondition } from "./messages.crud.js";
// ─── MessagesSearch Class ─────────────────────────────────────────────────────
export class MessagesSearch {
private logger: Logger;
constructor(
private db: NodePgDatabase<typeof schema>,
_parentLogger?: Logger,
) {
this.logger = createChildLogger("messages-search");
}
async searchMessages(input: {
query: string;
channelId?: string;
guildId?: string;
limit?: number;
}): Promise<MessageRecord[]> {
this.logger.debug({ query: input.query }, "searchMessages entry");
try {
const { query, channelId, guildId, limit = 20 } = input;
const searchPattern = `%${query}%`;
const conditions: (SQL | undefined)[] = [
isNull(messagesTable.deleted_at),
];
if (guildId) {
conditions.push(eq(messagesTable.guild_id, guildId));
}
if (channelId) {
conditions.push(channelOrThreadCondition(channelId));
}
conditions.push(
or(
sql`${messagesTable.content} LIKE ${searchPattern}`,
sql`${messagesTable.edited_content} LIKE ${searchPattern}`,
),
);
const validConditions = conditions.filter(
(c): c is SQL => c !== undefined,
);
const rows = await this.db
.select()
.from(messagesTable)
.where(and(...validConditions))
.orderBy(desc(messagesTable.created_at))
.limit(limit);
return rows as MessageRecord[];
} catch (error) {
this.logger.error(
{
query: input.query,
channelId: input.channelId,
guildId: input.guildId,
error: error instanceof Error ? error.message : String(error),
},
"Failed to search messages",
);
throw error;
}
}
}