@cogitator-ai/channels 0.16.0 → 0.18.0
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 +78 -14
- package/dist/approvals.d.ts +3 -0
- package/dist/approvals.d.ts.map +1 -1
- package/dist/approvals.js +1 -0
- package/dist/approvals.js.map +1 -1
- package/dist/assistant-config.d.ts +60 -0
- package/dist/assistant-config.d.ts.map +1 -1
- package/dist/assistant-config.js +102 -0
- package/dist/assistant-config.js.map +1 -1
- package/dist/channels/email/address.d.ts +44 -0
- package/dist/channels/email/address.d.ts.map +1 -0
- package/dist/channels/email/address.js +211 -0
- package/dist/channels/email/address.js.map +1 -0
- package/dist/channels/email/auth.d.ts +59 -0
- package/dist/channels/email/auth.d.ts.map +1 -0
- package/dist/channels/email/auth.js +212 -0
- package/dist/channels/email/auth.js.map +1 -0
- package/dist/channels/email/channel.d.ts +256 -0
- package/dist/channels/email/channel.d.ts.map +1 -0
- package/dist/channels/email/channel.js +701 -0
- package/dist/channels/email/channel.js.map +1 -0
- package/dist/channels/email/errors.d.ts +11 -0
- package/dist/channels/email/errors.d.ts.map +1 -0
- package/dist/channels/email/errors.js +13 -0
- package/dist/channels/email/errors.js.map +1 -0
- package/dist/channels/email/imap.d.ts +125 -0
- package/dist/channels/email/imap.d.ts.map +1 -0
- package/dist/channels/email/imap.js +309 -0
- package/dist/channels/email/imap.js.map +1 -0
- package/dist/channels/email/index.d.ts +7 -0
- package/dist/channels/email/index.d.ts.map +1 -0
- package/dist/channels/email/index.js +3 -0
- package/dist/channels/email/index.js.map +1 -0
- package/dist/channels/email/mime.d.ts +71 -0
- package/dist/channels/email/mime.d.ts.map +1 -0
- package/dist/channels/email/mime.js +83 -0
- package/dist/channels/email/mime.js.map +1 -0
- package/dist/channels/email/multipart.d.ts +28 -0
- package/dist/channels/email/multipart.d.ts.map +1 -0
- package/dist/channels/email/multipart.js +102 -0
- package/dist/channels/email/multipart.js.map +1 -0
- package/dist/channels/email/providers.d.ts +49 -0
- package/dist/channels/email/providers.d.ts.map +1 -0
- package/dist/channels/email/providers.js +292 -0
- package/dist/channels/email/providers.js.map +1 -0
- package/dist/channels/email/reply.d.ts +9 -0
- package/dist/channels/email/reply.d.ts.map +1 -0
- package/dist/channels/email/reply.js +122 -0
- package/dist/channels/email/reply.js.map +1 -0
- package/dist/channels/teams/activity.d.ts +108 -0
- package/dist/channels/teams/activity.d.ts.map +1 -0
- package/dist/channels/teams/activity.js +206 -0
- package/dist/channels/teams/activity.js.map +1 -0
- package/dist/channels/teams/auth.d.ts +65 -0
- package/dist/channels/teams/auth.d.ts.map +1 -0
- package/dist/channels/teams/auth.js +213 -0
- package/dist/channels/teams/auth.js.map +1 -0
- package/dist/channels/teams/cards.d.ts +23 -0
- package/dist/channels/teams/cards.d.ts.map +1 -0
- package/dist/channels/teams/cards.js +97 -0
- package/dist/channels/teams/cards.js.map +1 -0
- package/dist/channels/teams/channel.d.ts +303 -0
- package/dist/channels/teams/channel.d.ts.map +1 -0
- package/dist/channels/teams/channel.js +1071 -0
- package/dist/channels/teams/channel.js.map +1 -0
- package/dist/channels/teams/client.d.ts +72 -0
- package/dist/channels/teams/client.d.ts.map +1 -0
- package/dist/channels/teams/client.js +161 -0
- package/dist/channels/teams/client.js.map +1 -0
- package/dist/channels/teams/index.d.ts +7 -0
- package/dist/channels/teams/index.d.ts.map +1 -0
- package/dist/channels/teams/index.js +6 -0
- package/dist/channels/teams/index.js.map +1 -0
- package/dist/channels/teams/pacer.d.ts +47 -0
- package/dist/channels/teams/pacer.d.ts.map +1 -0
- package/dist/channels/teams/pacer.js +174 -0
- package/dist/channels/teams/pacer.js.map +1 -0
- package/dist/channels/teams/token.d.ts +47 -0
- package/dist/channels/teams/token.d.ts.map +1 -0
- package/dist/channels/teams/token.js +107 -0
- package/dist/channels/teams/token.js.map +1 -0
- package/dist/channels/telegram.d.ts.map +1 -1
- package/dist/channels/telegram.js +3 -0
- package/dist/channels/telegram.js.map +1 -1
- package/dist/formatters/html.d.ts +16 -0
- package/dist/formatters/html.d.ts.map +1 -0
- package/dist/formatters/html.js +535 -0
- package/dist/formatters/html.js.map +1 -0
- package/dist/formatters/markdown-images.d.ts +8 -0
- package/dist/formatters/markdown-images.d.ts.map +1 -0
- package/dist/formatters/markdown-images.js +181 -0
- package/dist/formatters/markdown-images.js.map +1 -0
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +22 -3
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/gateway.d.ts +23 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +113 -16
- package/dist/gateway.js.map +1 -1
- package/dist/inbound-debounce.d.ts +4 -1
- package/dist/inbound-debounce.d.ts.map +1 -1
- package/dist/inbound-debounce.js +4 -2
- package/dist/inbound-debounce.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime-builder.d.ts +9 -0
- package/dist/runtime-builder.d.ts.map +1 -1
- package/dist/runtime-builder.js +145 -1
- package/dist/runtime-builder.js.map +1 -1
- package/package.json +25 -8
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createPublicKey, verify } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/** Where Bot Framework publishes the OpenID metadata of the tokens it signs requests with. */
|
|
4
|
+
export const BOT_FRAMEWORK_OPENID_METADATA = 'https://login.botframework.com/v1/.well-known/openidconfiguration';
|
|
5
|
+
/** The issuer of the tokens Bot Framework sends with every activity. */
|
|
6
|
+
export const BOT_FRAMEWORK_ISSUER = 'https://api.botframework.com';
|
|
7
|
+
/** How far the clocks of Bot Framework and the bot may differ, in seconds. */
|
|
8
|
+
const CLOCK_SKEW = 300;
|
|
9
|
+
/** How long signing keys are used before they are loaded again, in ms. */
|
|
10
|
+
const KEYS_MAX_AGE = 24 * 60 * 60 * 1000;
|
|
11
|
+
/** How often a token signed with a key the bot does not know may load the keys again, in ms. */
|
|
12
|
+
const UNKNOWN_KEY_REFRESH = 5 * 60 * 1000;
|
|
13
|
+
/** How long after the keys failed to load requests are refused before they are tried again, in ms. */
|
|
14
|
+
const FAILED_LOAD_BACKOFF = 10 * 1000;
|
|
15
|
+
/** How long loading the metadata or the keys may take by default, in ms. */
|
|
16
|
+
const KEYS_TIMEOUT = 10_000;
|
|
17
|
+
/** The longest token taken, in characters: Bot Framework tokens are about 1.5 KB. */
|
|
18
|
+
const MAX_TOKEN_LENGTH = 16 * 1024;
|
|
19
|
+
const BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
20
|
+
/** Why a request was refused: `401` for a token that does not prove Bot Framework sent it, `403` for one it may not use. */
|
|
21
|
+
export class TeamsAuthError extends Error {
|
|
22
|
+
status;
|
|
23
|
+
constructor(status, message, options) {
|
|
24
|
+
super(`[teams] ${message}`, options);
|
|
25
|
+
this.status = status;
|
|
26
|
+
this.name = 'TeamsAuthError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const HeaderSchema = z.object({
|
|
30
|
+
alg: z.string(),
|
|
31
|
+
kid: z.string().optional(),
|
|
32
|
+
x5t: z.string().optional(),
|
|
33
|
+
});
|
|
34
|
+
const ClaimsSchema = z.object({
|
|
35
|
+
iss: z.string(),
|
|
36
|
+
aud: z.union([z.string(), z.array(z.string())]),
|
|
37
|
+
exp: z.number(),
|
|
38
|
+
nbf: z.number().optional(),
|
|
39
|
+
serviceurl: z.string().optional(),
|
|
40
|
+
serviceUrl: z.string().optional(),
|
|
41
|
+
});
|
|
42
|
+
const MetadataSchema = z.object({ jwks_uri: z.string() });
|
|
43
|
+
const JwkSchema = z.object({
|
|
44
|
+
kty: z.string(),
|
|
45
|
+
kid: z.string().optional(),
|
|
46
|
+
n: z.string().optional(),
|
|
47
|
+
e: z.string().optional(),
|
|
48
|
+
endorsements: z.array(z.string()).optional(),
|
|
49
|
+
});
|
|
50
|
+
const JwksSchema = z.object({ keys: z.array(z.unknown()) });
|
|
51
|
+
function decodeSegment(segment) {
|
|
52
|
+
return JSON.parse(Buffer.from(segment, 'base64url').toString('utf-8'));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Checks the JWT Bot Framework sends with every activity, with no dependency but `node:crypto`:
|
|
56
|
+
* RS256 only, signed by a key of the published JWKS, issued by {@link BOT_FRAMEWORK_ISSUER} for
|
|
57
|
+
* the bot's App ID, and valid now within five minutes of clock skew. The keys are cached and
|
|
58
|
+
* loaded again once a day, and a token signed by a key not seen yet loads them again at most
|
|
59
|
+
* every five minutes, so a rotated key is picked up and a flood of forged ones costs nothing.
|
|
60
|
+
*/
|
|
61
|
+
export class TeamsTokenVerifier {
|
|
62
|
+
options;
|
|
63
|
+
metadataUrl;
|
|
64
|
+
fetchImpl;
|
|
65
|
+
now;
|
|
66
|
+
timeout;
|
|
67
|
+
keys = new Map();
|
|
68
|
+
loadedAt = 0;
|
|
69
|
+
lastUnknownRefresh = Number.NEGATIVE_INFINITY;
|
|
70
|
+
lastFailure = Number.NEGATIVE_INFINITY;
|
|
71
|
+
loading;
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.options = options;
|
|
74
|
+
this.metadataUrl = options.metadataUrl ?? BOT_FRAMEWORK_OPENID_METADATA;
|
|
75
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
76
|
+
this.now = options.now ?? Date.now;
|
|
77
|
+
this.timeout = options.timeout ?? KEYS_TIMEOUT;
|
|
78
|
+
}
|
|
79
|
+
/** Checks a token, resolving with what it proves, or throws a {@link TeamsAuthError}. */
|
|
80
|
+
async verify(token) {
|
|
81
|
+
if (token.length > MAX_TOKEN_LENGTH)
|
|
82
|
+
throw new TeamsAuthError(401, 'The token is too long');
|
|
83
|
+
const parts = token.split('.');
|
|
84
|
+
if (parts.length !== 3 || !parts.every((part) => BASE64URL.test(part))) {
|
|
85
|
+
throw new TeamsAuthError(401, 'The token is not a signed JWT');
|
|
86
|
+
}
|
|
87
|
+
const [headerPart, payloadPart, signaturePart] = parts;
|
|
88
|
+
let header;
|
|
89
|
+
let claims;
|
|
90
|
+
try {
|
|
91
|
+
header = HeaderSchema.parse(decodeSegment(headerPart));
|
|
92
|
+
claims = ClaimsSchema.parse(decodeSegment(payloadPart));
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw new TeamsAuthError(401, 'The token cannot be read', { cause: error });
|
|
96
|
+
}
|
|
97
|
+
if (header.alg !== 'RS256') {
|
|
98
|
+
throw new TeamsAuthError(401, `The token is signed with ${header.alg}, not RS256`);
|
|
99
|
+
}
|
|
100
|
+
const kid = header.kid ?? header.x5t;
|
|
101
|
+
if (!kid)
|
|
102
|
+
throw new TeamsAuthError(401, 'The token names no signing key');
|
|
103
|
+
const signingKey = await this.keyFor(kid);
|
|
104
|
+
if (!signingKey)
|
|
105
|
+
throw new TeamsAuthError(401, 'The token is signed by an unknown key');
|
|
106
|
+
const signed = verify('RSA-SHA256', Buffer.from(`${headerPart}.${payloadPart}`), signingKey.key, Buffer.from(signaturePart, 'base64url'));
|
|
107
|
+
if (!signed)
|
|
108
|
+
throw new TeamsAuthError(401, 'The token signature is invalid');
|
|
109
|
+
if (claims.iss !== BOT_FRAMEWORK_ISSUER) {
|
|
110
|
+
throw new TeamsAuthError(401, 'The token was not issued by Bot Framework');
|
|
111
|
+
}
|
|
112
|
+
const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
|
|
113
|
+
if (!audiences.includes(this.options.appId)) {
|
|
114
|
+
throw new TeamsAuthError(401, 'The token was issued for another app');
|
|
115
|
+
}
|
|
116
|
+
const seconds = this.now() / 1000;
|
|
117
|
+
if (seconds > claims.exp + CLOCK_SKEW)
|
|
118
|
+
throw new TeamsAuthError(401, 'The token expired');
|
|
119
|
+
if (claims.nbf !== undefined && seconds < claims.nbf - CLOCK_SKEW) {
|
|
120
|
+
throw new TeamsAuthError(401, 'The token is not valid yet');
|
|
121
|
+
}
|
|
122
|
+
const serviceUrl = claims.serviceurl ?? claims.serviceUrl;
|
|
123
|
+
return {
|
|
124
|
+
...(serviceUrl !== undefined && { serviceUrl }),
|
|
125
|
+
endorsements: signingKey.endorsements,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The signing key of `kid`. Cached keys are used at once, and once they are a day old they are
|
|
130
|
+
* loaded again in the background. Only a cache that is still empty waits for the keys. A key
|
|
131
|
+
* the cache lacks loads them again, at most every five minutes.
|
|
132
|
+
*/
|
|
133
|
+
async keyFor(kid) {
|
|
134
|
+
if (this.keys.size === 0)
|
|
135
|
+
await this.refresh();
|
|
136
|
+
else if (this.now() - this.loadedAt > KEYS_MAX_AGE)
|
|
137
|
+
void this.refresh().catch(() => undefined);
|
|
138
|
+
const known = this.keys.get(kid);
|
|
139
|
+
if (known)
|
|
140
|
+
return known;
|
|
141
|
+
if (this.now() - this.lastUnknownRefresh < UNKNOWN_KEY_REFRESH)
|
|
142
|
+
return undefined;
|
|
143
|
+
if (!this.loading && this.now() - this.lastFailure < FAILED_LOAD_BACKOFF)
|
|
144
|
+
return undefined;
|
|
145
|
+
this.lastUnknownRefresh = this.now();
|
|
146
|
+
await this.refresh().catch(() => undefined);
|
|
147
|
+
return this.keys.get(kid);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Loads the keys once for every caller waiting. After a failure no load is tried for ten
|
|
151
|
+
* seconds, however many requests come, and the keys loaded before stay in use.
|
|
152
|
+
*/
|
|
153
|
+
refresh() {
|
|
154
|
+
if (this.loading)
|
|
155
|
+
return this.loading;
|
|
156
|
+
if (this.now() - this.lastFailure < FAILED_LOAD_BACKOFF) {
|
|
157
|
+
return Promise.reject(new TeamsAuthError(503, 'The Bot Framework signing keys could not be loaded'));
|
|
158
|
+
}
|
|
159
|
+
this.loading = this.load()
|
|
160
|
+
.catch((error) => {
|
|
161
|
+
this.lastFailure = this.now();
|
|
162
|
+
if (this.keys.size > 0) {
|
|
163
|
+
console.warn('[teams] Could not reload the Bot Framework signing keys:', error);
|
|
164
|
+
}
|
|
165
|
+
throw new TeamsAuthError(503, 'The Bot Framework signing keys could not be loaded', {
|
|
166
|
+
cause: error,
|
|
167
|
+
});
|
|
168
|
+
})
|
|
169
|
+
.finally(() => {
|
|
170
|
+
this.loading = undefined;
|
|
171
|
+
});
|
|
172
|
+
return this.loading;
|
|
173
|
+
}
|
|
174
|
+
async load() {
|
|
175
|
+
const metadata = MetadataSchema.parse(await this.json(this.metadataUrl));
|
|
176
|
+
const jwksUrl = new URL(metadata.jwks_uri);
|
|
177
|
+
if (jwksUrl.protocol !== 'https:' && new URL(this.metadataUrl).protocol === 'https:') {
|
|
178
|
+
throw new Error(`The JWKS address ${metadata.jwks_uri} is not https`);
|
|
179
|
+
}
|
|
180
|
+
const jwks = JwksSchema.parse(await this.json(jwksUrl.href));
|
|
181
|
+
const keys = new Map();
|
|
182
|
+
for (const entry of jwks.keys) {
|
|
183
|
+
const jwk = JwkSchema.safeParse(entry);
|
|
184
|
+
if (!jwk.success || jwk.data.kty !== 'RSA' || !jwk.data.kid || !jwk.data.n || !jwk.data.e) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
try {
|
|
188
|
+
const key = createPublicKey({
|
|
189
|
+
key: { kty: 'RSA', n: jwk.data.n, e: jwk.data.e },
|
|
190
|
+
format: 'jwk',
|
|
191
|
+
});
|
|
192
|
+
keys.set(jwk.data.kid, { key, endorsements: jwk.data.endorsements ?? [] });
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (keys.size === 0)
|
|
199
|
+
throw new Error('The JWKS holds no RSA keys');
|
|
200
|
+
this.keys = keys;
|
|
201
|
+
this.loadedAt = this.now();
|
|
202
|
+
}
|
|
203
|
+
async json(url) {
|
|
204
|
+
const response = await this.fetchImpl(url, {
|
|
205
|
+
headers: { accept: 'application/json' },
|
|
206
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
207
|
+
});
|
|
208
|
+
if (!response.ok)
|
|
209
|
+
throw new Error(`GET ${url} answered ${response.status}`);
|
|
210
|
+
return response.json();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../src/channels/teams/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,EAAkB,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8FAA8F;AAC9F,MAAM,CAAC,MAAM,6BAA6B,GACxC,mEAAmE,CAAC;AACtE,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAAG,8BAA8B,CAAC;AAEnE,8EAA8E;AAC9E,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,0EAA0E;AAC1E,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACzC,gGAAgG;AAChG,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAC1C,sGAAsG;AACtG,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AACtC,4EAA4E;AAC5E,MAAM,YAAY,GAAG,MAAM,CAAC;AAC5B,qFAAqF;AACrF,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AACnC,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC,4HAA4H;AAC5H,MAAM,OAAO,cAAe,SAAQ,KAAK;IAE5B;IADX,YACW,MAAuB,EAChC,OAAe,EACf,OAA6B;QAE7B,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QAJ5B,WAAM,GAAN,MAAM,CAAiB;QAKhC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC3B,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC/C,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAE1D,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACxB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACxB,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CAC7C,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;AA2B5D,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,OAAO,kBAAkB;IAWA;IAVZ,WAAW,CAAS;IACpB,SAAS,CAAe;IACxB,GAAG,CAAe;IAClB,OAAO,CAAS;IACzB,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAC;IACrC,QAAQ,GAAG,CAAC,CAAC;IACb,kBAAkB,GAAG,MAAM,CAAC,iBAAiB,CAAC;IAC9C,WAAW,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACvC,OAAO,CAAiB;IAEhC,YAA6B,OAAkC;QAAlC,YAAO,GAAP,OAAO,CAA2B;QAC7D,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,6BAA6B,CAAC;QACxE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;QACxC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC;IACjD,CAAC;IAED,yFAAyF;IACzF,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,IAAI,KAAK,CAAC,MAAM,GAAG,gBAAgB;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uBAAuB,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACvE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,+BAA+B,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,KAAK,CAAC;QACvD,IAAI,MAAoC,CAAC;QACzC,IAAI,MAAoC,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;YACvD,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,0BAA0B,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,MAAM,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,4BAA4B,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC;QACrF,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC;QACrC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;QAC1E,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,uCAAuC,CAAC,CAAC;QACxF,MAAM,MAAM,GAAG,MAAM,CACnB,YAAY,EACZ,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,IAAI,WAAW,EAAE,CAAC,EAC3C,UAAU,CAAC,GAAG,EACd,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CACxC,CAAC;QACF,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,gCAAgC,CAAC,CAAC;QAC7E,IAAI,MAAM,CAAC,GAAG,KAAK,oBAAoB,EAAE,CAAC;YACxC,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,2CAA2C,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAClC,IAAI,OAAO,GAAG,MAAM,CAAC,GAAG,GAAG,UAAU;YAAE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;QAC1F,IAAI,MAAM,CAAC,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,MAAM,CAAC,GAAG,GAAG,UAAU,EAAE,CAAC;YAClE,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,4BAA4B,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC;QAC1D,OAAO;YACL,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,UAAU,EAAE,CAAC;YAC/C,YAAY,EAAE,UAAU,CAAC,YAAY;SACtC,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,MAAM,CAAC,GAAW;QAC9B,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;aAC1C,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,GAAG,YAAY;YAAE,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC/F,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,kBAAkB,GAAG,mBAAmB;YAAE,OAAO,SAAS,CAAC;QACjF,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,GAAG,mBAAmB;YAAE,OAAO,SAAS,CAAC;QAC3F,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACK,OAAO;QACb,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,GAAG,mBAAmB,EAAE,CAAC;YACxD,OAAO,OAAO,CAAC,MAAM,CACnB,IAAI,cAAc,CAAC,GAAG,EAAE,oDAAoD,CAAC,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE;aACvB,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,IAAI,CAAC,0DAA0D,EAAE,KAAK,CAAC,CAAC;YAClF,CAAC;YACD,MAAM,IAAI,cAAc,CAAC,GAAG,EAAE,oDAAoD,EAAE;gBAClF,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAC3B,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC3C,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACrF,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,CAAC,QAAQ,eAAe,CAAC,CAAC;QACxE,CAAC;QACD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAC;QAC3C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC1F,SAAS;YACX,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,eAAe,CAAC;oBAC1B,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE;oBACjD,MAAM,EAAE,KAAK;iBACd,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC,CAAC;YAC7E,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,GAAW;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;YACzC,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;SAC1C,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,OAAO,GAAG,aAAa,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5E,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ChannelButton } from '@cogitator-ai/types';
|
|
2
|
+
/** The content type of an Adaptive Card attachment. */
|
|
3
|
+
export declare const ADAPTIVE_CARD = "application/vnd.microsoft.card.adaptive";
|
|
4
|
+
/** The content type of the card that asks a user to accept a file upload. */
|
|
5
|
+
export declare const FILE_CONSENT_CARD = "application/vnd.microsoft.teams.card.file.consent";
|
|
6
|
+
/** The content type of the card that shows an uploaded file. */
|
|
7
|
+
export declare const FILE_INFO_CARD = "application/vnd.microsoft.teams.card.file.info";
|
|
8
|
+
/** The `verb` of the `Action.Execute` buttons the channel sends. */
|
|
9
|
+
export declare const TEAMS_ACTION_VERB = "cogitator";
|
|
10
|
+
/** An Adaptive Card, as JSON. */
|
|
11
|
+
export type AdaptiveCard = Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* An Adaptive Card with rows of buttons, one action set per row. A button with `data` is an
|
|
14
|
+
* `Action.Execute` that falls back to `Action.Submit` on clients without Universal Actions, a
|
|
15
|
+
* `url` opens it, `copyText` shows the text to copy, and a disabled button is shown as text in
|
|
16
|
+
* its color, since older clients cannot disable actions.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buttonsCard(rows: ChannelButton[][]): AdaptiveCard;
|
|
19
|
+
/** The button data of an `Action.Submit` press, the `value` of the message it sends. */
|
|
20
|
+
export declare function submitDataOf(value: unknown): string | undefined;
|
|
21
|
+
/** The button data of an `Action.Execute` press, the `value` of its `adaptiveCard/action` invoke. */
|
|
22
|
+
export declare function executeDataOf(value: unknown): string | undefined;
|
|
23
|
+
//# sourceMappingURL=cards.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.d.ts","sourceRoot":"","sources":["../../../src/channels/teams/cards.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,uDAAuD;AACvD,eAAO,MAAM,aAAa,4CAA4C,CAAC;AACvE,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,sDAAsD,CAAC;AACrF,gEAAgE;AAChE,eAAO,MAAM,cAAc,mDAAmD,CAAC;AAC/E,oEAAoE;AACpE,eAAO,MAAM,iBAAiB,cAAc,CAAC;AAE7C,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA2CnD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,GAAG,YAAY,CAqBjE;AAYD,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG/D;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIhE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/** The content type of an Adaptive Card attachment. */
|
|
3
|
+
export const ADAPTIVE_CARD = 'application/vnd.microsoft.card.adaptive';
|
|
4
|
+
/** The content type of the card that asks a user to accept a file upload. */
|
|
5
|
+
export const FILE_CONSENT_CARD = 'application/vnd.microsoft.teams.card.file.consent';
|
|
6
|
+
/** The content type of the card that shows an uploaded file. */
|
|
7
|
+
export const FILE_INFO_CARD = 'application/vnd.microsoft.teams.card.file.info';
|
|
8
|
+
/** The `verb` of the `Action.Execute` buttons the channel sends. */
|
|
9
|
+
export const TEAMS_ACTION_VERB = 'cogitator';
|
|
10
|
+
const STYLES = {
|
|
11
|
+
primary: 'positive',
|
|
12
|
+
success: 'positive',
|
|
13
|
+
danger: 'destructive',
|
|
14
|
+
};
|
|
15
|
+
const COLORS = {
|
|
16
|
+
primary: 'accent',
|
|
17
|
+
success: 'good',
|
|
18
|
+
danger: 'attention',
|
|
19
|
+
};
|
|
20
|
+
function actionOf(button) {
|
|
21
|
+
const style = button.style ? { style: STYLES[button.style] } : {};
|
|
22
|
+
if (button.url !== undefined) {
|
|
23
|
+
return { type: 'Action.OpenUrl', title: button.text, url: button.url, ...style };
|
|
24
|
+
}
|
|
25
|
+
if (button.copyText !== undefined) {
|
|
26
|
+
return {
|
|
27
|
+
type: 'Action.ShowCard',
|
|
28
|
+
title: button.text,
|
|
29
|
+
card: {
|
|
30
|
+
type: 'AdaptiveCard',
|
|
31
|
+
body: [{ type: 'TextBlock', text: button.copyText, wrap: true, fontType: 'Monospace' }],
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (button.data !== undefined) {
|
|
36
|
+
const data = { [TEAMS_ACTION_VERB]: button.data };
|
|
37
|
+
return {
|
|
38
|
+
type: 'Action.Execute',
|
|
39
|
+
title: button.text,
|
|
40
|
+
verb: TEAMS_ACTION_VERB,
|
|
41
|
+
data,
|
|
42
|
+
...style,
|
|
43
|
+
fallback: { type: 'Action.Submit', title: button.text, data, ...style },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`[teams] Button "${button.text}" needs data, url, copyText or disabled`);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* An Adaptive Card with rows of buttons, one action set per row. A button with `data` is an
|
|
50
|
+
* `Action.Execute` that falls back to `Action.Submit` on clients without Universal Actions, a
|
|
51
|
+
* `url` opens it, `copyText` shows the text to copy, and a disabled button is shown as text in
|
|
52
|
+
* its color, since older clients cannot disable actions.
|
|
53
|
+
*/
|
|
54
|
+
export function buttonsCard(rows) {
|
|
55
|
+
const body = [];
|
|
56
|
+
for (const row of rows) {
|
|
57
|
+
for (const button of row.filter((candidate) => candidate.disabled)) {
|
|
58
|
+
body.push({
|
|
59
|
+
type: 'TextBlock',
|
|
60
|
+
text: button.text,
|
|
61
|
+
weight: 'Bolder',
|
|
62
|
+
wrap: true,
|
|
63
|
+
...(button.style && { color: COLORS[button.style] }),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const actions = row.filter((button) => !button.disabled).map(actionOf);
|
|
67
|
+
if (actions.length > 0)
|
|
68
|
+
body.push({ type: 'ActionSet', actions });
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
type: 'AdaptiveCard',
|
|
72
|
+
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
|
|
73
|
+
version: '1.4',
|
|
74
|
+
body,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const SubmitValueSchema = z.object({ [TEAMS_ACTION_VERB]: z.string() });
|
|
78
|
+
const ExecuteValueSchema = z.object({
|
|
79
|
+
action: z.object({
|
|
80
|
+
type: z.string().optional(),
|
|
81
|
+
verb: z.string().optional(),
|
|
82
|
+
data: z.unknown().optional(),
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
/** The button data of an `Action.Submit` press, the `value` of the message it sends. */
|
|
86
|
+
export function submitDataOf(value) {
|
|
87
|
+
const parsed = SubmitValueSchema.safeParse(value);
|
|
88
|
+
return parsed.success ? parsed.data[TEAMS_ACTION_VERB] : undefined;
|
|
89
|
+
}
|
|
90
|
+
/** The button data of an `Action.Execute` press, the `value` of its `adaptiveCard/action` invoke. */
|
|
91
|
+
export function executeDataOf(value) {
|
|
92
|
+
const parsed = ExecuteValueSchema.safeParse(value);
|
|
93
|
+
if (!parsed.success || parsed.data.action.verb !== TEAMS_ACTION_VERB)
|
|
94
|
+
return undefined;
|
|
95
|
+
return submitDataOf(parsed.data.action.data);
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=cards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cards.js","sourceRoot":"","sources":["../../../src/channels/teams/cards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { type RequestListener } from 'node:http';
|
|
2
|
+
import type { Attachment, Channel, ChannelAction, ChannelButton, ChannelMessage, SendOptions } from '@cogitator-ai/types';
|
|
3
|
+
import { type TeamsRetryOptions } from './client.js';
|
|
4
|
+
import { type TeamsTokenProvider } from './token.js';
|
|
5
|
+
/** The largest message Teams is advised to take, in bytes of the activity JSON. */
|
|
6
|
+
export declare const TEAMS_MAX_MESSAGE_BYTES: number;
|
|
7
|
+
/**
|
|
8
|
+
* The longest text sent as one message, in characters: JSON takes at most six bytes for one
|
|
9
|
+
* character (`\u0001`), so this many stay under {@link TEAMS_MAX_MESSAGE_BYTES} whatever they are.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TEAMS_MESSAGE_CHARS: number;
|
|
12
|
+
/** The port of the channel's own server by default, the port Bot Framework samples use. */
|
|
13
|
+
export declare const TEAMS_DEFAULT_PORT = 3978;
|
|
14
|
+
/** The path of the channel's own server by default, the messaging endpoint of Azure Bot Service. */
|
|
15
|
+
export declare const TEAMS_DEFAULT_PATH = "/api/messages";
|
|
16
|
+
/** The Bot Connector of the commercial cloud, for conversations no activity came from yet. */
|
|
17
|
+
export declare const TEAMS_GLOBAL_SERVICE_URL = "https://smba.trafficmanager.net/teams/";
|
|
18
|
+
/** The largest webhook body the channel's own server takes by default, in bytes. */
|
|
19
|
+
export declare const TEAMS_DEFAULT_MAX_BODY: number;
|
|
20
|
+
/**
|
|
21
|
+
* The shortest time between two updates of one message, in ms, so a streamed answer takes at
|
|
22
|
+
* most one of the 60 requests a conversation may have in 30 seconds every second, and new
|
|
23
|
+
* messages always find room.
|
|
24
|
+
*/
|
|
25
|
+
export declare const TEAMS_MIN_EDIT_INTERVAL = 1000;
|
|
26
|
+
/**
|
|
27
|
+
* The kind of the app registration, as Azure Bot Service names it. New bots are single-tenant
|
|
28
|
+
* or use a user-assigned managed identity: multi-tenant bots cannot be created since July 2025.
|
|
29
|
+
*/
|
|
30
|
+
export type TeamsAppType = 'SingleTenant' | 'MultiTenant' | 'UserAssignedMSI';
|
|
31
|
+
export interface TeamsWebhookConfig {
|
|
32
|
+
/**
|
|
33
|
+
* Run an HTTP server of the channel's own on this port. Without it, mount `handleWebhook` or
|
|
34
|
+
* `webhookListener()` in a server of yours.
|
|
35
|
+
*/
|
|
36
|
+
port?: number;
|
|
37
|
+
/** The path of the channel's own server (default `/api/messages`) */
|
|
38
|
+
path?: string;
|
|
39
|
+
host?: string;
|
|
40
|
+
/** The largest body taken, in bytes (default 1 MB), larger ones get `413` */
|
|
41
|
+
maxBody?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Plain HTTP routes served on the same port, by path, such as the webhook of another channel:
|
|
44
|
+
* `{ '/github/webhook': github.webhookListener() }`. Other paths answer `404`.
|
|
45
|
+
*/
|
|
46
|
+
routes?: Record<string, RequestListener>;
|
|
47
|
+
}
|
|
48
|
+
export interface TeamsChannelConfig {
|
|
49
|
+
/** The Microsoft App ID of the bot's app registration */
|
|
50
|
+
appId: string;
|
|
51
|
+
/** A client secret of the app registration, which outbound tokens are requested with */
|
|
52
|
+
appPassword?: string;
|
|
53
|
+
/** The tenant of the app registration: required for a single-tenant bot */
|
|
54
|
+
tenantId?: string;
|
|
55
|
+
/** Default `SingleTenant` */
|
|
56
|
+
appType?: TeamsAppType;
|
|
57
|
+
/**
|
|
58
|
+
* A function that returns a Bot Connector token, in place of `appPassword`: required for
|
|
59
|
+
* `UserAssignedMSI`, such as a token of the managed identity from `@azure/identity`.
|
|
60
|
+
*/
|
|
61
|
+
tokenProvider?: TeamsTokenProvider;
|
|
62
|
+
/** The server Bot Framework posts activities to. Without `port` mount `handleWebhook` */
|
|
63
|
+
webhook?: TeamsWebhookConfig;
|
|
64
|
+
/**
|
|
65
|
+
* The tenants whose users the bot answers, or `'*'` for every tenant the app is installed in.
|
|
66
|
+
* Default: the bot's own `tenantId`, so an app installed elsewhere answers nobody there.
|
|
67
|
+
*/
|
|
68
|
+
tenants?: string[] | '*';
|
|
69
|
+
/** Answer only these users, by Entra object id (`aadObjectId`) or Teams user id */
|
|
70
|
+
users?: string[];
|
|
71
|
+
/**
|
|
72
|
+
* Which messages of group chats and channels the bot answers: `mention` (default) only those
|
|
73
|
+
* that @mention it, `all` every message it receives. Teams delivers messages without a mention
|
|
74
|
+
* only when the app has the resource-specific consent `ChannelMessage.Read.Group` or
|
|
75
|
+
* `ChatMessage.Read.Chat`. Personal chats are always answered.
|
|
76
|
+
*/
|
|
77
|
+
trigger?: 'mention' | 'all';
|
|
78
|
+
/**
|
|
79
|
+
* The Bot Connector of conversations no activity came from since the start, such as a reminder
|
|
80
|
+
* after a restart (default the commercial cloud, {@link TEAMS_GLOBAL_SERVICE_URL}).
|
|
81
|
+
*/
|
|
82
|
+
serviceUrl?: string;
|
|
83
|
+
/**
|
|
84
|
+
* The hosts a service URL may have, such as `smba.trafficmanager.net`. Activities naming
|
|
85
|
+
* another are refused. Default: any service URL a verified token was issued for.
|
|
86
|
+
*/
|
|
87
|
+
serviceUrlHosts?: string[];
|
|
88
|
+
/** How requests Teams throttles or fails for a moment are tried again */
|
|
89
|
+
retry?: TeamsRetryOptions;
|
|
90
|
+
/** How long one Bot Connector request may take, in ms (default 30 000) */
|
|
91
|
+
requestTimeout?: number;
|
|
92
|
+
/** The OpenID metadata of inbound tokens (default Bot Framework's), for tests */
|
|
93
|
+
openIdMetadataUrl?: string;
|
|
94
|
+
/** Where outbound tokens come from (default `https://login.microsoftonline.com`), for tests and national clouds */
|
|
95
|
+
loginUrl?: string;
|
|
96
|
+
fetch?: typeof fetch;
|
|
97
|
+
}
|
|
98
|
+
/** A webhook request, as any HTTP server hands it over. */
|
|
99
|
+
export interface TeamsWebhookRequest {
|
|
100
|
+
method: string;
|
|
101
|
+
headers: Record<string, string | string[] | undefined>;
|
|
102
|
+
/** The body as received */
|
|
103
|
+
rawBody?: string | Uint8Array;
|
|
104
|
+
}
|
|
105
|
+
export interface TeamsWebhookResponse {
|
|
106
|
+
status: number;
|
|
107
|
+
body: string;
|
|
108
|
+
/** `application/json` for the answer to an invoke, `text/plain` otherwise */
|
|
109
|
+
contentType: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Markdown with its images turned into links (`` becomes `[alt](url)`) and raw
|
|
113
|
+
* `<img>` tags into text, outside code: Teams loads an image as soon as it shows the message,
|
|
114
|
+
* so an answer steered by injected text could send what it read to any address in an image URL.
|
|
115
|
+
*/
|
|
116
|
+
export declare function withoutImages(text: string): string;
|
|
117
|
+
/** Splits text into the messages Teams takes: at most {@link TEAMS_MESSAGE_CHARS} characters each. */
|
|
118
|
+
export declare function splitTeamsText(text: string): string[];
|
|
119
|
+
/**
|
|
120
|
+
* A Microsoft Teams channel on the Bot Framework protocol, with no SDK: Bot Framework posts
|
|
121
|
+
* activities to a webhook, each checked against its signed JWT, and answers go back through the
|
|
122
|
+
* Bot Connector REST API with a client credentials token of the app registration.
|
|
123
|
+
*
|
|
124
|
+
* Each chat is a conversation: `channelId` is the Teams conversation id of a personal or group
|
|
125
|
+
* chat, or of a team channel, whose threads are topics (`topicId` is the thread's root message
|
|
126
|
+
* id), so each channel thread is a conversation of its own. In group chats and channels the bot
|
|
127
|
+
* answers @mentions, without the mention in the text. Buttons are Adaptive Cards, answers are
|
|
128
|
+
* Markdown and can be edited, so they stream.
|
|
129
|
+
*/
|
|
130
|
+
export declare class TeamsChannel implements Channel {
|
|
131
|
+
readonly type = "teams";
|
|
132
|
+
readonly editable = true;
|
|
133
|
+
readonly nativeMarkdown = true;
|
|
134
|
+
readonly maxMessageChars: number;
|
|
135
|
+
private readonly config;
|
|
136
|
+
private readonly tenantId?;
|
|
137
|
+
private readonly verifier;
|
|
138
|
+
private readonly credentials;
|
|
139
|
+
private readonly client;
|
|
140
|
+
private readonly fetchImpl;
|
|
141
|
+
private handler;
|
|
142
|
+
private actionHandler;
|
|
143
|
+
/** Activities taken, by `conversation|id`, so one sent again is handled once */
|
|
144
|
+
private readonly delivered;
|
|
145
|
+
/** Messages handed to the gateway whose handler has not resolved yet, by delivery key */
|
|
146
|
+
private readonly inFlight;
|
|
147
|
+
/** Messages of `inFlight` the gateway handed back while they were being taken */
|
|
148
|
+
private readonly refused;
|
|
149
|
+
/** The delivery key of each message handed over, by message id */
|
|
150
|
+
private readonly deliveryKeys;
|
|
151
|
+
private readonly conversations;
|
|
152
|
+
private readonly sent;
|
|
153
|
+
private readonly updates;
|
|
154
|
+
private readonly pacer;
|
|
155
|
+
/** Conversations whose messages Teams blocks (`MessageWritesBlocked`), by pacing key */
|
|
156
|
+
private readonly blocked;
|
|
157
|
+
/** Messages whose webhook request was answered before the gateway took them, by delivery key */
|
|
158
|
+
private readonly answeredEarly;
|
|
159
|
+
private readonly activeRequests;
|
|
160
|
+
/** Waits that end at once when the channel stops */
|
|
161
|
+
private readonly onHalt;
|
|
162
|
+
/** Aborts every request and wait under way when the channel stops */
|
|
163
|
+
private halt;
|
|
164
|
+
private readonly answers;
|
|
165
|
+
/** The conversation of each button press, to answer it with a message once its request is over */
|
|
166
|
+
private readonly actionConversations;
|
|
167
|
+
private readonly uploads;
|
|
168
|
+
private server?;
|
|
169
|
+
private running;
|
|
170
|
+
private stopped;
|
|
171
|
+
constructor(config: TeamsChannelConfig);
|
|
172
|
+
onMessage(handler: (msg: ChannelMessage) => Promise<void>): void;
|
|
173
|
+
onAction(handler: (action: ChannelAction) => Promise<void>): void;
|
|
174
|
+
start(): Promise<void>;
|
|
175
|
+
stop(): Promise<void>;
|
|
176
|
+
/**
|
|
177
|
+
* Takes back a message the gateway does not answer because it stops. One it refuses while it
|
|
178
|
+
* is being handed over gets its webhook request answered `503`. One it dropped after its
|
|
179
|
+
* request was answered cannot come again, since Bot Framework does not send an activity twice
|
|
180
|
+
* once it was taken, so it is logged.
|
|
181
|
+
*/
|
|
182
|
+
redeliver(msg: ChannelMessage): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Answers a request Bot Framework posts: checks its token, answers at once and hands the
|
|
185
|
+
* activity to the agent after. A button press of an Adaptive Card (`Action.Execute`) is
|
|
186
|
+
* answered with the press's answer in the response, within a few seconds. Mount it in any HTTP
|
|
187
|
+
* server with the raw body. It works before `start()` too, answers a message `503` while no
|
|
188
|
+
* `onMessage` handler is set, so Bot Framework sends it again, and answers `503` once stopped.
|
|
189
|
+
*/
|
|
190
|
+
handleWebhook(request: TeamsWebhookRequest): Promise<TeamsWebhookResponse>;
|
|
191
|
+
/**
|
|
192
|
+
* A Node request listener that answers Bot Framework's requests on any path it is mounted at.
|
|
193
|
+
* Bodies over `webhook.maxBody` (1 MB by default) are refused with `413`.
|
|
194
|
+
*/
|
|
195
|
+
webhookListener(): RequestListener;
|
|
196
|
+
/**
|
|
197
|
+
* Sends `text` to the conversation `channelId`, in the channel thread of `topicId`, as several
|
|
198
|
+
* messages when it is longer than one holds. With `replyTo` the first one replies to that
|
|
199
|
+
* message, and `buttons` go under the last as an Adaptive Card. Resolves with the id of the
|
|
200
|
+
* first message.
|
|
201
|
+
*/
|
|
202
|
+
sendText(channelId: string, text: string, options?: SendOptions): Promise<string>;
|
|
203
|
+
/**
|
|
204
|
+
* Replaces the text of a message the bot sent, keeping its buttons as they are now, also when
|
|
205
|
+
* a change of them is still on its way, unless `buttons` are given. Updates of one message go
|
|
206
|
+
* out one at a time, at least {@link TEAMS_MIN_EDIT_INTERVAL} apart and behind new messages
|
|
207
|
+
* when the conversation nears its limits, and those made meanwhile are folded into the next,
|
|
208
|
+
* the latest text winning, so a streamed answer never queues more than Teams takes.
|
|
209
|
+
*/
|
|
210
|
+
editText(channelId: string, messageId: string, text: string, options?: SendOptions): Promise<void>;
|
|
211
|
+
/** Replaces the buttons of a message the bot sent, or removes them with `null`. */
|
|
212
|
+
editButtons(channelId: string, messageId: string, buttons: ChannelButton[][] | null): Promise<void>;
|
|
213
|
+
/** Deletes a message the bot sent. */
|
|
214
|
+
deleteMessage(channelId: string, messageId: string): Promise<void>;
|
|
215
|
+
/**
|
|
216
|
+
* Shows the typing indicator. It is skipped when the conversation has no room for it now, or
|
|
217
|
+
* Teams blocks the bot's messages there, since a late indicator says nothing.
|
|
218
|
+
*/
|
|
219
|
+
sendTyping(channelId: string, options?: Pick<SendOptions, 'topicId'>): Promise<void>;
|
|
220
|
+
/**
|
|
221
|
+
* Sends a file. A `buffer` goes out in personal chats only: Teams asks the user to accept it
|
|
222
|
+
* with a file consent card and the file is uploaded to their OneDrive once they do, which needs
|
|
223
|
+
* `supportsFiles` in the app manifest. Bots cannot upload files to group chats and channels
|
|
224
|
+
* without Microsoft Graph, so there a file needs a public `url`: an image is shown inline,
|
|
225
|
+
* another file is sent as a link. Resolves with the id of the message sent.
|
|
226
|
+
*/
|
|
227
|
+
sendFile(channelId: string, file: Attachment, options?: SendOptions): Promise<string>;
|
|
228
|
+
/**
|
|
229
|
+
* Answers a button press. An `Action.Execute` press still waiting for its answer gets it in
|
|
230
|
+
* the response, shown to the user who pressed. Otherwise a `text` is sent to the conversation
|
|
231
|
+
* as a message, since Teams has no other way to answer a press. An `alert`, meant for the one
|
|
232
|
+
* who pressed only (such as "this prompt expired"), is not posted where everyone reads it.
|
|
233
|
+
*/
|
|
234
|
+
answerAction(actionId: string, options?: {
|
|
235
|
+
text?: string;
|
|
236
|
+
alert?: boolean;
|
|
237
|
+
}): Promise<void>;
|
|
238
|
+
private allowedTenants;
|
|
239
|
+
/** Whether the sender of an activity may talk to the bot: by their tenant and the user list. */
|
|
240
|
+
private admits;
|
|
241
|
+
private allowsServiceUrl;
|
|
242
|
+
/** Remembers the Bot Connector and kind of the conversation of a verified activity. */
|
|
243
|
+
private rememberConversation;
|
|
244
|
+
private serviceUrlOf;
|
|
245
|
+
private typeOf;
|
|
246
|
+
/**
|
|
247
|
+
* Calls the Bot Connector for a conversation, paced by its limits. A conversation where Teams
|
|
248
|
+
* blocks the bot's messages is not called again until an activity comes from it, and nothing
|
|
249
|
+
* is called once the channel stopped: its waits and requests under way are aborted.
|
|
250
|
+
*/
|
|
251
|
+
private call;
|
|
252
|
+
/** Where a message of an activity goes: the chat, and the channel thread it is in. */
|
|
253
|
+
private placeOf;
|
|
254
|
+
/**
|
|
255
|
+
* Hands a message to the gateway and answers its request once the gateway took it, or after
|
|
256
|
+
* a few seconds. One the gateway refuses because it stops is answered `503`, and one it gives
|
|
257
|
+
* back after its request was answered is logged by `redeliver`.
|
|
258
|
+
*/
|
|
259
|
+
private handOver;
|
|
260
|
+
private receive;
|
|
261
|
+
/**
|
|
262
|
+
* The files of a message the agent can read: files sent in a personal chat, by their download
|
|
263
|
+
* URL, and inline images, downloaded with the bot's token from the Bot Connector the activity
|
|
264
|
+
* came through (and from nowhere else, so the token never leaves it).
|
|
265
|
+
*/
|
|
266
|
+
private inboundAttachments;
|
|
267
|
+
private download;
|
|
268
|
+
/** The action of a button press, from an `Action.Submit` message or an `Action.Execute` invoke. */
|
|
269
|
+
private actionOf;
|
|
270
|
+
private press;
|
|
271
|
+
private invoke;
|
|
272
|
+
/**
|
|
273
|
+
* Answers an `Action.Execute` press within the time Teams waits: with the text the press is
|
|
274
|
+
* answered with, or else the message's card once the press is handled (so it shows what the
|
|
275
|
+
* press changed, such as an approval's decision), or as it is when time runs out.
|
|
276
|
+
*/
|
|
277
|
+
private execute;
|
|
278
|
+
/**
|
|
279
|
+
* Uploads a file the user accepted, in the conversation the consent card was sent to. A card
|
|
280
|
+
* the bot no longer knows (it restarted, or the file expired) and a declined one are answered
|
|
281
|
+
* with a short message.
|
|
282
|
+
*/
|
|
283
|
+
private consented;
|
|
284
|
+
private upload;
|
|
285
|
+
private expireUploads;
|
|
286
|
+
/** Posts a message activity, as a reply to `replyTo` when given, and remembers what it holds. */
|
|
287
|
+
private post;
|
|
288
|
+
/**
|
|
289
|
+
* Updates a message the bot sent (`PUT`), one update of it at a time: changes made while one
|
|
290
|
+
* is under way are folded into the next, which sends the latest state.
|
|
291
|
+
*/
|
|
292
|
+
private update;
|
|
293
|
+
/**
|
|
294
|
+
* Sends the updates of a message one at a time. When one fails while a newer state waits,
|
|
295
|
+
* the newer one is still sent, so a streamed answer ends with its final text, and only a
|
|
296
|
+
* failure of the latest state reaches the callers.
|
|
297
|
+
*/
|
|
298
|
+
private drainUpdates;
|
|
299
|
+
private listen;
|
|
300
|
+
}
|
|
301
|
+
/** A Microsoft Teams channel on the Bot Framework protocol. */
|
|
302
|
+
export declare function teamsChannel(config: TeamsChannelConfig): TeamsChannel;
|
|
303
|
+
//# sourceMappingURL=channel.d.ts.map
|