UNPKG

eve

Version:

Filesystem-first framework for durable backend AI agents that run anywhere.

1 lines 5.81 kB
import{createLogger,logError}from"#internal/logging.js";import{isCardElement}from"#compiled/chat/index.js";import{encodeSlackApiBody}from"#public/channels/slack/api-encoding.js";import{cardToBlocks,cardToFallbackText}from"#public/channels/slack/blocks.js";import{gfmToSlackMrkdwn,rewriteBareMentions,slackMrkdwnToGfm}from"#public/channels/slack/mrkdwn.js";const log=createLogger(`slack.api`);function slackContinuationToken(e,t){return`${e}:${t}`}async function resolveSlackBotToken(e){let t=e??process.env.SLACK_BOT_TOKEN;if(!t)throw Error(`SLACK_BOT_TOKEN is required.`);return typeof t==`function`?await t():t}async function callSlackApi(e){let t=await resolveSlackBotToken(e.botToken),n=encodeSlackApiBody(e.body);return(await fetch(`https://slack.com/api/${e.operation}`,{method:`POST`,headers:{authorization:`Bearer ${t}`,"content-type":n.contentType},body:n.body})).json()}function createSlackRequester(e){return(t,n)=>callSlackApi({botToken:e,operation:t,body:n})}function buildSlackBinding(e){let n=createSlackRequester(e.botToken),r=[],i=e.threadTs;function handleMessageTs(t){i||t===i||(i=t,e.onThreadTsChanged?.(t))}async function uploadFiles(t,n){if(t.length===0)return{fileIds:[],raw:{ok:!0}};let r=n?.channelId??e.channelId,a=n?.threadTs??i,o=await resolveSlackBotToken(e.botToken),s=[];for(let n of t){let t=await readFileBytes(n.data),r=await callSlackApi({botToken:e.botToken,operation:`files.getUploadURLExternal`,body:{filename:n.filename,length:t.byteLength}});if(r.ok!==!0||typeof r.upload_url!=`string`||typeof r.file_id!=`string`)throw Error(`Slack files.getUploadURLExternal failed: ${r.error??`unknown_error`}`);let i=await fetch(r.upload_url,{method:`POST`,headers:{authorization:`Bearer ${o}`,"content-type":`application/octet-stream`},body:t});if(!i.ok)throw Error(`Slack upload POST returned HTTP ${i.status} for ${n.filename}.`);s.push(r.file_id)}let c={files:t.map((e,t)=>({id:s[t],title:e.filename}))};r&&(c.channel_id=r),a&&(c.thread_ts=a),n?.initialComment&&(c.initial_comment=n.initialComment);let l=await callSlackApi({botToken:e.botToken,operation:`files.completeUploadExternal`,body:c});if(l.ok!==!0)throw Error(`Slack files.completeUploadExternal failed: ${l.error??`unknown_error`}`);return{fileIds:s,raw:l}}return{thread:{recentMessages:r,async post(t){let r=normalizePostInput(t),a=r.files??[],c=`blocks`in r||`card`in r;if(a.length>0&&!c){let e=await uploadFiles(a,{initialComment:`markdown`in r?rewriteBareMentions(gfmToSlackMrkdwn(r.markdown)):`text`in r?rewriteBareMentions(r.text):void 0});return{id:Array.isArray(e.raw.files)&&e.raw.files.length>0?String(e.raw.files[0].id??``):``,raw:e.raw}}let u=await n(`chat.postMessage`,buildPostMessageBody(r,e.channelId,i));if(u.ok!==!0)throw Error(`Slack chat.postMessage failed: ${u.error??`unknown_error`}`);let d=typeof u.ts==`string`?u.ts:``;if(handleMessageTs(d),a.length>0&&c)try{await uploadFiles(a)}catch(e){log.warn(`file upload after structured post failed`,{error:e})}return{id:d,raw:u}},async postEphemeral(t,r){let a=buildPostMessageBody(normalizePostInput(r),e.channelId,i);a.user=t;let o=await n(`chat.postEphemeral`,a);if(o.ok!==!0)throw Error(`Slack chat.postEphemeral failed: ${o.error??`unknown_error`}`);return{id:typeof o.message_ts==`string`?o.message_ts:``,raw:o}},async postDirectMessage(e,t){let r=await n(`conversations.open`,{users:e}),i=r.ok===!0?r.channel?.id:void 0;if(typeof i!=`string`||i.length===0)throw Error(`Slack conversations.open failed: ${r.error??`unknown_error`}`);let a=await n(`chat.postMessage`,buildPostMessageBody(normalizePostInput(t),i,``));if(a.ok!==!0)throw Error(`Slack chat.postMessage failed: ${a.error??`unknown_error`}`);return{id:typeof a.ts==`string`?a.ts:``,raw:a}},async startTyping(r){if(!(!e.channelId||!i))try{let t={channel_id:e.channelId,thread_ts:i,status:r??``};r!==void 0&&r.length>0&&(t.loading_messages=[r]);let a=await n(`assistant.threads.setStatus`,t);a.ok!==!0&&log.warn(`assistant.threads.setStatus returned not-ok`,{error:a.error})}catch(n){logError(log,`startTyping threw — swallowed`,n,{channelId:e.channelId})}},async refresh(){if(r.length=0,!(!e.channelId||!i))try{let t=await n(`conversations.replies`,{channel:e.channelId,ts:i,limit:50});if(t.ok!==!0||!Array.isArray(t.messages)){log.warn(`conversations.replies returned not-ok`,{error:t.error});return}for(let e of t.messages)r.push(parseThreadMessage(e,i))}catch(n){logError(log,`refresh threw — swallowed`,n,{channelId:e.channelId})}},mentionUser(e){return`<@${e}>`}},slack:{channelId:e.channelId,get threadTs(){return i},teamId:e.teamId,request:n,uploadFiles}}}function normalizePostInput(e){return typeof e==`string`?{markdown:e}:isCardElement(e)?{card:e}:e}function buildPostMessageBody(e,t,n){let r={channel:t,unfurl_links:!1,unfurl_media:!1};return n&&(r.thread_ts=n),`card`in e?(r.blocks=cardToBlocks(e.card),r.text=e.fallbackText??cardToFallbackText(e.card),r):`blocks`in e?(r.blocks=e.blocks,e.text!==void 0&&(r.text=e.text),r):`markdown`in e?(r.markdown_text=rewriteBareMentions(e.markdown),r):(r.text=rewriteBareMentions(e.text),r)}function parseThreadMessage(e,t){let n=typeof e.text==`string`?e.text:``,r=typeof e.ts==`string`?e.ts:``,i=typeof e.thread_ts==`string`?e.thread_ts:t,a=typeof e.user==`string`?e.user:void 0,o=typeof e.bot_id==`string`?e.bot_id:void 0;return{text:n,markdown:slackMrkdwnToGfm(n),user:a,botId:o,ts:r,threadTs:i,isMe:o!==void 0,raw:e}}async function readFileBytes(e){if(e instanceof ArrayBuffer)return Buffer.from(e);if(typeof Blob<`u`&&e instanceof Blob)return Buffer.from(await e.arrayBuffer());if(ArrayBuffer.isView(e)){let t=e;return Buffer.from(t.buffer,t.byteOffset,t.byteLength)}throw Error(`FileUpload.data must be a Buffer, ArrayBuffer, or Blob.`)}export{buildSlackBinding,callSlackApi,resolveSlackBotToken,slackContinuationToken};