Telegram-бот Boltun AI на grammY + Mastra
- Отвечает только администраторам из ADMIN_IDS - Личности на чат: встроенные и пользовательские (/personas, /persona, /persona_add, /persona_del) - Вопросы через /ask, упоминание, ответ боту или личку - Сохраняет сообщения чата в SQLite; агент по просьбе читает их инструментом read_chat_messages - Модели через OpenRouter (по умолчанию DeepSeek) - Слои: libs, repositories, services, bot/handlers - Dockerfile для деплоя в Dokploy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
58
src/repositories/message-repository.ts
Normal file
58
src/repositories/message-repository.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { Db } from "../libs/db.js";
|
||||
import type { ChatMessage } from "../types.js";
|
||||
|
||||
interface MessageRow {
|
||||
chat_id: number;
|
||||
message_id: number;
|
||||
date: number;
|
||||
author: string;
|
||||
text: string;
|
||||
reply_to_message_id: number | null;
|
||||
}
|
||||
|
||||
const toMessage = (row: MessageRow): ChatMessage => ({
|
||||
chatId: row.chat_id,
|
||||
messageId: row.message_id,
|
||||
date: row.date,
|
||||
author: row.author,
|
||||
text: row.text,
|
||||
replyToMessageId: row.reply_to_message_id,
|
||||
});
|
||||
|
||||
export const createMessageRepository = (db: Db) => {
|
||||
const statements = {
|
||||
upsert: db.prepare(`
|
||||
INSERT INTO messages (chat_id, message_id, date, author, text, reply_to_message_id)
|
||||
VALUES (@chatId, @messageId, @date, @author, @text, @replyToMessageId)
|
||||
ON CONFLICT(chat_id, message_id) DO UPDATE SET text = excluded.text
|
||||
`),
|
||||
pruneChat: db.prepare(`
|
||||
DELETE FROM messages WHERE chat_id = ? AND message_id <= (
|
||||
SELECT message_id FROM messages WHERE chat_id = ?
|
||||
ORDER BY message_id DESC LIMIT 1 OFFSET ?
|
||||
)
|
||||
`),
|
||||
latest: db.prepare("SELECT * FROM messages WHERE chat_id = ? ORDER BY message_id DESC LIMIT ?"),
|
||||
findById: db.prepare("SELECT * FROM messages WHERE chat_id = ? AND message_id = ?"),
|
||||
};
|
||||
|
||||
/** Сохраняет сообщение (при повторе — обновляет текст) и оставляет в чате не больше `keep` последних. */
|
||||
const upsertAndPrune = db.transaction((message: ChatMessage, keep: number) => {
|
||||
statements.upsert.run(message);
|
||||
statements.pruneChat.run(message.chatId, message.chatId, keep);
|
||||
});
|
||||
|
||||
/** Последние `limit` сообщений чата в хронологическом порядке. */
|
||||
const latest = (chatId: number, limit: number): ChatMessage[] =>
|
||||
(statements.latest.all(chatId, limit) as MessageRow[]).reverse().map(toMessage);
|
||||
|
||||
const findById = (chatId: number, messageId: number): ChatMessage | undefined => {
|
||||
const row = statements.findById.get(chatId, messageId) as MessageRow | undefined;
|
||||
|
||||
return row && toMessage(row);
|
||||
};
|
||||
|
||||
return { upsertAndPrune, latest, findById };
|
||||
};
|
||||
|
||||
export type MessageRepository = ReturnType<typeof createMessageRepository>;
|
||||
Reference in New Issue
Block a user