opencode-discord-notify 0.1.1 → 0.2.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 +3 -0
- package/README.md +3 -0
- package/dist/index.js +98 -31
- package/package.json +1 -1
package/README-JP.md
CHANGED
|
@@ -62,6 +62,7 @@ OpenCode を再起動してください。
|
|
|
62
62
|
- `DISCORD_WEBHOOK_COMPLETE_MENTION`: `session.idle` / `session.error` の通知本文に付けるメンション(`@everyone` または `@here` のみ許容。Forum webhook の仕様上、ping は常に発生しない)
|
|
63
63
|
- `DISCORD_WEBHOOK_PERMISSION_MENTION`: `permission.updated` の通知本文に付けるメンション(`DISCORD_WEBHOOK_COMPLETE_MENTION` へのフォールバックなし。`@everyone` または `@here` のみ許容。Forum webhook の仕様上、ping は常に発生しない)
|
|
64
64
|
- `DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT`: `1` のとき input context(`<file>` から始まる user `text` part)を通知しない(デフォルト: `1` / `0` で無効化)
|
|
65
|
+
- `SEND_PARAMS`: embed の fields として送るキーをカンマ区切りで指定。指定可能キー: `sessionID`, `permissionID`, `type`, `pattern`, `messageID`, `callID`, `partID`, `role`, `directory`, `projectID`。未設定・空文字・空要素のみの場合は全て選択。`session.created` は `SEND_PARAMS` に関わらず `sessionID`, `projectID`, `directory` を必ず含みます。
|
|
65
66
|
|
|
66
67
|
## 仕様メモ
|
|
67
68
|
|
|
@@ -81,8 +82,10 @@ OpenCode を再起動してください。
|
|
|
81
82
|
- `message.updated` は通知しません(role 判定用に追跡。role 未確定で保留した `text` part を後から通知することがあります)。
|
|
82
83
|
- `message.part.updated` は以下の方針です。
|
|
83
84
|
- `text`: user は即時通知。assistant は `part.time.end` がある確定時のみ通知(ストリーミング途中更新は通知しない)
|
|
85
|
+
- embed タイトルは `User says` / `Agent says` です
|
|
84
86
|
- `tool`: 通知しない
|
|
85
87
|
- `reasoning`: 通知しない(内部思考が含まれる可能性があるため)
|
|
88
|
+
- `SEND_PARAMS` の制御対象は embed の fields のみです(title/description/content/timestamp などは対象外)。また `share` は fields としては送りません(Session started の embed URL には `shareUrl` を使います)。
|
|
86
89
|
|
|
87
90
|
## 動作確認(手動)
|
|
88
91
|
|
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ Optional:
|
|
|
64
64
|
- `DISCORD_WEBHOOK_COMPLETE_MENTION`: mention to put in `session.idle` / `session.error` messages (only `@everyone` or `@here` supported; Forum webhooks may not actually ping due to Discord behavior)
|
|
65
65
|
- `DISCORD_WEBHOOK_PERMISSION_MENTION`: mention to put in `permission.updated` messages (no fallback to `DISCORD_WEBHOOK_COMPLETE_MENTION`; only `@everyone` or `@here` supported; Forum webhooks may not actually ping due to Discord behavior)
|
|
66
66
|
- `DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT`: when set to `1`, exclude "input context" (user `text` parts that start with `<file>`) from notifications (default: `1`; set to `0` to disable)
|
|
67
|
+
- `SEND_PARAMS`: comma-separated list of keys to include as embed fields. Allowed keys: `sessionID`, `permissionID`, `type`, `pattern`, `messageID`, `callID`, `partID`, `role`, `directory`, `projectID`. If unset, empty, or containing only empty elements, all keys are selected. `session.created` always includes `sessionID`, `projectID`, `directory` regardless.
|
|
67
68
|
|
|
68
69
|
## Notes / behavior
|
|
69
70
|
|
|
@@ -83,8 +84,10 @@ Optional:
|
|
|
83
84
|
- `message.updated` is not posted (tracked for role inference; may post a previously-held text part later).
|
|
84
85
|
- `message.part.updated` policy:
|
|
85
86
|
- `text`: user is posted immediately; assistant is posted only when finalized (when `part.time.end` exists)
|
|
87
|
+
- Embed titles are `User says` / `Agent says`
|
|
86
88
|
- `tool`: not posted
|
|
87
89
|
- `reasoning`: not posted (to avoid exposing internal thoughts)
|
|
90
|
+
- `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).
|
|
88
91
|
|
|
89
92
|
## Manual test
|
|
90
93
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
var SEND_PARAM_KEYS = [
|
|
3
|
+
"sessionID",
|
|
4
|
+
"permissionID",
|
|
5
|
+
"type",
|
|
6
|
+
"pattern",
|
|
7
|
+
"messageID",
|
|
8
|
+
"callID",
|
|
9
|
+
"partID",
|
|
10
|
+
"role",
|
|
11
|
+
"directory",
|
|
12
|
+
"projectID"
|
|
13
|
+
];
|
|
14
|
+
var SEND_PARAM_KEY_SET = new Set(SEND_PARAM_KEYS);
|
|
2
15
|
var COLORS = {
|
|
3
16
|
info: 5793266,
|
|
4
17
|
success: 5763719,
|
|
@@ -32,6 +45,23 @@ function buildFields(fields, inline = false) {
|
|
|
32
45
|
}
|
|
33
46
|
return result.length ? result : void 0;
|
|
34
47
|
}
|
|
48
|
+
function isSendParamKey(value) {
|
|
49
|
+
return SEND_PARAM_KEY_SET.has(value);
|
|
50
|
+
}
|
|
51
|
+
function filterSendFields(fields, allowed) {
|
|
52
|
+
return fields.filter(([name]) => {
|
|
53
|
+
if (!isSendParamKey(name)) return false;
|
|
54
|
+
return allowed.has(name);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function getTextPartEmbedTitle(role) {
|
|
58
|
+
return role === "user" ? "User says" : "Agent says";
|
|
59
|
+
}
|
|
60
|
+
function withForcedSendParams(base, forced) {
|
|
61
|
+
const next = new Set(base);
|
|
62
|
+
for (const key of forced) next.add(key);
|
|
63
|
+
return next;
|
|
64
|
+
}
|
|
35
65
|
function getEnv(name) {
|
|
36
66
|
try {
|
|
37
67
|
return process.env[name];
|
|
@@ -39,6 +69,17 @@ function getEnv(name) {
|
|
|
39
69
|
return void 0;
|
|
40
70
|
}
|
|
41
71
|
}
|
|
72
|
+
function parseSendParams(raw) {
|
|
73
|
+
if (raw === void 0) return new Set(SEND_PARAM_KEYS);
|
|
74
|
+
const tokens = raw.split(",").map((v) => v.trim()).filter(Boolean);
|
|
75
|
+
if (!tokens.length) return new Set(SEND_PARAM_KEYS);
|
|
76
|
+
const result = /* @__PURE__ */ new Set();
|
|
77
|
+
for (const token of tokens) {
|
|
78
|
+
if (!SEND_PARAM_KEY_SET.has(token)) continue;
|
|
79
|
+
result.add(token);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
42
83
|
function withQuery(url, params) {
|
|
43
84
|
const u = new URL(url);
|
|
44
85
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -97,6 +138,7 @@ var plugin = async () => {
|
|
|
97
138
|
const permissionMention = permissionMentionRaw || void 0;
|
|
98
139
|
const excludeInputContextRaw = (getEnv("DISCORD_WEBHOOK_EXCLUDE_INPUT_CONTEXT") ?? "1").trim();
|
|
99
140
|
const excludeInputContext = excludeInputContextRaw !== "0";
|
|
141
|
+
const sendParams = parseSendParams(getEnv("SEND_PARAMS"));
|
|
100
142
|
const sessionToThread = /* @__PURE__ */ new Map();
|
|
101
143
|
const threadCreateInFlight = /* @__PURE__ */ new Map();
|
|
102
144
|
const pendingPostsBySession = /* @__PURE__ */ new Map();
|
|
@@ -321,12 +363,19 @@ var plugin = async () => {
|
|
|
321
363
|
color: COLORS.info,
|
|
322
364
|
timestamp: createdAt,
|
|
323
365
|
fields: buildFields(
|
|
324
|
-
|
|
325
|
-
[
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
366
|
+
filterSendFields(
|
|
367
|
+
[
|
|
368
|
+
["sessionID", sessionID],
|
|
369
|
+
["projectID", projectID],
|
|
370
|
+
["directory", directory],
|
|
371
|
+
["share", shareUrl]
|
|
372
|
+
],
|
|
373
|
+
withForcedSendParams(sendParams, [
|
|
374
|
+
"sessionID",
|
|
375
|
+
"projectID",
|
|
376
|
+
"directory"
|
|
377
|
+
])
|
|
378
|
+
),
|
|
330
379
|
false
|
|
331
380
|
)
|
|
332
381
|
};
|
|
@@ -345,14 +394,17 @@ var plugin = async () => {
|
|
|
345
394
|
color: COLORS.warning,
|
|
346
395
|
timestamp: toIsoTimestamp(p?.time?.created),
|
|
347
396
|
fields: buildFields(
|
|
348
|
-
|
|
349
|
-
[
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
397
|
+
filterSendFields(
|
|
398
|
+
[
|
|
399
|
+
["sessionID", sessionID],
|
|
400
|
+
["permissionID", p?.id],
|
|
401
|
+
["type", p?.type],
|
|
402
|
+
["pattern", p?.pattern],
|
|
403
|
+
["messageID", p?.messageID],
|
|
404
|
+
["callID", p?.callID]
|
|
405
|
+
],
|
|
406
|
+
sendParams
|
|
407
|
+
),
|
|
356
408
|
false
|
|
357
409
|
)
|
|
358
410
|
};
|
|
@@ -371,7 +423,10 @@ var plugin = async () => {
|
|
|
371
423
|
const embed = {
|
|
372
424
|
title: "Session completed",
|
|
373
425
|
color: COLORS.success,
|
|
374
|
-
fields: buildFields(
|
|
426
|
+
fields: buildFields(
|
|
427
|
+
filterSendFields([["sessionID", sessionID]], sendParams),
|
|
428
|
+
false
|
|
429
|
+
)
|
|
375
430
|
};
|
|
376
431
|
const mention = buildCompleteMention();
|
|
377
432
|
enqueueToThread(sessionID, {
|
|
@@ -390,7 +445,10 @@ var plugin = async () => {
|
|
|
390
445
|
title: "Session error",
|
|
391
446
|
color: COLORS.error,
|
|
392
447
|
description: errorStr ? errorStr.length > 4096 ? errorStr.slice(0, 4093) + "..." : errorStr : void 0,
|
|
393
|
-
fields: buildFields(
|
|
448
|
+
fields: buildFields(
|
|
449
|
+
filterSendFields([["sessionID", sessionID]], sendParams),
|
|
450
|
+
false
|
|
451
|
+
)
|
|
394
452
|
};
|
|
395
453
|
if (!sessionID) return;
|
|
396
454
|
const mention = buildCompleteMention();
|
|
@@ -409,7 +467,10 @@ var plugin = async () => {
|
|
|
409
467
|
const embed = {
|
|
410
468
|
title: "Todo updated",
|
|
411
469
|
color: COLORS.info,
|
|
412
|
-
fields: buildFields(
|
|
470
|
+
fields: buildFields(
|
|
471
|
+
filterSendFields([["sessionID", sessionID]], sendParams),
|
|
472
|
+
false
|
|
473
|
+
),
|
|
413
474
|
description: buildTodoChecklist(p?.todos)
|
|
414
475
|
};
|
|
415
476
|
enqueueToThread(sessionID, { embeds: [embed] });
|
|
@@ -450,15 +511,18 @@ var plugin = async () => {
|
|
|
450
511
|
firstUserTextBySession.set(sessionID, normalized);
|
|
451
512
|
}
|
|
452
513
|
const embed = {
|
|
453
|
-
title: role
|
|
514
|
+
title: getTextPartEmbedTitle(role),
|
|
454
515
|
color: COLORS.info,
|
|
455
516
|
fields: buildFields(
|
|
456
|
-
|
|
457
|
-
[
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
517
|
+
filterSendFields(
|
|
518
|
+
[
|
|
519
|
+
["sessionID", sessionID],
|
|
520
|
+
["messageID", messageID],
|
|
521
|
+
["partID", partID],
|
|
522
|
+
["role", role]
|
|
523
|
+
],
|
|
524
|
+
sendParams
|
|
525
|
+
),
|
|
462
526
|
false
|
|
463
527
|
),
|
|
464
528
|
description: truncateText(text || "(empty)", 4096)
|
|
@@ -507,15 +571,18 @@ var plugin = async () => {
|
|
|
507
571
|
firstUserTextBySession.set(sessionID, normalized);
|
|
508
572
|
}
|
|
509
573
|
const embed = {
|
|
510
|
-
title: role
|
|
574
|
+
title: getTextPartEmbedTitle(role),
|
|
511
575
|
color: COLORS.info,
|
|
512
576
|
fields: buildFields(
|
|
513
|
-
|
|
514
|
-
[
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
577
|
+
filterSendFields(
|
|
578
|
+
[
|
|
579
|
+
["sessionID", sessionID],
|
|
580
|
+
["messageID", messageID],
|
|
581
|
+
["partID", partID],
|
|
582
|
+
["role", role]
|
|
583
|
+
],
|
|
584
|
+
sendParams
|
|
585
|
+
),
|
|
519
586
|
false
|
|
520
587
|
),
|
|
521
588
|
description: truncateText(text || "(empty)", 4096)
|