diff --git a/services/telegram-bot/src/bots/telegram/attention-handler.ts b/services/telegram-bot/src/bots/telegram/attention-handler.ts index db0318633..77b23e880 100644 --- a/services/telegram-bot/src/bots/telegram/attention-handler.ts +++ b/services/telegram-bot/src/bots/telegram/attention-handler.ts @@ -15,8 +15,8 @@ export function createAttentionHandler(bot: BotSelf, config: AttentionConfig) { // Private utility functions const calculateNewResponseRate = () => { - const timeSinceLastInteraction = (Date.now() - state.stats.lastInteractionTime) / 60000 - const decayFactor = Math.max(0, 1 - timeSinceLastInteraction * config.decayRatePerMinute) + const minutesSinceLastInteraction = (Date.now() - state.stats.lastInteractionTime) / 60000 + const decayFactor = Math.max(0, 1 - minutesSinceLastInteraction * config.decayRatePerMinute) // Reset to max if at minimum and new interactions occurred if (state.currentResponseRate <= config.responseRateMin @@ -25,8 +25,8 @@ export function createAttentionHandler(bot: BotSelf, config: AttentionConfig) { } let newRate = state.currentResponseRate - newRate += state.stats.mentionCount * 0.2 // Mention multiplier - newRate += state.stats.triggerWordCount * 0.2 // Trigger word multiplier + newRate += state.stats.mentionCount * 100 // Mention multiplier + newRate += state.stats.triggerWordCount * 50 // Trigger word multiplier newRate *= decayFactor return Math.min(Math.max(newRate, config.responseRateMin), config.responseRateMax) diff --git a/services/telegram-bot/src/bots/telegram/index.ts b/services/telegram-bot/src/bots/telegram/index.ts index beea99963..cabf1c4a6 100644 --- a/services/telegram-bot/src/bots/telegram/index.ts +++ b/services/telegram-bot/src/bots/telegram/index.ts @@ -11,8 +11,8 @@ import { Bot } from 'grammy' import { imagineAnAction } from '../../llm/actions' import { interpretPhotos } from '../../llm/photo' import { interpretSticker } from '../../llm/sticker' +import { recordMessage } from '../../models' import { listJoinedChats, recordJoinedChat } from '../../models/chats' -import { createAttentionHandler } from './attention-handler' import { readMessage } from './loop/read-message' import { sendMayStructuredMessage } from './utils/message' @@ -22,50 +22,65 @@ async function isChatIdBotAdmin(chatId: number) { } async function handleLoop(state: BotSelf, msgs?: LLMMessage[], forGroupId?: string) { + state.logger.log('handleLoop') + // Create a new abort controller for this loop execution if (state.currentAbortController) { state.currentAbortController.abort() } state.currentAbortController = new AbortController() + const currentController = state.currentAbortController // Store reference to current controller + + if (msgs == null) { + msgs = [] + } try { try { - const action = await imagineAnAction(state.unreadMessages, state.currentAbortController, msgs) + const action = await imagineAnAction(state.unreadMessages, currentController, msgs) switch (action.action) { case 'readMessages': + // eslint-disable-next-line no-case-declarations + let unreadMessagesForThisChat: Message[] | undefined = state.unreadMessages[action.groupId] + if (forGroupId && forGroupId === action.groupId.toString() - && state.unreadMessages[action.groupId] - && state.unreadMessages[action.groupId].length > 0) { + && unreadMessagesForThisChat + && unreadMessagesForThisChat.length > 0) { state.logger.log(`Interrupting message processing for group ${action.groupId} - new messages arrived`) return handleLoop(state) } if (Object.keys(state.unreadMessages).length === 0) { + state.logger.log('No unread messages - deleting all unread messages') + state.unreadMessages = {} break } if (action.groupId == null) { + state.logger.log('No group ID - deleting all unread messages') + state.unreadMessages = {} break } - if (state.unreadMessages[action.groupId].length === 0) { + if (!Array.isArray(unreadMessagesForThisChat)) { + state.logger.log(`Unread messages for group ${action.groupId} is not an array - converting to array`) + unreadMessagesForThisChat = [] + } + if (unreadMessagesForThisChat.length === 0) { + state.logger.log(`No unread messages for group ${action.groupId} - deleting`) delete state.unreadMessages[action.groupId] break } - // eslint-disable-next-line no-case-declarations - const unreadMessages = state.unreadMessages[action.groupId] as Message[] + // // Add attention check before processing action + // // eslint-disable-next-line no-case-declarations + // const shouldRespond = await state.attentionHandler.shouldRespond(forGroupId, unreadMessagesForThisChat) - // Add attention check before processing action - // eslint-disable-next-line no-case-declarations - const shouldRespond = await state.attentionHandler.shouldRespond(forGroupId, unreadMessages) + // if (!shouldRespond.shouldAct) { + // state.logger.withField('reason', shouldRespond.reason).withField('responseRate', shouldRespond.responseRate).log('Skipping message due to attention check') + // state.unreadMessages[action.groupId] = unreadMessagesForThisChat.shift() + // return { break: true } + // } - if (!shouldRespond.shouldAct) { - state.logger.withField('reason', shouldRespond.reason) - .withField('responseRate', shouldRespond.responseRate) - .log('Skipping message due to attention check') - return { break: true } - } - - await readMessage(state, action, unreadMessages) + await readMessage(state, action, unreadMessagesForThisChat, currentController) break case 'listChats': msgs.push(message.user(`List of chats:${(await listJoinedChats()).map(chat => `ID:${chat.chat_id}, Name:${chat.chat_name}`).join('\n')}`)) @@ -74,6 +89,10 @@ async function handleLoop(state: BotSelf, msgs?: LLMMessage[], forGroupId?: stri case 'sendMessage': await sendMayStructuredMessage(state, action.content, action.groupId) break + default: + msgs.push(message.user(`The action you sent ${action.action} haven't implemented yet by developer.`)) + await handleLoop(state, msgs) + break } } catch (err) { @@ -81,35 +100,33 @@ async function handleLoop(state: BotSelf, msgs?: LLMMessage[], forGroupId?: stri } } catch (err) { - // Check if this is an abort error, which we can safely ignore if (err.name === 'AbortError') { state.logger.log('Operation was aborted due to interruption') return } + state.logger.withError(err).log('Error occurred') } finally { - // Clean up the abort controller - state.currentAbortController = null + // Only clean up if this is still the current controller + if (state.currentAbortController === currentController) { + state.currentAbortController = null + } } } function loop(state: BotSelf) { setTimeout(() => { handleLoop(state) - .then(() => loop(state)) + .then(() => {}) .catch((err) => { - if (err.name === 'AbortError') { - // This is expected when we interrupt processing - state.logger.log('Main loop was aborted - restarting loop') - } - else { - state.logger.withError(err).log('Error in main loop') - } - // Always continue the loop - loop(state) + if (err.name === 'AbortError') + state.logger.log('main loop was aborted - restarting loop') + else + state.logger.withError(err).log('error in main loop') }) - }, 5000) + .finally(() => loop(state)) + }, 5 * 60 * 1000) } function newBotSelf(bot: Bot, logger: Logg): BotSelf { @@ -125,16 +142,16 @@ function newBotSelf(bot: Bot, logger: Logg): BotSelf { attentionHandler: undefined, } - botSelf.attentionHandler = createAttentionHandler(botSelf, { - initialResponseRate: 0.3, - responseRateMin: 0.1, - responseRateMax: 0.8, - cooldownMs: 30000, // 30 seconds - triggerWords: ['hey bot', 'hello bot'], - ignoreWords: ['ignore me'], - decayRatePerMinute: 0.1, - decayCheckIntervalMs: 20000, - }) + // botSelf.attentionHandler = createAttentionHandler(botSelf, { + // initialResponseRate: 0.3, + // responseRateMin: 0.2, + // responseRateMax: 1, + // cooldownMs: 5000, // 30 seconds + // triggerWords: ['ReLU', 'relu', 'RELU', 'Relu', '热卤'], + // ignoreWords: ['ignore me'], + // decayRatePerMinute: 0.05, + // decayCheckIntervalMs: 20000, + // }) return botSelf } @@ -167,24 +184,29 @@ async function processMessageQueue(state: BotSelf) { if (nextMsg.status === 'ready') { await recordJoinedChat(nextMsg.message.chat.id.toString(), nextMsg.message.chat.title) - if (state.unreadMessages[nextMsg.message.chat.id] == null) { - state.unreadMessages[nextMsg.message.chat.id] = [] + await recordMessage(state.bot.botInfo, nextMsg.message) + + let unreadMessagesForThisChat = state.unreadMessages[nextMsg.message.chat.id] + + if (unreadMessagesForThisChat == null) { + state.logger.withField('chatId', nextMsg.message.chat.id).log('unread messages for this chat is null - creating empty array') + unreadMessagesForThisChat = [] + } + if (!Array.isArray(unreadMessagesForThisChat)) { + state.logger.withField('chatId', nextMsg.message.chat.id).log('unread messages for this chat is not an array - converting to array') + unreadMessagesForThisChat = [] } - state.unreadMessages[nextMsg.message.chat.id].push(nextMsg.message) - if (state.unreadMessages[nextMsg.message.chat.id].length > 20) { - state.unreadMessages[nextMsg.message.chat.id] = state.unreadMessages[nextMsg.message.chat.id].slice(-20) + unreadMessagesForThisChat.push(nextMsg.message) + + if (unreadMessagesForThisChat.length > 20) { + unreadMessagesForThisChat = unreadMessagesForThisChat.slice(-20) } - // Check if we're currently processing this chat group - if (state.currentAbortController - && state.currentTask - && state.unreadMessages[nextMsg.message.chat.id].length > 0) { - // Interrupt the current processing - state.currentAbortController.abort() - state.logger.log(`Interrupting due to new message in chat ${nextMsg.message.chat.id}`) - } + state.unreadMessages[nextMsg.message.chat.id] = unreadMessagesForThisChat + // Trigger immediate processing when messages are ready + handleLoop(state, [], nextMsg.message.chat.id.toString()) state.messageQueue.shift() } } @@ -261,11 +283,9 @@ export async function startTelegramBot() { } await bot.init() - log.withField('bot_username', bot.botInfo.username).log('Authorized bot') + log.withField('bot_username', bot.botInfo.username).log('bot initialized') - bot.start({ - drop_pending_updates: true, - }) + bot.start() try { loop(state) diff --git a/services/telegram-bot/src/bots/telegram/loop/read-message.ts b/services/telegram-bot/src/bots/telegram/loop/read-message.ts index 163bed1b5..df36bad6a 100644 --- a/services/telegram-bot/src/bots/telegram/loop/read-message.ts +++ b/services/telegram-bot/src/bots/telegram/loop/read-message.ts @@ -12,14 +12,19 @@ import { chatMessageToOneLine, telegramMessageToOneLine } from '../../../models/ import { systemPrompt } from '../../../prompts/system-v1' import { sendMayStructuredMessage } from '../utils/message' -export async function readMessage(state: BotSelf, action: ReadMessagesAction, unreadMessages: Message[]): Promise<{ - loop?: boolean - break?: boolean -}> { +export async function readMessage( + state: BotSelf, + action: ReadMessagesAction, + unreadMessages: Message[], + abortController: AbortController, +): Promise<{ + loop?: boolean + break?: boolean + }> { const logger = useLogg('readMessage').useGlobalConfig() const lastNMessages = await findLastNMessages(action.groupId, 30) - const lastNMessagesOneliner = lastNMessages.map(msg => chatMessageToOneLine(msg)).join('\n') + const lastNMessagesOneliner = lastNMessages.map(msg => chatMessageToOneLine(state.bot, msg)).join('\n') logger.withField('number_of_last_n_messages', lastNMessages.length).log('Successfully found last N messages') @@ -31,7 +36,7 @@ export async function readMessage(state: BotSelf, action: ReadMessagesAction, un apiKey: env.EMBEDDING_API_KEY!, model: env.EMBEDDING_MODEL!, input: msg.text || msg.caption || '', - abortSignal: state.currentAbortController.signal, + abortSignal: abortController.signal, }) return { @@ -48,7 +53,7 @@ export async function readMessage(state: BotSelf, action: ReadMessagesAction, un const unreadHistoryMessageOneliner = unreadHistoryMessages.join('\n') state.unreadMessages[action.groupId] = [] - const relevantChatMessages = await findRelevantMessages(unreadHistoryMessagesEmbedding) + const relevantChatMessages = await findRelevantMessages(state.bot, unreadHistoryMessagesEmbedding) const relevantChatMessagesOneliner = (await Promise.all(relevantChatMessages.map(async msgs => msgs.join('\n')))).join('\n') logger.withField('number_of_relevant_chat_messages', relevantChatMessages.length).log('Successfully composed relevant chat messages') @@ -62,13 +67,18 @@ export async function readMessage(state: BotSelf, action: ReadMessagesAction, un + 'Last 30 messages:\n' + `${lastNMessagesOneliner || 'No messages'}` + '\n' - + 'I helped you searched these relevant chat messages may help you recall the memories:' + + 'I helped you searched these relevant chat messages may help you recall the memories:\n' + `${relevantChatMessagesOneliner || 'No relevant messages'}` + '\n' - + 'All the messages you requested to read:' + + 'All the messages you requested to read:\n' + `${unreadHistoryMessageOneliner || 'No messages'}` + '\n' - + 'Choose your action. Would you like to say something? Or ignore?', + + 'Based on your personalities, imaging you have your own choice and interest over the world, ' + + 'giving the above context and chat history, would you like to participate in the conversation ' + + 'about the topic? Or will you aggressively diss or piss off about the opinions of others?\n' + + 'Feel free to ignore by just sending an empty array (i.e. []).' + + '\n' + + 'Choose your action.', ), ) @@ -80,7 +90,7 @@ export async function readMessage(state: BotSelf, action: ReadMessagesAction, un baseURL: env.LLM_API_BASE_URL!, model: env.LLM_MODEL!, messages, - abortSignal: state.currentAbortController.signal, + abortSignal: abortController.signal, }) response.text = response.text diff --git a/services/telegram-bot/src/bots/telegram/utils/message.ts b/services/telegram-bot/src/bots/telegram/utils/message.ts index 2d1e4a577..8f3cf393b 100644 --- a/services/telegram-bot/src/bots/telegram/utils/message.ts +++ b/services/telegram-bot/src/bots/telegram/utils/message.ts @@ -25,7 +25,7 @@ export function parseMayStructuredMessage(responseText: string) { return } - return array + return array.filter(Boolean) } return [responseText] @@ -39,6 +39,7 @@ export async function sendMayStructuredMessage( const chat = (await listJoinedChats()).find((chat) => { return chat.chat_id === groupId }) + state.logger.withField('chat', chat).log('Chat found') if (!chat) { state.logger.withField('groupId', groupId).log('Chat not found') return diff --git a/services/telegram-bot/src/llm/actions.ts b/services/telegram-bot/src/llm/actions.ts index c805876c6..6da406af6 100644 --- a/services/telegram-bot/src/llm/actions.ts +++ b/services/telegram-bot/src/llm/actions.ts @@ -14,76 +14,78 @@ export async function imagineAnAction(unreadMessages: Record, const logger = useLogg('imagineAnAction').useGlobalConfig() if (agentMessages == null) { - agentMessages = message.messages( - message.system('' - + `${systemPrompt()}` - + '\n' - + 'I am one of your system component, called Ticking system, which is responsible to keep track of the time, and ' - + 'help you schedule, retain focus, and keep eyes on different tasks, and ideas you have.' - + '\n' - + 'Please remember, I am not a human, I am a system that part of you. You should return system/machine readable ' - + 'messages to me, so I can understand and process them correctly.' - + '\n' - + 'Now, please, based on the following context, choose a right action from the listing of the tools you want to ' - + 'take next:', - ), - message.system( - [ - { - description: 'List all available chats, best to do before you want to send a message to a chat.', - example: { action: 'listChats' }, - }, - { - description: 'Send a message to a specific chat group. If you want to express anything to anyone or your friends in group, you can use this action.', - example: { action: 'sendMessage', content: '', groupId: 'id of chat to send to' }, - }, - { - description: 'Read unread messages from a specific chat group. If you want to read the unread messages from a specific chat group, you can use this action.', - example: { action: 'readMessages', groupId: 'id of chat to send to' }, - }, - { - description: 'Continue the current task, which means to keep your current state unchanged, I\'ll ask you again in next tick.', - example: { action: 'continue' }, - }, - { - description: 'Take a break, which means to clear out ongoing tasks, but keep the short-term memory, and I\'ll ask you again in next tick.', - example: { action: 'break' }, - }, - { - description: 'Sleep, which means to clear out ongoing tasks, and clear out the working memory, and I\'ll ask you again in next tick.', - example: { action: 'sleep' }, - }, - { - description: 'By giving references to contexts, come up ideas to record in long-term memory.', - example: { action: 'comeUpIdeas', ideas: ['I want to tell everyone a story of myself', 'I want to google how to make a AI like me'] }, - }, - { - description: 'By giving references to contexts, come up goals with deadline and priority to record in long-term memory.', - example: { action: 'comeUpGoals', goals: [{ text: 'Learn to play Minecraft', deadline: '2025-05-01 23:59:59', priority: 6 }, { text: 'Learn anime of this season', deadline: '2025-01-08 23:59:59', priority: 9 }] }, - }, - // { example: { action: 'lookupShortTermMemory', query: '', category: 'chat or self' }, description: 'Look up the short-term, which means to recall the short-term memory from memory component.' }, - // { example: { action: 'lookupLongTermMemory', query: '', category: 'chat or self' }, description: 'Look up the long-term, which means to recall the long-term memory from memory component.' }, - // { example: { action: 'memorizeShortMemory', content: '', tags: ['keyword tag'] }, description: 'Memorize to short-term memory, which means to append things the short-term memory which will be included for a while, but will be eventually forgot.' }, - // { example: { action: 'memorizeLongMemory', content: '', tags: ['keyword tag'] }, description: 'Memorize to long-term memory, which means to append things the long-term memory which will be included for a long time, and hard to forget.' }, - // { example: { action: 'forgetShortTermMemory', where: { id: '' } }, description: 'Remove specific short-term memory entry from the memory component.' }, - // { example: { action: 'forgetLongTermMemory', where: { id: '' } }, description: 'Remove specific long-term memory entry from the memory component.' }, - // { example: { action: 'searchGoogle', query: '' }, description: 'Search Google with the query.' }, - ] - .map((item, index) => `${index}: ${JSON.stringify(item.example)}: ${item.description}`) - .join('\n'), - ), - message.system('' - + `Now the time is: ${new Date().toLocaleString()}. ` - + `You have total ${Object.values(unreadMessages).reduce((acc, cur) => acc + cur.length, 0)} unread messages.` - + '\n' - + 'Unread messages count are:\n' - + `${Object.entries(unreadMessages).map(([key, value]) => `ID:${key}, Unread message count:${value.length}`).join('\n')}` - + '', - ), - message.user('What do you want to do? Respond with the action and parameters you choose in JSON only, without any explanation and markups'), - ) + agentMessages = [] } + agentMessages.push( + message.system('' + + `${systemPrompt()}` + + '\n' + + 'I am one of your system component, called Ticking system, which is responsible to keep track of the time, and ' + + 'help you schedule, retain focus, and keep eyes on different tasks, and ideas you have.' + + '\n' + + 'Please remember, I am not a human, I am a system that part of you. You should return system/machine readable ' + + 'messages to me, so I can understand and process them correctly.' + + '\n' + + 'Now, please, based on the following context, choose a right action from the listing of the tools you want to ' + + 'take next:', + ), + message.system( + [ + { + description: 'List all available chats, best to do before you want to send a message to a chat.', + example: { action: 'listChats' }, + }, + { + description: 'Send a message to a specific chat group. If you want to express anything to anyone or your friends in group, you can use this action.', + example: { action: 'sendMessage', content: '', groupId: 'id of chat to send to' }, + }, + { + description: 'Read unread messages from a specific chat group. If you want to read the unread messages from a specific chat group, you can use this action.', + example: { action: 'readMessages', groupId: 'id of chat to send to' }, + }, + { + description: 'Continue the current task, which means to keep your current state unchanged, I\'ll ask you again in next tick.', + example: { action: 'continue' }, + }, + { + description: 'Take a break, which means to clear out ongoing tasks, but keep the short-term memory, and I\'ll ask you again in next tick.', + example: { action: 'break' }, + }, + { + description: 'Sleep, which means to clear out ongoing tasks, and clear out the working memory, and I\'ll ask you again in next tick.', + example: { action: 'sleep' }, + }, + { + description: 'By giving references to contexts, come up ideas to record in long-term memory.', + example: { action: 'comeUpIdeas', ideas: ['I want to tell everyone a story of myself', 'I want to google how to make a AI like me'] }, + }, + { + description: 'By giving references to contexts, come up goals with deadline and priority to record in long-term memory.', + example: { action: 'comeUpGoals', goals: [{ text: 'Learn to play Minecraft', deadline: '2025-05-01 23:59:59', priority: 6 }, { text: 'Learn anime of this season', deadline: '2025-01-08 23:59:59', priority: 9 }] }, + }, + // { example: { action: 'lookupShortTermMemory', query: '', category: 'chat or self' }, description: 'Look up the short-term, which means to recall the short-term memory from memory component.' }, + // { example: { action: 'lookupLongTermMemory', query: '', category: 'chat or self' }, description: 'Look up the long-term, which means to recall the long-term memory from memory component.' }, + // { example: { action: 'memorizeShortMemory', content: '', tags: ['keyword tag'] }, description: 'Memorize to short-term memory, which means to append things the short-term memory which will be included for a while, but will be eventually forgot.' }, + // { example: { action: 'memorizeLongMemory', content: '', tags: ['keyword tag'] }, description: 'Memorize to long-term memory, which means to append things the long-term memory which will be included for a long time, and hard to forget.' }, + // { example: { action: 'forgetShortTermMemory', where: { id: '' } }, description: 'Remove specific short-term memory entry from the memory component.' }, + // { example: { action: 'forgetLongTermMemory', where: { id: '' } }, description: 'Remove specific long-term memory entry from the memory component.' }, + // { example: { action: 'searchGoogle', query: '' }, description: 'Search Google with the query.' }, + ] + .map((item, index) => `${index}: ${JSON.stringify(item.example)}: ${item.description}`) + .join('\n'), + ), + message.system('' + + `Now the time is: ${new Date().toLocaleString()}. ` + + `You have total ${Object.values(unreadMessages).reduce((acc, cur) => acc + cur.length, 0)} unread messages.` + + '\n' + + 'Unread messages count are:\n' + + `${Object.entries(unreadMessages).map(([key, value]) => `ID:${key}, Unread message count:${value.length}`).join('\n')}` + + '', + ), + message.user('What do you want to do? Respond with the action and parameters you choose in JSON only, without any explanation and markups'), + ) + const res = await generateText({ apiKey: env.LLM_API_KEY!, baseURL: env.LLM_API_BASE_URL!, diff --git a/services/telegram-bot/src/models/chat-message.ts b/services/telegram-bot/src/models/chat-message.ts index 04846da07..7b6b55c08 100644 --- a/services/telegram-bot/src/models/chat-message.ts +++ b/services/telegram-bot/src/models/chat-message.ts @@ -1,5 +1,6 @@ import type { EmbedResult } from '@xsai/embed' import type { SQL } from 'drizzle-orm' +import type { Bot } from 'grammy' import type { Message, UserFromGetMe } from 'grammy/types' import { env } from 'node:process' @@ -71,15 +72,17 @@ export async function recordMessage(botInfo: UserFromGetMe, message: Message) { } export async function findLastNMessages(chatId: string, n: number) { - return await useDrizzle() + const res = await useDrizzle() .select() .from(chatMessagesTable) .where(eq(chatMessagesTable.in_chat_id, chatId)) .orderBy(desc(chatMessagesTable.created_at)) .limit(n) + + return res.reverse() } -export async function findRelevantMessages(unreadHistoryMessagesEmbedding: { embedding: number[], message: Message }[]) { +export async function findRelevantMessages(bot: Bot, unreadHistoryMessagesEmbedding: { embedding: number[], message: Message }[]) { const db = useDrizzle() const contextWindowSize = 5 // Number of messages to include before and after const logger = useLogg('findRelevantMessages').useGlobalConfig() @@ -180,7 +183,7 @@ export async function findRelevantMessages(unreadHistoryMessagesEmbedding: { emb logger.withField('number_of_context_messages', contextMessages.length).log('Combined context messages') - const contextMessagesOneliner = (await Promise.all(contextMessages.map(m => chatMessageToOneLine(m)))) + const contextMessagesOneliner = (await Promise.all(contextMessages.map(m => chatMessageToOneLine(bot, m)))) return `One of the relevant message along with the context:\n${contextMessagesOneliner}` }), ) diff --git a/services/telegram-bot/src/models/common.ts b/services/telegram-bot/src/models/common.ts index 209a893a9..bcdbecbb1 100644 --- a/services/telegram-bot/src/models/common.ts +++ b/services/telegram-bot/src/models/common.ts @@ -5,12 +5,18 @@ import type { chatMessagesTable } from '../db/schema' import { findPhotoDescription } from './photos' import { findStickerDescription } from './stickers' -export function chatMessageToOneLine(message: Omit) { - if (message.is_reply) { - return `${new Date(message.created_at).toLocaleString()} User ${message.from_name} replied to ${message.reply_to_name} in same group said: ${message.content}` +export function chatMessageToOneLine(bot: Bot, message: Omit) { + let userDisplayName = `User [${message.from_name}]` + + if (bot.botInfo.id.toString() === message.from_id) { + userDisplayName = 'Yourself' } - return `${new Date(message.created_at).toLocaleString()} User ${message.from_name} sent in same group said: ${message.content}` + if (message.is_reply) { + return `${new Date(message.created_at).toLocaleString()} ${userDisplayName} replied to ${message.reply_to_name} in same group said: ${message.content}` + } + + return `${new Date(message.created_at).toLocaleString()} ${userDisplayName} sent in same group said: ${message.content}` } export async function telegramMessageToOneLine(bot: Bot, message: Message) { @@ -18,24 +24,27 @@ export async function telegramMessageToOneLine(bot: Bot, message: Message) { return '' } - const userDisplayName = `${message.from.first_name} ${message.from.last_name} (${message.from.username})` + let userDisplayName = `User [${message.from.first_name} ${message.from.last_name} (${message.from.username})]` + if (bot.botInfo.id.toString() === message.from.id.toString()) { + userDisplayName = 'Yourself' + } if (message.sticker != null) { const description = await findStickerDescription(message.sticker.file_id) - return `${new Date(message.date * 1000).toLocaleString()} User [${userDisplayName}] sent in Group [${message.chat.title}] a sticker, and description of the sticker is ${description}` + return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} sent in Group [${message.chat.title}] a sticker, and description of the sticker is ${description}` } if (message.photo != null) { const description = await findPhotoDescription(message.photo[0].file_id) - return `${new Date(message.date * 1000).toLocaleString()} User [${userDisplayName}] sent in Group [${message.chat.title}] a photo, and description of the photo is ${description}` + return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} sent in Group [${message.chat.title}] a photo, and description of the photo is ${description}` } if (message.reply_to_message != null) { if (bot.botInfo.username === message.reply_to_message.from.username) { - return `${new Date(message.date * 1000).toLocaleString()} User [${userDisplayName}] replied to your previous message [${message.reply_to_message.text}] in Group [${message.chat.title}] said: ${message.text}` + return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} replied to your previous message ${message.reply_to_message.text || message.caption} in Group [${message.chat.title}] said: ${message.text}` } else { - return `${new Date(message.date * 1000).toLocaleString()} User [${userDisplayName}] replied to [${message.reply_to_message.from.first_name}] in Group [${message.chat.title}] said: ${message.text}` + return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} replied to User [${message.reply_to_message.from.first_name} ${message.reply_to_message.from.last_name} (${message.reply_to_message.from.username})] in Group [${message.chat.title}] said: ${message.text}` } } - return `${new Date(message.date * 1000).toLocaleString()} User [${userDisplayName}] sent in Group [${message.chat.title}] said: ${message.text}` + return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} sent in Group [${message.chat.title}] said: ${message.text}` }