birdcash-chat-sdk-alpha 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,6 +134,28 @@ const token = await getAccessToken({
134
134
  // token.access_token, token.expires_in, …
135
135
  ```
136
136
 
137
+ ## Verifying webhooks
138
+
139
+ Validate incoming webhook requests before trusting them. The signature scheme
140
+ matches the server: `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`, sent in the
141
+ `X-Webhook-Signature` (optionally `sha256=`-prefixed) and `X-Webhook-Timestamp`
142
+ headers. Pass the **raw** body text, read before JSON-parsing.
143
+
144
+ ```ts
145
+ import { verifyWebhookSignature } from 'birdcash-chat-sdk-alpha'
146
+
147
+ const raw = await request.text()
148
+ const { valid, error } = await verifyWebhookSignature(request, env.WEBHOOK_SECRET, raw)
149
+ if (!valid) return new Response(error ?? 'bad signature', { status: 401 })
150
+
151
+ const event = JSON.parse(raw)
152
+ // …handle event
153
+ ```
154
+
155
+ It rejects missing headers, bad signatures (constant-time compare), and stale
156
+ timestamps (replay protection, default 300s — override with `{ toleranceSec }`).
157
+ Pass `{ logger: console }` for diagnostics.
158
+
137
159
  ## Lower-level exports
138
160
 
139
161
  The element builders and helpers are also exported if you want to assemble
package/dist/main.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="https://chat-api2-3rnt.onrender.com",t="web_link",s="ai_choice_prompt",i="official_account_tweet";async function a(t){var s;const{clientId:i,clientSecret:a}=t;if(!i||!a)throw new Error("getAccessToken: clientId and clientSecret are required");const n=e.replace(/\/$/,""),r=null!==(s=t.scope)&&void 0!==s?s:"chat:write uploads:write";if("undefined"==typeof fetch)throw new Error("getAccessToken: global fetch is not available");const o=await fetch(`${n}/v1/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:i,client_secret:a,scope:r})});if(!o.ok)throw new Error(`Token request failed: ${o.status} ${o.statusText}`);return await o.json()}var n;function r(e){return{elemType:exports.ElemType.Text,priority:0,textElem:{text:e.trim()}}}function o(e){return{elemType:exports.ElemType.Image,priority:0,imageElem:{upload_ids:e}}}function l(e){return{elemType:exports.ElemType.File,priority:0,fileElem:{upload_ids:e}}}function u(e,t=0){return{elemType:exports.ElemType.Sound,priority:0,soundElem:{uploadID:e,duration:t}}}function c(e,t){return{elemType:exports.ElemType.Sticker,priority:0,stickerElem:{packID:e,stickerID:t}}}function d(e){return{elemType:exports.ElemType.Custom,priority:0,customElem:{data:e}}}function p(e){return{elemType:exports.ElemType.MiniApp,priority:0,miniAppElem:e}}function g(e){const t=new Uint8Array(e.byteLength);return t.set(e),t}function h(e){const t=(new TextEncoder).encode(e);let s="";for(const e of t)s+=String.fromCharCode(e);return btoa(s)}function m(e,t=10){if(e.length<=t)return[e];const s=[];let i="";for(const a of e.split(" "))i.length+a.length+1>t&&i.length>0?(s.push(i.trim()+" "),i=a):i+=(i?" ":"")+a;return i&&s.push(i.trim()),s.length>0?s:[e]}exports.ElemType=void 0,(n=exports.ElemType||(exports.ElemType={}))[n.None=0]="None",n[n.Text=1]="Text",n[n.Image=2]="Image",n[n.Sound=3]="Sound",n[n.Video=4]="Video",n[n.File=5]="File",n[n.Sticker=6]="Sticker",n[n.GroupTips=7]="GroupTips",n[n.Merger=8]="Merger",n[n.Custom=9]="Custom",n[n.Location=10]="Location",n[n.GroupAnnouncement=11]="GroupAnnouncement",n[n.Quote=12]="Quote",n[n.InputStatus=14]="InputStatus",n[n.TypingStatus=15]="TypingStatus",n[n.MiniApp=16]="MiniApp",n[n.Order=17]="Order",n[n.Transfer=18]="Transfer";class f{constructor(t){var s,i;if(!(null==t?void 0:t.token))throw new Error("ChatClient: token is required");this.token=t.token,this.baseUrl=(null!==(s=t.baseUrl)&&void 0!==s?s:e).replace(/\/$/,""),this.logger=t.logger;const a=null!==(i=t.fetch)&&void 0!==i?i:"undefined"!=typeof fetch?fetch:void 0;if(!a)throw new Error("ChatClient: no fetch implementation available; pass `fetch` in options");this.fetchImpl=a.bind(globalThis)}static async fromCredentials(e){const{access_token:t}=await a({clientId:e.clientId,clientSecret:e.clientSecret,scope:e.scope});return new f({token:t,baseUrl:e.baseUrl,logger:e.logger,fetch:e.fetch})}get authHeaders(){return{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`}}replyUrl(e){return`${this.baseUrl}/v1/chat/message/${e}/reply`}newMsgID(){return globalThis.crypto.randomUUID()}async postElements(e,t,s="POST"){return await this.fetchImpl(this.replyUrl(e),{method:s,headers:this.authHeaders,body:JSON.stringify(t)})}async uploadImage(e,t,s="image/png"){var i;const a=new FormData;a.append("file",new Blob([g(e)],{type:s}),t);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/image`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),r=await n.json();if(!n.ok)throw new Error(`uploadImage failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadImage: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(i=r.url)&&void 0!==i?i:""}}async uploadFile(e,t,s="application/octet-stream"){var i;const a=new FormData;a.append("file",new Blob([g(e)],{type:s}),t);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/file`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),r=await n.json();if(!n.ok)throw new Error(`uploadFile failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadFile: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(i=r.url)&&void 0!==i?i:""}}async sendMessage(e,t,s){var i;let a=Array.isArray(t)?t.map((e=>e.trim())).filter(Boolean).map(r):[r(t)];if(0===a.length)return;s&&(a=a.map((e=>({...e,msgID:s}))));const n=await this.postElements(e,a),o=await n.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] message sent",{status:n.status,elements:a.length}),!n.ok)throw new Error(`sendMessage failed: ${n.status} ${o}`)}async sendImageMessage(e,t){var s;if(!(null==t?void 0:t.length))return;const i=await this.postElements(e,[o(t)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message sent",{status:i.status}),!i.ok)throw new Error(`sendImageMessage failed: ${i.status} ${a}`)}async sendFileMessage(e,t){var s;if(!(null==t?void 0:t.length))return;const i=await this.postElements(e,[l(t)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] file message sent",{status:i.status}),!i.ok)throw new Error(`sendFileMessage failed: ${i.status} ${a}`)}async sendSoundMessage(e,t,s=0){var i;if(!(null==t?void 0:t.trim()))return;const a=await this.postElements(e,[u(t,s)]),n=await a.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] sound message sent",{status:a.status}),!a.ok)throw new Error(`sendSoundMessage failed: ${a.status} ${n.slice(0,500)}`)}async sendStickerMessage(e,t,s,i){var a;if(!(null==t?void 0:t.trim())||!(null==s?void 0:s.trim()))return;const n=c(t,s),r=await this.postElements(e,[i?{...n,msgID:i}:n]),o=await r.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] sticker message sent",{status:r.status}),!r.ok)throw new Error(`sendStickerMessage failed: ${r.status} ${o}`)}async editTextMessage(e,t){var s;const i=await this.postElements(e,[{msgID:e,...r(t)}]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] text message edited",{status:i.status}),!i.ok)throw new Error(`editTextMessage failed: ${i.status} ${a.slice(0,500)}`)}async editStickerMessage(e,t,s){var i;const a=await this.postElements(e,[{msgID:e,...c(t,s)}]),n=await a.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] sticker message edited",{status:a.status}),!a.ok)throw new Error(`editStickerMessage failed: ${a.status} ${n.slice(0,500)}`)}async editImageMessage(e,t){var s;const i=await this.postElements(e,[{msgID:e,...o(t)}]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message edited",{status:i.status}),!i.ok)throw new Error(`editImageMessage failed: ${i.status} ${a.slice(0,500)}`)}async deleteMessage(e){var t;const s=await this.fetchImpl(`${this.baseUrl}/v1/chat/message/${e}`,{method:"DELETE",headers:this.authHeaders});if(null===(t=this.logger)||void 0===t||t.log("[chat-sdk] message deleted",{status:s.status}),!s.ok){const e=await s.text();throw new Error(`deleteMessage failed: ${s.status} ${e.slice(0,500)}`)}}async sendTypingStatus(e,t){var s,i;const a={businessID:"user_typing_status",typingStatus:t?1:0,version:1,userAction:14,actionParam:t?"EIMAMSG_InputStatus_Ing":"EIMAMSG_InputStatus_End"};try{await this.postElements(e,[d(h(JSON.stringify(a)))])}catch(e){null===(s=this.logger)||void 0===s||s.error("[chat-sdk] typing status failed",null!==(i=null==e?void 0:e.message)&&void 0!==i?i:String(e))}}async sendStreamingChunk(e,t,s,i){const a={businessID:"chatbotPlugin",src:0,chunks:t,isFinished:s,TMessageCell_Name:"ChatbotMessageCell_Minimalist",TMessageCell_Data_Name:"ChatbotMessageCellData"},n={msgID:e,...d(h(JSON.stringify(a)))},r=await this.postElements(e,[n],i?"POST":"PATCH");if(!r.ok)throw new Error(`sendStreamingChunk failed: ${r.statusText}`);await r.text()}async sendMiniAppMessage(e,t){var s;const i=await this.postElements(e,[p(t)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] miniapp card sent",{status:i.status}),!i.ok)throw new Error(`sendMiniAppMessage failed: ${i.status} ${a}`)}async sendWebLinkMessage(e,s){var i;const a={businessID:t,url:s},n=await this.postElements(e,[d(h(JSON.stringify(a)))]),r=await n.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] web link sent",{status:n.status}),!n.ok)throw new Error(`sendWebLinkMessage failed: ${n.status} ${r.slice(0,500)}`)}async sendChoicesMessage(e,t){var i,a,n;const r=this.newMsgID(),o={businessID:s,promptId:null!==(i=t.promptId)&&void 0!==i?i:this.newMsgID(),prompt:t.prompt,selectionMode:null!==(a=t.selectionMode)&&void 0!==a?a:"single",choices:t.choices},l=await this.postElements(e,[{msgID:r,...d(h(JSON.stringify(o)))}]),u=await l.text();if(null===(n=this.logger)||void 0===n||n.log("[chat-sdk] choices message sent",{status:l.status}),!l.ok)throw new Error(`sendChoicesMessage failed: ${l.status} ${u.slice(0,500)}`);return r}async sendOfficialAccountTweetMessage(e,t){var s;const a=this.newMsgID(),n={businessID:i,...t},r=await this.postElements(e,[{msgID:a,...d(h(JSON.stringify(n)))}]),o=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] official account tweet sent",{status:r.status}),!r.ok)throw new Error(`sendOfficialAccountTweetMessage failed: ${r.status} ${o.slice(0,500)}`);return a}async streamMessage(e,t,s=10){const i=m(t,s),a=[];for(let t=0;t<i.length;t++)a.push(i[t]),await this.sendStreamingChunk(e,a,t===i.length-1,0===t)}}f.ElemType=exports.ElemType,exports.AI_CHOICE_PROMPT_BUSINESS_ID=s,exports.ChatClient=f,exports.DEFAULT_BASE_URL=e,exports.OFFICIAL_ACCOUNT_TWEET_BUSINESS_ID=i,exports.WEB_LINK_BUSINESS_ID=t,exports.arrayBufferToBase64=function(e){let t="";const s=new Uint8Array(e),i=s.byteLength;for(let e=0;e<i;e++)t+=String.fromCharCode(s[e]);return btoa(t)},exports.base64ToArrayBuffer=function(e){const t=atob(e),s=t.length,i=new Uint8Array(s);for(let e=0;e<s;e++)i[e]=t.charCodeAt(e);return i.buffer},exports.customElem=d,exports.fileElem=l,exports.getAccessToken=a,exports.imageElem=o,exports.miniAppElem=p,exports.soundElem=u,exports.splitIntoChunks=m,exports.stickerElem=c,exports.textElem=r,exports.toBase64=h;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="https://chat-api2-3rnt.onrender.com",t="web_link",s="ai_choice_prompt",r="official_account_tweet";async function a(t){var s;const{clientId:r,clientSecret:a}=t;if(!r||!a)throw new Error("getAccessToken: clientId and clientSecret are required");const i=e.replace(/\/$/,""),o=null!==(s=t.scope)&&void 0!==s?s:"chat:write uploads:write";if("undefined"==typeof fetch)throw new Error("getAccessToken: global fetch is not available");const n=await fetch(`${i}/v1/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:r,client_secret:a,scope:o})});if(!n.ok)throw new Error(`Token request failed: ${n.status} ${n.statusText}`);return await n.json()}var i;function o(e){return{elemType:exports.ElemType.Text,priority:0,textElem:{text:e.trim()}}}function n(e){return{elemType:exports.ElemType.Image,priority:0,imageElem:{upload_ids:e}}}function l(e){return{elemType:exports.ElemType.File,priority:0,fileElem:{upload_ids:e}}}function u(e,t=0){return{elemType:exports.ElemType.Sound,priority:0,soundElem:{uploadID:e,duration:t}}}function c(e,t){return{elemType:exports.ElemType.Sticker,priority:0,stickerElem:{packID:e,stickerID:t}}}function d(e){return{elemType:exports.ElemType.Custom,priority:0,customElem:{data:e}}}function h(e){return{elemType:exports.ElemType.MiniApp,priority:0,miniAppElem:e}}function g(e){const t=new Uint8Array(e.byteLength);return t.set(e),t}function p(e){const t=(new TextEncoder).encode(e);let s="";for(const e of t)s+=String.fromCharCode(e);return btoa(s)}function m(e,t=10){if(e.length<=t)return[e];const s=[];let r="";for(const a of e.split(" "))r.length+a.length+1>t&&r.length>0?(s.push(r.trim()+" "),r=a):r+=(r?" ":"")+a;return r&&s.push(r.trim()),s.length>0?s:[e]}exports.ElemType=void 0,(i=exports.ElemType||(exports.ElemType={}))[i.None=0]="None",i[i.Text=1]="Text",i[i.Image=2]="Image",i[i.Sound=3]="Sound",i[i.Video=4]="Video",i[i.File=5]="File",i[i.Sticker=6]="Sticker",i[i.GroupTips=7]="GroupTips",i[i.Merger=8]="Merger",i[i.Custom=9]="Custom",i[i.Location=10]="Location",i[i.GroupAnnouncement=11]="GroupAnnouncement",i[i.Quote=12]="Quote",i[i.InputStatus=14]="InputStatus",i[i.TypingStatus=15]="TypingStatus",i[i.MiniApp=16]="MiniApp",i[i.Order=17]="Order",i[i.Transfer=18]="Transfer";class f{constructor(t){var s,r;if(!(null==t?void 0:t.token))throw new Error("ChatClient: token is required");this.token=t.token,this.baseUrl=(null!==(s=t.baseUrl)&&void 0!==s?s:e).replace(/\/$/,""),this.logger=t.logger;const a=null!==(r=t.fetch)&&void 0!==r?r:"undefined"!=typeof fetch?fetch:void 0;if(!a)throw new Error("ChatClient: no fetch implementation available; pass `fetch` in options");this.fetchImpl=a.bind(globalThis)}static async fromCredentials(e){const{access_token:t}=await a({clientId:e.clientId,clientSecret:e.clientSecret,scope:e.scope});return new f({token:t,baseUrl:e.baseUrl,logger:e.logger,fetch:e.fetch})}get authHeaders(){return{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`}}replyUrl(e){return`${this.baseUrl}/v1/chat/message/${e}/reply`}newMsgID(){return globalThis.crypto.randomUUID()}async postElements(e,t,s="POST"){return await this.fetchImpl(this.replyUrl(e),{method:s,headers:this.authHeaders,body:JSON.stringify(t)})}async uploadImage(e,t,s="image/png"){var r;const a=new FormData;a.append("file",new Blob([g(e)],{type:s}),t);const i=await this.fetchImpl(`${this.baseUrl}/v1/upload/image`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),o=await i.json();if(!i.ok)throw new Error(`uploadImage failed: ${i.status} ${JSON.stringify(o)}`);if(!o.upload_id)throw new Error(`uploadImage: missing upload_id: ${JSON.stringify(o)}`);return{upload_id:o.upload_id,url:null!==(r=o.url)&&void 0!==r?r:""}}async uploadFile(e,t,s="application/octet-stream"){var r;const a=new FormData;a.append("file",new Blob([g(e)],{type:s}),t);const i=await this.fetchImpl(`${this.baseUrl}/v1/upload/file`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),o=await i.json();if(!i.ok)throw new Error(`uploadFile failed: ${i.status} ${JSON.stringify(o)}`);if(!o.upload_id)throw new Error(`uploadFile: missing upload_id: ${JSON.stringify(o)}`);return{upload_id:o.upload_id,url:null!==(r=o.url)&&void 0!==r?r:""}}async sendMessage(e,t,s){var r;let a=Array.isArray(t)?t.map((e=>e.trim())).filter(Boolean).map(o):[o(t)];if(0===a.length)return;s&&(a=a.map((e=>({...e,msgID:s}))));const i=await this.postElements(e,a),n=await i.text();if(null===(r=this.logger)||void 0===r||r.log("[chat-sdk] message sent",{status:i.status,elements:a.length}),!i.ok)throw new Error(`sendMessage failed: ${i.status} ${n}`)}async sendImageMessage(e,t){var s;if(!(null==t?void 0:t.length))return;const r=await this.postElements(e,[n(t)]),a=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message sent",{status:r.status}),!r.ok)throw new Error(`sendImageMessage failed: ${r.status} ${a}`)}async sendFileMessage(e,t){var s;if(!(null==t?void 0:t.length))return;const r=await this.postElements(e,[l(t)]),a=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] file message sent",{status:r.status}),!r.ok)throw new Error(`sendFileMessage failed: ${r.status} ${a}`)}async sendSoundMessage(e,t,s=0){var r;if(!(null==t?void 0:t.trim()))return;const a=await this.postElements(e,[u(t,s)]),i=await a.text();if(null===(r=this.logger)||void 0===r||r.log("[chat-sdk] sound message sent",{status:a.status}),!a.ok)throw new Error(`sendSoundMessage failed: ${a.status} ${i.slice(0,500)}`)}async sendStickerMessage(e,t,s,r){var a;if(!(null==t?void 0:t.trim())||!(null==s?void 0:s.trim()))return;const i=c(t,s),o=await this.postElements(e,[r?{...i,msgID:r}:i]),n=await o.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] sticker message sent",{status:o.status}),!o.ok)throw new Error(`sendStickerMessage failed: ${o.status} ${n}`)}async editTextMessage(e,t){var s;const r=await this.postElements(e,[{msgID:e,...o(t)}]),a=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] text message edited",{status:r.status}),!r.ok)throw new Error(`editTextMessage failed: ${r.status} ${a.slice(0,500)}`)}async editStickerMessage(e,t,s){var r;const a=await this.postElements(e,[{msgID:e,...c(t,s)}]),i=await a.text();if(null===(r=this.logger)||void 0===r||r.log("[chat-sdk] sticker message edited",{status:a.status}),!a.ok)throw new Error(`editStickerMessage failed: ${a.status} ${i.slice(0,500)}`)}async editImageMessage(e,t){var s;const r=await this.postElements(e,[{msgID:e,...n(t)}]),a=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message edited",{status:r.status}),!r.ok)throw new Error(`editImageMessage failed: ${r.status} ${a.slice(0,500)}`)}async deleteMessage(e){var t;const s=await this.fetchImpl(`${this.baseUrl}/v1/chat/message/${e}`,{method:"DELETE",headers:this.authHeaders});if(null===(t=this.logger)||void 0===t||t.log("[chat-sdk] message deleted",{status:s.status}),!s.ok){const e=await s.text();throw new Error(`deleteMessage failed: ${s.status} ${e.slice(0,500)}`)}}async sendTypingStatus(e,t){var s,r;const a={businessID:"user_typing_status",typingStatus:t?1:0,version:1,userAction:14,actionParam:t?"EIMAMSG_InputStatus_Ing":"EIMAMSG_InputStatus_End"};try{await this.postElements(e,[d(p(JSON.stringify(a)))])}catch(e){null===(s=this.logger)||void 0===s||s.error("[chat-sdk] typing status failed",null!==(r=null==e?void 0:e.message)&&void 0!==r?r:String(e))}}async sendStreamingChunk(e,t,s,r){const a={businessID:"chatbotPlugin",src:0,chunks:t,isFinished:s,TMessageCell_Name:"ChatbotMessageCell_Minimalist",TMessageCell_Data_Name:"ChatbotMessageCellData"},i={msgID:e,...d(p(JSON.stringify(a)))},o=await this.postElements(e,[i],r?"POST":"PATCH");if(!o.ok)throw new Error(`sendStreamingChunk failed: ${o.statusText}`);await o.text()}async sendMiniAppMessage(e,t){var s;const r=await this.postElements(e,[h(t)]),a=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] miniapp card sent",{status:r.status}),!r.ok)throw new Error(`sendMiniAppMessage failed: ${r.status} ${a}`)}async sendWebLinkMessage(e,s){var r;const a={businessID:t,url:s},i=await this.postElements(e,[d(p(JSON.stringify(a)))]),o=await i.text();if(null===(r=this.logger)||void 0===r||r.log("[chat-sdk] web link sent",{status:i.status}),!i.ok)throw new Error(`sendWebLinkMessage failed: ${i.status} ${o.slice(0,500)}`)}async sendChoicesMessage(e,t){var r,a,i;const o=this.newMsgID(),n={businessID:s,promptId:null!==(r=t.promptId)&&void 0!==r?r:this.newMsgID(),prompt:t.prompt,selectionMode:null!==(a=t.selectionMode)&&void 0!==a?a:"single",choices:t.choices},l=await this.postElements(e,[{msgID:o,...d(p(JSON.stringify(n)))}]),u=await l.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] choices message sent",{status:l.status}),!l.ok)throw new Error(`sendChoicesMessage failed: ${l.status} ${u.slice(0,500)}`);return o}async sendOfficialAccountTweetMessage(e,t){var s;const a=this.newMsgID(),i={businessID:r,...t},o=await this.postElements(e,[{msgID:a,...d(p(JSON.stringify(i)))}]),n=await o.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] official account tweet sent",{status:o.status}),!o.ok)throw new Error(`sendOfficialAccountTweetMessage failed: ${o.status} ${n.slice(0,500)}`);return a}async streamMessage(e,t,s=10){const r=m(t,s),a=[];for(let t=0;t<r.length;t++)a.push(r[t]),await this.sendStreamingChunk(e,a,t===r.length-1,0===t)}}f.ElemType=exports.ElemType,exports.AI_CHOICE_PROMPT_BUSINESS_ID=s,exports.ChatClient=f,exports.DEFAULT_BASE_URL=e,exports.OFFICIAL_ACCOUNT_TWEET_BUSINESS_ID=r,exports.WEB_LINK_BUSINESS_ID=t,exports.arrayBufferToBase64=function(e){let t="";const s=new Uint8Array(e),r=s.byteLength;for(let e=0;e<r;e++)t+=String.fromCharCode(s[e]);return btoa(t)},exports.base64ToArrayBuffer=function(e){const t=atob(e),s=t.length,r=new Uint8Array(s);for(let e=0;e<s;e++)r[e]=t.charCodeAt(e);return r.buffer},exports.customElem=d,exports.fileElem=l,exports.getAccessToken=a,exports.imageElem=n,exports.miniAppElem=h,exports.soundElem=u,exports.splitIntoChunks=m,exports.stickerElem=c,exports.textElem=o,exports.toBase64=p,exports.verifyWebhookSignature=async function(e,t,s,r={}){const{toleranceSec:a=300,logger:i}=r,o=e.headers.get("X-Webhook-Signature");if(!o)return null==i||i.error("[chat-sdk] webhook missing X-Webhook-Signature header"),{valid:!1,error:"Missing X-Webhook-Signature header"};const n=e.headers.get("X-Webhook-Timestamp");if(!n)return null==i||i.error("[chat-sdk] webhook missing X-Webhook-Timestamp header"),{valid:!1,error:"Missing X-Webhook-Timestamp header"};const l=o.startsWith("sha256=")?o.slice(7):o,u=await async function(e,t){const s=new TextEncoder,r=await crypto.subtle.importKey("raw",s.encode(e),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await crypto.subtle.sign("HMAC",r,s.encode(t));return[...new Uint8Array(a)].map((e=>e.toString(16).padStart(2,"0"))).join("")}(t,`${n}.${s}`);if(!function(e,t){if(e.length!==t.length)return!1;let s=0;for(let r=0;r<e.length;r++)s|=e.charCodeAt(r)^t.charCodeAt(r);return 0===s}(l,u))return null==i||i.error("[chat-sdk] webhook invalid signature"),{valid:!1,error:"Invalid signature"};const c=parseInt(n,10),d=Math.floor(Date.now()/1e3);return!Number.isFinite(c)||Math.abs(d-c)>a?(null==i||i.error("[chat-sdk] webhook timestamp out of range"),{valid:!1,error:"Request timestamp too old or too far in future"}):(null==i||i.log("[chat-sdk] webhook signature valid"),{valid:!0})};
2
2
  //# sourceMappingURL=main.cjs.js.map
package/dist/main.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- const t="https://chat-api2-3rnt.onrender.com",e="web_link",s="ai_choice_prompt",i="official_account_tweet";async function a(e){var s;const{clientId:i,clientSecret:a}=e;if(!i||!a)throw new Error("getAccessToken: clientId and clientSecret are required");const n=t.replace(/\/$/,""),r=null!==(s=e.scope)&&void 0!==s?s:"chat:write uploads:write";if("undefined"==typeof fetch)throw new Error("getAccessToken: global fetch is not available");const o=await fetch(`${n}/v1/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:i,client_secret:a,scope:r})});if(!o.ok)throw new Error(`Token request failed: ${o.status} ${o.statusText}`);return await o.json()}var n;function r(t){return{elemType:n.Text,priority:0,textElem:{text:t.trim()}}}function o(t){return{elemType:n.Image,priority:0,imageElem:{upload_ids:t}}}function l(t){return{elemType:n.File,priority:0,fileElem:{upload_ids:t}}}function u(t,e=0){return{elemType:n.Sound,priority:0,soundElem:{uploadID:t,duration:e}}}function c(t,e){return{elemType:n.Sticker,priority:0,stickerElem:{packID:t,stickerID:e}}}function d(t){return{elemType:n.Custom,priority:0,customElem:{data:t}}}function g(t){return{elemType:n.MiniApp,priority:0,miniAppElem:t}}function h(t){const e=new Uint8Array(t.byteLength);return e.set(t),e}function p(t){let e="";const s=new Uint8Array(t),i=s.byteLength;for(let t=0;t<i;t++)e+=String.fromCharCode(s[t]);return btoa(e)}function m(t){const e=atob(t),s=e.length,i=new Uint8Array(s);for(let t=0;t<s;t++)i[t]=e.charCodeAt(t);return i.buffer}function f(t){const e=(new TextEncoder).encode(t);let s="";for(const t of e)s+=String.fromCharCode(t);return btoa(s)}function w(t,e=10){if(t.length<=e)return[t];const s=[];let i="";for(const a of t.split(" "))i.length+a.length+1>e&&i.length>0?(s.push(i.trim()+" "),i=a):i+=(i?" ":"")+a;return i&&s.push(i.trim()),s.length>0?s:[t]}!function(t){t[t.None=0]="None",t[t.Text=1]="Text",t[t.Image=2]="Image",t[t.Sound=3]="Sound",t[t.Video=4]="Video",t[t.File=5]="File",t[t.Sticker=6]="Sticker",t[t.GroupTips=7]="GroupTips",t[t.Merger=8]="Merger",t[t.Custom=9]="Custom",t[t.Location=10]="Location",t[t.GroupAnnouncement=11]="GroupAnnouncement",t[t.Quote=12]="Quote",t[t.InputStatus=14]="InputStatus",t[t.TypingStatus=15]="TypingStatus",t[t.MiniApp=16]="MiniApp",t[t.Order=17]="Order",t[t.Transfer=18]="Transfer"}(n||(n={}));class y{constructor(e){var s,i;if(!(null==e?void 0:e.token))throw new Error("ChatClient: token is required");this.token=e.token,this.baseUrl=(null!==(s=e.baseUrl)&&void 0!==s?s:t).replace(/\/$/,""),this.logger=e.logger;const a=null!==(i=e.fetch)&&void 0!==i?i:"undefined"!=typeof fetch?fetch:void 0;if(!a)throw new Error("ChatClient: no fetch implementation available; pass `fetch` in options");this.fetchImpl=a.bind(globalThis)}static async fromCredentials(t){const{access_token:e}=await a({clientId:t.clientId,clientSecret:t.clientSecret,scope:t.scope});return new y({token:e,baseUrl:t.baseUrl,logger:t.logger,fetch:t.fetch})}get authHeaders(){return{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`}}replyUrl(t){return`${this.baseUrl}/v1/chat/message/${t}/reply`}newMsgID(){return globalThis.crypto.randomUUID()}async postElements(t,e,s="POST"){return await this.fetchImpl(this.replyUrl(t),{method:s,headers:this.authHeaders,body:JSON.stringify(e)})}async uploadImage(t,e,s="image/png"){var i;const a=new FormData;a.append("file",new Blob([h(t)],{type:s}),e);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/image`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),r=await n.json();if(!n.ok)throw new Error(`uploadImage failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadImage: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(i=r.url)&&void 0!==i?i:""}}async uploadFile(t,e,s="application/octet-stream"){var i;const a=new FormData;a.append("file",new Blob([h(t)],{type:s}),e);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/file`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:a}),r=await n.json();if(!n.ok)throw new Error(`uploadFile failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadFile: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(i=r.url)&&void 0!==i?i:""}}async sendMessage(t,e,s){var i;let a=Array.isArray(e)?e.map((t=>t.trim())).filter(Boolean).map(r):[r(e)];if(0===a.length)return;s&&(a=a.map((t=>({...t,msgID:s}))));const n=await this.postElements(t,a),o=await n.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] message sent",{status:n.status,elements:a.length}),!n.ok)throw new Error(`sendMessage failed: ${n.status} ${o}`)}async sendImageMessage(t,e){var s;if(!(null==e?void 0:e.length))return;const i=await this.postElements(t,[o(e)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message sent",{status:i.status}),!i.ok)throw new Error(`sendImageMessage failed: ${i.status} ${a}`)}async sendFileMessage(t,e){var s;if(!(null==e?void 0:e.length))return;const i=await this.postElements(t,[l(e)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] file message sent",{status:i.status}),!i.ok)throw new Error(`sendFileMessage failed: ${i.status} ${a}`)}async sendSoundMessage(t,e,s=0){var i;if(!(null==e?void 0:e.trim()))return;const a=await this.postElements(t,[u(e,s)]),n=await a.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] sound message sent",{status:a.status}),!a.ok)throw new Error(`sendSoundMessage failed: ${a.status} ${n.slice(0,500)}`)}async sendStickerMessage(t,e,s,i){var a;if(!(null==e?void 0:e.trim())||!(null==s?void 0:s.trim()))return;const n=c(e,s),r=await this.postElements(t,[i?{...n,msgID:i}:n]),o=await r.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] sticker message sent",{status:r.status}),!r.ok)throw new Error(`sendStickerMessage failed: ${r.status} ${o}`)}async editTextMessage(t,e){var s;const i=await this.postElements(t,[{msgID:t,...r(e)}]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] text message edited",{status:i.status}),!i.ok)throw new Error(`editTextMessage failed: ${i.status} ${a.slice(0,500)}`)}async editStickerMessage(t,e,s){var i;const a=await this.postElements(t,[{msgID:t,...c(e,s)}]),n=await a.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] sticker message edited",{status:a.status}),!a.ok)throw new Error(`editStickerMessage failed: ${a.status} ${n.slice(0,500)}`)}async editImageMessage(t,e){var s;const i=await this.postElements(t,[{msgID:t,...o(e)}]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message edited",{status:i.status}),!i.ok)throw new Error(`editImageMessage failed: ${i.status} ${a.slice(0,500)}`)}async deleteMessage(t){var e;const s=await this.fetchImpl(`${this.baseUrl}/v1/chat/message/${t}`,{method:"DELETE",headers:this.authHeaders});if(null===(e=this.logger)||void 0===e||e.log("[chat-sdk] message deleted",{status:s.status}),!s.ok){const t=await s.text();throw new Error(`deleteMessage failed: ${s.status} ${t.slice(0,500)}`)}}async sendTypingStatus(t,e){var s,i;const a={businessID:"user_typing_status",typingStatus:e?1:0,version:1,userAction:14,actionParam:e?"EIMAMSG_InputStatus_Ing":"EIMAMSG_InputStatus_End"};try{await this.postElements(t,[d(f(JSON.stringify(a)))])}catch(t){null===(s=this.logger)||void 0===s||s.error("[chat-sdk] typing status failed",null!==(i=null==t?void 0:t.message)&&void 0!==i?i:String(t))}}async sendStreamingChunk(t,e,s,i){const a={businessID:"chatbotPlugin",src:0,chunks:e,isFinished:s,TMessageCell_Name:"ChatbotMessageCell_Minimalist",TMessageCell_Data_Name:"ChatbotMessageCellData"},n={msgID:t,...d(f(JSON.stringify(a)))},r=await this.postElements(t,[n],i?"POST":"PATCH");if(!r.ok)throw new Error(`sendStreamingChunk failed: ${r.statusText}`);await r.text()}async sendMiniAppMessage(t,e){var s;const i=await this.postElements(t,[g(e)]),a=await i.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] miniapp card sent",{status:i.status}),!i.ok)throw new Error(`sendMiniAppMessage failed: ${i.status} ${a}`)}async sendWebLinkMessage(t,s){var i;const a={businessID:e,url:s},n=await this.postElements(t,[d(f(JSON.stringify(a)))]),r=await n.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] web link sent",{status:n.status}),!n.ok)throw new Error(`sendWebLinkMessage failed: ${n.status} ${r.slice(0,500)}`)}async sendChoicesMessage(t,e){var i,a,n;const r=this.newMsgID(),o={businessID:s,promptId:null!==(i=e.promptId)&&void 0!==i?i:this.newMsgID(),prompt:e.prompt,selectionMode:null!==(a=e.selectionMode)&&void 0!==a?a:"single",choices:e.choices},l=await this.postElements(t,[{msgID:r,...d(f(JSON.stringify(o)))}]),u=await l.text();if(null===(n=this.logger)||void 0===n||n.log("[chat-sdk] choices message sent",{status:l.status}),!l.ok)throw new Error(`sendChoicesMessage failed: ${l.status} ${u.slice(0,500)}`);return r}async sendOfficialAccountTweetMessage(t,e){var s;const a=this.newMsgID(),n={businessID:i,...e},r=await this.postElements(t,[{msgID:a,...d(f(JSON.stringify(n)))}]),o=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] official account tweet sent",{status:r.status}),!r.ok)throw new Error(`sendOfficialAccountTweetMessage failed: ${r.status} ${o.slice(0,500)}`);return a}async streamMessage(t,e,s=10){const i=w(e,s),a=[];for(let e=0;e<i.length;e++)a.push(i[e]),await this.sendStreamingChunk(t,a,e===i.length-1,0===e)}}y.ElemType=n;export{s as AI_CHOICE_PROMPT_BUSINESS_ID,y as ChatClient,t as DEFAULT_BASE_URL,n as ElemType,i as OFFICIAL_ACCOUNT_TWEET_BUSINESS_ID,e as WEB_LINK_BUSINESS_ID,p as arrayBufferToBase64,m as base64ToArrayBuffer,d as customElem,l as fileElem,a as getAccessToken,o as imageElem,g as miniAppElem,u as soundElem,w as splitIntoChunks,c as stickerElem,r as textElem,f as toBase64};
1
+ const t="https://chat-api2-3rnt.onrender.com",e="web_link",s="ai_choice_prompt",a="official_account_tweet";async function i(e){var s;const{clientId:a,clientSecret:i}=e;if(!a||!i)throw new Error("getAccessToken: clientId and clientSecret are required");const n=t.replace(/\/$/,""),r=null!==(s=e.scope)&&void 0!==s?s:"chat:write uploads:write";if("undefined"==typeof fetch)throw new Error("getAccessToken: global fetch is not available");const o=await fetch(`${n}/v1/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:a,client_secret:i,scope:r})});if(!o.ok)throw new Error(`Token request failed: ${o.status} ${o.statusText}`);return await o.json()}var n;function r(t){return{elemType:n.Text,priority:0,textElem:{text:t.trim()}}}function o(t){return{elemType:n.Image,priority:0,imageElem:{upload_ids:t}}}function l(t){return{elemType:n.File,priority:0,fileElem:{upload_ids:t}}}function u(t,e=0){return{elemType:n.Sound,priority:0,soundElem:{uploadID:t,duration:e}}}function c(t,e){return{elemType:n.Sticker,priority:0,stickerElem:{packID:t,stickerID:e}}}function d(t){return{elemType:n.Custom,priority:0,customElem:{data:t}}}function h(t){return{elemType:n.MiniApp,priority:0,miniAppElem:t}}function g(t){const e=new Uint8Array(t.byteLength);return e.set(t),e}function p(t){let e="";const s=new Uint8Array(t),a=s.byteLength;for(let t=0;t<a;t++)e+=String.fromCharCode(s[t]);return btoa(e)}function m(t){const e=atob(t),s=e.length,a=new Uint8Array(s);for(let t=0;t<s;t++)a[t]=e.charCodeAt(t);return a.buffer}function f(t){const e=(new TextEncoder).encode(t);let s="";for(const t of e)s+=String.fromCharCode(t);return btoa(s)}function w(t,e=10){if(t.length<=e)return[t];const s=[];let a="";for(const i of t.split(" "))a.length+i.length+1>e&&a.length>0?(s.push(a.trim()+" "),a=i):a+=(a?" ":"")+i;return a&&s.push(a.trim()),s.length>0?s:[t]}!function(t){t[t.None=0]="None",t[t.Text=1]="Text",t[t.Image=2]="Image",t[t.Sound=3]="Sound",t[t.Video=4]="Video",t[t.File=5]="File",t[t.Sticker=6]="Sticker",t[t.GroupTips=7]="GroupTips",t[t.Merger=8]="Merger",t[t.Custom=9]="Custom",t[t.Location=10]="Location",t[t.GroupAnnouncement=11]="GroupAnnouncement",t[t.Quote=12]="Quote",t[t.InputStatus=14]="InputStatus",t[t.TypingStatus=15]="TypingStatus",t[t.MiniApp=16]="MiniApp",t[t.Order=17]="Order",t[t.Transfer=18]="Transfer"}(n||(n={}));class y{constructor(e){var s,a;if(!(null==e?void 0:e.token))throw new Error("ChatClient: token is required");this.token=e.token,this.baseUrl=(null!==(s=e.baseUrl)&&void 0!==s?s:t).replace(/\/$/,""),this.logger=e.logger;const i=null!==(a=e.fetch)&&void 0!==a?a:"undefined"!=typeof fetch?fetch:void 0;if(!i)throw new Error("ChatClient: no fetch implementation available; pass `fetch` in options");this.fetchImpl=i.bind(globalThis)}static async fromCredentials(t){const{access_token:e}=await i({clientId:t.clientId,clientSecret:t.clientSecret,scope:t.scope});return new y({token:e,baseUrl:t.baseUrl,logger:t.logger,fetch:t.fetch})}get authHeaders(){return{"Content-Type":"application/json",Authorization:`Bearer ${this.token}`}}replyUrl(t){return`${this.baseUrl}/v1/chat/message/${t}/reply`}newMsgID(){return globalThis.crypto.randomUUID()}async postElements(t,e,s="POST"){return await this.fetchImpl(this.replyUrl(t),{method:s,headers:this.authHeaders,body:JSON.stringify(e)})}async uploadImage(t,e,s="image/png"){var a;const i=new FormData;i.append("file",new Blob([g(t)],{type:s}),e);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/image`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:i}),r=await n.json();if(!n.ok)throw new Error(`uploadImage failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadImage: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(a=r.url)&&void 0!==a?a:""}}async uploadFile(t,e,s="application/octet-stream"){var a;const i=new FormData;i.append("file",new Blob([g(t)],{type:s}),e);const n=await this.fetchImpl(`${this.baseUrl}/v1/upload/file`,{method:"POST",headers:{Authorization:`Bearer ${this.token}`},body:i}),r=await n.json();if(!n.ok)throw new Error(`uploadFile failed: ${n.status} ${JSON.stringify(r)}`);if(!r.upload_id)throw new Error(`uploadFile: missing upload_id: ${JSON.stringify(r)}`);return{upload_id:r.upload_id,url:null!==(a=r.url)&&void 0!==a?a:""}}async sendMessage(t,e,s){var a;let i=Array.isArray(e)?e.map((t=>t.trim())).filter(Boolean).map(r):[r(e)];if(0===i.length)return;s&&(i=i.map((t=>({...t,msgID:s}))));const n=await this.postElements(t,i),o=await n.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] message sent",{status:n.status,elements:i.length}),!n.ok)throw new Error(`sendMessage failed: ${n.status} ${o}`)}async sendImageMessage(t,e){var s;if(!(null==e?void 0:e.length))return;const a=await this.postElements(t,[o(e)]),i=await a.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message sent",{status:a.status}),!a.ok)throw new Error(`sendImageMessage failed: ${a.status} ${i}`)}async sendFileMessage(t,e){var s;if(!(null==e?void 0:e.length))return;const a=await this.postElements(t,[l(e)]),i=await a.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] file message sent",{status:a.status}),!a.ok)throw new Error(`sendFileMessage failed: ${a.status} ${i}`)}async sendSoundMessage(t,e,s=0){var a;if(!(null==e?void 0:e.trim()))return;const i=await this.postElements(t,[u(e,s)]),n=await i.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] sound message sent",{status:i.status}),!i.ok)throw new Error(`sendSoundMessage failed: ${i.status} ${n.slice(0,500)}`)}async sendStickerMessage(t,e,s,a){var i;if(!(null==e?void 0:e.trim())||!(null==s?void 0:s.trim()))return;const n=c(e,s),r=await this.postElements(t,[a?{...n,msgID:a}:n]),o=await r.text();if(null===(i=this.logger)||void 0===i||i.log("[chat-sdk] sticker message sent",{status:r.status}),!r.ok)throw new Error(`sendStickerMessage failed: ${r.status} ${o}`)}async editTextMessage(t,e){var s;const a=await this.postElements(t,[{msgID:t,...r(e)}]),i=await a.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] text message edited",{status:a.status}),!a.ok)throw new Error(`editTextMessage failed: ${a.status} ${i.slice(0,500)}`)}async editStickerMessage(t,e,s){var a;const i=await this.postElements(t,[{msgID:t,...c(e,s)}]),n=await i.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] sticker message edited",{status:i.status}),!i.ok)throw new Error(`editStickerMessage failed: ${i.status} ${n.slice(0,500)}`)}async editImageMessage(t,e){var s;const a=await this.postElements(t,[{msgID:t,...o(e)}]),i=await a.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] image message edited",{status:a.status}),!a.ok)throw new Error(`editImageMessage failed: ${a.status} ${i.slice(0,500)}`)}async deleteMessage(t){var e;const s=await this.fetchImpl(`${this.baseUrl}/v1/chat/message/${t}`,{method:"DELETE",headers:this.authHeaders});if(null===(e=this.logger)||void 0===e||e.log("[chat-sdk] message deleted",{status:s.status}),!s.ok){const t=await s.text();throw new Error(`deleteMessage failed: ${s.status} ${t.slice(0,500)}`)}}async sendTypingStatus(t,e){var s,a;const i={businessID:"user_typing_status",typingStatus:e?1:0,version:1,userAction:14,actionParam:e?"EIMAMSG_InputStatus_Ing":"EIMAMSG_InputStatus_End"};try{await this.postElements(t,[d(f(JSON.stringify(i)))])}catch(t){null===(s=this.logger)||void 0===s||s.error("[chat-sdk] typing status failed",null!==(a=null==t?void 0:t.message)&&void 0!==a?a:String(t))}}async sendStreamingChunk(t,e,s,a){const i={businessID:"chatbotPlugin",src:0,chunks:e,isFinished:s,TMessageCell_Name:"ChatbotMessageCell_Minimalist",TMessageCell_Data_Name:"ChatbotMessageCellData"},n={msgID:t,...d(f(JSON.stringify(i)))},r=await this.postElements(t,[n],a?"POST":"PATCH");if(!r.ok)throw new Error(`sendStreamingChunk failed: ${r.statusText}`);await r.text()}async sendMiniAppMessage(t,e){var s;const a=await this.postElements(t,[h(e)]),i=await a.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] miniapp card sent",{status:a.status}),!a.ok)throw new Error(`sendMiniAppMessage failed: ${a.status} ${i}`)}async sendWebLinkMessage(t,s){var a;const i={businessID:e,url:s},n=await this.postElements(t,[d(f(JSON.stringify(i)))]),r=await n.text();if(null===(a=this.logger)||void 0===a||a.log("[chat-sdk] web link sent",{status:n.status}),!n.ok)throw new Error(`sendWebLinkMessage failed: ${n.status} ${r.slice(0,500)}`)}async sendChoicesMessage(t,e){var a,i,n;const r=this.newMsgID(),o={businessID:s,promptId:null!==(a=e.promptId)&&void 0!==a?a:this.newMsgID(),prompt:e.prompt,selectionMode:null!==(i=e.selectionMode)&&void 0!==i?i:"single",choices:e.choices},l=await this.postElements(t,[{msgID:r,...d(f(JSON.stringify(o)))}]),u=await l.text();if(null===(n=this.logger)||void 0===n||n.log("[chat-sdk] choices message sent",{status:l.status}),!l.ok)throw new Error(`sendChoicesMessage failed: ${l.status} ${u.slice(0,500)}`);return r}async sendOfficialAccountTweetMessage(t,e){var s;const i=this.newMsgID(),n={businessID:a,...e},r=await this.postElements(t,[{msgID:i,...d(f(JSON.stringify(n)))}]),o=await r.text();if(null===(s=this.logger)||void 0===s||s.log("[chat-sdk] official account tweet sent",{status:r.status}),!r.ok)throw new Error(`sendOfficialAccountTweetMessage failed: ${r.status} ${o.slice(0,500)}`);return i}async streamMessage(t,e,s=10){const a=w(e,s),i=[];for(let e=0;e<a.length;e++)i.push(a[e]),await this.sendStreamingChunk(t,i,e===a.length-1,0===e)}}async function k(t,e,s,a={}){const{toleranceSec:i=300,logger:n}=a,r=t.headers.get("X-Webhook-Signature");if(!r)return null==n||n.error("[chat-sdk] webhook missing X-Webhook-Signature header"),{valid:!1,error:"Missing X-Webhook-Signature header"};const o=t.headers.get("X-Webhook-Timestamp");if(!o)return null==n||n.error("[chat-sdk] webhook missing X-Webhook-Timestamp header"),{valid:!1,error:"Missing X-Webhook-Timestamp header"};const l=r.startsWith("sha256=")?r.slice(7):r,u=await async function(t,e){const s=new TextEncoder,a=await crypto.subtle.importKey("raw",s.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await crypto.subtle.sign("HMAC",a,s.encode(e));return[...new Uint8Array(i)].map((t=>t.toString(16).padStart(2,"0"))).join("")}(e,`${o}.${s}`);if(!function(t,e){if(t.length!==e.length)return!1;let s=0;for(let a=0;a<t.length;a++)s|=t.charCodeAt(a)^e.charCodeAt(a);return 0===s}(l,u))return null==n||n.error("[chat-sdk] webhook invalid signature"),{valid:!1,error:"Invalid signature"};const c=parseInt(o,10),d=Math.floor(Date.now()/1e3);return!Number.isFinite(c)||Math.abs(d-c)>i?(null==n||n.error("[chat-sdk] webhook timestamp out of range"),{valid:!1,error:"Request timestamp too old or too far in future"}):(null==n||n.log("[chat-sdk] webhook signature valid"),{valid:!0})}y.ElemType=n;export{s as AI_CHOICE_PROMPT_BUSINESS_ID,y as ChatClient,t as DEFAULT_BASE_URL,n as ElemType,a as OFFICIAL_ACCOUNT_TWEET_BUSINESS_ID,e as WEB_LINK_BUSINESS_ID,p as arrayBufferToBase64,m as base64ToArrayBuffer,d as customElem,l as fileElem,i as getAccessToken,o as imageElem,h as miniAppElem,u as soundElem,w as splitIntoChunks,c as stickerElem,r as textElem,f as toBase64,k as verifyWebhookSignature};
2
2
  //# sourceMappingURL=main.esm.js.map
@@ -95,3 +95,5 @@ export declare class ChatClient {
95
95
  /** Re-export of the element types for convenience. */
96
96
  static ElemType: typeof ElemType;
97
97
  }
98
+ /** Build a one-off SDK client bound to a bearer token. */
99
+ export declare function chatClient(token: string): ChatClient;
@@ -1,6 +1,8 @@
1
1
  export { ChatClient } from './client';
2
2
  export { getAccessToken } from './auth';
3
3
  export type { GetAccessTokenOptions } from './auth';
4
+ export { verifyWebhookSignature } from './webhook';
5
+ export type { VerifyWebhookOptions, WebhookVerificationResult } from './webhook';
4
6
  export * from './types';
5
7
  export * from './elements';
6
8
  export * from './constants';
@@ -0,0 +1,28 @@
1
+ import { Logger } from './types';
2
+ export interface VerifyWebhookOptions {
3
+ /** Max allowed clock skew in seconds (replay protection). Defaults to 300. */
4
+ toleranceSec?: number;
5
+ /** Optional logger for diagnostics. Omit for silence. */
6
+ logger?: Logger;
7
+ }
8
+ export interface WebhookVerificationResult {
9
+ valid: boolean;
10
+ error?: string;
11
+ }
12
+ /**
13
+ * Verify a chat-API webhook signature.
14
+ *
15
+ * Scheme (matches the Go webhook manager): the request carries an
16
+ * `X-Webhook-Signature` header (optionally `sha256=`-prefixed) and an
17
+ * `X-Webhook-Timestamp` header, where the signature is
18
+ * `HMAC-SHA256(secret, "${timestamp}.${rawBody}")`. Pass `bodyText` as the raw
19
+ * request body exactly as received (read it before JSON-parsing).
20
+ *
21
+ * ```ts
22
+ * const raw = await request.text()
23
+ * const { valid } = await verifyWebhookSignature(request, env.WEBHOOK_SECRET, raw)
24
+ * if (!valid) return new Response('bad signature', { status: 401 })
25
+ * const event = JSON.parse(raw)
26
+ * ```
27
+ */
28
+ export declare function verifyWebhookSignature(request: Request, secret: string, bodyText: string, options?: VerifyWebhookOptions): Promise<WebhookVerificationResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "birdcash-chat-sdk-alpha",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "TypeScript SDK for sending messages through the chat API (text, images, files, miniapp cards, streaming).",
5
5
  "main": "dist/main.cjs.js",
6
6
  "module": "dist/main.esm.js",