- Бот запоминает участников (имя, фамилия, ник, активность) и ID автора каждого сообщения - Упоминания в вопросе (@ник и по имени-ссылке) передаются модели с данными участника - Новые инструменты агента: list_chat_members, read_member_messages (поиск по нику/имени) - Бот обращается к людям по имени - Промпты вынесены в prompts/system.md и prompts/personas/<id>.md - Личности больше не редактируются из чата: скрипт seed синхронизирует их с БД при старте - Оставлена одна личность boltun (Dirty D) - Миграция старых баз: колонка messages.user_id Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
2.2 KiB
TypeScript
61 lines
2.2 KiB
TypeScript
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<typeof createChatMemberRepository>;
|