@jimiford/webex 0.1.1
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/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/channel-plugin.d.ts +18 -0
- package/dist/channel-plugin.js +410 -0
- package/dist/channel.d.ts +98 -0
- package/dist/channel.js +224 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +32 -0
- package/dist/plugin.d.ts +15 -0
- package/dist/plugin.js +23 -0
- package/dist/send.d.ts +92 -0
- package/dist/send.js +304 -0
- package/dist/types.d.ts +223 -0
- package/dist/types.js +6 -0
- package/dist/webhook.d.ts +64 -0
- package/dist/webhook.js +297 -0
- package/openclaw.plugin.json +33 -0
- package/package.json +71 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenClaw Channel Plugin for Webex
|
|
4
|
+
*
|
|
5
|
+
* Implements the ChannelPlugin interface for OpenClaw's plugin system.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.webexPlugin = void 0;
|
|
9
|
+
const send_1 = require("./send");
|
|
10
|
+
const webhook_1 = require("./webhook");
|
|
11
|
+
const DEFAULT_ACCOUNT_ID = "default";
|
|
12
|
+
function listWebexAccountIds(cfg) {
|
|
13
|
+
const section = cfg.channels?.webex;
|
|
14
|
+
if (!section)
|
|
15
|
+
return [];
|
|
16
|
+
const ids = [];
|
|
17
|
+
// Check for top-level config (default account)
|
|
18
|
+
if (section.token) {
|
|
19
|
+
ids.push(DEFAULT_ACCOUNT_ID);
|
|
20
|
+
}
|
|
21
|
+
// Check for named accounts
|
|
22
|
+
if (section.accounts) {
|
|
23
|
+
for (const id of Object.keys(section.accounts)) {
|
|
24
|
+
if (id !== DEFAULT_ACCOUNT_ID) {
|
|
25
|
+
ids.push(id);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return ids;
|
|
30
|
+
}
|
|
31
|
+
function resolveWebexAccount(opts) {
|
|
32
|
+
const { cfg, accountId = DEFAULT_ACCOUNT_ID } = opts;
|
|
33
|
+
const section = cfg.channels?.webex;
|
|
34
|
+
if (!section) {
|
|
35
|
+
return {
|
|
36
|
+
accountId,
|
|
37
|
+
enabled: false,
|
|
38
|
+
configured: false,
|
|
39
|
+
config: {},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Check for named account first
|
|
43
|
+
const namedAccount = section.accounts?.[accountId];
|
|
44
|
+
if (namedAccount) {
|
|
45
|
+
const token = namedAccount.token ?? section.token;
|
|
46
|
+
const webhookUrl = namedAccount.webhookUrl ?? section.webhookUrl;
|
|
47
|
+
return {
|
|
48
|
+
accountId,
|
|
49
|
+
name: namedAccount.name,
|
|
50
|
+
enabled: namedAccount.enabled !== false,
|
|
51
|
+
configured: Boolean(token && webhookUrl),
|
|
52
|
+
token,
|
|
53
|
+
webhookUrl,
|
|
54
|
+
config: {
|
|
55
|
+
token: token ?? "",
|
|
56
|
+
webhookUrl: webhookUrl ?? "",
|
|
57
|
+
webhookSecret: namedAccount.webhookSecret ?? section.webhookSecret,
|
|
58
|
+
dmPolicy: namedAccount.dmPolicy ?? section.dmPolicy ?? "allow",
|
|
59
|
+
allowFrom: namedAccount.allowFrom ?? section.allowFrom,
|
|
60
|
+
apiBaseUrl: namedAccount.apiBaseUrl ?? section.apiBaseUrl,
|
|
61
|
+
maxRetries: namedAccount.maxRetries ?? section.maxRetries,
|
|
62
|
+
retryDelayMs: namedAccount.retryDelayMs ?? section.retryDelayMs,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Fall back to top-level config (default account)
|
|
67
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
68
|
+
return {
|
|
69
|
+
accountId,
|
|
70
|
+
name: section.name,
|
|
71
|
+
enabled: section.enabled !== false,
|
|
72
|
+
configured: Boolean(section.token && section.webhookUrl),
|
|
73
|
+
token: section.token,
|
|
74
|
+
webhookUrl: section.webhookUrl,
|
|
75
|
+
config: {
|
|
76
|
+
token: section.token ?? "",
|
|
77
|
+
webhookUrl: section.webhookUrl ?? "",
|
|
78
|
+
webhookSecret: section.webhookSecret,
|
|
79
|
+
dmPolicy: section.dmPolicy ?? "allow",
|
|
80
|
+
allowFrom: section.allowFrom,
|
|
81
|
+
apiBaseUrl: section.apiBaseUrl,
|
|
82
|
+
maxRetries: section.maxRetries,
|
|
83
|
+
retryDelayMs: section.retryDelayMs,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// Account not found
|
|
88
|
+
return {
|
|
89
|
+
accountId,
|
|
90
|
+
enabled: false,
|
|
91
|
+
configured: false,
|
|
92
|
+
config: {},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const meta = {
|
|
96
|
+
id: "webex",
|
|
97
|
+
label: "Webex",
|
|
98
|
+
selectionLabel: "Cisco Webex",
|
|
99
|
+
docsPath: "/channels/webex",
|
|
100
|
+
docsLabel: "webex",
|
|
101
|
+
blurb: "Cisco Webex messaging via bot webhooks.",
|
|
102
|
+
order: 75,
|
|
103
|
+
aliases: ["cisco-webex"],
|
|
104
|
+
};
|
|
105
|
+
exports.webexPlugin = {
|
|
106
|
+
id: "webex",
|
|
107
|
+
meta,
|
|
108
|
+
capabilities: {
|
|
109
|
+
chatTypes: ["direct", "group"],
|
|
110
|
+
threads: true,
|
|
111
|
+
media: true,
|
|
112
|
+
},
|
|
113
|
+
reload: { configPrefixes: ["channels.webex"] },
|
|
114
|
+
config: {
|
|
115
|
+
listAccountIds: (cfg) => listWebexAccountIds(cfg),
|
|
116
|
+
resolveAccount: (cfg, accountId) => resolveWebexAccount({ cfg: cfg, accountId }),
|
|
117
|
+
defaultAccountId: () => DEFAULT_ACCOUNT_ID,
|
|
118
|
+
setAccountEnabled: ({ cfg, accountId, enabled }) => {
|
|
119
|
+
const config = cfg;
|
|
120
|
+
const section = config.channels?.webex ?? {};
|
|
121
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
122
|
+
return {
|
|
123
|
+
...config,
|
|
124
|
+
channels: {
|
|
125
|
+
...config.channels,
|
|
126
|
+
webex: {
|
|
127
|
+
...section,
|
|
128
|
+
enabled,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...config,
|
|
135
|
+
channels: {
|
|
136
|
+
...config.channels,
|
|
137
|
+
webex: {
|
|
138
|
+
...section,
|
|
139
|
+
accounts: {
|
|
140
|
+
...section.accounts,
|
|
141
|
+
[accountId]: {
|
|
142
|
+
...section.accounts?.[accountId],
|
|
143
|
+
enabled,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
},
|
|
150
|
+
deleteAccount: ({ cfg, accountId }) => {
|
|
151
|
+
const config = cfg;
|
|
152
|
+
const section = config.channels?.webex ?? {};
|
|
153
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
154
|
+
const { token, webhookUrl, webhookSecret, dmPolicy, allowFrom, ...rest } = section;
|
|
155
|
+
return {
|
|
156
|
+
...config,
|
|
157
|
+
channels: {
|
|
158
|
+
...config.channels,
|
|
159
|
+
webex: rest,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const accounts = { ...section.accounts };
|
|
164
|
+
delete accounts[accountId];
|
|
165
|
+
return {
|
|
166
|
+
...config,
|
|
167
|
+
channels: {
|
|
168
|
+
...config.channels,
|
|
169
|
+
webex: {
|
|
170
|
+
...section,
|
|
171
|
+
accounts,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
},
|
|
176
|
+
isConfigured: (account) => account.configured,
|
|
177
|
+
describeAccount: (account) => ({
|
|
178
|
+
accountId: account.accountId,
|
|
179
|
+
name: account.name,
|
|
180
|
+
enabled: account.enabled,
|
|
181
|
+
configured: account.configured,
|
|
182
|
+
baseUrl: account.config.apiBaseUrl ?? "https://webexapis.com/v1",
|
|
183
|
+
}),
|
|
184
|
+
resolveAllowFrom: ({ cfg }) => (cfg.channels?.webex?.allowFrom ?? []).map(String),
|
|
185
|
+
formatAllowFrom: ({ allowFrom }) => allowFrom.map((entry) => entry.trim().toLowerCase()),
|
|
186
|
+
},
|
|
187
|
+
security: {
|
|
188
|
+
resolveDmPolicy: ({ account }) => {
|
|
189
|
+
const policy = account.config.dmPolicy ?? "allow";
|
|
190
|
+
// Map "allowlisted" to "allowlist" for OpenClaw compatibility
|
|
191
|
+
const normalizedPolicy = policy === "allowlisted" ? "allowlist" : policy;
|
|
192
|
+
return {
|
|
193
|
+
policy: normalizedPolicy,
|
|
194
|
+
allowFrom: account.config.allowFrom ?? [],
|
|
195
|
+
policyPath: "channels.webex.dmPolicy",
|
|
196
|
+
allowFromPath: "channels.webex.allowFrom",
|
|
197
|
+
approveHint: "Add user ID or email to channels.webex.allowFrom",
|
|
198
|
+
normalizeEntry: (raw) => raw.trim().toLowerCase(),
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
threading: {
|
|
203
|
+
resolveReplyToMode: () => "off",
|
|
204
|
+
buildToolContext: ({ context, hasRepliedRef }) => ({
|
|
205
|
+
currentChannelId: context.To?.trim() || undefined,
|
|
206
|
+
currentThreadTs: context.MessageThreadId != null
|
|
207
|
+
? String(context.MessageThreadId)
|
|
208
|
+
: context.ReplyToId,
|
|
209
|
+
hasRepliedRef,
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
messaging: {
|
|
213
|
+
normalizeTarget: (raw) => {
|
|
214
|
+
let normalized = raw.trim();
|
|
215
|
+
if (!normalized)
|
|
216
|
+
return undefined;
|
|
217
|
+
if (normalized.toLowerCase().startsWith("webex:")) {
|
|
218
|
+
normalized = normalized.slice("webex:".length).trim();
|
|
219
|
+
}
|
|
220
|
+
return normalized || undefined;
|
|
221
|
+
},
|
|
222
|
+
targetResolver: {
|
|
223
|
+
looksLikeId: (raw) => {
|
|
224
|
+
const trimmed = raw.trim();
|
|
225
|
+
if (!trimmed)
|
|
226
|
+
return false;
|
|
227
|
+
// Webex IDs are base64-encoded and start with a specific prefix
|
|
228
|
+
if (trimmed.startsWith("Y2lzY29zcGFyazovL3"))
|
|
229
|
+
return true;
|
|
230
|
+
// Also accept emails
|
|
231
|
+
return trimmed.includes("@");
|
|
232
|
+
},
|
|
233
|
+
hint: "<roomId|personId|email>",
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
outbound: {
|
|
237
|
+
deliveryMode: "direct",
|
|
238
|
+
textChunkLimit: 7000, // Webex has a 7439 byte limit
|
|
239
|
+
sendText: async ({ to, text, account, replyToId }) => {
|
|
240
|
+
const sender = new send_1.WebexSender(account.config);
|
|
241
|
+
const result = await sender.send({
|
|
242
|
+
to,
|
|
243
|
+
content: { text },
|
|
244
|
+
parentId: replyToId,
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
channel: "webex",
|
|
248
|
+
messageId: result.id,
|
|
249
|
+
roomId: result.roomId,
|
|
250
|
+
};
|
|
251
|
+
},
|
|
252
|
+
sendMedia: async ({ to, text, mediaUrl, account, replyToId }) => {
|
|
253
|
+
const sender = new send_1.WebexSender(account.config);
|
|
254
|
+
const result = await sender.send({
|
|
255
|
+
to,
|
|
256
|
+
content: {
|
|
257
|
+
text,
|
|
258
|
+
files: mediaUrl ? [mediaUrl] : undefined,
|
|
259
|
+
},
|
|
260
|
+
parentId: replyToId,
|
|
261
|
+
});
|
|
262
|
+
return {
|
|
263
|
+
channel: "webex",
|
|
264
|
+
messageId: result.id,
|
|
265
|
+
roomId: result.roomId,
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
status: {
|
|
270
|
+
defaultRuntime: {
|
|
271
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
272
|
+
running: false,
|
|
273
|
+
lastStartAt: null,
|
|
274
|
+
lastStopAt: null,
|
|
275
|
+
lastError: null,
|
|
276
|
+
},
|
|
277
|
+
collectStatusIssues: (accounts) => accounts.flatMap((account) => {
|
|
278
|
+
const lastError = typeof account.lastError === "string" ? account.lastError.trim() : "";
|
|
279
|
+
if (!lastError)
|
|
280
|
+
return [];
|
|
281
|
+
return [
|
|
282
|
+
{
|
|
283
|
+
channel: "webex",
|
|
284
|
+
accountId: account.accountId,
|
|
285
|
+
kind: "runtime",
|
|
286
|
+
message: `Channel error: ${lastError}`,
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
}),
|
|
290
|
+
buildChannelSummary: ({ snapshot }) => ({
|
|
291
|
+
configured: (snapshot.configured ?? false),
|
|
292
|
+
baseUrl: (snapshot.baseUrl ?? null),
|
|
293
|
+
running: (snapshot.running ?? false),
|
|
294
|
+
lastStartAt: (snapshot.lastStartAt ?? null),
|
|
295
|
+
lastStopAt: (snapshot.lastStopAt ?? null),
|
|
296
|
+
lastError: (snapshot.lastError ?? null),
|
|
297
|
+
}),
|
|
298
|
+
probeAccount: async ({ account, timeoutMs }) => {
|
|
299
|
+
if (!account.configured) {
|
|
300
|
+
return {
|
|
301
|
+
ok: false,
|
|
302
|
+
error: "Account not configured",
|
|
303
|
+
elapsedMs: 0,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const start = Date.now();
|
|
307
|
+
try {
|
|
308
|
+
const response = await fetch(`${account.config.apiBaseUrl ?? "https://webexapis.com/v1"}/people/me`, {
|
|
309
|
+
method: "GET",
|
|
310
|
+
headers: {
|
|
311
|
+
Authorization: `Bearer ${account.config.token}`,
|
|
312
|
+
"Content-Type": "application/json",
|
|
313
|
+
},
|
|
314
|
+
signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined,
|
|
315
|
+
});
|
|
316
|
+
const elapsedMs = Date.now() - start;
|
|
317
|
+
if (!response.ok) {
|
|
318
|
+
return {
|
|
319
|
+
ok: false,
|
|
320
|
+
error: `HTTP ${response.status}: ${response.statusText}`,
|
|
321
|
+
elapsedMs,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return { ok: true, elapsedMs };
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
return {
|
|
328
|
+
ok: false,
|
|
329
|
+
error: err instanceof Error ? err.message : String(err),
|
|
330
|
+
elapsedMs: Date.now() - start,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
|
335
|
+
accountId: account.accountId,
|
|
336
|
+
name: account.name,
|
|
337
|
+
enabled: account.enabled,
|
|
338
|
+
configured: account.configured,
|
|
339
|
+
baseUrl: account.config.apiBaseUrl ?? "https://webexapis.com/v1",
|
|
340
|
+
running: runtime?.running ?? false,
|
|
341
|
+
lastStartAt: runtime?.lastStartAt ?? null,
|
|
342
|
+
lastStopAt: runtime?.lastStopAt ?? null,
|
|
343
|
+
lastError: runtime?.lastError ?? null,
|
|
344
|
+
probe,
|
|
345
|
+
lastProbeAt: runtime?.lastProbeAt ?? null,
|
|
346
|
+
}),
|
|
347
|
+
},
|
|
348
|
+
gateway: {
|
|
349
|
+
startAccount: async (ctx) => {
|
|
350
|
+
const { account, runtime, log, setStatus } = ctx;
|
|
351
|
+
setStatus({
|
|
352
|
+
accountId: account.accountId,
|
|
353
|
+
baseUrl: account.config.apiBaseUrl ?? "https://webexapis.com/v1",
|
|
354
|
+
});
|
|
355
|
+
log?.info?.(`[${account.accountId}] starting Webex provider (webhook mode)`);
|
|
356
|
+
// Initialize webhook handler
|
|
357
|
+
const webhookHandler = new webhook_1.WebexWebhookHandler(account.config);
|
|
358
|
+
await webhookHandler.initialize();
|
|
359
|
+
// Register webhooks with Webex
|
|
360
|
+
try {
|
|
361
|
+
await webhookHandler.registerWebhooks();
|
|
362
|
+
log?.info?.(`[${account.accountId}] webhooks registered`);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
log?.warn?.(`[${account.accountId}] failed to register webhooks: ${err instanceof Error ? err.message : err}`);
|
|
366
|
+
}
|
|
367
|
+
// Register HTTP handler for incoming webhooks
|
|
368
|
+
const webhookPath = `/webhooks/webex/${account.accountId}`;
|
|
369
|
+
runtime.http.registerHandler({
|
|
370
|
+
method: "POST",
|
|
371
|
+
path: webhookPath,
|
|
372
|
+
handler: async (req) => {
|
|
373
|
+
try {
|
|
374
|
+
const signature = req.headers["x-spark-signature"];
|
|
375
|
+
const payload = req.body;
|
|
376
|
+
const envelope = await webhookHandler.handleWebhook(payload, signature);
|
|
377
|
+
if (envelope) {
|
|
378
|
+
// Forward to OpenClaw's message pipeline
|
|
379
|
+
await runtime.messaging.handleInbound({
|
|
380
|
+
channel: "webex",
|
|
381
|
+
accountId: account.accountId,
|
|
382
|
+
senderId: envelope.author.id,
|
|
383
|
+
senderEmail: envelope.author.email,
|
|
384
|
+
conversationId: envelope.conversationId,
|
|
385
|
+
messageId: envelope.id,
|
|
386
|
+
text: envelope.content.text ?? "",
|
|
387
|
+
roomType: envelope.metadata.roomType,
|
|
388
|
+
threadId: envelope.metadata.parentId,
|
|
389
|
+
timestamp: new Date(envelope.metadata.timestamp),
|
|
390
|
+
raw: envelope.metadata.raw,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return { status: 200, body: { ok: true } };
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
log?.error?.(`[${account.accountId}] webhook error: ${err instanceof Error ? err.message : err}`);
|
|
397
|
+
return { status: 500, body: { error: "Internal error" } };
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
log?.info?.(`[${account.accountId}] HTTP webhook handler registered at ${webhookPath}`);
|
|
402
|
+
// Return cleanup function
|
|
403
|
+
return async () => {
|
|
404
|
+
log?.info?.(`[${account.accountId}] stopping Webex provider`);
|
|
405
|
+
runtime.http.unregisterHandler(webhookPath);
|
|
406
|
+
};
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhbm5lbC1wbHVnaW4uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2hhbm5lbC1wbHVnaW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7O0dBSUc7OztBQVFILGlDQUFxQztBQUNyQyx1Q0FBZ0Q7QUFtRGhELE1BQU0sa0JBQWtCLEdBQUcsU0FBUyxDQUFDO0FBRXJDLFNBQVMsbUJBQW1CLENBQUMsR0FBZTtJQUMxQyxNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQztJQUNwQyxJQUFJLENBQUMsT0FBTztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBRXhCLE1BQU0sR0FBRyxHQUFhLEVBQUUsQ0FBQztJQUV6QiwrQ0FBK0M7SUFDL0MsSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDbEIsR0FBRyxDQUFDLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRCwyQkFBMkI7SUFDM0IsSUFBSSxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUM7UUFDckIsS0FBSyxNQUFNLEVBQUUsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQy9DLElBQUksRUFBRSxLQUFLLGtCQUFrQixFQUFFLENBQUM7Z0JBQzlCLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDZixDQUFDO1FBQ0gsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUM7QUFFRCxTQUFTLG1CQUFtQixDQUFDLElBRzVCO0lBQ0MsTUFBTSxFQUFFLEdBQUcsRUFBRSxTQUFTLEdBQUcsa0JBQWtCLEVBQUUsR0FBRyxJQUFJLENBQUM7SUFDckQsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLFFBQVEsRUFBRSxLQUFLLENBQUM7SUFFcEMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsT0FBTztZQUNMLFNB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QixHQUFHLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FDcEYsQ0FBQzt3QkFDRixPQUFPLEVBQUUsTUFBTSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRSxLQUFLLEVBQUUsZ0JBQWdCLEVBQUUsRUFBRSxDQUFDO29CQUM1RCxDQUFDO2dCQUNILENBQUM7YUFDRixDQUFDLENBQUM7WUFFSCxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQ1QsSUFBSSxPQUFPLENBQUMsU0FBUyx3Q0FBd0MsV0FBVyxFQUFFLENBQzNFLENBQUM7WUFFRiwwQkFBMEI7WUFDMUIsT0FBTyxLQUFLLElBQUksRUFBRTtnQkFDaEIsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDLElBQUksT0FBTyxDQUFDLFNBQVMsMkJBQTJCLENBQUMsQ0FBQztnQkFDOUQsT0FBTyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxXQUFXLENBQUMsQ0FBQztZQUM5QyxDQUFDLENBQUM7UUFDSixDQUFDO0tBQ0Y7Q0FDRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBPcGVuQ2xhdyBDaGFubmVsIFBsdWdpbiBmb3IgV2ViZXhcbiAqXG4gKiBJbXBsZW1lbnRzIHRoZSBDaGFubmVsUGx1Z2luIGludGVyZmFjZSBmb3IgT3BlbkNsYXcncyBwbHVnaW4gc3lzdGVtLlxuICovXG5cbmltcG9ydCB0eXBlIHtcbiAgQ2hhbm5lbFBsdWdpbixcbiAgUGx1Z2luUnVudGltZSxcbiAgSHR0cFJlcXVlc3QsXG59IGZyb20gXCJvcGVuY2xhdy9wbHVnaW4tc2RrXCI7XG5cbmltcG9ydCB7IFdlYmV4U2VuZGVyIH0gZnJvbSBcIi4vc2VuZFwiO1xuaW1wb3J0IHsgV2ViZXhXZWJob29rSGFuZGxlciB9IGZyb20gXCIuL3dlYmhvb2tcIjtcbmltcG9ydCB0eXBlIHsgV2ViZXhDaGFubmVsQ29uZmlnLCBXZWJleFdlYmhvb2tQYXlsb2FkIH0gZnJvbSBcIi4vdHlwZXNcIjtcblxuLyoqIFJlc29sdmVkIGFjY291bnQgY29uZmlndXJhdGlvbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXNvbHZlZFdlYmV4QWNjb3VudCB7XG4gIGFjY291bnRJZDogc3RyaW5nO1xuICBuYW1lPzogc3RyaW5nO1xuICBlbmFibGVkOiBib29sZWFuO1xuICBjb25maWd1cmVkOiBib29sZWFuO1xuICBjb25maWc6IFdlYmV4Q2hhbm5lbENvbmZpZztcbiAgdG9rZW4/OiBzdHJpbmc7XG4gIHdlYmhvb2tVcmw/OiBzdHJpbmc7XG59XG5cbi8qKiBDb3JlIGNvbmZpZyB0eXBlIGZvciBhY2Nlc3NpbmcgY2hhbm5lbHMud2ViZXggKi9cbmludGVyZmFjZSBDb3JlQ29uZmlnIHtcbiAgY2hhbm5lbHM/OiB7XG4gICAgd2ViZXg/OiBXZWJleENoYW5uZWxTZWN0aW9uO1xuICAgIGRlZmF1bHRzPzoge1xuICAgICAgZ3JvdXBQb2xpY3k/OiBzdHJpbmc7XG4gICAgfTtcbiAgfTtcbn1cblxuaW50ZXJmYWNlIFdlYmV4Q2hhbm5lbFNlY3Rpb24ge1xuICBlbmFibGVkPzogYm9vbGVhbjtcbiAgbmFtZT86IHN0cmluZztcbiAgdG9rZW4/OiBzdHJpbmc7XG4gIHdlYmhvb2tVcmw/OiBzdHJpbmc7XG4gIHdlYmhvb2tTZWNyZXQ/OiBzdHJpbmc7XG4gIGRtUG9saWN5PzogXCJhbGxvd1wiIHwgXCJkZW55XCIgfCBcImFsbG93bGlzdGVkXCIgfCBcInBhaXJpbmdcIjtcbiAgYWxsb3dGcm9tPzogc3RyaW5nW107XG4gIGFwaUJhc2VVcmw/OiBzdHJpbmc7XG4gIG1heFJldHJpZXM/OiBudW1iZXI7XG4gIHJldHJ5RGVsYXlNcz86IG51bWJlcjtcbiAgYWNjb3VudHM/OiBSZWNvcmQ8c3RyaW5nLCBXZWJleEFjY291bnRDb25maWc+O1xufVxuXG5pbnRlcmZhY2UgV2ViZXhBY2NvdW50Q29uZmlnIHtcbiAgZW5hYmxlZD86IGJvb2xlYW47XG4gIG5hbWU/OiBzdHJpbmc7XG4gIHRva2VuPzogc3RyaW5nO1xuICB3ZWJob29rVXJsPzogc3RyaW5nO1xuICB3ZWJob29rU2VjcmV0Pzogc3RyaW5nO1xuICBkbVBvbGljeT86IFwiYWxsb3dcIiB8IFwiZGVueVwiIHwgXCJhbGxvd2xpc3RlZFwiIHwgXCJwYWlyaW5nXCI7XG4gIGFsbG93RnJvbT86IHN0cmluZ1tdO1xuICBhcGlCYXNlVXJsPzogc3RyaW5nO1xuICBtYXhSZXRyaWVzPzogbnVtYmVyO1xuICByZXRyeURlbGF5TXM/OiBudW1iZXI7XG59XG5cbmNvbnN0IERFRkFVTFRfQUNDT1VOVF9JRCA9IFwiZGVmYXVsdFwiO1xuXG5mdW5jdGlvbiBsaXN0V2ViZXhBY2NvdW50SWRzKGNmZzogQ29yZUNvbmZpZyk6IHN0cmluZ1tdIHtcbiAgY29uc3Qgc2VjdGlvbiA9IGNmZy5jaGFubmVscz8ud2ViZXg7XG4gIGlmICghc2VjdGlvbikgcmV0dXJuIFtdO1xuXG4gIGNvbnN0IGlkczogc3RyaW5nW10gPSBbXTtcblxuICAvLyBDaGVjayBmb3IgdG9wLWxldmVsIGNvbmZpZyAoZGVmYXVsdCBhY2NvdW50KVxuICBpZiAoc2VjdGlvbi50b2tlbikge1xuICAgIGlkcy5wdXNoKERFRkFVTFRfQUNDT1VOVF9JRCk7XG4gIH1cblxuICAvLyBDaGVjayBmb3IgbmFtZWQgYWNjb3VudHNcbiAgaWYgKHNlY3Rpb24uYWNjb3VudHMpIHtcbiAgICBmb3IgKGNvbnN0IGlkIG9mIE9iamVjdC5rZXlzKHNlY3Rpb24uYWNjb3VudHMpKSB7XG4gICAgICBpZiAoaWQgIT09IERFRkFVTFRfQUNDT1VOVF9JRCkge1xuICAgICAgICBpZHMucHVzaChpZCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIGlkcztcbn1cblxuZnVuY3Rpb24gcmVzb2x2ZVdlYmV4QWNjb3VudChvcHRzOiB7XG4gIGNmZzogQ29yZUNvbmZpZztcbiAgYWNjb3VudElkPzogc3RyaW5nO1xufSk6IFJlc29sdmVkV2ViZXhBY2NvdW50IHtcbiAgY29uc3QgeyBjZmcsIGFjY291bnRJZCA9IERFRkFVTFRfQUNDT1VOVF9JRCB9ID0gb3B0cztcbiAgY29uc3Qgc2VjdGlvbiA9IGNmZy5jaGFubmVscz8ud2ViZXg7XG5cbiAgaWYgKCFzZWN0aW9uKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGFjY291bnRJZCxcbiAgICAgIGVuYWJsZWQ6IGZhbHNlLFxuICAgICAgY29uZmlndXJlZDogZmFsc2UsXG4gICAgICBjb25maWc6IHt9IGFzIFdlYmV4Q2hhbm5lbENvbmZpZyxcbiAgICB9O1xuICB9XG5cbiAgLy8gQ2hlY2sgZm9yIG5hbWVkIGFjY291bnQgZmlyc3RcbiAgY29uc3QgbmFtZWRBY2NvdW50ID0gc2VjdGlvbi5hY2NvdW50cz8uW2FjY291bnRJZF07XG5cbiAgaWYgKG5hbWVkQWNjb3VudCkge1xuICAgIGNvbnN0IHRva2VuID0gbmFtZWRBY2NvdW50LnRva2VuID8/IHNlY3Rpb24udG9rZW47XG4gICAgY29uc3Qgd2ViaG9va1VybCA9IG5hbWVkQWNjb3VudC53ZWJob29rVXJsID8/IHNlY3Rpb24ud2ViaG9va1VybDtcblxuICAgIHJldHVybiB7XG4gICAgICBhY2NvdW50SWQsXG4gICAgICBuYW1lOiBuYW1lZEFjY291bnQubmFtZSxcbiAgICAgIGVuYWJsZWQ6IG5hbWVkQWNjb3VudC5lbmFibGVkICE9PSBmYWxzZSxcbiAgICAgIGNvbmZpZ3VyZWQ6IEJvb2xlYW4odG9rZW4gJiYgd2ViaG9va1VybCksXG4gICAgICB0b2tlbixcbiAgICAgIHdlYmhvb2tVcmwsXG4gICAgICBjb25maWc6IHtcbiAgICAgICAgdG9rZW46IHRva2VuID8/IFwiXCIsXG4gICAgICAgIHdlYmhvb2tVcmw6IHdlYmhvb2tVcmwgPz8gXCJcIixcbiAgICAgICAgd2ViaG9va1NlY3JldDogbmFtZWRBY2NvdW50LndlYmhvb2tTZWNyZXQgPz8gc2VjdGlvbi53ZWJob29rU2VjcmV0LFxuICAgICAgICBkbVBvbGljeTogbmFtZWRBY2NvdW50LmRtUG9saWN5ID8/IHNlY3Rpb24uZG1Qb2xpY3kgPz8gXCJhbGxvd1wiLFxuICAgICAgICBhbGxvd0Zyb206IG5hbWVkQWNjb3VudC5hbGxvd0Zyb20gPz8gc2VjdGlvbi5hbGxvd0Zyb20sXG4gICAgICAgIGFwaUJhc2VVcmw6IG5hbWVkQWNjb3VudC5hcGlCYXNlVXJsID8/IHNlY3Rpb24uYXBpQmFzZVVybCxcbiAgICAgICAgbWF4UmV0cmllczogbmFtZWRBY2NvdW50Lm1heFJldHJpZXMgPz8gc2VjdGlvbi5tYXhSZXRyaWVzLFxuICAgICAgICByZXRyeURlbGF5TXM6IG5hbWVkQWNjb3VudC5yZXRyeURlbGF5TXMgPz8gc2VjdGlvbi5yZXRyeURlbGF5TXMsXG4gICAgICB9LFxuICAgIH07XG4gIH1cblxuICAvLyBGYWxsIGJhY2sgdG8gdG9wLWxldmVsIGNvbmZpZyAoZGVmYXVsdCBhY2NvdW50KVxuICBpZiAoYWNjb3VudElkID09PSBERUZBVUxUX0FDQ09VTlRfSUQpIHtcbiAgICByZXR1cm4ge1xuICAgICAgYWNjb3VudElkLFxuICAgICAgbmFtZTogc2VjdGlvbi5uYW1lLFxuICAgICAgZW5hYmxlZDogc2VjdGlvbi5lbmFibGVkICE9PSBmYWxzZSxcbiAgICAgIGNvbmZpZ3VyZWQ6IEJvb2xlYW4oc2VjdGlvbi50b2tlbiAmJiBzZWN0aW9uLndlYmhvb2tVcmwpLFxuICAgICAgdG9rZW46IHNlY3Rpb24udG9rZW4sXG4gICAgICB3ZWJob29rVXJsOiBzZWN0aW9uLndlYmhvb2tVcmwsXG4gICAgICBjb25maWc6IHtcbiAgICAgICAgdG9rZW46IHNlY3Rpb24udG9rZW4gPz8gXCJcIixcbiAgICAgICAgd2ViaG9va1VybDogc2VjdGlvbi53ZWJob29rVXJsID8/IFwiXCIsXG4gICAgICAgIHdlYmhvb2tTZWNyZXQ6IHNlY3Rpb24ud2ViaG9va1NlY3JldCxcbiAgICAgICAgZG1Qb2xpY3k6IHNlY3Rpb24uZG1Qb2xpY3kgPz8gXCJhbGxvd1wiLFxuICAgICAgICBhbGxvd0Zyb206IHNlY3Rpb24uYWxsb3dGcm9tLFxuICAgICAgICBhcGlCYXNlVXJsOiBzZWN0aW9uLmFwaUJhc2VVcmwsXG4gICAgICAgIG1heFJldHJpZXM6IHNlY3Rpb24ubWF4UmV0cmllcyxcbiAgICAgICAgcmV0cnlEZWxheU1zOiBzZWN0aW9uLnJldHJ5RGVsYXlNcyxcbiAgICAgIH0sXG4gICAgfTtcbiAgfVxuXG4gIC8vIEFjY291bnQgbm90IGZvdW5kXG4gIHJldHVybiB7XG4gICAgYWNjb3VudElkLFxuICAgIGVuYWJsZWQ6IGZhbHNlLFxuICAgIGNvbmZpZ3VyZWQ6IGZhbHNlLFxuICAgIGNvbmZpZzoge30gYXMgV2ViZXhDaGFubmVsQ29uZmlnLFxuICB9O1xufVxuXG5jb25zdCBtZXRhID0ge1xuICBpZDogXCJ3ZWJleFwiLFxuICBsYWJlbDogXCJXZWJleFwiLFxuICBzZWxlY3Rpb25MYWJlbDogXCJDaXNjbyBXZWJleFwiLFxuICBkb2NzUGF0aDogXCIvY2hhbm5lbHMvd2ViZXhcIixcbiAgZG9jc0xhYmVsOiBcIndlYmV4XCIsXG4gIGJsdXJiOiBcIkNpc2NvIFdlYmV4IG1lc3NhZ2luZyB2aWEgYm90IHdlYmhvb2tzLlwiLFxuICBvcmRlcjogNzUsXG4gIGFsaWFzZXM6IFtcImNpc2NvLXdlYmV4XCJdLFxufTtcblxuZXhwb3J0IGNvbnN0IHdlYmV4UGx1Z2luOiBDaGFubmVsUGx1Z2luPFJlc29sdmVkV2ViZXhBY2NvdW50PiA9IHtcbiAgaWQ6IFwid2ViZXhcIixcbiAgbWV0YSxcblxuICBjYXBhYmlsaXRpZXM6IHtcbiAgICBjaGF0VHlwZXM6IFtcImRpcmVjdFwiLCBcImdyb3VwXCJdLFxuICAgIHRocmVhZHM6IHRydWUsXG4gICAgbWVkaWE6IHRydWUsXG4gIH0sXG5cbiAgcmVsb2FkOiB7IGNvbmZpZ1ByZWZpeGVzOiBbXCJjaGFubmVscy53ZWJleFwiXSB9LFxuXG4gIGNvbmZpZzoge1xuICAgIGxpc3RBY2NvdW50SWRzOiAoY2ZnKSA9PiBsaXN0V2ViZXhBY2NvdW50SWRzKGNmZyBhcyBDb3JlQ29uZmlnKSxcblxuICAgIHJlc29sdmVBY2NvdW50OiAoY2ZnLCBhY2NvdW50SWQpID0+XG4gICAgICByZXNvbHZlV2ViZXhBY2NvdW50KHsgY2ZnOiBjZmcgYXMgQ29yZUNvbmZpZywgYWNjb3VudElkIH0pLFxuXG4gICAgZGVmYXVsdEFjY291bnRJZDogKCkgPT4gREVGQVVMVF9BQ0NPVU5UX0lELFxuXG4gICAgc2V0QWNjb3VudEVuYWJsZWQ6ICh7IGNmZywgYWNjb3VudElkLCBlbmFibGVkIH0pID0+IHtcbiAgICAgIGNvbnN0IGNvbmZpZyA9IGNmZyBhcyBDb3JlQ29uZmlnO1xuICAgICAgY29uc3Qgc2VjdGlvbiA9IGNvbmZpZy5jaGFubmVscz8ud2ViZXggPz8ge307XG5cbiAgICAgIGlmIChhY2NvdW50SWQgPT09IERFRkFVTFRfQUNDT1VOVF9JRCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLmNvbmZpZyxcbiAgICAgICAgICBjaGFubmVsczoge1xuICAgICAgICAgICAgLi4uY29uZmlnLmNoYW5uZWxzLFxuICAgICAgICAgICAgd2ViZXg6IHtcbiAgICAgICAgICAgICAgLi4uc2VjdGlvbixcbiAgICAgICAgICAgICAgZW5hYmxlZCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgLi4uY29uZmlnLFxuICAgICAgICBjaGFubmVsczoge1xuICAgICAgICAgIC4uLmNvbmZpZy5jaGFubmVscyxcbiAgICAgICAgICB3ZWJleDoge1xuICAgICAgICAgICAgLi4uc2VjdGlvbixcbiAgICAgICAgICAgIGFjY291bnRzOiB7XG4gICAgICAgICAgICAgIC4uLnNlY3Rpb24uYWNjb3VudHMsXG4gICAgICAgICAgICAgIFthY2NvdW50SWRdOiB7XG4gICAgICAgICAgICAgICAgLi4uc2VjdGlvbi5hY2NvdW50cz8uW2FjY291bnRJZF0sXG4gICAgICAgICAgICAgICAgZW5hYmxlZCxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSxcblxuICAgIGRlbGV0ZUFjY291bnQ6ICh7IGNmZywgYWNjb3VudElkIH0pID0+IHtcbiAgICAgIGNvbnN0IGNvbmZpZyA9IGNmZyBhcyBDb3JlQ29uZmlnO1xuICAgICAgY29uc3Qgc2VjdGlvbiA9IGNvbmZpZy5jaGFubmVscz8ud2ViZXggPz8ge307XG5cbiAgICAgIGlmIChhY2NvdW50SWQgPT09IERFRkFVTFRfQUNDT1VOVF9JRCkge1xuICAgICAgICBjb25zdCB7IHRva2VuLCB3ZWJob29rVXJsLCB3ZWJob29rU2VjcmV0LCBkbVBvbGljeSwgYWxsb3dGcm9tLCAuLi5yZXN0IH0gPSBzZWN0aW9uO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLmNvbmZpZyxcbiAgICAgICAgICBjaGFubmVsczoge1xuICAgICAgICAgICAgLi4uY29uZmlnLmNoYW5uZWxzLFxuICAgICAgICAgICAgd2ViZXg6IHJlc3QsXG4gICAgICAgICAgfSxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgY29uc3QgYWNjb3VudHMgPSB7IC4uLnNlY3Rpb24uYWNjb3VudHMgfTtcbiAgICAgIGRlbGV0ZSBhY2NvdW50c1thY2NvdW50SWRdO1xuXG4gICAgICByZXR1cm4ge1xuICAgICAgICAuLi5jb25maWcsXG4gICAgICAgIGNoYW5uZWxzOiB7XG4gICAgICAgICAgLi4uY29uZmlnLmNoYW5uZWxzLFxuICAgICAgICAgIHdlYmV4OiB7XG4gICAgICAgICAgICAuLi5zZWN0aW9uLFxuICAgICAgICAgICAgYWNjb3VudHMsXG4gICAgICAgICAgfSxcbiAgICAgICAgfSxcbiAgICAgIH07XG4gICAgfSxcblxuICAgIGlzQ29uZmlndXJlZDogKGFjY291bnQpID0+IGFjY291bnQuY29uZmlndXJlZCxcblxuICAgIGRlc2NyaWJlQWNjb3VudDogKGFjY291bnQpID0+ICh7XG4gICAgICBhY2NvdW50SWQ6IGFjY291bnQuYWNjb3VudElkLFxuICAgICAgbmFtZTogYWNjb3VudC5uYW1lLFxuICAgICAgZW5hYmxlZDogYWNjb3VudC5lbmFibGVkLFxuICAgICAgY29uZmlndXJlZDogYWNjb3VudC5jb25maWd1cmVkLFxuICAgICAgYmFzZVVybDogYWNjb3VudC5jb25maWcuYXBpQmFzZVVybCA/PyBcImh0dHBzOi8vd2ViZXhhcGlzLmNvbS92MVwiLFxuICAgIH0pLFxuXG4gICAgcmVzb2x2ZUFsbG93RnJvbTogKHsgY2ZnIH0pID0+XG4gICAgICAoKGNmZyBhcyBDb3JlQ29uZmlnKS5jaGFubmVscz8ud2ViZXg/LmFsbG93RnJvbSA/PyBbXSkubWFwKFN0cmluZyksXG5cbiAgICBmb3JtYXRBbGxvd0Zyb206ICh7IGFsbG93RnJvbSB9KSA9PlxuICAgICAgYWxsb3dGcm9tLm1hcCgoZW50cnkpID0+IGVudHJ5LnRyaW0oKS50b0xvd2VyQ2FzZSgpKSxcbiAgfSxcblxuICBzZWN1cml0eToge1xuICAgIHJlc29sdmVEbVBvbGljeTogKHsgYWNjb3VudCB9KSA9PiB7XG4gICAgICBjb25zdCBwb2xpY3kgPSBhY2NvdW50LmNvbmZpZy5kbVBvbGljeSA/PyBcImFsbG93XCI7XG4gICAgICAvLyBNYXAgXCJhbGxvd2xpc3RlZFwiIHRvIFwiYWxsb3dsaXN0XCIgZm9yIE9wZW5DbGF3IGNvbXBhdGliaWxpdHlcbiAgICAgIGNvbnN0IG5vcm1hbGl6ZWRQb2xpY3kgPSBwb2xpY3kgPT09IFwiYWxsb3dsaXN0ZWRcIiA/IFwiYWxsb3dsaXN0XCIgOiBwb2xpY3k7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIHBvbGljeTogbm9ybWFsaXplZFBvbGljeSBhcyBcImFsbG93XCIgfCBcImRlbnlcIiB8IFwiYWxsb3dsaXN0XCIgfCBcInBhaXJpbmdcIixcbiAgICAgICAgYWxsb3dGcm9tOiBhY2NvdW50LmNvbmZpZy5hbGxvd0Zyb20gPz8gW10sXG4gICAgICAgIHBvbGljeVBhdGg6IFwiY2hhbm5lbHMud2ViZXguZG1Qb2xpY3lcIixcbiAgICAgICAgYWxsb3dGcm9tUGF0aDogXCJjaGFubmVscy53ZWJleC5hbGxvd0Zyb21cIixcbiAgICAgICAgYXBwcm92ZUhpbnQ6IFwiQWRkIHVzZXIgSUQgb3IgZW1haWwgdG8gY2hhbm5lbHMud2ViZXguYWxsb3dGcm9tXCIsXG4gICAgICAgIG5vcm1hbGl6ZUVudHJ5OiAocmF3KSA9PiByYXcudHJpbSgpLnRvTG93ZXJDYXNlKCksXG4gICAgICB9O1xuICAgIH0sXG4gIH0sXG5cbiAgdGhyZWFkaW5nOiB7XG4gICAgcmVzb2x2ZVJlcGx5VG9Nb2RlOiAoKSA9PiBcIm9mZlwiLFxuICAgIGJ1aWxkVG9vbENvbnRleHQ6ICh7IGNvbnRleHQsIGhhc1JlcGxpZWRSZWYgfSkgPT4gKHtcbiAgICAgIGN1cnJlbnRDaGFubmVsSWQ6IGNvbnRleHQuVG8/LnRyaW0oKSB8fCB1bmRlZmluZWQsXG4gICAgICBjdXJyZW50VGhyZWFkVHM6IGNvbnRleHQuTWVzc2FnZVRocmVhZElkICE9IG51bGxcbiAgICAgICAgPyBTdHJpbmcoY29udGV4dC5NZXNzYWdlVGhyZWFkSWQpXG4gICAgICAgIDogY29udGV4dC5SZXBseVRvSWQsXG4gICAgICBoYXNSZXBsaWVkUmVmLFxuICAgIH0pLFxuICB9LFxuXG4gIG1lc3NhZ2luZzoge1xuICAgIG5vcm1hbGl6ZVRhcmdldDogKHJhdzogc3RyaW5nKSA9PiB7XG4gICAgICBsZXQgbm9ybWFsaXplZCA9IHJhdy50cmltKCk7XG4gICAgICBpZiAoIW5vcm1hbGl6ZWQpIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICBpZiAobm9ybWFsaXplZC50b0xvd2VyQ2FzZSgpLnN0YXJ0c1dpdGgoXCJ3ZWJleDpcIikpIHtcbiAgICAgICAgbm9ybWFsaXplZCA9IG5vcm1hbGl6ZWQuc2xpY2UoXCJ3ZWJleDpcIi5sZW5ndGgpLnRyaW0oKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBub3JtYWxpemVkIHx8IHVuZGVmaW5lZDtcbiAgICB9LFxuICAgIHRhcmdldFJlc29sdmVyOiB7XG4gICAgICBsb29rc0xpa2VJZDogKHJhdykgPT4ge1xuICAgICAgICBjb25zdCB0cmltbWVkID0gcmF3LnRyaW0oKTtcbiAgICAgICAgaWYgKCF0cmltbWVkKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIC8vIFdlYmV4IElEcyBhcmUgYmFzZTY0LWVuY29kZWQgYW5kIHN0YXJ0IHdpdGggYSBzcGVjaWZpYyBwcmVmaXhcbiAgICAgICAgaWYgKHRyaW1tZWQuc3RhcnRzV2l0aChcIlkybHpZMjl6Y0dGeWF6b3ZMM1wiKSkgcmV0dXJuIHRydWU7XG4gICAgICAgIC8vIEFsc28gYWNjZXB0IGVtYWlsc1xuICAgICAgICByZXR1cm4gdHJpbW1lZC5pbmNsdWRlcyhcIkBcIik7XG4gICAgICB9LFxuICAgICAgaGludDogXCI8cm9vbUlkfHBlcnNvbklkfGVtYWlsPlwiLFxuICAgIH0sXG4gIH0sXG5cbiAgb3V0Ym91bmQ6IHtcbiAgICBkZWxpdmVyeU1vZGU6IFwiZGlyZWN0XCIsXG4gICAgdGV4dENodW5rTGltaXQ6IDcwMDAsIC8vIFdlYmV4IGhhcyBhIDc0MzkgYnl0ZSBsaW1pdFxuXG4gICAgc2VuZFRleHQ6IGFzeW5jICh7IHRvLCB0ZXh0LCBhY2NvdW50LCByZXBseVRvSWQgfSkgPT4ge1xuICAgICAgY29uc3Qgc2VuZGVyID0gbmV3IFdlYmV4U2VuZGVyKGFjY291bnQuY29uZmlnKTtcblxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc2VuZGVyLnNlbmQoe1xuICAgICAgICB0byxcbiAgICAgICAgY29udGVudDogeyB0ZXh0IH0sXG4gICAgICAgIHBhcmVudElkOiByZXBseVRvSWQsXG4gICAgICB9KTtcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgY2hhbm5lbDogXCJ3ZWJleFwiLFxuICAgICAgICBtZXNzYWdlSWQ6IHJlc3VsdC5pZCxcbiAgICAgICAgcm9vbUlkOiByZXN1bHQucm9vbUlkLFxuICAgICAgfTtcbiAgICB9LFxuXG4gICAgc2VuZE1lZGlhOiBhc3luYyAoeyB0bywgdGV4dCwgbWVkaWFVcmwsIGFjY291bnQsIHJlcGx5VG9JZCB9KSA9PiB7XG4gICAgICBjb25zdCBzZW5kZXIgPSBuZXcgV2ViZXhTZW5kZXIoYWNjb3VudC5jb25maWcpO1xuXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZW5kZXIuc2VuZCh7XG4gICAgICAgIHRvLFxuICAgICAgICBjb250ZW50OiB7XG4gICAgICAgICAgdGV4dCxcbiAgICAgICAgICBmaWxlczogbWVkaWFVcmwgPyBbbWVkaWFVcmxdIDogdW5kZWZpbmVkLFxuICAgICAgICB9LFxuICAgICAgICBwYXJlbnRJZDogcmVwbHlUb0lkLFxuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNoYW5uZWw6IFwid2ViZXhcIixcbiAgICAgICAgbWVzc2FnZUlkOiByZXN1bHQuaWQsXG4gICAgICAgIHJvb21JZDogcmVzdWx0LnJvb21JZCxcbiAgICAgIH07XG4gICAgfSxcbiAgfSxcblxuICBzdGF0dXM6IHtcbiAgICBkZWZhdWx0UnVudGltZToge1xuICAgICAgYWNjb3VudElkOiBERUZBVUxUX0FDQ09VTlRfSUQsXG4gICAgICBydW5uaW5nOiBmYWxzZSxcbiAgICAgIGxhc3RTdGFydEF0OiBudWxsLFxuICAgICAgbGFzdFN0b3BBdDogbnVsbCxcbiAgICAgIGxhc3RFcnJvcjogbnVsbCxcbiAgICB9LFxuXG4gICAgY29sbGVjdFN0YXR1c0lzc3VlczogKGFjY291bnRzKSA9PlxuICAgICAgYWNjb3VudHMuZmxhdE1hcCgoYWNjb3VudCkgPT4ge1xuICAgICAgICBjb25zdCBsYXN0RXJyb3IgPSB0eXBlb2YgYWNjb3VudC5sYXN0RXJyb3IgPT09IFwic3RyaW5nXCIgPyBhY2NvdW50Lmxhc3RFcnJvci50cmltKCkgOiBcIlwiO1xuICAgICAgICBpZiAoIWxhc3RFcnJvcikgcmV0dXJuIFtdO1xuICAgICAgICByZXR1cm4gW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIGNoYW5uZWw6IFwid2ViZXhcIixcbiAgICAgICAgICAgIGFjY291bnRJZDogYWNjb3VudC5hY2NvdW50SWQsXG4gICAgICAgICAgICBraW5kOiBcInJ1bnRpbWVcIiBhcyBjb25zdCxcbiAgICAgICAgICAgIG1lc3NhZ2U6IGBDaGFubmVsIGVycm9yOiAke2xhc3RFcnJvcn1gLFxuICAgICAgICAgIH0sXG4gICAgICAgIF07XG4gICAgICB9KSxcblxuICAgIGJ1aWxkQ2hhbm5lbFN1bW1hcnk6ICh7IHNuYXBzaG90IH0pID0+ICh7XG4gICAgICBjb25maWd1cmVkOiAoc25hcHNob3QuY29uZmlndXJlZCA/PyBmYWxzZSkgYXMgYm9vbGVhbixcbiAgICAgIGJhc2VVcmw6IChzbmFwc2hvdC5iYXNlVXJsID8/IG51bGwpIGFzIHN0cmluZyB8IG51bGwsXG4gICAgICBydW5uaW5nOiAoc25hcHNob3QucnVubmluZyA/PyBmYWxzZSkgYXMgYm9vbGVhbixcbiAgICAgIGxhc3RTdGFydEF0OiAoc25hcHNob3QubGFzdFN0YXJ0QXQgPz8gbnVsbCkgYXMgRGF0ZSB8IG51bGwsXG4gICAgICBsYXN0U3RvcEF0OiAoc25hcHNob3QubGFzdFN0b3BBdCA/PyBudWxsKSBhcyBEYXRlIHwgbnVsbCxcbiAgICAgIGxhc3RFcnJvcjogKHNuYXBzaG90Lmxhc3RFcnJvciA/PyBudWxsKSBhcyBzdHJpbmcgfCBudWxsLFxuICAgIH0pLFxuXG4gICAgcHJvYmVBY2NvdW50OiBhc3luYyAoeyBhY2NvdW50LCB0aW1lb3V0TXMgfSkgPT4ge1xuICAgICAgaWYgKCFhY2NvdW50LmNvbmZpZ3VyZWQpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBvazogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IFwiQWNjb3VudCBub3QgY29uZmlndXJlZFwiLFxuICAgICAgICAgIGVsYXBzZWRNczogMCxcbiAgICAgICAgfTtcbiAgICAgIH1cblxuICAgICAgY29uc3Qgc3RhcnQgPSBEYXRlLm5vdygpO1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChcbiAgICAgICAgICBgJHthY2NvdW50LmNvbmZpZy5hcGlCYXNlVXJsID8/IFwiaHR0cHM6Ly93ZWJleGFwaXMuY29tL3YxXCJ9L3Blb3BsZS9tZWAsXG4gICAgICAgICAge1xuICAgICAgICAgICAgbWV0aG9kOiBcIkdFVFwiLFxuICAgICAgICAgICAgaGVhZGVyczoge1xuICAgICAgICAgICAgICBBdXRob3JpemF0aW9uOiBgQmVhcmVyICR7YWNjb3VudC5jb25maWcudG9rZW59YCxcbiAgICAgICAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uXCIsXG4gICAgICAgICAgICB9LFxuICAgICAgICAgICAgc2lnbmFsOiB0aW1lb3V0TXMgPyBBYm9ydFNpZ25hbC50aW1lb3V0KHRpbWVvdXRNcykgOiB1bmRlZmluZWQsXG4gICAgICAgICAgfVxuICAgICAgICApO1xuXG4gICAgICAgIGNvbnN0IGVsYXBzZWRNcyA9IERhdGUubm93KCkgLSBzdGFydDtcblxuICAgICAgICBpZiAoIXJlc3BvbnNlLm9rKSB7XG4gICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIG9rOiBmYWxzZSxcbiAgICAgICAgICAgIGVycm9yOiBgSFRUUCAke3Jlc3BvbnNlLnN0YXR1c306ICR7cmVzcG9uc2Uuc3RhdHVzVGV4dH1gLFxuICAgICAgICAgICAgZWxhcHNlZE1zLFxuICAgICAgICAgIH07XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4geyBvazogdHJ1ZSwgZWxhcHNlZE1zIH07XG4gICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBvazogZmFsc2UsXG4gICAgICAgICAgZXJyb3I6IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiBTdHJpbmcoZXJyKSxcbiAgICAgICAgICBlbGFwc2VkTXM6IERhdGUubm93KCkgLSBzdGFydCxcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9LFxuXG4gICAgYnVpbGRBY2NvdW50U25hcHNob3Q6ICh7IGFjY291bnQsIHJ1bnRpbWUsIHByb2JlIH0pID0+ICh7XG4gICAgICBhY2NvdW50SWQ6IGFjY291bnQuYWNjb3VudElkLFxuICAgICAgbmFtZTogYWNjb3VudC5uYW1lLFxuICAgICAgZW5hYmxlZDogYWNjb3VudC5lbmFibGVkLFxuICAgICAgY29uZmlndXJlZDogYWNjb3VudC5jb25maWd1cmVkLFxuICAgICAgYmFzZVVybDogYWNjb3VudC5jb25maWcuYXBpQmFzZVVybCA/PyBcImh0dHBzOi8vd2ViZXhhcGlzLmNvbS92MVwiLFxuICAgICAgcnVubmluZzogcnVudGltZT8ucnVubmluZyA/PyBmYWxzZSxcbiAgICAgIGxhc3RTdGFydEF0OiBydW50aW1lPy5sYXN0U3RhcnRBdCA/PyBudWxsLFxuICAgICAgbGFzdFN0b3BBdDogcnVudGltZT8ubGFzdFN0b3BBdCA/PyBudWxsLFxuICAgICAgbGFzdEVycm9yOiBydW50aW1lPy5sYXN0RXJyb3IgPz8gbnVsbCxcbiAgICAgIHByb2JlLFxuICAgICAgbGFzdFByb2JlQXQ6IHJ1bnRpbWU/Lmxhc3RQcm9iZUF0ID8/IG51bGwsXG4gICAgfSksXG4gIH0sXG5cbiAgZ2F0ZXdheToge1xuICAgIHN0YXJ0QWNjb3VudDogYXN5bmMgKGN0eCkgPT4ge1xuICAgICAgY29uc3QgeyBhY2NvdW50LCBydW50aW1lLCBsb2csIHNldFN0YXR1cyB9ID0gY3R4O1xuXG4gICAgICBzZXRTdGF0dXMoe1xuICAgICAgICBhY2NvdW50SWQ6IGFjY291bnQuYWNjb3VudElkLFxuICAgICAgICBiYXNlVXJsOiBhY2NvdW50LmNvbmZpZy5hcGlCYXNlVXJsID8/IFwiaHR0cHM6Ly93ZWJleGFwaXMuY29tL3YxXCIsXG4gICAgICB9KTtcblxuICAgICAgbG9nPy5pbmZvPy4oXG4gICAgICAgIGBbJHthY2NvdW50LmFjY291bnRJZH1dIHN0YXJ0aW5nIFdlYmV4IHByb3ZpZGVyICh3ZWJob29rIG1vZGUpYFxuICAgICAgKTtcblxuICAgICAgLy8gSW5pdGlhbGl6ZSB3ZWJob29rIGhhbmRsZXJcbiAgICAgIGNvbnN0IHdlYmhvb2tIYW5kbGVyID0gbmV3IFdlYmV4V2ViaG9va0hhbmRsZXIoYWNjb3VudC5jb25maWcpO1xuICAgICAgYXdhaXQgd2ViaG9va0hhbmRsZXIuaW5pdGlhbGl6ZSgpO1xuXG4gICAgICAvLyBSZWdpc3RlciB3ZWJob29rcyB3aXRoIFdlYmV4XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB3ZWJob29rSGFuZGxlci5yZWdpc3RlcldlYmhvb2tzKCk7XG4gICAgICAgIGxvZz8uaW5mbz8uKGBbJHthY2NvdW50LmFjY291bnRJZH1dIHdlYmhvb2tzIHJlZ2lzdGVyZWRgKTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBsb2c/Lndhcm4/LihcbiAgICAgICAgICBgWyR7YWNjb3VudC5hY2NvdW50SWR9XSBmYWlsZWQgdG8gcmVnaXN0ZXIgd2ViaG9va3M6ICR7ZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6IGVycn1gXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIC8vIFJlZ2lzdGVyIEhUVFAgaGFuZGxlciBmb3IgaW5jb21pbmcgd2ViaG9va3NcbiAgICAgIGNvbnN0IHdlYmhvb2tQYXRoID0gYC93ZWJob29rcy93ZWJleC8ke2FjY291bnQuYWNjb3VudElkfWA7XG5cbiAgICAgIHJ1bnRpbWUuaHR0cC5yZWdpc3RlckhhbmRsZXIoe1xuICAgICAgICBtZXRob2Q6IFwiUE9TVFwiLFxuICAgICAgICBwYXRoOiB3ZWJob29rUGF0aCxcbiAgICAgICAgaGFuZGxlcjogYXN5bmMgKHJlcSkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBzaWduYXR1cmUgPSByZXEuaGVhZGVyc1tcIngtc3Bhcmstc2lnbmF0dXJlXCJdIGFzIHN0cmluZyB8IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSByZXEuYm9keSBhcyBXZWJleFdlYmhvb2tQYXlsb2FkO1xuXG4gICAgICAgICAgICBjb25zdCBlbnZlbG9wZSA9IGF3YWl0IHdlYmhvb2tIYW5kbGVyLmhhbmRsZVdlYmhvb2socGF5bG9hZCwgc2lnbmF0dXJlKTtcblxuICAgICAgICAgICAgaWYgKGVudmVsb3BlKSB7XG4gICAgICAgICAgICAgIC8vIEZvcndhcmQgdG8gT3BlbkNsYXcncyBtZXNzYWdlIHBpcGVsaW5lXG4gICAgICAgICAgICAgIGF3YWl0IHJ1bnRpbWUubWVzc2FnaW5nLmhhbmRsZUluYm91bmQoe1xuICAgICAgICAgICAgICAgIGNoYW5uZWw6IFwid2ViZXhcIixcbiAgICAgICAgICAgICAgICBhY2NvdW50SWQ6IGFjY291bnQuYWNjb3VudElkLFxuICAgICAgICAgICAgICAgIHNlbmRlcklkOiBlbnZlbG9wZS5hdXRob3IuaWQsXG4gICAgICAgICAgICAgICAgc2VuZGVyRW1haWw6IGVudmVsb3BlLmF1dGhvci5lbWFpbCxcbiAgICAgICAgICAgICAgICBjb252ZXJzYXRpb25JZDogZW52ZWxvcGUuY29udmVyc2F0aW9uSWQsXG4gICAgICAgICAgICAgICAgbWVzc2FnZUlkOiBlbnZlbG9wZS5pZCxcbiAgICAgICAgICAgICAgICB0ZXh0OiBlbnZlbG9wZS5jb250ZW50LnRleHQgPz8gXCJcIixcbiAgICAgICAgICAgICAgICByb29tVHlwZTogZW52ZWxvcGUubWV0YWRhdGEucm9vbVR5cGUsXG4gICAgICAgICAgICAgICAgdGhyZWFkSWQ6IGVudmVsb3BlLm1ldGFkYXRhLnBhcmVudElkLFxuICAgICAgICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoZW52ZWxvcGUubWV0YWRhdGEudGltZXN0YW1wKSxcbiAgICAgICAgICAgICAgICByYXc6IGVudmVsb3BlLm1ldGFkYXRhLnJhdyxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiB7IHN0YXR1czogMjAwLCBib2R5OiB7IG9rOiB0cnVlIH0gfTtcbiAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgIGxvZz8uZXJyb3I/LihcbiAgICAgICAgICAgICAgYFske2FjY291bnQuYWNjb3VudElkfV0gd2ViaG9vayBlcnJvcjogJHtlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogZXJyfWBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm4geyBzdGF0dXM6IDUwMCwgYm9keTogeyBlcnJvcjogXCJJbnRlcm5hbCBlcnJvclwiIH0gfTtcbiAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgbG9nPy5pbmZvPy4oXG4gICAgICAgIGBbJHthY2NvdW50LmFjY291bnRJZH1dIEhUVFAgd2ViaG9vayBoYW5kbGVyIHJlZ2lzdGVyZWQgYXQgJHt3ZWJob29rUGF0aH1gXG4gICAgICApO1xuXG4gICAgICAvLyBSZXR1cm4gY2xlYW51cCBmdW5jdGlvblxuICAgICAgcmV0dXJuIGFzeW5jICgpID0+IHtcbiAgICAgICAgbG9nPy5pbmZvPy4oYFske2FjY291bnQuYWNjb3VudElkfV0gc3RvcHBpbmcgV2ViZXggcHJvdmlkZXJgKTtcbiAgICAgICAgcnVudGltZS5odHRwLnVucmVnaXN0ZXJIYW5kbGVyKHdlYmhvb2tQYXRoKTtcbiAgICAgIH07XG4gICAgfSxcbiAgfSxcbn07XG4iXX0=
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webex Channel - Main Channel Logic
|
|
3
|
+
*/
|
|
4
|
+
import type { WebexChannelConfig, WebexChannelPlugin, WebexMessage, WebexWebhook, WebexWebhookPayload, OpenClawEnvelope, OpenClawOutboundMessage, WebhookHandler } from './types';
|
|
5
|
+
import { WebexSender } from './send';
|
|
6
|
+
import { WebexWebhookHandler } from './webhook';
|
|
7
|
+
/**
|
|
8
|
+
* WebexChannel implements the OpenClaw channel plugin interface for Cisco Webex
|
|
9
|
+
*/
|
|
10
|
+
export declare class WebexChannel implements WebexChannelPlugin {
|
|
11
|
+
readonly name = "webex";
|
|
12
|
+
readonly version = "1.0.0";
|
|
13
|
+
private config;
|
|
14
|
+
private sender;
|
|
15
|
+
private webhookHandler;
|
|
16
|
+
private messageHandlers;
|
|
17
|
+
private initialized;
|
|
18
|
+
/**
|
|
19
|
+
* Initialize the channel with configuration
|
|
20
|
+
*/
|
|
21
|
+
initialize(config: WebexChannelConfig): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Validate configuration
|
|
24
|
+
*/
|
|
25
|
+
private validateConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Ensure the channel is initialized
|
|
28
|
+
*/
|
|
29
|
+
private ensureInitialized;
|
|
30
|
+
/**
|
|
31
|
+
* Send a message
|
|
32
|
+
*/
|
|
33
|
+
send(message: OpenClawOutboundMessage): Promise<WebexMessage>;
|
|
34
|
+
/**
|
|
35
|
+
* Send a simple text message to a room
|
|
36
|
+
*/
|
|
37
|
+
sendText(roomId: string, text: string): Promise<WebexMessage>;
|
|
38
|
+
/**
|
|
39
|
+
* Send a markdown message to a room
|
|
40
|
+
*/
|
|
41
|
+
sendMarkdown(roomId: string, markdown: string): Promise<WebexMessage>;
|
|
42
|
+
/**
|
|
43
|
+
* Send a direct message to a person
|
|
44
|
+
*/
|
|
45
|
+
sendDirect(personIdOrEmail: string, text: string): Promise<WebexMessage>;
|
|
46
|
+
/**
|
|
47
|
+
* Reply to a message in a thread
|
|
48
|
+
*/
|
|
49
|
+
reply(roomId: string, parentId: string, text: string): Promise<WebexMessage>;
|
|
50
|
+
/**
|
|
51
|
+
* Handle incoming webhook
|
|
52
|
+
*/
|
|
53
|
+
handleWebhook(payload: WebexWebhookPayload, signature?: string): Promise<OpenClawEnvelope | null>;
|
|
54
|
+
/**
|
|
55
|
+
* Register a message handler
|
|
56
|
+
*/
|
|
57
|
+
onMessage(handler: WebhookHandler): void;
|
|
58
|
+
/**
|
|
59
|
+
* Remove a message handler
|
|
60
|
+
*/
|
|
61
|
+
offMessage(handler: WebhookHandler): void;
|
|
62
|
+
/**
|
|
63
|
+
* Notify all registered handlers of a new message
|
|
64
|
+
*/
|
|
65
|
+
private notifyHandlers;
|
|
66
|
+
/**
|
|
67
|
+
* Register webhooks with Webex
|
|
68
|
+
*/
|
|
69
|
+
registerWebhooks(): Promise<WebexWebhook[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Get the sender instance for advanced operations
|
|
72
|
+
*/
|
|
73
|
+
getSender(): WebexSender;
|
|
74
|
+
/**
|
|
75
|
+
* Get the webhook handler instance for advanced operations
|
|
76
|
+
*/
|
|
77
|
+
getWebhookHandler(): WebexWebhookHandler;
|
|
78
|
+
/**
|
|
79
|
+
* Get the current configuration
|
|
80
|
+
*/
|
|
81
|
+
getConfig(): WebexChannelConfig | null;
|
|
82
|
+
/**
|
|
83
|
+
* Check if the channel is initialized
|
|
84
|
+
*/
|
|
85
|
+
isInitialized(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Cleanup and shutdown
|
|
88
|
+
*/
|
|
89
|
+
shutdown(): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create a new Webex channel instance
|
|
93
|
+
*/
|
|
94
|
+
export declare function createWebexChannel(): WebexChannel;
|
|
95
|
+
/**
|
|
96
|
+
* Create and initialize a Webex channel with config
|
|
97
|
+
*/
|
|
98
|
+
export declare function createAndInitialize(config: WebexChannelConfig): Promise<WebexChannel>;
|