import type { Db } from "../libs/db.js"; import type { ChatMember, MemberProfile } from "../types.js"; interface ChatMemberRow { chat_id: number; user_id: number; username: string | null; first_name: string; last_name: string | null; message_count: number; last_seen: number; } const toMember = (row: ChatMemberRow): ChatMember => ({ chatId: row.chat_id, userId: row.user_id, username: row.username, firstName: row.first_name, lastName: row.last_name, messageCount: row.message_count, lastSeen: row.last_seen, }); export const createChatMemberRepository = (db: Db) => { const statements = { upsert: db.prepare(` INSERT INTO chat_members (chat_id, user_id, username, first_name, last_name, message_count, last_seen) VALUES (@chatId, @userId, @username, @firstName, @lastName, @messages, @seenAt) ON CONFLICT(chat_id, user_id) DO UPDATE SET username = excluded.username, first_name = excluded.first_name, last_name = excluded.last_name, message_count = message_count + excluded.message_count, last_seen = MAX(last_seen, excluded.last_seen) `), listByChat: db.prepare("SELECT * FROM chat_members WHERE chat_id = ? ORDER BY last_seen DESC"), findById: db.prepare("SELECT * FROM chat_members WHERE chat_id = ? AND user_id = ?"), }; /** * Сохраняет актуальные имя и ник участника. * `messages` добавляется к счётчику сообщений, `seenAt` сдвигает время последней активности вперёд. */ const upsert = (chatId: number, profile: MemberProfile, activity: { messages: number; seenAt: number }): void => { statements.upsert.run({ chatId, ...profile, ...activity }); }; const listByChat = (chatId: number): ChatMember[] => (statements.listByChat.all(chatId) as ChatMemberRow[]).map(toMember); const findById = (chatId: number, userId: number): ChatMember | undefined => { const row = statements.findById.get(chatId, userId) as ChatMemberRow | undefined; return row && toMember(row); }; return { upsert, listByChat, findById }; }; export type ChatMemberRepository = ReturnType;