@airnauts/comments-notifier-slack 0.1.0 → 0.5.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/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,22 @@ import type { NotificationEvent, Notifier } from '@airnauts/comments-server';
|
|
|
2
2
|
export type SlackNotifierOptions = {
|
|
3
3
|
/** Slack Incoming Webhook URL. The target channel is baked into this URL. */
|
|
4
4
|
webhookUrl: string;
|
|
5
|
+
/**
|
|
6
|
+
* Query param the widget reads to focus a thread on load (the client's
|
|
7
|
+
* `threadParam`). Must match the host page's config so the deep-link opens
|
|
8
|
+
* the right thread. Defaults to `comments-thread`.
|
|
9
|
+
*/
|
|
10
|
+
threadParam?: string;
|
|
5
11
|
};
|
|
6
12
|
export declare function slackNotifier(opts: SlackNotifierOptions): Notifier;
|
|
7
13
|
export type SlackMessage = {
|
|
8
14
|
text: string;
|
|
9
15
|
blocks: unknown[];
|
|
10
16
|
};
|
|
17
|
+
export type FormatOptions = {
|
|
18
|
+
/** Override the deep-link query param (defaults to `comments-thread`). */
|
|
19
|
+
threadParam?: string;
|
|
20
|
+
};
|
|
11
21
|
/** Render a NotificationEvent as a Slack Block Kit message (with a plain-text fallback). */
|
|
12
|
-
export declare function formatSlackMessage(event: NotificationEvent): SlackMessage;
|
|
22
|
+
export declare function formatSlackMessage(event: NotificationEvent, opts?: FormatOptions): SlackMessage;
|
|
13
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAE5E,MAAM,MAAM,oBAAoB,GAAG;IACjC,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAQD,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,QAAQ,CAgBlE;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAaD,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,iBAAiB,EACxB,IAAI,GAAE,aAAkB,GACvB,YAAY,CAiCd"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
var DEFAULT_THREAD_PARAM = "comments-thread";
|
|
2
3
|
var TIMEOUT_MS = 3e3;
|
|
3
4
|
function slackNotifier(opts) {
|
|
4
5
|
return {
|
|
@@ -7,7 +8,7 @@ function slackNotifier(opts) {
|
|
|
7
8
|
const res = await fetch(opts.webhookUrl, {
|
|
8
9
|
method: "POST",
|
|
9
10
|
headers: { "content-type": "application/json" },
|
|
10
|
-
body: JSON.stringify(formatSlackMessage(event)),
|
|
11
|
+
body: JSON.stringify(formatSlackMessage(event, { threadParam: opts.threadParam })),
|
|
11
12
|
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
12
13
|
});
|
|
13
14
|
if (!res.ok) {
|
|
@@ -16,9 +17,15 @@ function slackNotifier(opts) {
|
|
|
16
17
|
}
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
|
-
function
|
|
20
|
+
function threadLink(event, param) {
|
|
21
|
+
const url = new URL(event.pageUrl);
|
|
22
|
+
url.searchParams.set(param, event.threadId);
|
|
23
|
+
return url.toString();
|
|
24
|
+
}
|
|
25
|
+
function formatSlackMessage(event, opts = {}) {
|
|
20
26
|
const heading = event.type === "comment.added" ? "New reply" : "New comment";
|
|
21
27
|
const where = event.pageTitle ?? event.pageUrl;
|
|
28
|
+
const link = threadLink(event, opts.threadParam ?? DEFAULT_THREAD_PARAM);
|
|
22
29
|
const who = event.author.name ? `${event.author.name} (${event.author.email})` : event.author.email;
|
|
23
30
|
const body = event.text.trim() === "" ? "(image comment)" : event.text;
|
|
24
31
|
const quoted = body.replace(/\n/g, "\n>");
|
|
@@ -30,7 +37,7 @@ function formatSlackMessage(event) {
|
|
|
30
37
|
type: "section",
|
|
31
38
|
text: {
|
|
32
39
|
type: "mrkdwn",
|
|
33
|
-
text: `:speech_balloon: *${heading}* \xB7 <${
|
|
40
|
+
text: `:speech_balloon: *${heading}* \xB7 <${link}|${where}>`
|
|
34
41
|
}
|
|
35
42
|
},
|
|
36
43
|
{
|
|
@@ -39,7 +46,7 @@ function formatSlackMessage(event) {
|
|
|
39
46
|
},
|
|
40
47
|
{
|
|
41
48
|
type: "context",
|
|
42
|
-
elements: [{ type: "mrkdwn", text: `${who} \xB7 <${
|
|
49
|
+
elements: [{ type: "mrkdwn", text: `${who} \xB7 <${link}|Open thread>` }]
|
|
43
50
|
}
|
|
44
51
|
]
|
|
45
52
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { NotificationEvent, Notifier } from '@airnauts/comments-server'\n\nexport type SlackNotifierOptions = {\n /** Slack Incoming Webhook URL. The target channel is baked into this URL. */\n webhookUrl: string\n}\n\n/** Abort the webhook request after this many ms so a hung endpoint can't stall a write. */\nconst TIMEOUT_MS = 3000\n\nexport function slackNotifier(opts: SlackNotifierOptions): Notifier {\n return {\n name: 'slack',\n async notify(event: NotificationEvent): Promise<void> {\n const res = await fetch(opts.webhookUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(formatSlackMessage(event)),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n })\n if (!res.ok) {\n // Never include the webhook URL — it is a credential that ends up in logs.\n throw new Error(`slack webhook responded ${res.status}`)\n }\n },\n }\n}\n\nexport type SlackMessage = {\n text: string\n blocks: unknown[]\n}\n\n/** Render a NotificationEvent as a Slack Block Kit message (with a plain-text fallback). */\nexport function formatSlackMessage(event: NotificationEvent): SlackMessage {\n const heading = event.type === 'comment.added' ? 'New reply' : 'New comment'\n const where = event.pageTitle ?? event.pageUrl\n const who = event.author.name\n ? `${event.author.name} (${event.author.email})`\n : event.author.email\n // Image-only comments are allowed (empty text + an attachment), so fall back to\n // a label rather than rendering an empty quote / dangling \"by … :\".\n const body = event.text.trim() === '' ? '(image comment)' : event.text\n const quoted = body.replace(/\\n/g, '\\n>')\n\n return {\n // Plain-text fallback for notifications / accessibility.\n text: `${heading} by ${who}: ${body}`,\n blocks: [\n {\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `:speech_balloon: *${heading}* · <${
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { NotificationEvent, Notifier } from '@airnauts/comments-server'\n\nexport type SlackNotifierOptions = {\n /** Slack Incoming Webhook URL. The target channel is baked into this URL. */\n webhookUrl: string\n /**\n * Query param the widget reads to focus a thread on load (the client's\n * `threadParam`). Must match the host page's config so the deep-link opens\n * the right thread. Defaults to `comments-thread`.\n */\n threadParam?: string\n}\n\n/** Default deep-link param — mirrors the client's `DEFAULT_THREAD_PARAM`. */\nconst DEFAULT_THREAD_PARAM = 'comments-thread'\n\n/** Abort the webhook request after this many ms so a hung endpoint can't stall a write. */\nconst TIMEOUT_MS = 3000\n\nexport function slackNotifier(opts: SlackNotifierOptions): Notifier {\n return {\n name: 'slack',\n async notify(event: NotificationEvent): Promise<void> {\n const res = await fetch(opts.webhookUrl, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(formatSlackMessage(event, { threadParam: opts.threadParam })),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n })\n if (!res.ok) {\n // Never include the webhook URL — it is a credential that ends up in logs.\n throw new Error(`slack webhook responded ${res.status}`)\n }\n },\n }\n}\n\nexport type SlackMessage = {\n text: string\n blocks: unknown[]\n}\n\nexport type FormatOptions = {\n /** Override the deep-link query param (defaults to `comments-thread`). */\n threadParam?: string\n}\n\n/**\n * Build the thread deep-link Slack should point at — the same URL the widget's\n * \"Copy link\" action produces (`pageUrl?comments-thread=<id>`), so the reader\n * lands on the focused thread rather than the bare page.\n */\nfunction threadLink(event: NotificationEvent, param: string): string {\n const url = new URL(event.pageUrl)\n url.searchParams.set(param, event.threadId)\n return url.toString()\n}\n\n/** Render a NotificationEvent as a Slack Block Kit message (with a plain-text fallback). */\nexport function formatSlackMessage(\n event: NotificationEvent,\n opts: FormatOptions = {},\n): SlackMessage {\n const heading = event.type === 'comment.added' ? 'New reply' : 'New comment'\n const where = event.pageTitle ?? event.pageUrl\n const link = threadLink(event, opts.threadParam ?? DEFAULT_THREAD_PARAM)\n const who = event.author.name\n ? `${event.author.name} (${event.author.email})`\n : event.author.email\n // Image-only comments are allowed (empty text + an attachment), so fall back to\n // a label rather than rendering an empty quote / dangling \"by … :\".\n const body = event.text.trim() === '' ? '(image comment)' : event.text\n const quoted = body.replace(/\\n/g, '\\n>')\n\n return {\n // Plain-text fallback for notifications / accessibility.\n text: `${heading} by ${who}: ${body}`,\n blocks: [\n {\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: `:speech_balloon: *${heading}* · <${link}|${where}>`,\n },\n },\n {\n type: 'section',\n text: { type: 'mrkdwn', text: `>${quoted}` },\n },\n {\n type: 'context',\n elements: [{ type: 'mrkdwn', text: `${who} · <${link}|Open thread>` }],\n },\n ],\n }\n}\n"],"mappings":";AAcA,IAAM,uBAAuB;AAG7B,IAAM,aAAa;AAEZ,SAAS,cAAc,MAAsC;AAClE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,OAAO,OAAyC;AACpD,YAAM,MAAM,MAAM,MAAM,KAAK,YAAY;AAAA,QACvC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,mBAAmB,OAAO,EAAE,aAAa,KAAK,YAAY,CAAC,CAAC;AAAA,QACjF,QAAQ,YAAY,QAAQ,UAAU;AAAA,MACxC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AAEX,cAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,EAAE;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACF;AAiBA,SAAS,WAAW,OAA0B,OAAuB;AACnE,QAAM,MAAM,IAAI,IAAI,MAAM,OAAO;AACjC,MAAI,aAAa,IAAI,OAAO,MAAM,QAAQ;AAC1C,SAAO,IAAI,SAAS;AACtB;AAGO,SAAS,mBACd,OACA,OAAsB,CAAC,GACT;AACd,QAAM,UAAU,MAAM,SAAS,kBAAkB,cAAc;AAC/D,QAAM,QAAQ,MAAM,aAAa,MAAM;AACvC,QAAM,OAAO,WAAW,OAAO,KAAK,eAAe,oBAAoB;AACvE,QAAM,MAAM,MAAM,OAAO,OACrB,GAAG,MAAM,OAAO,IAAI,KAAK,MAAM,OAAO,KAAK,MAC3C,MAAM,OAAO;AAGjB,QAAM,OAAO,MAAM,KAAK,KAAK,MAAM,KAAK,oBAAoB,MAAM;AAClE,QAAM,SAAS,KAAK,QAAQ,OAAO,KAAK;AAExC,SAAO;AAAA;AAAA,IAEL,MAAM,GAAG,OAAO,OAAO,GAAG,KAAK,IAAI;AAAA,IACnC,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,MAAM,qBAAqB,OAAO,WAAQ,IAAI,IAAI,KAAK;AAAA,QACzD;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM,EAAE,MAAM,UAAU,MAAM,IAAI,MAAM,GAAG;AAAA,MAC7C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU,CAAC,EAAE,MAAM,UAAU,MAAM,GAAG,GAAG,UAAO,IAAI,gBAAgB,CAAC;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airnauts/comments-notifier-slack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Slack Incoming Webhook notifier for the Airnauts commenting tool server.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"comments",
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public"
|
|
40
40
|
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@airnauts/comments-core": "^0.5.0",
|
|
43
|
+
"@airnauts/comments-server": "^0.5.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@airnauts/comments-test-support": "0.0.0"
|
|
47
|
+
},
|
|
41
48
|
"scripts": {
|
|
42
49
|
"build": "tsup && tsc --build --force",
|
|
43
50
|
"typecheck": "tsc --build",
|
|
44
51
|
"test": "vitest run"
|
|
45
|
-
},
|
|
46
|
-
"dependencies": {
|
|
47
|
-
"@airnauts/comments-core": "workspace:^",
|
|
48
|
-
"@airnauts/comments-server": "workspace:^"
|
|
49
|
-
},
|
|
50
|
-
"devDependencies": {
|
|
51
|
-
"@airnauts/comments-test-support": "workspace:*"
|
|
52
52
|
}
|
|
53
|
-
}
|
|
53
|
+
}
|