opencode-discord-notify 0.3.1 → 0.3.3
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/dist/index.d.ts +1 -64
- package/dist/index.js +164 -89
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,68 +1,5 @@
|
|
|
1
1
|
import { Plugin } from '@opencode-ai/plugin';
|
|
2
2
|
|
|
3
|
-
type DiscordWebhookMessageResponse = {
|
|
4
|
-
id: string;
|
|
5
|
-
channel_id: string;
|
|
6
|
-
};
|
|
7
|
-
type DiscordEmbed = {
|
|
8
|
-
title?: string;
|
|
9
|
-
description?: string;
|
|
10
|
-
url?: string;
|
|
11
|
-
color?: number;
|
|
12
|
-
timestamp?: string;
|
|
13
|
-
fields?: Array<{
|
|
14
|
-
name: string;
|
|
15
|
-
value: string;
|
|
16
|
-
inline?: boolean;
|
|
17
|
-
}>;
|
|
18
|
-
};
|
|
19
|
-
type DiscordAllowedMentions = {
|
|
20
|
-
parse?: Array<'everyone' | 'roles' | 'users'>;
|
|
21
|
-
roles?: string[];
|
|
22
|
-
users?: string[];
|
|
23
|
-
};
|
|
24
|
-
type DiscordExecuteWebhookBody = {
|
|
25
|
-
content?: string;
|
|
26
|
-
username?: string;
|
|
27
|
-
avatar_url?: string;
|
|
28
|
-
thread_name?: string;
|
|
29
|
-
embeds?: DiscordEmbed[];
|
|
30
|
-
allowed_mentions?: DiscordAllowedMentions;
|
|
31
|
-
};
|
|
32
|
-
declare function toIsoTimestamp(ms: unknown): string | undefined;
|
|
33
|
-
declare function buildFields(fields: Array<[string, unknown]>, inline?: boolean): DiscordEmbed['fields'];
|
|
34
|
-
declare function buildMention(mention: string | undefined, nameForLog: string): {
|
|
35
|
-
content?: string;
|
|
36
|
-
allowed_mentions?: DiscordAllowedMentions;
|
|
37
|
-
} | undefined;
|
|
38
|
-
declare function buildTodoChecklist(todos: unknown): string;
|
|
39
|
-
type ToastVariant = 'info' | 'success' | 'warning' | 'error';
|
|
40
|
-
type MaybeAlertError = (input: {
|
|
41
|
-
key: string;
|
|
42
|
-
title?: string;
|
|
43
|
-
message: string;
|
|
44
|
-
variant: ToastVariant;
|
|
45
|
-
}) => Promise<void>;
|
|
46
|
-
type PostDiscordWebhookDeps = {
|
|
47
|
-
showErrorAlert: boolean;
|
|
48
|
-
maybeAlertError: MaybeAlertError;
|
|
49
|
-
waitOnRateLimitMs: number;
|
|
50
|
-
fetchImpl?: typeof fetch;
|
|
51
|
-
sleepImpl?: (ms: number) => Promise<void>;
|
|
52
|
-
};
|
|
53
|
-
declare function postDiscordWebhook(input: {
|
|
54
|
-
webhookUrl: string;
|
|
55
|
-
threadId?: string;
|
|
56
|
-
wait?: boolean;
|
|
57
|
-
body: DiscordExecuteWebhookBody;
|
|
58
|
-
}, deps: PostDiscordWebhookDeps): Promise<DiscordWebhookMessageResponse | undefined>;
|
|
59
3
|
declare const plugin: Plugin;
|
|
60
|
-
declare const __test__: {
|
|
61
|
-
buildMention: typeof buildMention;
|
|
62
|
-
buildTodoChecklist: typeof buildTodoChecklist;
|
|
63
|
-
buildFields: typeof buildFields;
|
|
64
|
-
toIsoTimestamp: typeof toIsoTimestamp;
|
|
65
|
-
postDiscordWebhook: typeof postDiscordWebhook;
|
|
66
|
-
};
|
|
67
4
|
|
|
68
|
-
export {
|
|
5
|
+
export { plugin as default };
|
package/dist/index.js
CHANGED
|
@@ -264,6 +264,7 @@ var plugin = async ({ client }) => {
|
|
|
264
264
|
const toastCooldownMs = 3e4;
|
|
265
265
|
const sendParams = parseSendParams(getEnv("DISCORD_SEND_PARAMS"));
|
|
266
266
|
const lastAlertAtByKey = /* @__PURE__ */ new Map();
|
|
267
|
+
const sentTextPartIds = /* @__PURE__ */ new Set();
|
|
267
268
|
const showToast = async ({ title, message, variant }) => {
|
|
268
269
|
try {
|
|
269
270
|
await client.tui.showToast({
|
|
@@ -424,6 +425,50 @@ var plugin = async ({ client }) => {
|
|
|
424
425
|
function buildPermissionMention() {
|
|
425
426
|
return buildMention(permissionMention, "DISCORD_WEBHOOK_PERMISSION_MENTION");
|
|
426
427
|
}
|
|
428
|
+
function escapeDiscordMention(text) {
|
|
429
|
+
return text.replace(/^@/g, "@\u200B");
|
|
430
|
+
}
|
|
431
|
+
async function handleTextPart(input) {
|
|
432
|
+
const { part, role, sessionID, messageID, replay } = input;
|
|
433
|
+
const partID = part?.id;
|
|
434
|
+
if (!partID) return;
|
|
435
|
+
if (!replay && role === "assistant" && !part?.time?.end) return;
|
|
436
|
+
if (sentTextPartIds.has(partID)) return;
|
|
437
|
+
sentTextPartIds.add(partID);
|
|
438
|
+
const text = escapeDiscordMention(safeString(part?.text));
|
|
439
|
+
if (role === "user" && excludeInputContext && isInputContextText(text)) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (role === "user" && text.trim() === "" || text.trim() === "(empty)") {
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (role === "user" && !firstUserTextBySession.has(sessionID)) {
|
|
446
|
+
const normalized = normalizeThreadTitle(text);
|
|
447
|
+
if (normalized) firstUserTextBySession.set(sessionID, normalized);
|
|
448
|
+
}
|
|
449
|
+
const embed = {
|
|
450
|
+
title: getTextPartEmbedTitle(role),
|
|
451
|
+
color: COLORS.info,
|
|
452
|
+
fields: buildFields(
|
|
453
|
+
filterSendFields(
|
|
454
|
+
[
|
|
455
|
+
["sessionID", sessionID],
|
|
456
|
+
["messageID", messageID],
|
|
457
|
+
["partID", partID],
|
|
458
|
+
["role", role]
|
|
459
|
+
],
|
|
460
|
+
sendParams
|
|
461
|
+
)
|
|
462
|
+
),
|
|
463
|
+
description: truncateText(text || "(empty)", 4096)
|
|
464
|
+
};
|
|
465
|
+
enqueueToThread(sessionID, { embeds: [embed] });
|
|
466
|
+
if (role === "user") {
|
|
467
|
+
await flushPending(sessionID);
|
|
468
|
+
} else if (shouldFlush(sessionID)) {
|
|
469
|
+
await flushPending(sessionID);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
427
472
|
function setIfChanged(map, key, next) {
|
|
428
473
|
const prev = map.get(key);
|
|
429
474
|
if (prev === next) return false;
|
|
@@ -465,7 +510,102 @@ var plugin = async ({ client }) => {
|
|
|
465
510
|
};
|
|
466
511
|
lastSessionInfo.set(sessionID, { title, shareUrl });
|
|
467
512
|
enqueueToThread(sessionID, { embeds: [embed] });
|
|
468
|
-
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
case "permission.updated": {
|
|
516
|
+
const p = event.properties;
|
|
517
|
+
const sessionID = p?.sessionID;
|
|
518
|
+
if (!sessionID) return;
|
|
519
|
+
const embed = {
|
|
520
|
+
title: "Permission required",
|
|
521
|
+
description: p?.title,
|
|
522
|
+
color: COLORS.warning,
|
|
523
|
+
timestamp: toIsoTimestamp(p?.time?.created),
|
|
524
|
+
fields: buildFields(
|
|
525
|
+
filterSendFields(
|
|
526
|
+
[
|
|
527
|
+
["sessionID", sessionID],
|
|
528
|
+
["permissionID", p?.id],
|
|
529
|
+
["type", p?.type],
|
|
530
|
+
["pattern", p?.pattern],
|
|
531
|
+
["messageID", p?.messageID],
|
|
532
|
+
["callID", p?.callID]
|
|
533
|
+
],
|
|
534
|
+
sendParams
|
|
535
|
+
)
|
|
536
|
+
)
|
|
537
|
+
};
|
|
538
|
+
const mention = buildPermissionMention();
|
|
539
|
+
enqueueToThread(sessionID, {
|
|
540
|
+
content: mention ? `${mention.content}` : void 0,
|
|
541
|
+
allowed_mentions: mention?.allowed_mentions,
|
|
542
|
+
embeds: [embed]
|
|
543
|
+
});
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
case "session.idle": {
|
|
547
|
+
const sessionID = event.properties?.sessionID;
|
|
548
|
+
if (!sessionID) return;
|
|
549
|
+
const embed = {
|
|
550
|
+
title: "Session completed",
|
|
551
|
+
color: COLORS.success,
|
|
552
|
+
fields: buildFields(
|
|
553
|
+
filterSendFields(
|
|
554
|
+
[["sessionID", sessionID]],
|
|
555
|
+
withForcedSendParams(sendParams, ["sessionID"])
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
};
|
|
559
|
+
const mention = buildCompleteMention();
|
|
560
|
+
enqueueToThread(sessionID, {
|
|
561
|
+
content: mention ? `${mention.content}` : void 0,
|
|
562
|
+
allowed_mentions: mention?.allowed_mentions,
|
|
563
|
+
embeds: [embed]
|
|
564
|
+
});
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
case "session.error": {
|
|
568
|
+
const p = event.properties;
|
|
569
|
+
const sessionID = p?.sessionID;
|
|
570
|
+
const errorStr = safeString(p?.error);
|
|
571
|
+
const embed = {
|
|
572
|
+
title: "Session error",
|
|
573
|
+
color: COLORS.error,
|
|
574
|
+
description: errorStr ? errorStr.length > 4096 ? errorStr.slice(0, 4093) + "..." : errorStr : void 0,
|
|
575
|
+
fields: buildFields(
|
|
576
|
+
filterSendFields(
|
|
577
|
+
[["sessionID", sessionID]],
|
|
578
|
+
withForcedSendParams(sendParams, [
|
|
579
|
+
"sessionID",
|
|
580
|
+
"projectID",
|
|
581
|
+
"directory"
|
|
582
|
+
])
|
|
583
|
+
)
|
|
584
|
+
)
|
|
585
|
+
};
|
|
586
|
+
if (!sessionID) return;
|
|
587
|
+
const mention = buildCompleteMention();
|
|
588
|
+
enqueueToThread(sessionID, {
|
|
589
|
+
content: mention ? `$Session error` : void 0,
|
|
590
|
+
allowed_mentions: mention?.allowed_mentions,
|
|
591
|
+
embeds: [embed]
|
|
592
|
+
});
|
|
593
|
+
await flushPending(sessionID);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
case "todo.updated": {
|
|
597
|
+
const p = event.properties;
|
|
598
|
+
const sessionID = p?.sessionID;
|
|
599
|
+
if (!sessionID) return;
|
|
600
|
+
const embed = {
|
|
601
|
+
title: "Todo updated",
|
|
602
|
+
color: COLORS.info,
|
|
603
|
+
fields: buildFields(
|
|
604
|
+
filterSendFields([["sessionID", sessionID]], sendParams)
|
|
605
|
+
),
|
|
606
|
+
description: buildTodoChecklist(p?.todos)
|
|
607
|
+
};
|
|
608
|
+
enqueueToThread(sessionID, { embeds: [embed] });
|
|
469
609
|
return;
|
|
470
610
|
}
|
|
471
611
|
case "message.updated": {
|
|
@@ -473,52 +613,22 @@ var plugin = async ({ client }) => {
|
|
|
473
613
|
const messageID = info?.id;
|
|
474
614
|
const role = info?.role;
|
|
475
615
|
if (!messageID) return;
|
|
476
|
-
if (role
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
});
|
|
493
|
-
setIfChanged(/* @__PURE__ */ new Map(), partID, snapshot);
|
|
494
|
-
continue;
|
|
495
|
-
}
|
|
496
|
-
if (role === "user" && !firstUserTextBySession.has(sessionID)) {
|
|
497
|
-
const normalized = normalizeThreadTitle(text);
|
|
498
|
-
if (normalized)
|
|
499
|
-
firstUserTextBySession.set(sessionID, normalized);
|
|
500
|
-
}
|
|
501
|
-
const embed = {
|
|
502
|
-
title: getTextPartEmbedTitle(role),
|
|
503
|
-
color: COLORS.info,
|
|
504
|
-
fields: buildFields(
|
|
505
|
-
filterSendFields(
|
|
506
|
-
[
|
|
507
|
-
["sessionID", sessionID],
|
|
508
|
-
["messageID", messageID],
|
|
509
|
-
["partID", partID],
|
|
510
|
-
["role", role]
|
|
511
|
-
],
|
|
512
|
-
sendParams
|
|
513
|
-
)
|
|
514
|
-
),
|
|
515
|
-
description: truncateText(text || "(empty)", 4096)
|
|
516
|
-
};
|
|
517
|
-
enqueueToThread(sessionID, { embeds: [embed] });
|
|
518
|
-
if (role === "user") await flushPending(sessionID);
|
|
519
|
-
else if (shouldFlush(sessionID)) await flushPending(sessionID);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
616
|
+
if (role !== "user" && role !== "assistant") return;
|
|
617
|
+
messageRoleById.set(messageID, role);
|
|
618
|
+
const pendingParts = pendingTextPartsByMessageId.get(messageID);
|
|
619
|
+
if (!pendingParts?.length) return;
|
|
620
|
+
pendingTextPartsByMessageId.delete(messageID);
|
|
621
|
+
for (const part of pendingParts) {
|
|
622
|
+
const sessionID = part?.sessionID;
|
|
623
|
+
const partID = part?.id;
|
|
624
|
+
if (!sessionID || !partID || part?.type !== "text") continue;
|
|
625
|
+
await handleTextPart({
|
|
626
|
+
part,
|
|
627
|
+
role,
|
|
628
|
+
sessionID,
|
|
629
|
+
messageID,
|
|
630
|
+
replay: true
|
|
631
|
+
});
|
|
522
632
|
}
|
|
523
633
|
return;
|
|
524
634
|
}
|
|
@@ -538,46 +648,12 @@ var plugin = async ({ client }) => {
|
|
|
538
648
|
pendingTextPartsByMessageId.set(messageID, list);
|
|
539
649
|
return;
|
|
540
650
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
role,
|
|
548
|
-
skipped: "input_context"
|
|
549
|
-
});
|
|
550
|
-
setIfChanged(/* @__PURE__ */ new Map(), partID, snapshot);
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
if (role === "user" && !firstUserTextBySession.has(sessionID)) {
|
|
554
|
-
const normalized = normalizeThreadTitle(text);
|
|
555
|
-
if (normalized)
|
|
556
|
-
firstUserTextBySession.set(sessionID, normalized);
|
|
557
|
-
}
|
|
558
|
-
const embed = {
|
|
559
|
-
title: getTextPartEmbedTitle(role),
|
|
560
|
-
color: COLORS.info,
|
|
561
|
-
fields: buildFields(
|
|
562
|
-
filterSendFields(
|
|
563
|
-
[
|
|
564
|
-
["sessionID", sessionID],
|
|
565
|
-
["messageID", messageID],
|
|
566
|
-
["partID", partID],
|
|
567
|
-
["role", role]
|
|
568
|
-
],
|
|
569
|
-
sendParams
|
|
570
|
-
)
|
|
571
|
-
),
|
|
572
|
-
description: truncateText(text || "(empty)", 4096)
|
|
573
|
-
};
|
|
574
|
-
enqueueToThread(sessionID, { embeds: [embed] });
|
|
575
|
-
if (role === "user") {
|
|
576
|
-
await flushPending(sessionID);
|
|
577
|
-
} else if (shouldFlush(sessionID)) {
|
|
578
|
-
await flushPending(sessionID);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
651
|
+
await handleTextPart({
|
|
652
|
+
part,
|
|
653
|
+
role,
|
|
654
|
+
sessionID,
|
|
655
|
+
messageID
|
|
656
|
+
});
|
|
581
657
|
return;
|
|
582
658
|
}
|
|
583
659
|
default:
|
|
@@ -588,7 +664,7 @@ var plugin = async ({ client }) => {
|
|
|
588
664
|
}
|
|
589
665
|
};
|
|
590
666
|
};
|
|
591
|
-
|
|
667
|
+
plugin.__test__ = {
|
|
592
668
|
buildMention,
|
|
593
669
|
buildTodoChecklist,
|
|
594
670
|
buildFields,
|
|
@@ -597,6 +673,5 @@ var __test__ = {
|
|
|
597
673
|
};
|
|
598
674
|
var index_default = plugin;
|
|
599
675
|
export {
|
|
600
|
-
__test__,
|
|
601
676
|
index_default as default
|
|
602
677
|
};
|