fix(telegram-bot): incorrect interruption handling & better prompt & reply to message

This commit is contained in:
Neko Ayaka
2025-03-26 18:26:28 +08:00
parent b34c397b8e
commit a1afd159f2
11 changed files with 612 additions and 85 deletions
@@ -0,0 +1,3 @@
ALTER TABLE "chat_messages" ADD COLUMN "platform_message_id" text DEFAULT '' NOT NULL;--> statement-breakpoint
ALTER TABLE "chat_messages" ADD COLUMN "reply_to_id" text DEFAULT '' NOT NULL;--> statement-breakpoint
ALTER TABLE "joined_chats" ADD CONSTRAINT "joined_chats_chat_id_unique" UNIQUE("chat_id");
@@ -0,0 +1,518 @@
{
"id": "ac870f0c-2008-4053-b630-f3fb06ee7f8a",
"prevId": "04759fdd-d3ec-4177-a6c3-69fc9df8105c",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.chat_messages": {
"name": "chat_messages",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"platform": {
"name": "platform",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"platform_message_id": {
"name": "platform_message_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"from_id": {
"name": "from_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"from_name": {
"name": "from_name",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"in_chat_id": {
"name": "in_chat_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"is_reply": {
"name": "is_reply",
"type": "boolean",
"primaryKey": false,
"notNull": true,
"default": false
},
"reply_to_name": {
"name": "reply_to_name",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"reply_to_id": {
"name": "reply_to_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"created_at": {
"name": "created_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"updated_at": {
"name": "updated_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"content_vector_1536": {
"name": "content_vector_1536",
"type": "vector(1536)",
"primaryKey": false,
"notNull": false
},
"content_vector_1024": {
"name": "content_vector_1024",
"type": "vector(1024)",
"primaryKey": false,
"notNull": false
},
"content_vector_768": {
"name": "content_vector_768",
"type": "vector(768)",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"chat_messages_content_vector_1536_index": {
"name": "chat_messages_content_vector_1536_index",
"columns": [
{
"expression": "content_vector_1536",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"chat_messages_content_vector_1024_index": {
"name": "chat_messages_content_vector_1024_index",
"columns": [
{
"expression": "content_vector_1024",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"chat_messages_content_vector_768_index": {
"name": "chat_messages_content_vector_768_index",
"columns": [
{
"expression": "content_vector_768",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.joined_chats": {
"name": "joined_chats",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"platform": {
"name": "platform",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"chat_id": {
"name": "chat_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"chat_name": {
"name": "chat_name",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"created_at": {
"name": "created_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"updated_at": {
"name": "updated_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"joined_chats_chat_id_unique": {
"name": "joined_chats_chat_id_unique",
"nullsNotDistinct": false,
"columns": [
"chat_id"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.photos": {
"name": "photos",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"platform": {
"name": "platform",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"file_id": {
"name": "file_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"image_base64": {
"name": "image_base64",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"image_path": {
"name": "image_path",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"created_at": {
"name": "created_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"updated_at": {
"name": "updated_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"description_vector_1536": {
"name": "description_vector_1536",
"type": "vector(1536)",
"primaryKey": false,
"notNull": false
},
"description_vector_1024": {
"name": "description_vector_1024",
"type": "vector(1024)",
"primaryKey": false,
"notNull": false
},
"description_vector_768": {
"name": "description_vector_768",
"type": "vector(768)",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"photos_description_vector_1536_index": {
"name": "photos_description_vector_1536_index",
"columns": [
{
"expression": "description_vector_1536",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"photos_description_vector_1024_index": {
"name": "photos_description_vector_1024_index",
"columns": [
{
"expression": "description_vector_1024",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"photos_description_vector_768_index": {
"name": "photos_description_vector_768_index",
"columns": [
{
"expression": "description_vector_768",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.stickers": {
"name": "stickers",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"platform": {
"name": "platform",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"file_id": {
"name": "file_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"image_base64": {
"name": "image_base64",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"image_path": {
"name": "image_path",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true,
"default": "''"
},
"created_at": {
"name": "created_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"updated_at": {
"name": "updated_at",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"default": 0
},
"description_vector_1536": {
"name": "description_vector_1536",
"type": "vector(1536)",
"primaryKey": false,
"notNull": false
},
"description_vector_1024": {
"name": "description_vector_1024",
"type": "vector(1024)",
"primaryKey": false,
"notNull": false
},
"description_vector_768": {
"name": "description_vector_768",
"type": "vector(768)",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"stickers_description_vector_1536_index": {
"name": "stickers_description_vector_1536_index",
"columns": [
{
"expression": "description_vector_1536",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"stickers_description_vector_1024_index": {
"name": "stickers_description_vector_1024_index",
"columns": [
{
"expression": "description_vector_1024",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
},
"stickers_description_vector_768_index": {
"name": "stickers_description_vector_768_index",
"columns": [
{
"expression": "description_vector_768",
"isExpression": false,
"asc": true,
"nulls": "last",
"opclass": "vector_cosine_ops"
}
],
"isUnique": false,
"concurrently": false,
"method": "hnsw",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
@@ -15,6 +15,13 @@
"when": 1742736839659,
"tag": "0001_next_talkback",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1742980513463,
"tag": "0002_calm_magdalene",
"breakpoints": true
}
]
}
@@ -225,7 +225,17 @@ async function processMessageQueue(state: BotSelf) {
}
if (nextMsg.status === 'ready') {
await recordJoinedChat(nextMsg.message.chat.id.toString(), nextMsg.message.chat.title)
switch (nextMsg.message.chat.type) {
case 'private':
await recordJoinedChat(nextMsg.message.chat.id.toString(), `${nextMsg.message.from.first_name} ${nextMsg.message.from.last_name}`)
break
case 'channel':
case 'group':
case 'supergroup':
await recordJoinedChat(nextMsg.message.chat.id.toString(), nextMsg.message.chat.title)
break
}
await recordMessage(state.bot.botInfo, nextMsg.message)
let unreadMessagesForThisChat = state.unreadMessages[nextMsg.message.chat.id]
@@ -250,7 +260,7 @@ async function processMessageQueue(state: BotSelf) {
state.logger.withField('chatId', nextMsg.message.chat.id).log('message queue processed, triggering immediate reaction')
// Trigger immediate processing when messages are ready
await handleLoop(state, [], nextMsg.message.chat.id.toString())
handleLoop(state, [], nextMsg.message.chat.id.toString())
state.messageQueue.shift()
}
}
@@ -72,11 +72,16 @@ export async function readMessage(
+ 'All the messages you requested to read:\n'
+ `${unreadHistoryMessageOneliner || 'No messages'}`
+ '\n'
+ 'Based on your personalities, imaging you have your own choice and interest over different, '
+ 'Based on your personalities, imaging you have your own choice and interest over different topics, '
+ '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. []).'
+ 'If you would like to participate, send me an array of messages you would like to send without telling you willing to participate.'
+ 'Feel free to ignore by just sending an empty array within a object with key "messages" (i.e.'
+ '{ "messages": [] }).'
+ 'If you would like to participate, send me an array of messages (i.e. { "messages": [] }) you would '
+ 'like to send without telling you willing to participate.'
+ 'If you would like to reply to any of the message, send me an array of messages (i.e. { "messages":'
+ '["message content"], "reply_to_message_id": "1234567890" }) with the message id of the message you '
+ 'want to reply to.'
+ '\n'
+ 'Choose your action.',
),
@@ -4,33 +4,42 @@ import { parseMayStructuredMessage } from './message'
describe('parseMayStructuredMessage', () => {
it('should return an array of messages', () => {
const result = parseMayStructuredMessage('["Hello, world!"]')
expect(result).toEqual(['Hello, world!'])
const result = parseMayStructuredMessage('{"messages": ["Hello, world!"]}')
expect(result).toMatchObject({ messages: ['Hello, world!'] })
})
it('should return an array of messages from multi-line input', () => {
const result = parseMayStructuredMessage(`[
const result = parseMayStructuredMessage(`{"messages": [
"Hello, world!",
"Hello, world!"
]`)
expect(result).toEqual(['Hello, world!', 'Hello, world!'])
]}`)
expect(result).toMatchObject({ messages: ['Hello, world!', 'Hello, world!'] })
})
it('should return an array of messages from multi-line input with extra whitespace', () => {
const result = parseMayStructuredMessage(`[
const result = parseMayStructuredMessage(`{"messages": [
"Hello, world!",
"Hello, world!"
]`)
expect(result).toEqual(['Hello, world!', 'Hello, world!'])
]}`)
expect(result).toMatchObject({ messages: ['Hello, world!', 'Hello, world!'] })
})
it('should return an object with messages and reply_to_message_id', () => {
const result = parseMayStructuredMessage(`{"messages": [
"Hello, world!",
"Hello, world!"
], "reply_to_message_id": "1234567890"
}`)
expect(result).toMatchObject({ messages: ['Hello, world!', 'Hello, world!'], reply_to_message_id: '1234567890' })
})
it('should return an array of messages from multi-line elements of input', () => {
const result = parseMayStructuredMessage(`[
const result = parseMayStructuredMessage(`{"messages": [
"Hello,
world!",
"Hello,
world!"
]`)
expect(result).toEqual(['Hello,\nworld!', 'Hello,\nworld!'])
]}`)
expect(result).toMatchObject({ messages: ['Hello,\nworld!', 'Hello,\nworld!'] })
})
})
@@ -12,23 +12,15 @@ import { cancellable, sleep } from '../../../utils/promise'
export function parseMayStructuredMessage(responseText: string) {
const logger = useLogg('parseMayStructuredMessage').useGlobalConfig()
// If we get here, the task wasn't cancelled, so we can send the response
// eslint-disable-next-line regexp/no-unused-capturing-group, regexp/no-super-linear-backtracking, regexp/confusing-quantifier
if (/\[(\s*.*)+\]/u.test(responseText)) {
// eslint-disable-next-line regexp/no-super-linear-backtracking, regexp/confusing-quantifier, regexp/no-unused-capturing-group
const result = /\[(\s*.*)+\]/u.exec(responseText)
logger.withField('text', JSON.stringify(responseText)).withField('result', result?.[0]).log('Multiple messages detected')
// eslint-disable-next-line regexp/no-super-linear-backtracking, regexp/optimal-quantifier-concatenation
const result = /^\{(("?)*.*\s*)*\}$/u.exec(responseText)
if (result) {
logger.withField('text', JSON.stringify(responseText)).withField('result', result).log('Multiple messages detected')
const array = parse(result?.[0]) as string[]
if (array == null || !Array.isArray(array) || array.length === 0) {
logger.withField('text', JSON.stringify(responseText)).withField('result', result?.[0]).log('No messages to send')
return
}
return array.filter(Boolean)
return parse(result?.[0]) as ({ messages?: string[], reply_to_message_id?: string } | undefined)
}
return [responseText]
return { messages: [responseText], reply_to_message_id: undefined }
}
export async function sendMayStructuredMessage(
@@ -59,24 +51,34 @@ export async function sendMayStructuredMessage(
return // Don't send the message, let the next processing loop handle it
}
const array = parseMayStructuredMessage(responseText)
if (array == null) {
const structuredMessage = parseMayStructuredMessage(responseText)
if (structuredMessage == null) {
state.logger.log(`Not sending message to ${chatId} - no messages to send`)
return
}
state.logger.withField('texts', array).log('Sending messages')
state.logger.withField('texts', structuredMessage).log('Sending messages')
// If we get here, the task wasn't cancelled, so we can send the response
for (const item of array) {
for (let i = 0; i < structuredMessage.messages.length; i++) {
const item = structuredMessage.messages[i]
// Create cancellable typing and reply tasks
await state.bot.api.sendChatAction(chatId, 'typing')
await sleep(item.length * 200)
await sleep(item.length * 50)
const replyTask = cancellable((async (): Promise<Message.TextMessage> => {
try {
const sentResult = await state.bot.api.sendMessage(chatId, item)
return sentResult
const validReplyToMessageId = structuredMessage.reply_to_message_id ? Number.parseInt(structuredMessage.reply_to_message_id) : undefined
if (i === 0 && validReplyToMessageId && !Number.isNaN(validReplyToMessageId)) {
const sentResult = await state.bot.api.sendMessage(chatId, item, { reply_parameters: { message_id: validReplyToMessageId } })
return sentResult
}
else {
const sentResult = await state.bot.api.sendMessage(chatId, item)
return sentResult
}
}
catch (err) {
state.logger.withError(err).log('Failed to send message')
+2
View File
@@ -3,12 +3,14 @@ import { bigint, boolean, index, pgTable, text, uniqueIndex, uuid, vector } from
export const chatMessagesTable = pgTable('chat_messages', {
id: uuid().primaryKey().defaultRandom(),
platform: text().notNull().default(''),
platform_message_id: text().notNull().default(''),
from_id: text().notNull().default(''),
from_name: text().notNull().default(''),
in_chat_id: text().notNull().default(''),
content: text().notNull().default(''),
is_reply: boolean().notNull().default(false),
reply_to_name: text().notNull().default(''),
reply_to_id: text().notNull().default(''),
created_at: bigint({ mode: 'number' }).notNull().default(0).$defaultFn(() => Date.now()),
updated_at: bigint({ mode: 'number' }).notNull().default(0).$defaultFn(() => Date.now()),
content_vector_1536: vector({ dimensions: 1536 }),
+3 -41
View File
@@ -4,21 +4,18 @@ import type { Action } from '../types'
import { env } from 'node:process'
import { useLogg } from '@guiiai/logg'
import { embed } from '@xsai/embed'
import { generateText } from '@xsai/generate-text'
import { message } from '@xsai/utils-chat'
import { parse } from 'best-effort-json-parser'
import { chatMessageToOneLine, findLastNMessages, findRelevantMessages } from '../models'
import { systemPrompt } from '../prompts/system-v1'
import { div, ul } from '../prompts/utils'
export async function imagineAnAction(
botId: string,
_botId: string,
unreadMessages: Record<string, Message[]>,
currentAbortController: AbortController,
agentMessages: LLMMessage[],
lastInteractedNChatIds: string[],
_lastInteractedNChatIds: string[],
) {
const logger = useLogg('imagineAnAction').useGlobalConfig()
@@ -26,41 +23,6 @@ export async function imagineAnAction(
agentMessages = []
}
lastInteractedNChatIds.map(async (chatId) => {
const lastNMessages = await findLastNMessages(chatId, 15)
const lastNMessagesOneliner = lastNMessages.map(msg => chatMessageToOneLine(botId, msg))
const lastNMessagesEmbeddingPromises = lastNMessages
.map(async (msg) => {
const embeddingResult = await embed({
baseURL: env.EMBEDDING_API_BASE_URL!,
apiKey: env.EMBEDDING_API_KEY!,
model: env.EMBEDDING_MODEL!,
input: chatMessageToOneLine(botId, msg),
abortSignal: currentAbortController.signal,
})
return {
embedding: embeddingResult.embedding,
message: msg,
}
})
const lastNMessagesEmbedding = await Promise.all(lastNMessagesEmbeddingPromises)
const relevantChatMessages = await findRelevantMessages(botId, chatId, lastNMessagesEmbedding)
const relevantChatMessagesOneliner = await Promise.all(relevantChatMessages.map(async msgs => msgs.join('\n')))
return message.user(
div(
'Here are a brief list of the recent interacted chat ids\' message review',
`Last 15 messages in chat ${chatId}:`,
ul(...lastNMessagesOneliner),
`Relevant messages in chat ${chatId}:`,
ul(...relevantChatMessagesOneliner),
),
)
})
agentMessages.push(
message.system(''
+ `${systemPrompt()}`
@@ -82,7 +44,7 @@ export async function imagineAnAction(
},
{
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.${!!env.LLM_RESPONSE_LANGUAGE}` ? `The language of the sending message should be in ${env.LLM_RESPONSE_LANGUAGE}.` : '',
example: { action: 'sendMessage', content: '<content>', chatId: 'id of chat to send to' },
example: { action: 'sendMessage', content: '<content>', chatId: 'id of chat to send to', reply_to_message_id: 'id of message to reply to (Optional)' },
},
{
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.',
@@ -44,11 +44,13 @@ export async function recordMessage(botInfo: UserFromGetMe, message: Message) {
const values: Partial<Omit<typeof chatMessagesTable.$inferSelect, 'id' | 'created_at' | 'updated_at'>> = {
platform: 'telegram',
from_id: message.from.id.toString(),
platform_message_id: message.message_id.toString(),
from_name: message.from.first_name,
in_chat_id: message.chat.id.toString(),
content: text,
is_reply: !!message.reply_to_message,
reply_to_name: replyToName === botInfo.first_name ? 'Yourself' : replyToName,
reply_to_id: message.reply_to_message?.message_id.toString() || '',
}
switch (env.EMBEDDING_DIMENSION) {
@@ -113,12 +115,14 @@ export async function findRelevantMessages(botId: string, chatId: string, unread
.select({
id: chatMessagesTable.id,
platform: chatMessagesTable.platform,
platform_message_id: chatMessagesTable.platform_message_id,
from_id: chatMessagesTable.from_id,
from_name: chatMessagesTable.from_name,
in_chat_id: chatMessagesTable.in_chat_id,
content: chatMessagesTable.content,
is_reply: chatMessagesTable.is_reply,
reply_to_name: chatMessagesTable.reply_to_name,
reply_to_id: chatMessagesTable.reply_to_id,
created_at: chatMessagesTable.created_at,
updated_at: chatMessagesTable.updated_at,
similarity: sql`${similarity} AS "similarity"`,
@@ -144,12 +148,14 @@ export async function findRelevantMessages(botId: string, chatId: string, unread
.select({
id: chatMessagesTable.id,
platform: chatMessagesTable.platform,
platform_message_id: chatMessagesTable.platform_message_id,
from_id: chatMessagesTable.from_id,
from_name: chatMessagesTable.from_name,
in_chat_id: chatMessagesTable.in_chat_id,
content: chatMessagesTable.content,
is_reply: chatMessagesTable.is_reply,
reply_to_name: chatMessagesTable.reply_to_name,
reply_to_id: chatMessagesTable.reply_to_id,
created_at: chatMessagesTable.created_at,
updated_at: chatMessagesTable.updated_at,
})
@@ -167,12 +173,14 @@ export async function findRelevantMessages(botId: string, chatId: string, unread
.select({
id: chatMessagesTable.id,
platform: chatMessagesTable.platform,
platform_message_id: chatMessagesTable.platform_message_id,
from_id: chatMessagesTable.from_id,
from_name: chatMessagesTable.from_name,
in_chat_id: chatMessagesTable.in_chat_id,
content: chatMessagesTable.content,
is_reply: chatMessagesTable.is_reply,
reply_to_name: chatMessagesTable.reply_to_name,
reply_to_id: chatMessagesTable.reply_to_id,
created_at: chatMessagesTable.created_at,
updated_at: chatMessagesTable.updated_at,
})
+8 -7
View File
@@ -12,10 +12,10 @@ export function chatMessageToOneLine(botId: string, message: Omit<typeof chatMes
}
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 `Message ID: ${message.platform_message_id || 'Unknown'} sent on ${new Date(message.created_at).toLocaleString()} ${userDisplayName} replied to ${message.reply_to_name} with id ${message.reply_to_id} in same group said: ${message.content}`
}
return `${new Date(message.created_at).toLocaleString()} ${userDisplayName} sent in same group said: ${message.content}`
return `Message ID: ${message.platform_message_id || 'Unknown'} sent on ${new Date(message.created_at).toLocaleString()} ${userDisplayName} sent in same group said: ${message.content}`
}
export async function telegramMessageToOneLine(botId: string, message: Message) {
@@ -23,6 +23,7 @@ export async function telegramMessageToOneLine(botId: string, message: Message)
return ''
}
const sentOn = new Date(message.date * 1000).toLocaleString()
let userDisplayName = `User [${message.from.first_name} ${message.from.last_name} (${message.from.username})]`
if (botId === message.from.id.toString()) {
userDisplayName = 'Yourself'
@@ -30,20 +31,20 @@ export async function telegramMessageToOneLine(botId: string, message: Message)
if (message.sticker != null) {
const description = await findStickerDescription(message.sticker.file_id)
return `${new Date(message.date * 1000).toLocaleString()} ${userDisplayName} sent in Group [${message.chat.title}] a sticker, and description of the sticker is ${description}`
return `Message ID: ${message.message_id || 'Unknown'} sent on ${sentOn} ${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()} ${userDisplayName} sent in Group [${message.chat.title}] a photo, and description of the photo is ${description}`
return `Message ID: ${message.message_id || 'Unknown'} sent on ${sentOn} ${userDisplayName} sent in Group [${message.chat.title}] a photo, and description of the photo is ${description}`
}
if (message.reply_to_message != null) {
if (botId === message.reply_to_message.from.id.toString()) {
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}`
return `Message ID: ${message.message_id || 'Unknown'} sent on ${sentOn} ${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()} ${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 `Message ID: ${message.message_id || 'Unknown'} sent on ${sentOn} ${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()} ${userDisplayName} sent in Group [${message.chat.title}] said: ${message.text}`
return `Message ID: ${message.message_id || 'Unknown'} sent on ${sentOn} ${userDisplayName} sent in Group [${message.chat.title}] said: ${message.text}`
}