opencode-discord-notify 0.5.1 → 0.6.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-JP.md +6 -0
- package/README.md +6 -0
- package/dist/index.js +92 -10
- package/package.json +1 -1
package/README-JP.md
CHANGED
|
@@ -66,6 +66,7 @@ OpenCode を再起動してください。
|
|
|
66
66
|
- **デフォルト動作**(未設定・空文字): 全て無効化(何も送信しない)
|
|
67
67
|
- **全て送信したい場合**: 全キーを列挙してください
|
|
68
68
|
- **注意**: `session.created` は `DISCORD_SEND_PARAMS` に関わらず `sessionID` を必ず含みます
|
|
69
|
+
- `DISCORD_WEBHOOK_FALLBACK_URL`: フォールバック先のテキストチャネル webhook URL(任意。設定すると、`@everyone` または `@here` を含むメッセージが自動的にこの webhook にも送信されます。Forum webhook ではメンションが ping しない Discord の仕様上、有用)
|
|
69
70
|
|
|
70
71
|
## 仕様メモ
|
|
71
72
|
|
|
@@ -91,6 +92,11 @@ OpenCode を再起動してください。
|
|
|
91
92
|
- `tool`: 通知しない
|
|
92
93
|
- `reasoning`: 通知しない(内部思考が含まれる可能性があるため)
|
|
93
94
|
- `DISCORD_SEND_PARAMS` の制御対象は embed の fields のみです(title/description/content/timestamp などは対象外)。また `share` は fields としては送りません(Session started の embed URL には `shareUrl` を使います)。
|
|
95
|
+
- `DISCORD_WEBHOOK_FALLBACK_URL` が設定されている場合:
|
|
96
|
+
- `@everyone` または `@here` を含むメッセージ(`DISCORD_WEBHOOK_COMPLETE_MENTION` または `DISCORD_WEBHOOK_PERMISSION_MENTION` 経由)は、Forum webhook(スレッド投稿)とフォールバック先のテキストチャネル webhook の両方に自動的に送信されます。
|
|
97
|
+
- これにより、Forum webhook ではメンションが ping しない Discord の仕様上、確実に通知を届けることができます。
|
|
98
|
+
- フォールバックメッセージには、`DISCORD_SEND_PARAMS` の設定に関わらず、常に `sessionID` と `thread title` フィールドが含まれます(テキストチャネルでのコンテキスト提供のため)。`thread title` は Forum のスレッド名と同じ値(最初のユーザーテキスト、または存在しない場合はセッションタイトル)です。
|
|
99
|
+
- フォールバック送信は Forum スレッドキューとは独立しており、即座に実行されます。
|
|
94
100
|
|
|
95
101
|
## 動作確認(手動)
|
|
96
102
|
|
package/README.md
CHANGED
|
@@ -68,6 +68,7 @@ Optional:
|
|
|
68
68
|
- **Default behavior** (unset/empty): all fields are disabled (nothing sent)
|
|
69
69
|
- **To send all fields**: list all keys explicitly
|
|
70
70
|
- **Note**: `session.created` always includes `sessionID` regardless
|
|
71
|
+
- `DISCORD_WEBHOOK_FALLBACK_URL`: fallback webhook URL for text channel (optional; when set, messages containing `@everyone` or `@here` are automatically sent to this webhook as well; useful because Forum webhooks may not ping mentions due to Discord behavior)
|
|
71
72
|
|
|
72
73
|
## Notes / behavior
|
|
73
74
|
|
|
@@ -93,6 +94,11 @@ Optional:
|
|
|
93
94
|
- `tool`: not posted
|
|
94
95
|
- `reasoning`: not posted (to avoid exposing internal thoughts)
|
|
95
96
|
- `DISCORD_SEND_PARAMS` controls embed fields only (it does not affect title/description/content/timestamp). `share` is not an embed field (but Session started uses `shareUrl` as the embed URL).
|
|
97
|
+
- When `DISCORD_WEBHOOK_FALLBACK_URL` is set:
|
|
98
|
+
- Messages containing `@everyone` or `@here` (via `DISCORD_WEBHOOK_COMPLETE_MENTION` or `DISCORD_WEBHOOK_PERMISSION_MENTION`) are automatically sent to both the Forum webhook (as a thread post) and the fallback text channel webhook.
|
|
99
|
+
- This ensures reliable notifications while maintaining thread structure in Forums, since Forum webhooks may not ping mentions due to Discord behavior.
|
|
100
|
+
- Fallback messages always include `sessionID` and `thread title` fields, regardless of `DISCORD_SEND_PARAMS` settings, to provide context in the text channel. The `thread title` is the same as the Forum thread name (first user text, or session title if unavailable).
|
|
101
|
+
- Fallback sending is independent of the Forum thread queue and happens immediately.
|
|
96
102
|
|
|
97
103
|
## Manual test
|
|
98
104
|
|
package/dist/index.js
CHANGED
|
@@ -260,6 +260,49 @@ async function postDiscordWebhook(input, deps) {
|
|
|
260
260
|
`Discord webhook failed: ${response.status} ${response.statusText} ${text}`
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
|
+
async function postFallbackIfNeeded(input, deps) {
|
|
264
|
+
const {
|
|
265
|
+
body,
|
|
266
|
+
mention,
|
|
267
|
+
sessionID,
|
|
268
|
+
fallbackUrl,
|
|
269
|
+
firstUserTextBySession,
|
|
270
|
+
lastSessionInfo
|
|
271
|
+
} = input;
|
|
272
|
+
if (!fallbackUrl || !mention) return;
|
|
273
|
+
const fallbackBody = {
|
|
274
|
+
...body,
|
|
275
|
+
// thread_nameは削除(テキストチャネルでは不要)
|
|
276
|
+
thread_name: void 0
|
|
277
|
+
};
|
|
278
|
+
if (fallbackBody.embeds && fallbackBody.embeds.length > 0) {
|
|
279
|
+
const originalEmbed = fallbackBody.embeds[0];
|
|
280
|
+
const threadTitle = firstUserTextBySession.get(sessionID) || lastSessionInfo.get(sessionID)?.title;
|
|
281
|
+
const additionalFields = buildFields([
|
|
282
|
+
["sessionID", sessionID],
|
|
283
|
+
["thread title", threadTitle]
|
|
284
|
+
]);
|
|
285
|
+
fallbackBody.embeds = [
|
|
286
|
+
{
|
|
287
|
+
...originalEmbed,
|
|
288
|
+
fields: [
|
|
289
|
+
...originalEmbed.fields ?? [],
|
|
290
|
+
...additionalFields ?? []
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
try {
|
|
296
|
+
await postDiscordWebhook(
|
|
297
|
+
{
|
|
298
|
+
webhookUrl: fallbackUrl,
|
|
299
|
+
body: fallbackBody
|
|
300
|
+
},
|
|
301
|
+
deps
|
|
302
|
+
);
|
|
303
|
+
} catch (e) {
|
|
304
|
+
}
|
|
305
|
+
}
|
|
263
306
|
var GLOBAL_GUARD_KEY = "__opencode_discord_notify_registered__";
|
|
264
307
|
var plugin = async ({ client }) => {
|
|
265
308
|
const globalWithGuard = globalThis;
|
|
@@ -281,6 +324,7 @@ var plugin = async ({ client }) => {
|
|
|
281
324
|
const showErrorAlert = showErrorAlertRaw !== "0";
|
|
282
325
|
const waitOnRateLimitMs = DEFAULT_RATE_LIMIT_WAIT_MS;
|
|
283
326
|
const sendParams = parseSendParams(getEnv("DISCORD_SEND_PARAMS"));
|
|
327
|
+
const fallbackWebhookUrl = (getEnv("DISCORD_WEBHOOK_FALLBACK_URL") ?? "").trim() || void 0;
|
|
284
328
|
const lastAlertAtByKey = /* @__PURE__ */ new Map();
|
|
285
329
|
const sentTextPartIds = /* @__PURE__ */ new Set();
|
|
286
330
|
const showToast = async ({ title, message, variant }) => {
|
|
@@ -529,6 +573,7 @@ var plugin = async ({ client }) => {
|
|
|
529
573
|
const p = event.properties;
|
|
530
574
|
const sessionID = p?.sessionID;
|
|
531
575
|
if (!sessionID) return;
|
|
576
|
+
const mention = buildPermissionMention();
|
|
532
577
|
const embed = {
|
|
533
578
|
title: "Permission required",
|
|
534
579
|
description: p?.title,
|
|
@@ -548,17 +593,29 @@ var plugin = async ({ client }) => {
|
|
|
548
593
|
)
|
|
549
594
|
)
|
|
550
595
|
};
|
|
551
|
-
const
|
|
552
|
-
enqueueToThread(sessionID, {
|
|
596
|
+
const body = {
|
|
553
597
|
content: mention ? `${mention.content}` : void 0,
|
|
554
598
|
allowed_mentions: mention?.allowed_mentions,
|
|
555
599
|
embeds: [embed]
|
|
556
|
-
}
|
|
600
|
+
};
|
|
601
|
+
enqueueToThread(sessionID, body);
|
|
602
|
+
await postFallbackIfNeeded(
|
|
603
|
+
{
|
|
604
|
+
body,
|
|
605
|
+
mention,
|
|
606
|
+
sessionID,
|
|
607
|
+
fallbackUrl: fallbackWebhookUrl,
|
|
608
|
+
firstUserTextBySession,
|
|
609
|
+
lastSessionInfo
|
|
610
|
+
},
|
|
611
|
+
postDeps
|
|
612
|
+
);
|
|
557
613
|
return;
|
|
558
614
|
}
|
|
559
615
|
case "session.idle": {
|
|
560
616
|
const sessionID = event.properties?.sessionID;
|
|
561
617
|
if (!sessionID) return;
|
|
618
|
+
const mention = buildCompleteMention();
|
|
562
619
|
const embed = {
|
|
563
620
|
title: "Session completed",
|
|
564
621
|
color: COLORS.success,
|
|
@@ -569,12 +626,23 @@ var plugin = async ({ client }) => {
|
|
|
569
626
|
)
|
|
570
627
|
)
|
|
571
628
|
};
|
|
572
|
-
const
|
|
573
|
-
enqueueToThread(sessionID, {
|
|
629
|
+
const body = {
|
|
574
630
|
content: mention ? `${mention.content}` : void 0,
|
|
575
631
|
allowed_mentions: mention?.allowed_mentions,
|
|
576
632
|
embeds: [embed]
|
|
577
|
-
}
|
|
633
|
+
};
|
|
634
|
+
enqueueToThread(sessionID, body);
|
|
635
|
+
await postFallbackIfNeeded(
|
|
636
|
+
{
|
|
637
|
+
body,
|
|
638
|
+
mention,
|
|
639
|
+
sessionID,
|
|
640
|
+
fallbackUrl: fallbackWebhookUrl,
|
|
641
|
+
firstUserTextBySession,
|
|
642
|
+
lastSessionInfo
|
|
643
|
+
},
|
|
644
|
+
postDeps
|
|
645
|
+
);
|
|
578
646
|
return;
|
|
579
647
|
}
|
|
580
648
|
case "session.error": {
|
|
@@ -601,11 +669,24 @@ var plugin = async ({ client }) => {
|
|
|
601
669
|
};
|
|
602
670
|
if (!sessionID) return;
|
|
603
671
|
const mention = buildCompleteMention();
|
|
604
|
-
|
|
605
|
-
|
|
672
|
+
const body = {
|
|
673
|
+
// 🐛 既存バグ修正: `$Session error` → `${mention.content}`
|
|
674
|
+
content: mention ? `${mention.content}` : void 0,
|
|
606
675
|
allowed_mentions: mention?.allowed_mentions,
|
|
607
676
|
embeds: [embed]
|
|
608
|
-
}
|
|
677
|
+
};
|
|
678
|
+
enqueueToThread(sessionID, body);
|
|
679
|
+
await postFallbackIfNeeded(
|
|
680
|
+
{
|
|
681
|
+
body,
|
|
682
|
+
mention,
|
|
683
|
+
sessionID,
|
|
684
|
+
fallbackUrl: fallbackWebhookUrl,
|
|
685
|
+
firstUserTextBySession,
|
|
686
|
+
lastSessionInfo
|
|
687
|
+
},
|
|
688
|
+
postDeps
|
|
689
|
+
);
|
|
609
690
|
await flushPending(sessionID);
|
|
610
691
|
return;
|
|
611
692
|
}
|
|
@@ -687,7 +768,8 @@ plugin.__test__ = {
|
|
|
687
768
|
toIsoTimestamp,
|
|
688
769
|
postDiscordWebhook,
|
|
689
770
|
parseSendParams,
|
|
690
|
-
getTodoStatusMarker
|
|
771
|
+
getTodoStatusMarker,
|
|
772
|
+
postFallbackIfNeeded
|
|
691
773
|
};
|
|
692
774
|
var index_default = plugin;
|
|
693
775
|
export {
|