From 6b25b8c37b5c5c35efe4d86c7b8d1b1cd62e51d9 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Wed, 18 Dec 2024 19:02:02 -0500 Subject: [PATCH 1/5] support image message --- .../client-telegram/src/messageManager.ts | 499 ++++++++++++------ 1 file changed, 328 insertions(+), 171 deletions(-) diff --git a/packages/client-telegram/src/messageManager.ts b/packages/client-telegram/src/messageManager.ts index 6b37981f39e..36cc65ec9cf 100644 --- a/packages/client-telegram/src/messageManager.ts +++ b/packages/client-telegram/src/messageManager.ts @@ -1,5 +1,5 @@ import { Message } from "@telegraf/types"; -import { Context, Telegraf } from "telegraf"; +import { Context, Telegraf, TelegramBot } from "telegraf"; import { composeContext, elizaLogger, ServiceType } from "@ai16z/eliza"; import { getEmbeddingZeroVector } from "@ai16z/eliza"; @@ -12,6 +12,7 @@ import { ModelClass, State, UUID, + Media, } from "@ai16z/eliza"; import { stringToUuid } from "@ai16z/eliza"; @@ -23,9 +24,11 @@ import { MESSAGE_CONSTANTS, TIMING_CONSTANTS, RESPONSE_CHANCES, - TEAM_COORDINATION + TEAM_COORDINATION, } from "./constants"; +import fs from "fs"; + const MAX_MESSAGE_LENGTH = 4096; // Telegram's max message length const telegramShouldRespondTemplate = @@ -166,25 +169,35 @@ export class MessageManager { this.bot = bot; this.runtime = runtime; - this._initializeTeamMemberUsernames().catch(error => - elizaLogger.error("Error initializing team member usernames:", error) + this._initializeTeamMemberUsernames().catch((error) => + elizaLogger.error( + "Error initializing team member usernames:", + error + ) ); } private async _initializeTeamMemberUsernames(): Promise { - if (!this.runtime.character.clientConfig?.telegram?.isPartOfTeam) return; + if (!this.runtime.character.clientConfig?.telegram?.isPartOfTeam) + return; - const teamAgentIds = this.runtime.character.clientConfig.telegram.teamAgentIds || []; + const teamAgentIds = + this.runtime.character.clientConfig.telegram.teamAgentIds || []; for (const id of teamAgentIds) { try { const chat = await this.bot.telegram.getChat(id); - if ('username' in chat && chat.username) { + if ("username" in chat && chat.username) { this.teamMemberUsernames.set(id, chat.username); - elizaLogger.info(`Cached username for team member ${id}: ${chat.username}`); + elizaLogger.info( + `Cached username for team member ${id}: ${chat.username}` + ); } } catch (error) { - elizaLogger.error(`Error getting username for team member ${id}:`, error); + elizaLogger.error( + `Error getting username for team member ${id}:`, + error + ); } } } @@ -194,7 +207,7 @@ export class MessageManager { } private _getNormalizedUserId(id: string | number): string { - return id.toString().replace(/[^0-9]/g, ''); + return id.toString().replace(/[^0-9]/g, ""); } private _isTeamMember(userId: string | number): boolean { @@ -202,23 +215,30 @@ export class MessageManager { if (!teamConfig?.isPartOfTeam || !teamConfig.teamAgentIds) return false; const normalizedUserId = this._getNormalizedUserId(userId); - return teamConfig.teamAgentIds.some(teamId => - this._getNormalizedUserId(teamId) === normalizedUserId + return teamConfig.teamAgentIds.some( + (teamId) => this._getNormalizedUserId(teamId) === normalizedUserId ); } private _isTeamLeader(): boolean { - return this.bot.botInfo?.id.toString() === this.runtime.character.clientConfig?.telegram?.teamLeaderId; + return ( + this.bot.botInfo?.id.toString() === + this.runtime.character.clientConfig?.telegram?.teamLeaderId + ); } private _isTeamCoordinationRequest(content: string): boolean { const contentLower = content.toLowerCase(); - return TEAM_COORDINATION.KEYWORDS?.some(keyword => + return TEAM_COORDINATION.KEYWORDS?.some((keyword) => contentLower.includes(keyword.toLowerCase()) ); } - private _isRelevantToTeamMember(content: string, chatId: string, lastAgentMemory: Memory | null = null): boolean { + private _isRelevantToTeamMember( + content: string, + chatId: string, + lastAgentMemory: Memory | null = null + ): boolean { const teamConfig = this.runtime.character.clientConfig?.telegram; // Check leader's context based on last message @@ -233,7 +253,10 @@ export class MessageManager { lastAgentMemory.content.text.toLowerCase() ); - return similarity >= MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS; + return ( + similarity >= + MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS + ); } // Check team member keywords @@ -242,16 +265,20 @@ export class MessageManager { } // Check if content matches any team member keywords - return teamConfig.teamMemberInterestKeywords.some(keyword => + return teamConfig.teamMemberInterestKeywords.some((keyword) => content.toLowerCase().includes(keyword.toLowerCase()) ); } - private async _analyzeContextSimilarity(currentMessage: string, previousContext?: MessageContext, agentLastMessage?: string): Promise { + private async _analyzeContextSimilarity( + currentMessage: string, + previousContext?: MessageContext, + agentLastMessage?: string + ): Promise { if (!previousContext) return 1; const timeDiff = Date.now() - previousContext.timestamp; - const timeWeight = Math.max(0, 1 - (timeDiff / (5 * 60 * 1000))); + const timeWeight = Math.max(0, 1 - timeDiff / (5 * 60 * 1000)); const similarity = cosineSimilarity( currentMessage.toLowerCase(), @@ -262,9 +289,16 @@ export class MessageManager { return similarity * timeWeight; } - private async _shouldRespondBasedOnContext(message: Message, chatState: InterestChats[string]): Promise { - const messageText = 'text' in message ? message.text : - 'caption' in message ? (message as any).caption : ''; + private async _shouldRespondBasedOnContext( + message: Message, + chatState: InterestChats[string] + ): Promise { + const messageText = + "text" in message + ? message.text + : "caption" in message + ? (message as any).caption + : ""; if (!messageText) return false; @@ -272,42 +306,46 @@ export class MessageManager { if (this._isMessageForMe(message)) return true; // If we're not the current handler, don't respond - if (chatState?.currentHandler !== this.bot.botInfo?.id.toString()) return false; + if (chatState?.currentHandler !== this.bot.botInfo?.id.toString()) + return false; // Check if we have messages to compare if (!chatState.messages?.length) return false; // Get last user message (not from the bot) - const lastUserMessage = [...chatState.messages] - .reverse() - .find((m, index) => + const lastUserMessage = [...chatState.messages].reverse().find( + (m, index) => index > 0 && // Skip first message (current) m.userId !== this.runtime.agentId - ); + ); if (!lastUserMessage) return false; const lastSelfMemories = await this.runtime.messageManager.getMemories({ - roomId: stringToUuid(message.chat.id.toString() + "-" + this.runtime.agentId), + roomId: stringToUuid( + message.chat.id.toString() + "-" + this.runtime.agentId + ), unique: false, - count: 5 + count: 5, }); - const lastSelfSortedMemories = lastSelfMemories?.filter(m => m.userId === this.runtime.agentId) + const lastSelfSortedMemories = lastSelfMemories + ?.filter((m) => m.userId === this.runtime.agentId) .sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)); // Calculate context similarity const contextSimilarity = await this._analyzeContextSimilarity( messageText, { - content: lastUserMessage.content.text || '', - timestamp: Date.now() + content: lastUserMessage.content.text || "", + timestamp: Date.now(), }, lastSelfSortedMemories?.[0]?.content?.text ); const similarityThreshold = - this.runtime.character.clientConfig?.telegram?.messageSimilarityThreshold || + this.runtime.character.clientConfig?.telegram + ?.messageSimilarityThreshold || chatState.contextSimilarityThreshold || MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD; @@ -318,19 +356,31 @@ export class MessageManager { const botUsername = this.bot.botInfo?.username; if (!botUsername) return false; - const messageText = 'text' in message ? message.text : - 'caption' in message ? (message as any).caption : ''; + const messageText = + "text" in message + ? message.text + : "caption" in message + ? (message as any).caption + : ""; if (!messageText) return false; - const isReplyToBot = (message as any).reply_to_message?.from?.is_bot === true && - (message as any).reply_to_message?.from?.username === botUsername; + const isReplyToBot = + (message as any).reply_to_message?.from?.is_bot === true && + (message as any).reply_to_message?.from?.username === botUsername; const isMentioned = messageText.includes(`@${botUsername}`); - const hasUsername = messageText.toLowerCase().includes(botUsername.toLowerCase()); - - return isReplyToBot || isMentioned || (!this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions && hasUsername); + const hasUsername = messageText + .toLowerCase() + .includes(botUsername.toLowerCase()); + + return ( + isReplyToBot || + isMentioned || + (!this.runtime.character.clientConfig?.telegram + ?.shouldRespondOnlyToMentions && + hasUsername) + ); } - private _checkInterest(chatId: string): boolean { const chatState = this.interestChats[chatId]; if (!chatState) return false; @@ -341,17 +391,30 @@ export class MessageManager { if (timeSinceLastMessage > MESSAGE_CONSTANTS.INTEREST_DECAY_TIME) { delete this.interestChats[chatId]; return false; - } else if (timeSinceLastMessage > MESSAGE_CONSTANTS.PARTIAL_INTEREST_DECAY) { - return this._isRelevantToTeamMember(lastMessage?.content.text || '', chatId); + } else if ( + timeSinceLastMessage > MESSAGE_CONSTANTS.PARTIAL_INTEREST_DECAY + ) { + return this._isRelevantToTeamMember( + lastMessage?.content.text || "", + chatId + ); } // Team leader specific checks if (this._isTeamLeader() && chatState.messages.length > 0) { - if (!this._isRelevantToTeamMember(lastMessage?.content.text || '', chatId)) { - const recentTeamResponses = chatState.messages.slice(-3).some(m => - m.userId !== this.runtime.agentId && - this._isTeamMember(m.userId.toString()) - ); + if ( + !this._isRelevantToTeamMember( + lastMessage?.content.text || "", + chatId + ) + ) { + const recentTeamResponses = chatState.messages + .slice(-3) + .some( + (m) => + m.userId !== this.runtime.agentId && + this._isTeamMember(m.userId.toString()) + ); if (recentTeamResponses) { delete this.interestChats[chatId]; @@ -370,7 +433,7 @@ export class MessageManager { try { let imageUrl: string | null = null; - elizaLogger.info(`Telegram Message: ${message}`) + elizaLogger.info(`Telegram Message: ${message}`); if ("photo" in message && message.photo?.length > 0) { const photo = message.photo[message.photo.length - 1]; @@ -409,8 +472,10 @@ export class MessageManager { message: Message, state: State ): Promise { - - if (this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions) { + if ( + this.runtime.character.clientConfig?.telegram + ?.shouldRespondOnlyToMentions + ) { return this._isMessageForMe(message); } @@ -419,7 +484,7 @@ export class MessageManager { "text" in message && message.text?.includes(`@${this.bot.botInfo?.username}`) ) { - elizaLogger.info(`Bot mentioned`) + elizaLogger.info(`Bot mentioned`); return true; } @@ -439,41 +504,62 @@ export class MessageManager { const chatId = message.chat.id.toString(); const chatState = this.interestChats[chatId]; - const messageText = 'text' in message ? message.text : - 'caption' in message ? (message as any).caption : ''; + const messageText = + "text" in message + ? message.text + : "caption" in message + ? (message as any).caption + : ""; // Check if team member has direct interest first - if (this.runtime.character.clientConfig?.discord?.isPartOfTeam && + if ( + this.runtime.character.clientConfig?.discord?.isPartOfTeam && !this._isTeamLeader() && - this._isRelevantToTeamMember(messageText, chatId)) { - + this._isRelevantToTeamMember(messageText, chatId) + ) { return true; } // Team-based response logic if (this.runtime.character.clientConfig?.telegram?.isPartOfTeam) { // Team coordination - if(this._isTeamCoordinationRequest(messageText)) { + if (this._isTeamCoordinationRequest(messageText)) { if (this._isTeamLeader()) { return true; } else { - const randomDelay = Math.floor(Math.random() * (TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MAX - TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN)) + - TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN; // 1-3 second random delay - await new Promise(resolve => setTimeout(resolve, randomDelay)); + const randomDelay = + Math.floor( + Math.random() * + (TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MAX - + TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN) + ) + TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN; // 1-3 second random delay + await new Promise((resolve) => + setTimeout(resolve, randomDelay) + ); return true; } } - if (!this._isTeamLeader() && this._isRelevantToTeamMember(messageText, chatId)) { + if ( + !this._isTeamLeader() && + this._isRelevantToTeamMember(messageText, chatId) + ) { // Add small delay for non-leader responses - await new Promise(resolve => setTimeout(resolve, TIMING_CONSTANTS.TEAM_MEMBER_DELAY)); //1.5 second delay + await new Promise((resolve) => + setTimeout(resolve, TIMING_CONSTANTS.TEAM_MEMBER_DELAY) + ); //1.5 second delay // If leader has responded in last few seconds, reduce chance of responding if (chatState.messages?.length) { - const recentMessages = chatState.messages.slice(-MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT); - const leaderResponded = recentMessages.some(m => - m.userId === this.runtime.character.clientConfig?.telegram?.teamLeaderId && - Date.now() - chatState.lastMessageSent < 3000 + const recentMessages = chatState.messages.slice( + -MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT + ); + const leaderResponded = recentMessages.some( + (m) => + m.userId === + this.runtime.character.clientConfig?.telegram + ?.teamLeaderId && + Date.now() - chatState.lastMessageSent < 3000 ); if (leaderResponded) { @@ -486,17 +572,29 @@ export class MessageManager { } // If I'm the leader but message doesn't match my keywords, add delay and check for team responses - if (this._isTeamLeader() && !this._isRelevantToTeamMember(messageText, chatId)) { - const randomDelay = Math.floor(Math.random() * (TIMING_CONSTANTS.LEADER_DELAY_MAX - TIMING_CONSTANTS.LEADER_DELAY_MIN)) + - TIMING_CONSTANTS.LEADER_DELAY_MIN; // 2-4 second random delay - await new Promise(resolve => setTimeout(resolve, randomDelay)); + if ( + this._isTeamLeader() && + !this._isRelevantToTeamMember(messageText, chatId) + ) { + const randomDelay = + Math.floor( + Math.random() * + (TIMING_CONSTANTS.LEADER_DELAY_MAX - + TIMING_CONSTANTS.LEADER_DELAY_MIN) + ) + TIMING_CONSTANTS.LEADER_DELAY_MIN; // 2-4 second random delay + await new Promise((resolve) => + setTimeout(resolve, randomDelay) + ); // After delay, check if another team member has already responded if (chatState?.messages?.length) { - const recentResponses = chatState.messages.slice(-MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT); - const otherTeamMemberResponded = recentResponses.some(m => - m.userId !== this.runtime.agentId && - this._isTeamMember(m.userId) + const recentResponses = chatState.messages.slice( + -MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT + ); + const otherTeamMemberResponded = recentResponses.some( + (m) => + m.userId !== this.runtime.agentId && + this._isTeamMember(m.userId) ); if (otherTeamMemberResponded) { @@ -509,7 +607,8 @@ export class MessageManager { if (this._isMessageForMe(message)) { const channelState = this.interestChats[chatId]; if (channelState) { - channelState.currentHandler = this.bot.botInfo?.id.toString() + channelState.currentHandler = + this.bot.botInfo?.id.toString(); channelState.lastMessageSent = Date.now(); } return true; @@ -517,43 +616,43 @@ export class MessageManager { // Don't respond if another teammate is handling the conversation if (chatState?.currentHandler) { - if (chatState.currentHandler !== this.bot.botInfo?.id.toString() && - this._isTeamMember(chatState.currentHandler)) { + if ( + chatState.currentHandler !== + this.bot.botInfo?.id.toString() && + this._isTeamMember(chatState.currentHandler) + ) { return false; } } // Natural conversation cadence if (!this._isMessageForMe(message) && this.interestChats[chatId]) { - - const recentMessages = this.interestChats[chatId].messages - .slice(-MESSAGE_CONSTANTS.CHAT_HISTORY_COUNT); - const ourMessageCount = recentMessages.filter(m => - m.userId === this.runtime.agentId + const recentMessages = this.interestChats[ + chatId + ].messages.slice(-MESSAGE_CONSTANTS.CHAT_HISTORY_COUNT); + const ourMessageCount = recentMessages.filter( + (m) => m.userId === this.runtime.agentId ).length; if (ourMessageCount > 2) { - const responseChance = Math.pow(0.5, ourMessageCount - 2); if (Math.random() > responseChance) { return; } } } - } // Check context-based response for team conversations if (chatState?.currentHandler) { - const shouldRespondContext = await this._shouldRespondBasedOnContext(message, chatState); + const shouldRespondContext = + await this._shouldRespondBasedOnContext(message, chatState); if (!shouldRespondContext) { return false; } - } - // Use AI to decide for text or captions if ("text" in message || ("caption" in message && message.caption)) { const shouldRespondContext = composeContext({ @@ -580,29 +679,62 @@ export class MessageManager { // Send long messages in chunks private async sendMessageInChunks( ctx: Context, - content: string, + content: Content, replyToMessageId?: number ): Promise { - const chunks = this.splitMessage(content); - const sentMessages: Message.TextMessage[] = []; + if (content.attachments && content.attachments.length > 0) { + content.attachments.map(async (attachment: Media) => { + this.sendImage(ctx, attachment.url, attachment.description); + }); + } else { + const chunks = this.splitMessage(content.text); + const sentMessages: Message.TextMessage[] = []; + + for (let i = 0; i < chunks.length; i++) { + const chunk = chunks[i]; + const sentMessage = (await ctx.telegram.sendMessage( + ctx.chat.id, + chunk, + { + reply_parameters: + i === 0 && replyToMessageId + ? { message_id: replyToMessageId } + : undefined, + } + )) as Message.TextMessage; + + sentMessages.push(sentMessage); + } - for (let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - const sentMessage = (await ctx.telegram.sendMessage( + return sentMessages; + } + } + private async sendImage( + ctx: Context, + imagePath: string, + caption?: string + ): Promise { + try { + if (!fs.existsSync(imagePath)) { + throw new Error(`File not found: ${imagePath}`); + } + + const fileStream = fs.createReadStream(imagePath); + + await ctx.telegram.sendPhoto( ctx.chat.id, - chunk, { - reply_parameters: - i === 0 && replyToMessageId - ? { message_id: replyToMessageId } - : undefined, + source: fileStream, + }, + { + caption, } - )) as Message.TextMessage; + ); - sentMessages.push(sentMessage); + elizaLogger.info(`Image sent successfully: ${imagePath}`); + } catch (error) { + elizaLogger.error("Error sending image:", error); } - - return sentMessages; } // Split message into smaller parts @@ -676,40 +808,50 @@ export class MessageManager { const message = ctx.message; const chatId = ctx.chat?.id.toString(); - const messageText = 'text' in message ? message.text : - 'caption' in message ? (message as any).caption : ''; + const messageText = + "text" in message + ? message.text + : "caption" in message + ? (message as any).caption + : ""; // Add team handling at the start - if (this.runtime.character.clientConfig?.telegram?.isPartOfTeam && - !this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions) { - + if ( + this.runtime.character.clientConfig?.telegram?.isPartOfTeam && + !this.runtime.character.clientConfig?.telegram + ?.shouldRespondOnlyToMentions + ) { const isDirectlyMentioned = this._isMessageForMe(message); const hasInterest = this._checkInterest(chatId); - // Non-leader team member showing interest based on keywords - if (!this._isTeamLeader() && this._isRelevantToTeamMember(messageText, chatId)) { - + if ( + !this._isTeamLeader() && + this._isRelevantToTeamMember(messageText, chatId) + ) { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [] + messages: [], }; } const isTeamRequest = this._isTeamCoordinationRequest(messageText); const isLeader = this._isTeamLeader(); - // Check for continued interest if (hasInterest && !isDirectlyMentioned) { - const lastSelfMemories = await this.runtime.messageManager.getMemories({ - roomId: stringToUuid(chatId + "-" + this.runtime.agentId), - unique: false, - count: 5 - }); - - const lastSelfSortedMemories = lastSelfMemories?.filter(m => m.userId === this.runtime.agentId) + const lastSelfMemories = + await this.runtime.messageManager.getMemories({ + roomId: stringToUuid( + chatId + "-" + this.runtime.agentId + ), + unique: false, + count: 5, + }); + + const lastSelfSortedMemories = lastSelfMemories + ?.filter((m) => m.userId === this.runtime.agentId) .sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)); const isRelevant = this._isRelevantToTeamMember( @@ -730,35 +872,39 @@ export class MessageManager { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [] + messages: [], }; } else { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [] + messages: [], }; if (!isDirectlyMentioned) { this.interestChats[chatId].lastMessageSent = 0; } - } } // Check for other team member mentions using cached usernames - const otherTeamMembers = this.runtime.character.clientConfig.telegram.teamAgentIds.filter( - id => id !== this.bot.botInfo?.id.toString() - ); + const otherTeamMembers = + this.runtime.character.clientConfig.telegram.teamAgentIds.filter( + (id) => id !== this.bot.botInfo?.id.toString() + ); - const mentionedTeamMember = otherTeamMembers.find(id => { + const mentionedTeamMember = otherTeamMembers.find((id) => { const username = this._getTeamMemberUsername(id); return username && messageText?.includes(`@${username}`); }); // If another team member is mentioned, clear our interest if (mentionedTeamMember) { - if (hasInterest || this.interestChats[chatId]?.currentHandler === this.bot.botInfo?.id.toString()) { + if ( + hasInterest || + this.interestChats[chatId]?.currentHandler === + this.bot.botInfo?.id.toString() + ) { delete this.interestChats[chatId]; // Only return if we're not the mentioned member @@ -773,7 +919,7 @@ export class MessageManager { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [] + messages: [], }; } else if (!isTeamRequest && !hasInterest) { return; @@ -783,13 +929,20 @@ export class MessageManager { if (this.interestChats[chatId]) { this.interestChats[chatId].messages.push({ userId: stringToUuid(ctx.from.id.toString()), - userName: ctx.from.username || ctx.from.first_name || "Unknown User", - content: { text: messageText, source: "telegram" } + userName: + ctx.from.username || + ctx.from.first_name || + "Unknown User", + content: { text: messageText, source: "telegram" }, }); - if (this.interestChats[chatId].messages.length > MESSAGE_CONSTANTS.MAX_MESSAGES) { - this.interestChats[chatId].messages = - this.interestChats[chatId].messages.slice(-MESSAGE_CONSTANTS.MAX_MESSAGES); + if ( + this.interestChats[chatId].messages.length > + MESSAGE_CONSTANTS.MAX_MESSAGES + ) { + this.interestChats[chatId].messages = this.interestChats[ + chatId + ].messages.slice(-MESSAGE_CONSTANTS.MAX_MESSAGES); } } } @@ -906,46 +1059,50 @@ export class MessageManager { const callback: HandlerCallback = async (content: Content) => { const sentMessages = await this.sendMessageInChunks( ctx, - content.text, + content, message.message_id ); - const memories: Memory[] = []; - - // Create memories for each sent message - for (let i = 0; i < sentMessages.length; i++) { - const sentMessage = sentMessages[i]; - const isLastMessage = i === sentMessages.length - 1; - - const memory: Memory = { - id: stringToUuid( - sentMessage.message_id.toString() + - "-" + - this.runtime.agentId - ), - agentId, - userId: agentId, - roomId, - content: { - ...content, - text: sentMessage.text, - inReplyTo: messageId, - }, - createdAt: sentMessage.date * 1000, - embedding: getEmbeddingZeroVector(), - }; - - // Set action to CONTINUE for all messages except the last one - // For the last message, use the original action from the response content - memory.content.action = !isLastMessage - ? "CONTINUE" - : content.action; - - await this.runtime.messageManager.createMemory(memory); - memories.push(memory); + if (sentMessages) { + const memories: Memory[] = []; + + // Create memories for each sent message + for (let i = 0; i < sentMessages.length; i++) { + const sentMessage = sentMessages[i]; + const isLastMessage = i === sentMessages.length - 1; + + const memory: Memory = { + id: stringToUuid( + sentMessage.message_id.toString() + + "-" + + this.runtime.agentId + ), + agentId, + userId: agentId, + roomId, + content: { + ...content, + text: sentMessage.text, + inReplyTo: messageId, + }, + createdAt: sentMessage.date * 1000, + embedding: getEmbeddingZeroVector(), + }; + + // Set action to CONTINUE for all messages except the last one + // For the last message, use the original action from the response content + memory.content.action = !isLastMessage + ? "CONTINUE" + : content.action; + + await this.runtime.messageManager.createMemory( + memory + ); + memories.push(memory); + } + + return memories; } - - return memories; }; // Execute callback to send messages and log memories @@ -969,4 +1126,4 @@ export class MessageManager { elizaLogger.error("Error sending message:", error); } } -} \ No newline at end of file +} From 98fbe39510f99cdf755d82310ab58e7cb68cc25a Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Wed, 18 Dec 2024 19:03:16 -0500 Subject: [PATCH 2/5] check content type --- packages/client-telegram/src/messageManager.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client-telegram/src/messageManager.ts b/packages/client-telegram/src/messageManager.ts index 36cc65ec9cf..a79f1160cde 100644 --- a/packages/client-telegram/src/messageManager.ts +++ b/packages/client-telegram/src/messageManager.ts @@ -684,7 +684,9 @@ export class MessageManager { ): Promise { if (content.attachments && content.attachments.length > 0) { content.attachments.map(async (attachment: Media) => { - this.sendImage(ctx, attachment.url, attachment.description); + if (attachment.contentType.startsWith("image")) { + this.sendImage(ctx, attachment.url, attachment.description); + } }); } else { const chunks = this.splitMessage(content.text); From 7ac6616c53f20e98f1005a284b43904738946d42 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Wed, 18 Dec 2024 19:05:57 -0500 Subject: [PATCH 3/5] correct content type --- packages/client-twitter/src/utils.ts | 14 ++------------ packages/plugin-image-generation/src/index.ts | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/client-twitter/src/utils.ts b/packages/client-twitter/src/utils.ts index 2fcbc6a9a7c..526e2e12a1f 100644 --- a/packages/client-twitter/src/utils.ts +++ b/packages/client-twitter/src/utils.ts @@ -165,16 +165,6 @@ export async function buildConversationThread( return thread; } -export function getMediaType(attachment: Media) { - if (attachment.contentType?.startsWith("video")) { - return "video"; - } else if (attachment.contentType?.startsWith("image")) { - return "image"; - } else { - throw new Error(`Unsupported media type`); - } -} - export async function sendTweet( client: ClientBase, content: Content, @@ -207,14 +197,14 @@ export async function sendTweet( const mediaBuffer = Buffer.from( await response.arrayBuffer() ); - const mediaType = getMediaType(attachment); + const mediaType = attachment.contentType; return { data: mediaBuffer, mediaType }; } else if (fs.existsSync(attachment.url)) { // Handle local file paths const mediaBuffer = await fs.promises.readFile( path.resolve(attachment.url) ); - const mediaType = getMediaType(attachment); + const mediaType = attachment.contentType; return { data: mediaBuffer, mediaType }; } else { throw new Error( diff --git a/packages/plugin-image-generation/src/index.ts b/packages/plugin-image-generation/src/index.ts index fd7f8b52467..9c654699190 100644 --- a/packages/plugin-image-generation/src/index.ts +++ b/packages/plugin-image-generation/src/index.ts @@ -207,7 +207,7 @@ const imageGeneration: Action = { source: "imageGeneration", description: "...", //caption.title, text: "...", //caption.description, - contentType: "image", + contentType: "image/png", }, ], }, From e47b1d986ca7de495146098f012f2e951c19efa8 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Wed, 18 Dec 2024 19:14:07 -0500 Subject: [PATCH 4/5] clean code --- .../client-telegram/src/messageManager.ts | 371 ++++++------------ 1 file changed, 126 insertions(+), 245 deletions(-) diff --git a/packages/client-telegram/src/messageManager.ts b/packages/client-telegram/src/messageManager.ts index a79f1160cde..68b710c2a9f 100644 --- a/packages/client-telegram/src/messageManager.ts +++ b/packages/client-telegram/src/messageManager.ts @@ -1,5 +1,5 @@ import { Message } from "@telegraf/types"; -import { Context, Telegraf, TelegramBot } from "telegraf"; +import { Context, Telegraf } from "telegraf"; import { composeContext, elizaLogger, ServiceType } from "@ai16z/eliza"; import { getEmbeddingZeroVector } from "@ai16z/eliza"; @@ -24,7 +24,7 @@ import { MESSAGE_CONSTANTS, TIMING_CONSTANTS, RESPONSE_CHANCES, - TEAM_COORDINATION, + TEAM_COORDINATION } from "./constants"; import fs from "fs"; @@ -169,35 +169,25 @@ export class MessageManager { this.bot = bot; this.runtime = runtime; - this._initializeTeamMemberUsernames().catch((error) => - elizaLogger.error( - "Error initializing team member usernames:", - error - ) + this._initializeTeamMemberUsernames().catch(error => + elizaLogger.error("Error initializing team member usernames:", error) ); } private async _initializeTeamMemberUsernames(): Promise { - if (!this.runtime.character.clientConfig?.telegram?.isPartOfTeam) - return; + if (!this.runtime.character.clientConfig?.telegram?.isPartOfTeam) return; - const teamAgentIds = - this.runtime.character.clientConfig.telegram.teamAgentIds || []; + const teamAgentIds = this.runtime.character.clientConfig.telegram.teamAgentIds || []; for (const id of teamAgentIds) { try { const chat = await this.bot.telegram.getChat(id); - if ("username" in chat && chat.username) { + if ('username' in chat && chat.username) { this.teamMemberUsernames.set(id, chat.username); - elizaLogger.info( - `Cached username for team member ${id}: ${chat.username}` - ); + elizaLogger.info(`Cached username for team member ${id}: ${chat.username}`); } } catch (error) { - elizaLogger.error( - `Error getting username for team member ${id}:`, - error - ); + elizaLogger.error(`Error getting username for team member ${id}:`, error); } } } @@ -207,7 +197,7 @@ export class MessageManager { } private _getNormalizedUserId(id: string | number): string { - return id.toString().replace(/[^0-9]/g, ""); + return id.toString().replace(/[^0-9]/g, ''); } private _isTeamMember(userId: string | number): boolean { @@ -215,30 +205,23 @@ export class MessageManager { if (!teamConfig?.isPartOfTeam || !teamConfig.teamAgentIds) return false; const normalizedUserId = this._getNormalizedUserId(userId); - return teamConfig.teamAgentIds.some( - (teamId) => this._getNormalizedUserId(teamId) === normalizedUserId + return teamConfig.teamAgentIds.some(teamId => + this._getNormalizedUserId(teamId) === normalizedUserId ); } private _isTeamLeader(): boolean { - return ( - this.bot.botInfo?.id.toString() === - this.runtime.character.clientConfig?.telegram?.teamLeaderId - ); + return this.bot.botInfo?.id.toString() === this.runtime.character.clientConfig?.telegram?.teamLeaderId; } private _isTeamCoordinationRequest(content: string): boolean { const contentLower = content.toLowerCase(); - return TEAM_COORDINATION.KEYWORDS?.some((keyword) => + return TEAM_COORDINATION.KEYWORDS?.some(keyword => contentLower.includes(keyword.toLowerCase()) ); } - private _isRelevantToTeamMember( - content: string, - chatId: string, - lastAgentMemory: Memory | null = null - ): boolean { + private _isRelevantToTeamMember(content: string, chatId: string, lastAgentMemory: Memory | null = null): boolean { const teamConfig = this.runtime.character.clientConfig?.telegram; // Check leader's context based on last message @@ -253,10 +236,7 @@ export class MessageManager { lastAgentMemory.content.text.toLowerCase() ); - return ( - similarity >= - MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS - ); + return similarity >= MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD_FOLLOW_UPS; } // Check team member keywords @@ -265,20 +245,16 @@ export class MessageManager { } // Check if content matches any team member keywords - return teamConfig.teamMemberInterestKeywords.some((keyword) => + return teamConfig.teamMemberInterestKeywords.some(keyword => content.toLowerCase().includes(keyword.toLowerCase()) ); } - private async _analyzeContextSimilarity( - currentMessage: string, - previousContext?: MessageContext, - agentLastMessage?: string - ): Promise { + private async _analyzeContextSimilarity(currentMessage: string, previousContext?: MessageContext, agentLastMessage?: string): Promise { if (!previousContext) return 1; const timeDiff = Date.now() - previousContext.timestamp; - const timeWeight = Math.max(0, 1 - timeDiff / (5 * 60 * 1000)); + const timeWeight = Math.max(0, 1 - (timeDiff / (5 * 60 * 1000))); const similarity = cosineSimilarity( currentMessage.toLowerCase(), @@ -289,16 +265,9 @@ export class MessageManager { return similarity * timeWeight; } - private async _shouldRespondBasedOnContext( - message: Message, - chatState: InterestChats[string] - ): Promise { - const messageText = - "text" in message - ? message.text - : "caption" in message - ? (message as any).caption - : ""; + private async _shouldRespondBasedOnContext(message: Message, chatState: InterestChats[string]): Promise { + const messageText = 'text' in message ? message.text : + 'caption' in message ? (message as any).caption : ''; if (!messageText) return false; @@ -306,46 +275,42 @@ export class MessageManager { if (this._isMessageForMe(message)) return true; // If we're not the current handler, don't respond - if (chatState?.currentHandler !== this.bot.botInfo?.id.toString()) - return false; + if (chatState?.currentHandler !== this.bot.botInfo?.id.toString()) return false; // Check if we have messages to compare if (!chatState.messages?.length) return false; // Get last user message (not from the bot) - const lastUserMessage = [...chatState.messages].reverse().find( - (m, index) => + const lastUserMessage = [...chatState.messages] + .reverse() + .find((m, index) => index > 0 && // Skip first message (current) m.userId !== this.runtime.agentId - ); + ); if (!lastUserMessage) return false; const lastSelfMemories = await this.runtime.messageManager.getMemories({ - roomId: stringToUuid( - message.chat.id.toString() + "-" + this.runtime.agentId - ), + roomId: stringToUuid(message.chat.id.toString() + "-" + this.runtime.agentId), unique: false, - count: 5, + count: 5 }); - const lastSelfSortedMemories = lastSelfMemories - ?.filter((m) => m.userId === this.runtime.agentId) + const lastSelfSortedMemories = lastSelfMemories?.filter(m => m.userId === this.runtime.agentId) .sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)); // Calculate context similarity const contextSimilarity = await this._analyzeContextSimilarity( messageText, { - content: lastUserMessage.content.text || "", - timestamp: Date.now(), + content: lastUserMessage.content.text || '', + timestamp: Date.now() }, lastSelfSortedMemories?.[0]?.content?.text ); const similarityThreshold = - this.runtime.character.clientConfig?.telegram - ?.messageSimilarityThreshold || + this.runtime.character.clientConfig?.telegram?.messageSimilarityThreshold || chatState.contextSimilarityThreshold || MESSAGE_CONSTANTS.DEFAULT_SIMILARITY_THRESHOLD; @@ -356,31 +321,19 @@ export class MessageManager { const botUsername = this.bot.botInfo?.username; if (!botUsername) return false; - const messageText = - "text" in message - ? message.text - : "caption" in message - ? (message as any).caption - : ""; + const messageText = 'text' in message ? message.text : + 'caption' in message ? (message as any).caption : ''; if (!messageText) return false; - const isReplyToBot = - (message as any).reply_to_message?.from?.is_bot === true && - (message as any).reply_to_message?.from?.username === botUsername; + const isReplyToBot = (message as any).reply_to_message?.from?.is_bot === true && + (message as any).reply_to_message?.from?.username === botUsername; const isMentioned = messageText.includes(`@${botUsername}`); - const hasUsername = messageText - .toLowerCase() - .includes(botUsername.toLowerCase()); - - return ( - isReplyToBot || - isMentioned || - (!this.runtime.character.clientConfig?.telegram - ?.shouldRespondOnlyToMentions && - hasUsername) - ); + const hasUsername = messageText.toLowerCase().includes(botUsername.toLowerCase()); + + return isReplyToBot || isMentioned || (!this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions && hasUsername); } + private _checkInterest(chatId: string): boolean { const chatState = this.interestChats[chatId]; if (!chatState) return false; @@ -391,30 +344,17 @@ export class MessageManager { if (timeSinceLastMessage > MESSAGE_CONSTANTS.INTEREST_DECAY_TIME) { delete this.interestChats[chatId]; return false; - } else if ( - timeSinceLastMessage > MESSAGE_CONSTANTS.PARTIAL_INTEREST_DECAY - ) { - return this._isRelevantToTeamMember( - lastMessage?.content.text || "", - chatId - ); + } else if (timeSinceLastMessage > MESSAGE_CONSTANTS.PARTIAL_INTEREST_DECAY) { + return this._isRelevantToTeamMember(lastMessage?.content.text || '', chatId); } // Team leader specific checks if (this._isTeamLeader() && chatState.messages.length > 0) { - if ( - !this._isRelevantToTeamMember( - lastMessage?.content.text || "", - chatId - ) - ) { - const recentTeamResponses = chatState.messages - .slice(-3) - .some( - (m) => - m.userId !== this.runtime.agentId && - this._isTeamMember(m.userId.toString()) - ); + if (!this._isRelevantToTeamMember(lastMessage?.content.text || '', chatId)) { + const recentTeamResponses = chatState.messages.slice(-3).some(m => + m.userId !== this.runtime.agentId && + this._isTeamMember(m.userId.toString()) + ); if (recentTeamResponses) { delete this.interestChats[chatId]; @@ -433,7 +373,7 @@ export class MessageManager { try { let imageUrl: string | null = null; - elizaLogger.info(`Telegram Message: ${message}`); + elizaLogger.info(`Telegram Message: ${message}`) if ("photo" in message && message.photo?.length > 0) { const photo = message.photo[message.photo.length - 1]; @@ -472,10 +412,8 @@ export class MessageManager { message: Message, state: State ): Promise { - if ( - this.runtime.character.clientConfig?.telegram - ?.shouldRespondOnlyToMentions - ) { + + if (this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions) { return this._isMessageForMe(message); } @@ -484,7 +422,7 @@ export class MessageManager { "text" in message && message.text?.includes(`@${this.bot.botInfo?.username}`) ) { - elizaLogger.info(`Bot mentioned`); + elizaLogger.info(`Bot mentioned`) return true; } @@ -504,62 +442,41 @@ export class MessageManager { const chatId = message.chat.id.toString(); const chatState = this.interestChats[chatId]; - const messageText = - "text" in message - ? message.text - : "caption" in message - ? (message as any).caption - : ""; + const messageText = 'text' in message ? message.text : + 'caption' in message ? (message as any).caption : ''; // Check if team member has direct interest first - if ( - this.runtime.character.clientConfig?.discord?.isPartOfTeam && + if (this.runtime.character.clientConfig?.discord?.isPartOfTeam && !this._isTeamLeader() && - this._isRelevantToTeamMember(messageText, chatId) - ) { + this._isRelevantToTeamMember(messageText, chatId)) { + return true; } // Team-based response logic if (this.runtime.character.clientConfig?.telegram?.isPartOfTeam) { // Team coordination - if (this._isTeamCoordinationRequest(messageText)) { + if(this._isTeamCoordinationRequest(messageText)) { if (this._isTeamLeader()) { return true; } else { - const randomDelay = - Math.floor( - Math.random() * - (TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MAX - - TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN) - ) + TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN; // 1-3 second random delay - await new Promise((resolve) => - setTimeout(resolve, randomDelay) - ); + const randomDelay = Math.floor(Math.random() * (TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MAX - TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN)) + + TIMING_CONSTANTS.TEAM_MEMBER_DELAY_MIN; // 1-3 second random delay + await new Promise(resolve => setTimeout(resolve, randomDelay)); return true; } } - if ( - !this._isTeamLeader() && - this._isRelevantToTeamMember(messageText, chatId) - ) { + if (!this._isTeamLeader() && this._isRelevantToTeamMember(messageText, chatId)) { // Add small delay for non-leader responses - await new Promise((resolve) => - setTimeout(resolve, TIMING_CONSTANTS.TEAM_MEMBER_DELAY) - ); //1.5 second delay + await new Promise(resolve => setTimeout(resolve, TIMING_CONSTANTS.TEAM_MEMBER_DELAY)); //1.5 second delay // If leader has responded in last few seconds, reduce chance of responding if (chatState.messages?.length) { - const recentMessages = chatState.messages.slice( - -MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT - ); - const leaderResponded = recentMessages.some( - (m) => - m.userId === - this.runtime.character.clientConfig?.telegram - ?.teamLeaderId && - Date.now() - chatState.lastMessageSent < 3000 + const recentMessages = chatState.messages.slice(-MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT); + const leaderResponded = recentMessages.some(m => + m.userId === this.runtime.character.clientConfig?.telegram?.teamLeaderId && + Date.now() - chatState.lastMessageSent < 3000 ); if (leaderResponded) { @@ -572,29 +489,17 @@ export class MessageManager { } // If I'm the leader but message doesn't match my keywords, add delay and check for team responses - if ( - this._isTeamLeader() && - !this._isRelevantToTeamMember(messageText, chatId) - ) { - const randomDelay = - Math.floor( - Math.random() * - (TIMING_CONSTANTS.LEADER_DELAY_MAX - - TIMING_CONSTANTS.LEADER_DELAY_MIN) - ) + TIMING_CONSTANTS.LEADER_DELAY_MIN; // 2-4 second random delay - await new Promise((resolve) => - setTimeout(resolve, randomDelay) - ); + if (this._isTeamLeader() && !this._isRelevantToTeamMember(messageText, chatId)) { + const randomDelay = Math.floor(Math.random() * (TIMING_CONSTANTS.LEADER_DELAY_MAX - TIMING_CONSTANTS.LEADER_DELAY_MIN)) + + TIMING_CONSTANTS.LEADER_DELAY_MIN; // 2-4 second random delay + await new Promise(resolve => setTimeout(resolve, randomDelay)); // After delay, check if another team member has already responded if (chatState?.messages?.length) { - const recentResponses = chatState.messages.slice( - -MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT - ); - const otherTeamMemberResponded = recentResponses.some( - (m) => - m.userId !== this.runtime.agentId && - this._isTeamMember(m.userId) + const recentResponses = chatState.messages.slice(-MESSAGE_CONSTANTS.RECENT_MESSAGE_COUNT); + const otherTeamMemberResponded = recentResponses.some(m => + m.userId !== this.runtime.agentId && + this._isTeamMember(m.userId) ); if (otherTeamMemberResponded) { @@ -607,8 +512,7 @@ export class MessageManager { if (this._isMessageForMe(message)) { const channelState = this.interestChats[chatId]; if (channelState) { - channelState.currentHandler = - this.bot.botInfo?.id.toString(); + channelState.currentHandler = this.bot.botInfo?.id.toString() channelState.lastMessageSent = Date.now(); } return true; @@ -616,43 +520,43 @@ export class MessageManager { // Don't respond if another teammate is handling the conversation if (chatState?.currentHandler) { - if ( - chatState.currentHandler !== - this.bot.botInfo?.id.toString() && - this._isTeamMember(chatState.currentHandler) - ) { + if (chatState.currentHandler !== this.bot.botInfo?.id.toString() && + this._isTeamMember(chatState.currentHandler)) { return false; } } // Natural conversation cadence if (!this._isMessageForMe(message) && this.interestChats[chatId]) { - const recentMessages = this.interestChats[ - chatId - ].messages.slice(-MESSAGE_CONSTANTS.CHAT_HISTORY_COUNT); - const ourMessageCount = recentMessages.filter( - (m) => m.userId === this.runtime.agentId + + const recentMessages = this.interestChats[chatId].messages + .slice(-MESSAGE_CONSTANTS.CHAT_HISTORY_COUNT); + const ourMessageCount = recentMessages.filter(m => + m.userId === this.runtime.agentId ).length; if (ourMessageCount > 2) { + const responseChance = Math.pow(0.5, ourMessageCount - 2); if (Math.random() > responseChance) { return; } } } + } // Check context-based response for team conversations if (chatState?.currentHandler) { - const shouldRespondContext = - await this._shouldRespondBasedOnContext(message, chatState); + const shouldRespondContext = await this._shouldRespondBasedOnContext(message, chatState); if (!shouldRespondContext) { return false; } + } + // Use AI to decide for text or captions if ("text" in message || ("caption" in message && message.caption)) { const shouldRespondContext = composeContext({ @@ -711,6 +615,7 @@ export class MessageManager { return sentMessages; } } + private async sendImage( ctx: Context, imagePath: string, @@ -810,50 +715,40 @@ export class MessageManager { const message = ctx.message; const chatId = ctx.chat?.id.toString(); - const messageText = - "text" in message - ? message.text - : "caption" in message - ? (message as any).caption - : ""; + const messageText = 'text' in message ? message.text : + 'caption' in message ? (message as any).caption : ''; // Add team handling at the start - if ( - this.runtime.character.clientConfig?.telegram?.isPartOfTeam && - !this.runtime.character.clientConfig?.telegram - ?.shouldRespondOnlyToMentions - ) { + if (this.runtime.character.clientConfig?.telegram?.isPartOfTeam && + !this.runtime.character.clientConfig?.telegram?.shouldRespondOnlyToMentions) { + const isDirectlyMentioned = this._isMessageForMe(message); const hasInterest = this._checkInterest(chatId); + // Non-leader team member showing interest based on keywords - if ( - !this._isTeamLeader() && - this._isRelevantToTeamMember(messageText, chatId) - ) { + if (!this._isTeamLeader() && this._isRelevantToTeamMember(messageText, chatId)) { + this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [], + messages: [] }; } const isTeamRequest = this._isTeamCoordinationRequest(messageText); const isLeader = this._isTeamLeader(); + // Check for continued interest if (hasInterest && !isDirectlyMentioned) { - const lastSelfMemories = - await this.runtime.messageManager.getMemories({ - roomId: stringToUuid( - chatId + "-" + this.runtime.agentId - ), - unique: false, - count: 5, - }); - - const lastSelfSortedMemories = lastSelfMemories - ?.filter((m) => m.userId === this.runtime.agentId) + const lastSelfMemories = await this.runtime.messageManager.getMemories({ + roomId: stringToUuid(chatId + "-" + this.runtime.agentId), + unique: false, + count: 5 + }); + + const lastSelfSortedMemories = lastSelfMemories?.filter(m => m.userId === this.runtime.agentId) .sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0)); const isRelevant = this._isRelevantToTeamMember( @@ -874,39 +769,35 @@ export class MessageManager { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [], + messages: [] }; } else { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [], + messages: [] }; if (!isDirectlyMentioned) { this.interestChats[chatId].lastMessageSent = 0; } + } } // Check for other team member mentions using cached usernames - const otherTeamMembers = - this.runtime.character.clientConfig.telegram.teamAgentIds.filter( - (id) => id !== this.bot.botInfo?.id.toString() - ); + const otherTeamMembers = this.runtime.character.clientConfig.telegram.teamAgentIds.filter( + id => id !== this.bot.botInfo?.id.toString() + ); - const mentionedTeamMember = otherTeamMembers.find((id) => { + const mentionedTeamMember = otherTeamMembers.find(id => { const username = this._getTeamMemberUsername(id); return username && messageText?.includes(`@${username}`); }); // If another team member is mentioned, clear our interest if (mentionedTeamMember) { - if ( - hasInterest || - this.interestChats[chatId]?.currentHandler === - this.bot.botInfo?.id.toString() - ) { + if (hasInterest || this.interestChats[chatId]?.currentHandler === this.bot.botInfo?.id.toString()) { delete this.interestChats[chatId]; // Only return if we're not the mentioned member @@ -921,7 +812,7 @@ export class MessageManager { this.interestChats[chatId] = { currentHandler: this.bot.botInfo?.id.toString(), lastMessageSent: Date.now(), - messages: [], + messages: [] }; } else if (!isTeamRequest && !hasInterest) { return; @@ -931,20 +822,13 @@ export class MessageManager { if (this.interestChats[chatId]) { this.interestChats[chatId].messages.push({ userId: stringToUuid(ctx.from.id.toString()), - userName: - ctx.from.username || - ctx.from.first_name || - "Unknown User", - content: { text: messageText, source: "telegram" }, + userName: ctx.from.username || ctx.from.first_name || "Unknown User", + content: { text: messageText, source: "telegram" } }); - if ( - this.interestChats[chatId].messages.length > - MESSAGE_CONSTANTS.MAX_MESSAGES - ) { - this.interestChats[chatId].messages = this.interestChats[ - chatId - ].messages.slice(-MESSAGE_CONSTANTS.MAX_MESSAGES); + if (this.interestChats[chatId].messages.length > MESSAGE_CONSTANTS.MAX_MESSAGES) { + this.interestChats[chatId].messages = + this.interestChats[chatId].messages.slice(-MESSAGE_CONSTANTS.MAX_MESSAGES); } } } @@ -1064,7 +948,6 @@ export class MessageManager { content, message.message_id ); - if (sentMessages) { const memories: Memory[] = []; @@ -1072,7 +955,7 @@ export class MessageManager { for (let i = 0; i < sentMessages.length; i++) { const sentMessage = sentMessages[i]; const isLastMessage = i === sentMessages.length - 1; - + const memory: Memory = { id: stringToUuid( sentMessage.message_id.toString() + @@ -1090,19 +973,17 @@ export class MessageManager { createdAt: sentMessage.date * 1000, embedding: getEmbeddingZeroVector(), }; - + // Set action to CONTINUE for all messages except the last one // For the last message, use the original action from the response content memory.content.action = !isLastMessage ? "CONTINUE" : content.action; - - await this.runtime.messageManager.createMemory( - memory - ); + + await this.runtime.messageManager.createMemory(memory); memories.push(memory); } - + return memories; } }; @@ -1128,4 +1009,4 @@ export class MessageManager { elizaLogger.error("Error sending message:", error); } } -} +} \ No newline at end of file From cd5fc2f6599feef9a7efbe71bb39034a32991015 Mon Sep 17 00:00:00 2001 From: Ting Chien Meng Date: Wed, 18 Dec 2024 19:25:30 -0500 Subject: [PATCH 5/5] handle http image --- .../client-telegram/src/messageManager.ts | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/client-telegram/src/messageManager.ts b/packages/client-telegram/src/messageManager.ts index 68b710c2a9f..99a2212a30b 100644 --- a/packages/client-telegram/src/messageManager.ts +++ b/packages/client-telegram/src/messageManager.ts @@ -622,22 +622,34 @@ export class MessageManager { caption?: string ): Promise { try { - if (!fs.existsSync(imagePath)) { - throw new Error(`File not found: ${imagePath}`); - } - - const fileStream = fs.createReadStream(imagePath); - - await ctx.telegram.sendPhoto( - ctx.chat.id, - { - source: fileStream, - }, - { - caption, + if (/^(http|https):\/\//.test(imagePath)) { + // Handle HTTP URLs + await ctx.telegram.sendPhoto( + ctx.chat.id, + imagePath, + { + caption, + } + ); + } else { + // Handle local file paths + if (!fs.existsSync(imagePath)) { + throw new Error(`File not found: ${imagePath}`); } - ); - + + const fileStream = fs.createReadStream(imagePath); + + await ctx.telegram.sendPhoto( + ctx.chat.id, + { + source: fileStream, + }, + { + caption, + } + ); + } + elizaLogger.info(`Image sent successfully: ${imagePath}`); } catch (error) { elizaLogger.error("Error sending image:", error);