@stacksjs/chat 0.70.22 โ 0.70.25
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 +3 -3
- package/dist/index.js +1 -1
- package/dist/src/drivers/discord.d.ts +75 -0
- package/dist/src/drivers/slack.d.ts +76 -0
- package/dist/src/drivers/teams.d.ts +69 -0
- package/dist/src/index.d.ts +43 -0
- package/package.json +23 -9
- package/dist/base.d.ts +0 -111
- package/dist/index.d.ts +0 -3
- package/dist/teams.d.ts +0 -1
package/README.md
CHANGED
|
@@ -39,9 +39,9 @@ Drivers are configured with the following environment variables:
|
|
|
39
39
|
#### Slack
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
SLACK*APPLICATION*ID=SAID123
|
|
43
|
+
SLACK*CLIENT*ID=SCID123
|
|
44
|
+
SLACK*SECRET*KEY=SSK123
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
## ๐งช Testing
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var e={};var s={};var t={};export{t as teams,s as slack,e as discord};
|
|
2
|
+
var w=Object.defineProperty;var h=(q)=>q;function f(q,z){this[q]=h.bind(null,z)}var B=(q,z)=>{for(var F in z)w(q,F,{get:z[F],enumerable:!0,configurable:!0,set:f.bind(z,F)})};var j={};B(j,{sendWebhook:()=>P,sendEmbed:()=>S,send:()=>Z,driver:()=>u,default:()=>k,configure:()=>N,Driver:()=>O,DiscordDriver:()=>O});import{log as R}from"@stacksjs/logging";import{log as D}from"@stacksjs/logging";class K{config;constructor(q){this.config={maxRetries:q?.maxRetries||3,retryTimeout:q?.retryTimeout||1000,...q}}configure(q){this.config={...this.config,...q}}validateMessage(q){if(!q.to)throw Error("Message recipient is required");if(!q.content&&!q.template)throw Error("Either message content or template is required");return!0}async handleError(q,z){let F=q instanceof Error?q:Error(String(q));D.error(`[${this.name}] Message sending failed`,{error:F.message,stack:F.stack?.replace(/https?:\/\/[^\s)\]"']+/g,"[REDACTED-URL]"),to:z.to,subject:z.subject});let G={message:`Message sending failed: ${F.message}`,success:!1,provider:this.name};if(z.onError){let H=z.onError(F),J=H instanceof Promise?await H:H;G={...G,...J,success:!1,provider:this.name}}return G}async handleSuccess(q,z){let F={message:"Message sent successfully",success:!0,provider:this.name,messageId:z};try{if(q.handle){let G=q.handle(),H=G instanceof Promise?await G:G;F={...F,...H,success:!0,provider:this.name,messageId:z}}if(q.onSuccess){let G=q.onSuccess(),H=G instanceof Promise?await G:G;F={...F,...H,success:!0,provider:this.name,messageId:z}}}catch(G){return this.handleError(G,q)}return F}}var L={};function N(q){if(q.webhookUrl){let z;try{z=new URL(q.webhookUrl)}catch{throw Error(`[chat/discord] webhookUrl is not a valid URL: ${q.webhookUrl}`)}if(z.protocol!=="https:")throw Error("[chat/discord] webhookUrl must use https://");if(!/(^|\.)discord(?:app)?\.com$/i.test(z.hostname))throw Error(`[chat/discord] webhookUrl host "${z.hostname}" is not a Discord-owned domain.`)}L={...L,...q}}class O extends K{name="discord";constructor(q){super({maxRetries:q?.maxRetries??3,retryTimeout:q?.retryTimeout??1000});if(q)N(q)}async send(q,z){let F={provider:this.name,to:q.to,subject:q.subject||"No subject"};R.info("Sending message via Discord...",F);try{return this.validateMessage(q),await this.sendWithRetry(q,z)}catch(G){return this.handleError(G,q)}}async sendWithRetry(q,z,F=1){try{let G=await this.sendMessage(q,z);return this.handleSuccess(q,G.id)}catch(G){if(F<this.config.maxRetries)return R.warn(`[${this.name}] Message send failed, retrying (${F}/${this.config.maxRetries})`),await new Promise((H)=>setTimeout(H,this.config.retryTimeout)),this.sendWithRetry(q,z,F+1);throw G}}async sendMessage(q,z){if(L.webhookUrl)return this.sendViaWebhook(q);if(L.botToken)return this.sendViaBotToken(q);throw Error("Discord not configured: provide webhookUrl or botToken")}async sendViaWebhook(q){let z={content:q.content,username:q.from};if(q.subject||q.template)z.embeds=[this.buildEmbed(q)];let F=await fetch(L.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(z)});if(!F.ok){let H=await F.text();throw Error(`Discord webhook failed: ${F.status} - ${H}`)}if(F.status===204)return{};return{id:(await F.json()).id}}async sendViaBotToken(q){let z=q.to,F={content:q.content};if(q.subject||q.template)F.embeds=[this.buildEmbed(q)];let G=await fetch(`https://discord.com/api/v10/channels/${z}/messages`,{method:"POST",headers:{Authorization:`Bot ${L.botToken}`,"Content-Type":"application/json"},body:JSON.stringify(F)});if(!G.ok){let J=await G.json();throw Error(`Discord API failed: ${G.status} - ${J.message}`)}return{id:(await G.json()).id}}buildEmbed(q){let z={};if(q.subject)z.title=q.subject;if(q.content)z.description=q.content;return z.color=5793266,z.timestamp=new Date().toISOString(),z}}async function Z(q,z){return new O().send(q,z)}async function P(q,z,F){try{let G={content:z,...F},H=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(G)});if(!H.ok){let J=await H.text();return{success:!1,provider:"discord",message:`Webhook failed: ${H.status} - ${J}`}}return{success:!0,provider:"discord",message:"Message sent successfully"}}catch(G){return{success:!1,provider:"discord",message:(G instanceof Error?G:Error(String(G))).message}}}async function S(q,z,F){return P(q,"",{...F,embeds:[z]})}var u=new O,k={send:Z,sendWebhook:P,sendEmbed:S,configure:N,DiscordDriver:O};var W={};B(W,{sendWebhook:()=>$,send:()=>_,driver:()=>b,default:()=>v,configure:()=>Q,SlackDriver:()=>V,Driver:()=>V});import{log as y}from"@stacksjs/logging";var M={};function Q(q){if(q.webhookUrl){let z;try{z=new URL(q.webhookUrl)}catch{throw Error(`[chat/slack] webhookUrl is not a valid URL: ${q.webhookUrl}`)}if(z.protocol!=="https:")throw Error("[chat/slack] webhookUrl must use https:// \u2014 Slack does not accept plain HTTP webhooks.");if(!/(^|\.)slack\.com$/i.test(z.hostname))throw Error(`[chat/slack] webhookUrl host "${z.hostname}" is not a Slack-owned domain.`)}M={...M,...q}}class V extends K{name="slack";constructor(q){super({maxRetries:q?.maxRetries??3,retryTimeout:q?.retryTimeout??1000});if(q)Q(q)}async send(q,z){let F={provider:this.name,to:q.to,subject:q.subject||"No subject"};y.info("Sending message via Slack...",F);try{return this.validateMessage(q),await this.sendWithRetry(q,z)}catch(G){return this.handleError(G,q)}}async sendWithRetry(q,z,F=1){try{let G=await this.sendMessage(q,z);return this.handleSuccess(q,G.ts)}catch(G){if(F<this.config.maxRetries)return y.warn(`[${this.name}] Message send failed, retrying (${F}/${this.config.maxRetries})`),await new Promise((H)=>setTimeout(H,this.config.retryTimeout)),this.sendWithRetry(q,z,F+1);throw G}}async sendMessage(q,z){if(M.webhookUrl)return this.sendViaWebhook(q);if(M.botToken)return this.sendViaBotToken(q);throw Error("Slack not configured: provide webhookUrl or botToken")}async sendViaWebhook(q){let F=q.content||q.subject||"",H={text:F.length>40000?`${F.slice(0,39986)}\u2026 [truncated]`:F,username:q.from,mrkdwn:!0};if(q.template)H.blocks=this.buildBlocks(q);let J=await fetch(M.webhookUrl,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(H)});if(!J.ok){let x=await J.text();throw Error(`Slack webhook failed: ${J.status} - ${x}`)}return{}}async sendViaBotToken(q){let z={channel:q.to,text:q.content||q.subject,mrkdwn:!0};if(q.template)z.blocks=this.buildBlocks(q);let F=await fetch("https://slack.com/api/chat.postMessage",{method:"POST",headers:{Authorization:`Bearer ${M.botToken}`,"Content-Type":"application/json"},body:JSON.stringify(z)});if(!F.ok)throw Error(`Slack API failed: ${F.status}`);let G=await F.json();if(!G.ok)throw Error(`Slack API error: ${G.error}`);return{ts:G.ts}}buildBlocks(q){let z=[];if(q.subject)z.push({type:"header",text:{type:"plain_text",text:q.subject,emoji:!0}});if(q.content)z.push({type:"section",text:{type:"mrkdwn",text:q.content}});return z}}async function _(q,z){return new V().send(q,z)}async function $(q,z,F){try{let G={text:z,...F},H=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(G)});if(!H.ok){let J=await H.text();return{success:!1,provider:"slack",message:`Webhook failed: ${H.status} - ${J}`}}return{success:!0,provider:"slack",message:"Message sent successfully"}}catch(G){return{success:!1,provider:"slack",message:(G instanceof Error?G:Error(String(G))).message}}}var b=new V,v={send:_,sendWebhook:$,configure:Q,SlackDriver:V};var C={};B(C,{sendWebhook:()=>E,sendCard:()=>T,send:()=>A,driver:()=>d,default:()=>c,configure:()=>X,TeamsDriver:()=>Y,Driver:()=>Y});import{log as U}from"@stacksjs/logging";var I={};function X(q){I={...I,...q}}class Y extends K{name="teams";constructor(q){super({maxRetries:q?.maxRetries??3,retryTimeout:q?.retryTimeout??1000});if(q)X(q)}async send(q,z){let F={provider:this.name,to:q.to,subject:q.subject||"No subject"};U.info("Sending message via Microsoft Teams...",F);try{return this.validateMessage(q),await this.sendWithRetry(q,z)}catch(G){return this.handleError(G,q)}}async sendWithRetry(q,z,F=1){try{return await this.sendMessage(q,z),this.handleSuccess(q)}catch(G){if(F<this.config.maxRetries)return U.warn(`[${this.name}] Message send failed, retrying (${F}/${this.config.maxRetries})`),await new Promise((H)=>setTimeout(H,this.config.retryTimeout)),this.sendWithRetry(q,z,F+1);throw G}}async sendMessage(q,z){let F=I.webhookUrl||q.to;if(!F||!F.includes("webhook.office.com"))throw Error("Teams webhook URL not configured or invalid");let G=this.buildPayload(q),H=await fetch(F,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(G)});if(!H.ok){let J=await H.text();throw Error(`Teams webhook failed: ${H.status} - ${J}`)}}buildPayload(q){if(!q.subject&&!q.template)return{type:"message",text:q.content||""};let z=this.buildAdaptiveCard(q);return{type:"message",summary:q.subject||q.content?.substring(0,50)||"Notification",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:z}]}}buildAdaptiveCard(q){let z=[];if(q.subject)z.push({type:"TextBlock",text:q.subject,size:"Large",weight:"Bolder",wrap:!0});if(q.content)z.push({type:"TextBlock",text:q.content,wrap:!0});return z.push({type:"TextBlock",text:new Date().toLocaleString(),size:"Small",color:"Dark",wrap:!0}),{type:"AdaptiveCard",version:"1.4",$schema:"http://adaptivecards.io/schemas/adaptive-card.json",body:z}}}async function A(q,z){return new Y().send(q,z)}async function E(q,z){try{let G=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"message",text:z})});if(!G.ok){let H=await G.text();return{success:!1,provider:"teams",message:`Webhook failed: ${G.status} - ${H}`}}return{success:!0,provider:"teams",message:"Message sent successfully"}}catch(F){return{success:!1,provider:"teams",message:(F instanceof Error?F:Error(String(F))).message}}}async function T(q,z,F){try{let H=await fetch(q,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({type:"message",summary:F||"Notification",attachments:[{contentType:"application/vnd.microsoft.card.adaptive",content:z}]})});if(!H.ok){let J=await H.text();return{success:!1,provider:"teams",message:`Webhook failed: ${H.status} - ${J}`}}return{success:!0,provider:"teams",message:"Card sent successfully"}}catch(G){return{success:!1,provider:"teams",message:(G instanceof Error?G:Error(String(G))).message}}}var d=new Y,c={send:A,sendWebhook:E,sendCard:T,configure:X,TeamsDriver:Y};async function o(q,z={}){let F=z.driver??"slack";switch(F){case"slack":return _(q);case"discord":return Z(q);case"teams":return A(q);default:return{success:!1,provider:F,message:`Unknown chat driver: ${F}`}}}async function s(q,z,F){return $(q,z,F)}async function e(q,z,F){return P(q,z,F)}async function qq(q,z){return E(q,z)}function zq(q){Q(q)}function Fq(q){N(q)}function Gq(q){X(q)}export{C as teams,W as slack,qq as sendToTeams,s as sendToSlack,e as sendToDiscord,o as send,j as discord,Gq as configureTeams,zq as configureSlack,Fq as configureDiscord};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BaseChatDriver } from './base';
|
|
2
|
+
import type { ChatMessage, ChatResult, RenderOptions } from '@stacksjs/types';
|
|
3
|
+
/**
|
|
4
|
+
* Configure Discord with webhook URL or bot token.
|
|
5
|
+
*
|
|
6
|
+
* Validates that the webhook URL (when provided) is HTTPS pointing at
|
|
7
|
+
* Discord's host. Same reasoning as the Slack driver โ `http://` URLs
|
|
8
|
+
* leak the webhook token; off-domain URLs are typo'd configuration.
|
|
9
|
+
*/
|
|
10
|
+
export declare function configure(options: DiscordConfig): void;
|
|
11
|
+
/**
|
|
12
|
+
* Send a message to Discord
|
|
13
|
+
*/
|
|
14
|
+
export declare function send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Send a simple message via Discord webhook
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendWebhook(webhookUrl: string, content: string, options?: Partial<DiscordMessage>): Promise<ChatResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Send an embed message via Discord webhook
|
|
21
|
+
*/
|
|
22
|
+
export declare function sendEmbed(webhookUrl: string, embed: DiscordEmbed, options?: Partial<DiscordMessage>): Promise<ChatResult>;
|
|
23
|
+
export declare const driver: DiscordDriver;
|
|
24
|
+
export declare interface DiscordConfig {
|
|
25
|
+
webhookUrl?: string
|
|
26
|
+
botToken?: string
|
|
27
|
+
maxRetries?: number
|
|
28
|
+
retryTimeout?: number
|
|
29
|
+
}
|
|
30
|
+
export declare interface DiscordEmbed {
|
|
31
|
+
title?: string
|
|
32
|
+
description?: string
|
|
33
|
+
url?: string
|
|
34
|
+
color?: number
|
|
35
|
+
timestamp?: string
|
|
36
|
+
footer?: {
|
|
37
|
+
text: string
|
|
38
|
+
icon_url?: string
|
|
39
|
+
}
|
|
40
|
+
image?: {
|
|
41
|
+
url: string
|
|
42
|
+
}
|
|
43
|
+
thumbnail?: {
|
|
44
|
+
url: string
|
|
45
|
+
}
|
|
46
|
+
author?: {
|
|
47
|
+
name: string
|
|
48
|
+
url?: string
|
|
49
|
+
icon_url?: string
|
|
50
|
+
}
|
|
51
|
+
fields?: Array<{
|
|
52
|
+
name: string
|
|
53
|
+
value: string
|
|
54
|
+
inline?: boolean
|
|
55
|
+
}>
|
|
56
|
+
}
|
|
57
|
+
export declare interface DiscordMessage {
|
|
58
|
+
content?: string
|
|
59
|
+
username?: string
|
|
60
|
+
avatarUrl?: string
|
|
61
|
+
tts?: boolean
|
|
62
|
+
embeds?: DiscordEmbed[]
|
|
63
|
+
allowedMentions?: {
|
|
64
|
+
parse?: Array<'roles' | 'users' | 'everyone'>
|
|
65
|
+
roles?: string[]
|
|
66
|
+
users?: string[]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export declare class DiscordDriver extends BaseChatDriver {
|
|
70
|
+
name: string;
|
|
71
|
+
constructor(options?: DiscordConfig);
|
|
72
|
+
send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
73
|
+
}
|
|
74
|
+
export { DiscordDriver as Driver };
|
|
75
|
+
export default { send, sendWebhook, sendEmbed, configure, DiscordDriver };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { BaseChatDriver } from './base';
|
|
2
|
+
import type { ChatMessage, ChatResult, RenderOptions } from '@stacksjs/types';
|
|
3
|
+
/**
|
|
4
|
+
* Configure Slack with webhook URL or bot token.
|
|
5
|
+
*
|
|
6
|
+
* Validates that the webhook URL (when provided) is HTTPS pointing at
|
|
7
|
+
* Slack's webhook host. A misconfigured `http://` URL would leak the
|
|
8
|
+
* channel-specific token in plaintext to anyone watching network
|
|
9
|
+
* traffic; a typo'd domain would silently send messages to whatever
|
|
10
|
+
* server happens to accept them.
|
|
11
|
+
*/
|
|
12
|
+
export declare function configure(options: SlackConfig): void;
|
|
13
|
+
/**
|
|
14
|
+
* Send a message to Slack
|
|
15
|
+
*/
|
|
16
|
+
export declare function send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Send a simple text message to a Slack channel via webhook
|
|
19
|
+
*/
|
|
20
|
+
export declare function sendWebhook(webhookUrl: string, text: string, options?: Partial<SlackMessage>): Promise<ChatResult>;
|
|
21
|
+
export declare const driver: SlackDriver;
|
|
22
|
+
export declare interface SlackConfig {
|
|
23
|
+
webhookUrl?: string
|
|
24
|
+
botToken?: string
|
|
25
|
+
maxRetries?: number
|
|
26
|
+
retryTimeout?: number
|
|
27
|
+
}
|
|
28
|
+
export declare interface SlackMessage {
|
|
29
|
+
channel?: string
|
|
30
|
+
text?: string
|
|
31
|
+
blocks?: SlackBlock[]
|
|
32
|
+
attachments?: SlackAttachment[]
|
|
33
|
+
username?: string
|
|
34
|
+
iconEmoji?: string
|
|
35
|
+
iconUrl?: string
|
|
36
|
+
threadTs?: string
|
|
37
|
+
mrkdwn?: boolean
|
|
38
|
+
}
|
|
39
|
+
export declare interface SlackBlock {
|
|
40
|
+
type: 'section' | 'divider' | 'header' | 'context' | 'actions' | 'image'
|
|
41
|
+
text?: {
|
|
42
|
+
type: 'plain_text' | 'mrkdwn'
|
|
43
|
+
text: string
|
|
44
|
+
emoji?: boolean
|
|
45
|
+
}
|
|
46
|
+
accessory?: any
|
|
47
|
+
elements?: any[]
|
|
48
|
+
block_id?: string
|
|
49
|
+
}
|
|
50
|
+
export declare interface SlackAttachment {
|
|
51
|
+
color?: string
|
|
52
|
+
pretext?: string
|
|
53
|
+
author_name?: string
|
|
54
|
+
author_link?: string
|
|
55
|
+
author_icon?: string
|
|
56
|
+
title?: string
|
|
57
|
+
title_link?: string
|
|
58
|
+
text?: string
|
|
59
|
+
fields?: Array<{
|
|
60
|
+
title: string
|
|
61
|
+
value: string
|
|
62
|
+
short?: boolean
|
|
63
|
+
}>
|
|
64
|
+
image_url?: string
|
|
65
|
+
thumb_url?: string
|
|
66
|
+
footer?: string
|
|
67
|
+
footer_icon?: string
|
|
68
|
+
ts?: number
|
|
69
|
+
}
|
|
70
|
+
export declare class SlackDriver extends BaseChatDriver {
|
|
71
|
+
name: string;
|
|
72
|
+
constructor(options?: SlackConfig);
|
|
73
|
+
send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
74
|
+
}
|
|
75
|
+
export { SlackDriver as Driver };
|
|
76
|
+
export default { send, sendWebhook, configure, SlackDriver };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { BaseChatDriver } from './base';
|
|
2
|
+
import type { ChatMessage, ChatResult, RenderOptions } from '@stacksjs/types';
|
|
3
|
+
/**
|
|
4
|
+
* Configure Teams with webhook URL
|
|
5
|
+
*/
|
|
6
|
+
export declare function configure(options: TeamsConfig): void;
|
|
7
|
+
/**
|
|
8
|
+
* Send a message to Microsoft Teams
|
|
9
|
+
*/
|
|
10
|
+
export declare function send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Send a simple text message to Teams via webhook
|
|
13
|
+
*/
|
|
14
|
+
export declare function sendWebhook(webhookUrl: string, text: string): Promise<ChatResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Send an Adaptive Card to Teams via webhook
|
|
17
|
+
*/
|
|
18
|
+
export declare function sendCard(webhookUrl: string, card: TeamsAdaptiveCard, summary?: string): Promise<ChatResult>;
|
|
19
|
+
export declare const driver: TeamsDriver;
|
|
20
|
+
export declare interface TeamsConfig {
|
|
21
|
+
webhookUrl?: string
|
|
22
|
+
maxRetries?: number
|
|
23
|
+
retryTimeout?: number
|
|
24
|
+
}
|
|
25
|
+
export declare interface TeamsAdaptiveCard {
|
|
26
|
+
type: 'AdaptiveCard'
|
|
27
|
+
version: string
|
|
28
|
+
body: TeamsCardElement[]
|
|
29
|
+
actions?: TeamsCardAction[]
|
|
30
|
+
$schema?: string
|
|
31
|
+
}
|
|
32
|
+
export declare interface TeamsCardElement {
|
|
33
|
+
type: 'TextBlock' | 'Image' | 'Container' | 'ColumnSet' | 'Column' | 'FactSet' | 'ImageSet'
|
|
34
|
+
text?: string
|
|
35
|
+
size?: 'Small' | 'Default' | 'Medium' | 'Large' | 'ExtraLarge'
|
|
36
|
+
weight?: 'Lighter' | 'Default' | 'Bolder'
|
|
37
|
+
color?: 'Default' | 'Dark' | 'Light' | 'Accent' | 'Good' | 'Warning' | 'Attention'
|
|
38
|
+
wrap?: boolean
|
|
39
|
+
url?: string
|
|
40
|
+
altText?: string
|
|
41
|
+
items?: TeamsCardElement[]
|
|
42
|
+
columns?: TeamsCardElement[]
|
|
43
|
+
width?: string
|
|
44
|
+
facts?: Array<{ title: string, value: string }>
|
|
45
|
+
images?: Array<{ type: 'Image', url: string, size?: string }>
|
|
46
|
+
}
|
|
47
|
+
export declare interface TeamsCardAction {
|
|
48
|
+
type: 'Action.OpenUrl' | 'Action.Submit' | 'Action.ShowCard'
|
|
49
|
+
title: string
|
|
50
|
+
url?: string
|
|
51
|
+
data?: Record<string, any>
|
|
52
|
+
card?: TeamsAdaptiveCard
|
|
53
|
+
}
|
|
54
|
+
export declare interface TeamsMessage {
|
|
55
|
+
type?: 'message'
|
|
56
|
+
summary?: string
|
|
57
|
+
text?: string
|
|
58
|
+
attachments?: Array<{
|
|
59
|
+
contentType: 'application/vnd.microsoft.card.adaptive'
|
|
60
|
+
content: TeamsAdaptiveCard
|
|
61
|
+
}>
|
|
62
|
+
}
|
|
63
|
+
export declare class TeamsDriver extends BaseChatDriver {
|
|
64
|
+
name: string;
|
|
65
|
+
constructor(options?: TeamsConfig);
|
|
66
|
+
send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>;
|
|
67
|
+
}
|
|
68
|
+
export { TeamsDriver as Driver };
|
|
69
|
+
export default { send, sendWebhook, sendCard, configure, TeamsDriver };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as discord from './drivers/discord';
|
|
2
|
+
import * as slack from './drivers/slack';
|
|
3
|
+
import * as teams from './drivers/teams';
|
|
4
|
+
import type { ChatMessage, ChatResult } from '@stacksjs/types';
|
|
5
|
+
// Re-export types
|
|
6
|
+
export type { SlackConfig, SlackMessage, SlackBlock, SlackAttachment } from './drivers/slack';
|
|
7
|
+
export type { DiscordConfig, DiscordMessage, DiscordEmbed } from './drivers/discord';
|
|
8
|
+
export type { TeamsConfig, TeamsMessage, TeamsAdaptiveCard, TeamsCardElement, TeamsCardAction } from './drivers/teams';
|
|
9
|
+
/**
|
|
10
|
+
* Send a chat message using the specified driver
|
|
11
|
+
*/
|
|
12
|
+
export declare function send(message: ChatMessage, options?: SendOptions): Promise<ChatResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Send a message to Slack via webhook
|
|
15
|
+
*/
|
|
16
|
+
export declare function sendToSlack(webhookUrl: string, text: string, options?: Parameters<typeof slack.sendWebhook>[2]): Promise<ChatResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Send a message to Discord via webhook
|
|
19
|
+
*/
|
|
20
|
+
export declare function sendToDiscord(webhookUrl: string, content: string, options?: Parameters<typeof discord.sendWebhook>[2]): Promise<ChatResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Send a message to Teams via webhook
|
|
23
|
+
*/
|
|
24
|
+
export declare function sendToTeams(webhookUrl: string, text: string): Promise<ChatResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Configure Slack driver
|
|
27
|
+
*/
|
|
28
|
+
export declare function configureSlack(config: slack.SlackConfig): void;
|
|
29
|
+
/**
|
|
30
|
+
* Configure Discord driver
|
|
31
|
+
*/
|
|
32
|
+
export declare function configureDiscord(config: discord.DiscordConfig): void;
|
|
33
|
+
/**
|
|
34
|
+
* Configure Teams driver
|
|
35
|
+
*/
|
|
36
|
+
export declare function configureTeams(config: teams.TeamsConfig): void;
|
|
37
|
+
export declare interface SendOptions {
|
|
38
|
+
driver?: ChatDriver
|
|
39
|
+
}
|
|
40
|
+
export type ChatDriver = 'slack' | 'discord' | 'teams';
|
|
41
|
+
export * as discord from './drivers/discord';
|
|
42
|
+
export * as slack from './drivers/slack';
|
|
43
|
+
export * as teams from './drivers/teams';
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/chat",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.70.
|
|
4
|
+
"version": "0.70.25",
|
|
5
5
|
"description": "Easily interact with chat APIs.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
|
-
"contributors": [
|
|
7
|
+
"contributors": [
|
|
8
|
+
"Chris Breuer <chris@stacksjs.com>"
|
|
9
|
+
],
|
|
8
10
|
"license": "MIT",
|
|
9
11
|
"funding": "https://github.com/sponsors/chrisbbreuer",
|
|
10
12
|
"homepage": "https://github.com/stacksjs/stacks/tree/main/storage/framework/core/chat#readme",
|
|
@@ -16,28 +18,40 @@
|
|
|
16
18
|
"bugs": {
|
|
17
19
|
"url": "https://github.com/stacksjs/stacks/issues"
|
|
18
20
|
},
|
|
19
|
-
"keywords": [
|
|
21
|
+
"keywords": [
|
|
22
|
+
"chat",
|
|
23
|
+
"stacks",
|
|
24
|
+
"discord",
|
|
25
|
+
"microsoft teams",
|
|
26
|
+
"slack"
|
|
27
|
+
],
|
|
20
28
|
"exports": {
|
|
21
29
|
".": {
|
|
30
|
+
"bun": "./src/index.ts",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
22
32
|
"import": "./dist/index.js"
|
|
23
33
|
},
|
|
24
34
|
"./*": {
|
|
35
|
+
"bun": "./src/*",
|
|
25
36
|
"import": "./dist/*"
|
|
26
37
|
}
|
|
27
38
|
},
|
|
28
39
|
"module": "dist/index.js",
|
|
29
40
|
"types": "dist/index.d.ts",
|
|
30
|
-
"files": [
|
|
41
|
+
"files": [
|
|
42
|
+
"README.md",
|
|
43
|
+
"dist"
|
|
44
|
+
],
|
|
31
45
|
"scripts": {
|
|
32
46
|
"build": "bun build.ts",
|
|
33
47
|
"typecheck": "bun tsc --noEmit",
|
|
34
48
|
"prepublishOnly": "bun run build"
|
|
35
49
|
},
|
|
36
50
|
"devDependencies": {
|
|
37
|
-
"@stacksjs/cli": "0.70.
|
|
38
|
-
"@stacksjs/config": "0.70.
|
|
39
|
-
"
|
|
40
|
-
"@stacksjs/error-handling": "0.70.
|
|
41
|
-
"@stacksjs/types": "0.70.
|
|
51
|
+
"@stacksjs/cli": "0.70.23",
|
|
52
|
+
"@stacksjs/config": "0.70.23",
|
|
53
|
+
"better-dx": "^0.2.12",
|
|
54
|
+
"@stacksjs/error-handling": "0.70.23",
|
|
55
|
+
"@stacksjs/types": "0.70.23"
|
|
42
56
|
}
|
|
43
57
|
}
|
package/dist/base.d.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import type { ChatDriver, ChatDriverConfig, ChatMessage, ChatResult, RenderOptions } from '@stacksjs/types';
|
|
2
|
-
|
|
3
|
-
export declare abstract class BaseChatDriver implements ChatDriver {
|
|
4
|
-
public abstract name: string
|
|
5
|
-
protected config: Required<ChatDriverConfig>
|
|
6
|
-
|
|
7
|
-
constructor(config?: ChatDriverConfig) {
|
|
8
|
-
this.config = {
|
|
9
|
-
maxRetries: config?.maxRetries || 3,
|
|
10
|
-
retryTimeout: config?.retryTimeout || 1000,
|
|
11
|
-
...config,
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public configure(config: ChatDriverConfig): void {
|
|
16
|
-
this.config = { ...this.config, ...config }
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public abstract send(message: ChatMessage, options?: RenderOptions): Promise<ChatResult>
|
|
20
|
-
|
|
21
|
-
protected validateMessage(message: ChatMessage): boolean {
|
|
22
|
-
if (!message.to) {
|
|
23
|
-
throw new Error('Message recipient is required')
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!message.content && !message.template) {
|
|
27
|
-
throw new Error('Either message content or template is required')
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
protected async handleError(error: unknown, message: ChatMessage): Promise<ChatResult> {
|
|
34
|
-
const err = error instanceof Error ? error : new Error(String(error))
|
|
35
|
-
|
|
36
|
-
log.error(`[${this.name}] Message sending failed`, {
|
|
37
|
-
error: err.message,
|
|
38
|
-
stack: err.stack,
|
|
39
|
-
to: message.to,
|
|
40
|
-
subject: message.subject,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
let result: ChatResult = {
|
|
44
|
-
message: `Message sending failed: ${err.message}`,
|
|
45
|
-
success: false,
|
|
46
|
-
provider: this.name,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (message.onError) {
|
|
50
|
-
const customResult = message.onError(err)
|
|
51
|
-
const handlerResult = customResult instanceof Promise
|
|
52
|
-
? await customResult
|
|
53
|
-
: customResult
|
|
54
|
-
|
|
55
|
-
result = {
|
|
56
|
-
...result,
|
|
57
|
-
...handlerResult,
|
|
58
|
-
success: false,
|
|
59
|
-
provider: this.name,
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return result
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
protected async handleSuccess(message: ChatMessage, messageId?: string): Promise<ChatResult> {
|
|
67
|
-
let result: ChatResult = {
|
|
68
|
-
message: 'Message sent successfully',
|
|
69
|
-
success: true,
|
|
70
|
-
provider: this.name,
|
|
71
|
-
messageId,
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
if (message.handle) {
|
|
76
|
-
const customResult = message.handle()
|
|
77
|
-
const handlerResult = customResult instanceof Promise
|
|
78
|
-
? await customResult
|
|
79
|
-
: customResult
|
|
80
|
-
|
|
81
|
-
result = {
|
|
82
|
-
...result,
|
|
83
|
-
...handlerResult,
|
|
84
|
-
success: true,
|
|
85
|
-
provider: this.name,
|
|
86
|
-
messageId,
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (message.onSuccess) {
|
|
91
|
-
const successResult = message.onSuccess()
|
|
92
|
-
const handlerResult = successResult instanceof Promise
|
|
93
|
-
? await successResult
|
|
94
|
-
: successResult
|
|
95
|
-
|
|
96
|
-
result = {
|
|
97
|
-
...result,
|
|
98
|
-
...handlerResult,
|
|
99
|
-
success: true,
|
|
100
|
-
provider: this.name,
|
|
101
|
-
messageId,
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
return this.handleError(error, message)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return result
|
|
110
|
-
}
|
|
111
|
-
}
|
package/dist/index.d.ts
DELETED
package/dist/teams.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type {}
|