eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
1 lines • 9.33 kB
JavaScript
import{createLogger,logError}from"#internal/logging.js";import{isCompiledChannel}from"#channel/compiled-channel.js";import{defaultDeliverResult}from"#channel/adapter.js";import{parseJsonObject}from"#shared/json.js";import{POST,defineChannel}from"#public/definitions/defineChannel.js";import{mergeUploadPolicy}from"#public/channels/upload-policy.js";import{answerTelegramCallbackQuery,callTelegramApi,editTelegramMessageReplyMarkup,sendTelegramChatAction,sendTelegramMessage,splitTelegramMessageText,telegramContinuationToken}from"#public/channels/telegram/api.js";import{TELEGRAM_HITL_CALLBACK_PREFIX,isTelegramSyntheticResponse,resolveTelegramInputResponses,telegramCallbackInputResponse,telegramReplyInputResponse}from"#public/channels/telegram/hitl.js";import{formatTelegramContextBlock,parseTelegramUpdate}from"#public/channels/telegram/inbound.js";import{buildTelegramTurnMessage,collectTelegramFileParts,createTelegramFetchFile}from"#public/channels/telegram/attachments.js";import{defaultEvents,defaultOnMessage}from"#public/channels/telegram/defaults.js";import{verifyTelegramRequest}from"#public/channels/telegram/verify.js";const log=createLogger(`telegram.channel`);function telegramChannel(e={}){let t=mergeUploadPolicy(e.uploadPolicy),n=e.onMessage??defaultOnMessage,r={...defaultEvents,...e.events},c=defineChannel({kindHint:`telegram`,state:initialTelegramState(e.botUsername),metadata:e=>({chatId:e.chatId,chatType:e.chatType,triggeringUserId:e.triggeringUserId??null}),fetchFile:createTelegramFetchFile({api:e.api,credentials:e.credentials,policy:t}),context(t,n){return rebuildTelegramContext(t,n,e)},routes:[POST(e.route??`/eve/v1/telegram`,async(r,{send:a,waitUntil:o})=>{let s=await verifyInbound(r,e.credentials);if(s===null)return new Response(`unauthorized`,{status:401});let c;try{c=parseJsonObject(JSON.parse(s))}catch(e){return log.warn(`inbound Telegram body is not valid JSON`,{error:e}),new Response(`ok`)}let l=parseTelegramUpdate(c);return l===null?new Response(`ok`):l.kind===`message`?(o(dispatchMessage({config:e,message:l.message,onMessage:n,send:a,uploadPolicy:t})),new Response(`ok`)):(o(dispatchCallbackQuery({config:e,query:l.callbackQuery,send:a})),new Response(`ok`))})],async receive(t,{send:n}){let r=t.target,i=readChatId(r.chatId);if(i===void 0)throw Error(`telegramChannel().receive requires target.chatId.`);let a=typeof r.messageThreadId==`number`?r.messageThreadId:void 0,o=readOptionalString(r.conversationId),s=r.initialMessage;if(s!==void 0&&o!==void 0)throw Error("telegramChannel().receive: `conversationId` and `initialMessage` are mutually exclusive.");let c=o;return s!==void 0&&(c=(await buildTelegramHandle({config:e,state:{...initialTelegramState(e.botUsername),chatId:i,messageThreadId:a??null}}).sendMessage(s)).id||void 0),n(t.message,{auth:t.auth,continuationToken:telegramContinuationToken({chatId:i,conversationId:c,messageThreadId:a}),state:{...initialTelegramState(e.botUsername),chatId:i,conversationId:c??null,messageThreadId:a??null}})},events:r});return attachTelegramDeliver(c),c}function rebuildTelegramContext(e,t,n){return{state:e,telegram:buildTelegramHandle({config:n,session:t,state:e})}}function buildTelegramHandle(e){let n=e.config.api,r=e.state,i=e.config.credentials;function anchor(t){!t.id||r.chatType===`private`||(r.conversationId=t.id,r.chatId&&e.session?.setContinuationToken(telegramContinuationToken({chatId:r.chatId,conversationId:t.id,messageThreadId:r.messageThreadId??void 0})))}async function sendOne(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for outbound message.`);let a=await sendTelegramMessage({apiBaseUrl:n?.apiBaseUrl,body:{...e,message_thread_id:e.message_thread_id??r.messageThreadId??void 0},credentials:i,fetch:n?.fetch,fileBaseUrl:n?.fileBaseUrl,chatId:t});return anchor(a),a}return{botUsername:r.botUsername??e.config.botUsername,chatId:r.chatId??``,chatType:r.chatType??void 0,conversationId:r.conversationId??void 0,messageThreadId:r.messageThreadId??void 0,answerCallbackQuery(e){return answerTelegramCallbackQuery({apiBaseUrl:n?.apiBaseUrl,callbackQueryId:e.callbackQueryId,credentials:i,fetch:n?.fetch,showAlert:e.showAlert,text:e.text})},editMessageReplyMarkup(e){let t=r.chatId??``;if(!t)throw Error(`telegramChannel: missing chat id for reply-markup edit.`);return editTelegramMessageReplyMarkup({apiBaseUrl:n?.apiBaseUrl,chatId:t,credentials:i,fetch:n?.fetch,messageId:e.messageId,replyMarkup:e.replyMarkup})},post(e){return postTelegramMessage(e,sendOne)},request(e,t){return callTelegramApi({apiBaseUrl:n?.apiBaseUrl,body:t,botToken:i?.botToken,fetch:n?.fetch,method:e})},sendMessage(e){return postTelegramMessage(e,sendOne)},async startTyping(e=`typing`){let a=r.chatId??``;if(a)try{await sendTelegramChatAction({action:e,apiBaseUrl:n?.apiBaseUrl,chatId:a,credentials:i,fetch:n?.fetch,messageThreadId:r.messageThreadId??void 0})}catch(e){logError(log,`Telegram typing indicator failed — swallowed`,e,{chatId:a})}}}}async function postTelegramMessage(e,t){let n=typeof e==`string`?{text:e}:e,r=splitTelegramMessageText(n.text),i;for(let[e,a]of r.entries()){let r=await t(e===0?{...n,text:a}:{text:a});i===void 0&&(i=r)}return i??{id:``,raw:null}}async function verifyInbound(e,t){try{return await verifyTelegramRequest(e,{secretToken:t?.webhookVerifier?void 0:t?.webhookSecretToken,webhookVerifier:t?.webhookVerifier})}catch(e){return log.warn(`telegram inbound verification failed`,{error:e}),null}}async function dispatchMessage(e){if(e.message.from?.isBot===!0)return;let t=stateFromMessage(e.message,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})},r;try{r=await e.onMessage(n,e.message)}catch(e){log.error(`message handler failed`,{error:e});return}if(r==null)return;let i=collectTelegramFileParts(e.message.attachments,e.uploadPolicy),a=buildTelegramTurnMessage(e.message,i),o=formatTelegramContextBlock({botUsername:e.config.botUsername,chatId:e.message.chat.id,chatTitle:e.message.chat.title,chatType:e.message.chat.type,messageId:e.message.messageId,messageThreadId:e.message.messageThreadId,userId:e.message.from?.id,username:e.message.from?.username}),s=r.context??[],c=e.message.text||e.message.caption,l=e.message.replyToMessage?.from?.isBot===!0&&c.trim().length>0?[telegramReplyInputResponse({messageId:e.message.replyToMessage.messageId,text:c})]:void 0;try{await e.send({inputResponses:l,message:a,context:[o,...s]},{auth:r.auth,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`message delivery failed`,{error:e})}}async function dispatchCallbackQuery(e){let t=stateFromCallbackQuery(e.query,e.config),n={telegram:buildTelegramHandle({config:e.config,state:t})};if(e.query.data?.startsWith(TELEGRAM_HITL_CALLBACK_PREFIX)===!0){try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Answer received.`})}catch(e){log.warn(`Telegram callback-query acknowledgement failed`,{error:e})}if(!e.query.message||!t.chatId)return;try{await e.send({inputResponses:[telegramCallbackInputResponse(e.query.data)]},{auth:null,continuationToken:continuationTokenFromState(t),state:t})}catch(e){log.error(`callback query delivery failed`,{error:e})}return}if(e.config.onCallbackQuery!==void 0){try{await e.config.onCallbackQuery(n,e.query)}catch(e){log.error(`custom callback-query handler failed`,{error:e})}return}try{await n.telegram.answerCallbackQuery({callbackQueryId:e.query.id,text:`Unsupported action.`})}catch(e){log.warn(`Telegram unsupported callback-query acknowledgement failed`,{error:e})}}function attachTelegramDeliver(e){if(!isCompiledChannel(e))return;let t=e.adapter;t.deliver=(e,t)=>{let n=e.inputResponses??[];if(n.some(isTelegramSyntheticResponse)){let r=resolveTelegramInputResponses(t.state,n);return r.length>0?{inputResponses:r,context:e.context}:e.message===void 0?void 0:{message:e.message,context:e.context}}return defaultDeliverResult(e)}}function stateFromMessage(e,t){let n=e.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:e.chat.id,chatType:e.chat.type,conversationId:n?null:conversationIdForMessage(e),messageThreadId:e.messageThreadId??null,triggeringUserId:e.from?.id??null}}function stateFromCallbackQuery(e,t){let n=e.message;if(!n)return{...initialTelegramState(t.botUsername),triggeringUserId:e.from.id};let r=n.chat.type===`private`;return{...initialTelegramState(t.botUsername),chatId:n.chat.id,chatType:n.chat.type,conversationId:r?null:n.messageId,messageThreadId:n.messageThreadId??null,triggeringUserId:e.from.id}}function conversationIdForMessage(e){return e.replyToMessage?.from?.isBot===!0?e.replyToMessage.messageId:e.messageId}function continuationTokenFromState(e){return telegramContinuationToken({chatId:e.chatId??``,conversationId:e.chatType===`private`?void 0:e.conversationId??void 0,messageThreadId:e.messageThreadId??void 0})}function initialTelegramState(e){return{botUsername:e??null,chatId:null,chatType:null,conversationId:null,hitlCallbacks:{},messageThreadId:null,nextHitlCallbackId:0,pendingFreeformReplies:{},triggeringUserId:null}}function readChatId(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}function readOptionalString(e){if(typeof e==`string`&&e.length>0)return e;if(typeof e==`number`&&Number.isFinite(e))return String(e)}export{telegramChannel};