refactor: rename mascot to chatbot across entire codebase
Build & Deploy / build-and-push (discord-gateway) (push) Failing after 30s
Build & Deploy / build-and-push (backend) (push) Failing after 2m28s
Build & Deploy / build-and-push (proxy) (push) Successful in 2m28s

- Backend: mascot-chat module → chatbot, routes /mascot/chat → /chat
- Shared schema: pgMascotChatMessagesTable → pgChatbotMessagesTable
- Frontend: MascotProvider/useMascot → ChatbotProvider/useChatbot
- Gateway schema: update exports to match shared schema
- Docs: update all .md references (CLAUDE.md, README, specs, plans)
- All API routes, controller names, service classes, types renamed

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Developer
2026-07-28 15:00:29 +07:00
co-authored by Claude Opus 4.8
parent bd9e7d8151
commit 977a6f9653
38 changed files with 243 additions and 1221 deletions
+2 -2
View File
@@ -10,7 +10,7 @@ import { createAnalysisRouter } from "../modules/analysis/index.js";
import { createConfigRouter } from "../modules/config/index.js";
import { createDashboardRouter } from "../modules/dashboard/index.js";
import { createHealthRouter } from "../modules/health/index.js";
import { createMascotChatRouter } from "../modules/mascot-chat/index.js";
import { createChatbotRouter } from "../modules/chatbot/index.js";
import { createMediaRouter } from "../modules/media/index.js";
import { createMessagesRouter } from "../modules/messages/index.js";
import { createRecordingsRouter } from "../modules/recordings/index.js";
@@ -64,7 +64,7 @@ export function createHttpApp(): Express {
app.use("/api", createDashboardRouter());
app.use("/api", createMessagesRouter());
app.use("/api", createAnalysisRouter());
app.use("/api", createMascotChatRouter());
app.use("/api", createChatbotRouter());
app.use("/api", createRecordingsRouter());
app.use("/api", createUiStateRouter());
app.use("/api", createMediaRouter());
@@ -1,15 +1,15 @@
import { createChildLogger } from "@bete/shared/logger";
import type { Request, Response } from "express";
import { asyncHandler } from "../../shared/middlewares/index.js";
import { mascotChatService } from "./mascot-chat.service.js";
import { chatbotService } from "./chatbot.service.js";
const logger = createChildLogger("mascot-chat.controller");
const logger = createChildLogger("chatbot.controller");
interface AuthenticatedRequest extends Request {
userId?: string;
}
export const handleMascotChat = asyncHandler(
export const handleChatbotChat = asyncHandler(
async (req: Request, res: Response) => {
const { message, context } = req.body as {
message: string;
@@ -29,26 +29,26 @@ export const handleMascotChat = asyncHandler(
logger.debug(
{ userId, messageLength: message.length, context },
"Received mascot chat message",
"Received chatbot chat message",
);
// Process message & generate response
const response = await mascotChatService.processMessage(
const response = await chatbotService.processMessage(
message,
context,
userId,
);
// Save conversation to database
await mascotChatService.saveConversation({
await chatbotService.saveConversation({
userId,
userMessage: message,
mascotResponse: response,
botResponse: response,
context,
timestamp: new Date(),
});
logger.info({ userId }, "Mascot chat processed successfully");
logger.info({ userId }, "Chatbot chat processed successfully");
res.status(200).json({
response,
@@ -57,12 +57,12 @@ export const handleMascotChat = asyncHandler(
},
);
export const getMascotChatHistory = asyncHandler(
export const getChatbotHistory = asyncHandler(
async (req: Request, res: Response) => {
const userId = (req as AuthenticatedRequest).userId || "anonymous";
const limit = Math.min(parseInt(req.query.limit as string, 10) || 50, 100);
const history = await mascotChatService.getChatHistory(userId, limit);
const history = await chatbotService.getChatHistory(userId, limit);
res.status(200).json({
history,
@@ -71,11 +71,11 @@ export const getMascotChatHistory = asyncHandler(
},
);
export const clearMascotChatHistory = asyncHandler(
export const clearChatbotHistory = asyncHandler(
async (req: Request, res: Response) => {
const userId = (req as AuthenticatedRequest).userId || "anonymous";
await mascotChatService.clearChatHistory(userId);
await chatbotService.clearChatHistory(userId);
res.status(200).json({
message: "Chat history cleared successfully",
@@ -1,11 +1,11 @@
import { pgMascotChatMessagesTable, pgMessagesTable } from "@bete/shared";
import { pgChatbotMessagesTable, pgMessagesTable } from "@bete/shared";
import { createChildLogger } from "@bete/shared/logger";
import { and, desc, eq, type SQL, sql } from "drizzle-orm";
import { getDatabase } from "../../shared/database/index.js";
const logger = createChildLogger("mascot-chat.repository");
const logger = createChildLogger("chatbot.repository");
export interface MascotChatContext {
export interface ChatbotContext {
messageCount?: number;
activeParticipants?: number;
lastActivity?: string;
@@ -17,17 +17,17 @@ export interface MascotChatContext {
export interface SaveConversationInput {
userId: string;
userMessage: string;
mascotResponse: string;
context?: MascotChatContext;
botResponse: string;
context?: ChatbotContext;
timestamp: Date;
}
export interface MascotChatHistoryRow {
export interface ChatbotHistoryRow {
id: string;
user_id: string;
user_message: string;
mascot_response: string;
context: MascotChatContext | null;
bot_response: string;
context: ChatbotContext | null;
created_at: string;
}
@@ -38,14 +38,14 @@ export interface ServerInsights {
warned: number;
}
export class MascotChatRepository {
export class ChatbotRepository {
async saveConversation(input: SaveConversationInput): Promise<void> {
const db = getDatabase();
await db.insert(pgMascotChatMessagesTable).values({
await db.insert(pgChatbotMessagesTable).values({
user_id: input.userId,
user_message: input.userMessage,
mascot_response: input.mascotResponse,
bot_response: input.botResponse,
context: (input.context ?? {}) as Record<string, unknown>,
created_at: input.timestamp,
});
@@ -56,27 +56,27 @@ export class MascotChatRepository {
async getChatHistory(
userId: string,
limit: number,
): Promise<MascotChatHistoryRow[]> {
): Promise<ChatbotHistoryRow[]> {
const db = getDatabase();
const rows = await db
.select()
.from(pgMascotChatMessagesTable)
.where(eq(pgMascotChatMessagesTable.user_id, userId))
.orderBy(desc(pgMascotChatMessagesTable.created_at))
.from(pgChatbotMessagesTable)
.where(eq(pgChatbotMessagesTable.user_id, userId))
.orderBy(desc(pgChatbotMessagesTable.created_at))
.limit(limit);
logger.debug({ userId, count: rows.length }, "Chat history fetched");
return rows.reverse() as unknown as MascotChatHistoryRow[];
return rows.reverse() as unknown as ChatbotHistoryRow[];
}
async clearChatHistory(userId: string): Promise<void> {
const db = getDatabase();
const deleted = await db
.delete(pgMascotChatMessagesTable)
.where(eq(pgMascotChatMessagesTable.user_id, userId))
.returning({ id: pgMascotChatMessagesTable.id });
.delete(pgChatbotMessagesTable)
.where(eq(pgChatbotMessagesTable.user_id, userId))
.returning({ id: pgChatbotMessagesTable.id });
logger.info(
{ userId, deletedRows: deleted.length },
@@ -135,4 +135,4 @@ export class MascotChatRepository {
}
}
export const mascotChatRepository = new MascotChatRepository();
export const chatbotRepository = new ChatbotRepository();
@@ -0,0 +1,22 @@
import express, { type Router } from "express";
import { validateBody } from "../../shared/middlewares/index.js";
import {
clearChatbotHistory,
getChatbotHistory,
handleChatbotChat,
} from "./chatbot.controller.js";
import { chatRequestSchema } from "./chatbot.schema.js";
export function createChatbotRouter(): Router {
const router = express.Router();
router.post(
"/chat",
validateBody(chatRequestSchema),
handleChatbotChat,
);
router.get("/chat/history", getChatbotHistory);
router.delete("/chat/history", clearChatbotHistory);
return router;
}
@@ -1,18 +1,18 @@
import { createChildLogger } from "@bete/shared/logger";
import { config } from "../../shared/config/index.js";
import type {
MascotChatContext,
MascotChatHistoryRow,
ChatbotContext,
ChatbotHistoryRow,
SaveConversationInput,
} from "./mascot-chat.repository.js";
import { mascotChatRepository } from "./mascot-chat.repository.js";
} from "./chatbot.repository.js";
import { chatbotRepository } from "./chatbot.repository.js";
const logger = createChildLogger("mascot-chat.service");
const logger = createChildLogger("chatbot.service");
class MascotChatService {
class ChatbotService {
async processMessage(
message: string,
context: MascotChatContext | undefined,
context: ChatbotContext | undefined,
userId: string,
): Promise<string> {
logger.info(
@@ -20,7 +20,7 @@ class MascotChatService {
"processMessage called",
);
const recentContext = await this.getRecentConversationContext(userId);
const serverInsights = await mascotChatRepository.getServerInsights(
const serverInsights = await chatbotRepository.getServerInsights(
context?.guildId,
context?.channelId,
);
@@ -39,29 +39,29 @@ class MascotChatService {
async saveConversation(input: SaveConversationInput): Promise<void> {
logger.info({ userId: input.userId }, "saveConversation called");
await mascotChatRepository.saveConversation(input);
await chatbotRepository.saveConversation(input);
}
async getChatHistory(
userId: string,
limit: number,
): Promise<MascotChatHistoryRow[]> {
): Promise<ChatbotHistoryRow[]> {
logger.debug({ userId, limit }, "getChatHistory called");
return mascotChatRepository.getChatHistory(userId, limit);
return chatbotRepository.getChatHistory(userId, limit);
}
async clearChatHistory(userId: string): Promise<void> {
logger.info({ userId }, "clearChatHistory called");
await mascotChatRepository.clearChatHistory(userId);
await chatbotRepository.clearChatHistory(userId);
}
private async getRecentConversationContext(
userId: string,
): Promise<string[]> {
const history = await mascotChatRepository.getChatHistory(userId, 3);
const history = await chatbotRepository.getChatHistory(userId, 3);
return history.flatMap((row) => [
`User: ${row.user_message}`,
`Mascot: ${row.mascot_response}`,
`Bot: ${row.bot_response}`,
]);
}
@@ -71,7 +71,7 @@ class MascotChatService {
flagged: number;
warned: number;
}): string {
return `Kamu lagi ngobrol sama mascot Discord Watcher — temen ngobrol yang tau keadaan server.
return `Kamu lagi ngobrol sama chatbot Discord Watcher — temen ngobrol yang tau keadaan server.
Data server saat ini:
- Pesan: ${insights.total_messages}
@@ -92,7 +92,7 @@ Gaya ngobrol:
private buildHistoryMessages(
recentContext: string[],
): Array<{ role: "user" | "assistant"; content: string }> {
// recentContext is alternating User/Mascot messages
// recentContext is alternating User/Bot messages
return recentContext.map((text) => {
if (text.startsWith("User: ")) {
return { role: "user" as const, content: text.slice(6) };
@@ -178,4 +178,4 @@ Gaya ngobrol:
}
}
export const mascotChatService = new MascotChatService();
export const chatbotService = new ChatbotService();
@@ -0,0 +1 @@
export { createChatbotRouter } from "./chatbot.routes.js";
@@ -1 +0,0 @@
export { createMascotChatRouter } from "./mascot-chat.routes.js";
@@ -1,22 +0,0 @@
import express, { type Router } from "express";
import { validateBody } from "../../shared/middlewares/index.js";
import {
clearMascotChatHistory,
getMascotChatHistory,
handleMascotChat,
} from "./mascot-chat.controller.js";
import { chatRequestSchema } from "./mascot-chat.schema.js";
export function createMascotChatRouter(): Router {
const router = express.Router();
router.post(
"/mascot/chat",
validateBody(chatRequestSchema),
handleMascotChat,
);
router.get("/mascot/chat/history", getMascotChatHistory);
router.delete("/mascot/chat/history", clearMascotChatHistory);
return router;
}