@crewhaus/channel-adapter-slack 0.1.7 → 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/dist/index.d.ts +21 -0
- package/dist/index.js +47 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,24 @@ export type InboundEvent = {
|
|
|
48
48
|
readonly text: string;
|
|
49
49
|
readonly subtype: "app_mention" | "message";
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* A 👍/👎 reaction a user placed on one of the bot's messages. Normalised from
|
|
53
|
+
* Slack's `reaction_added` event. `messageTs` is the reacted-to message's ts;
|
|
54
|
+
* `vote` is the emoji mapped to a thumbs direction. The session-router records
|
|
55
|
+
* this as a `user_feedback` event-log line rather than driving a model turn.
|
|
56
|
+
* Only meaningful emojis (👍/👎) reach here — the bot's own status reactions
|
|
57
|
+
* (👀/✅/⚠️) never map to a vote, so they never produce feedback.
|
|
58
|
+
*/
|
|
59
|
+
export type InboundReaction = {
|
|
60
|
+
readonly idempotencyKey: string;
|
|
61
|
+
readonly workspaceId: string;
|
|
62
|
+
readonly channelId: string;
|
|
63
|
+
/** ts of the reacted-to message (typically the bot's reply). */
|
|
64
|
+
readonly messageTs: string;
|
|
65
|
+
/** the user who added the reaction. */
|
|
66
|
+
readonly userId: string;
|
|
67
|
+
readonly vote: "up" | "down";
|
|
68
|
+
};
|
|
51
69
|
/**
|
|
52
70
|
* The result of `parseInbound`. Three shapes:
|
|
53
71
|
* - { kind: "event", event } — a real inbound message to route
|
|
@@ -59,6 +77,9 @@ export type InboundEvent = {
|
|
|
59
77
|
export type ParsedInbound = {
|
|
60
78
|
readonly kind: "event";
|
|
61
79
|
readonly event: InboundEvent;
|
|
80
|
+
} | {
|
|
81
|
+
readonly kind: "reaction";
|
|
82
|
+
readonly reaction: InboundReaction;
|
|
62
83
|
} | {
|
|
63
84
|
readonly kind: "challenge";
|
|
64
85
|
readonly challenge: string;
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,19 @@ export class SlackAdapterError extends CrewhausError {
|
|
|
31
31
|
super("channel", message, cause);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
/** Map a Slack reaction name to a thumbs vote, or undefined when the emoji is
|
|
35
|
+
* not a 👍/👎 (which then produces no feedback). Slack delivers a skin-toned
|
|
36
|
+
* reaction as `+1::skin-tone-4`, so the base name is taken before matching. */
|
|
37
|
+
function reactionToVote(name) {
|
|
38
|
+
if (typeof name !== "string")
|
|
39
|
+
return undefined;
|
|
40
|
+
const base = name.replace(/^:|:$/g, "").split("::")[0] ?? "";
|
|
41
|
+
if (base === "+1" || base === "thumbsup")
|
|
42
|
+
return "up";
|
|
43
|
+
if (base === "-1" || base === "thumbsdown")
|
|
44
|
+
return "down";
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
34
47
|
const DEFAULT_API_BASE_URL = "https://slack.com/api";
|
|
35
48
|
export function createSlackAdapter(config, opts = {}) {
|
|
36
49
|
const apiBaseUrl = opts.apiBaseUrl ?? process.env["SLACK_API_BASE_URL"] ?? DEFAULT_API_BASE_URL;
|
|
@@ -63,6 +76,40 @@ export function createSlackAdapter(config, opts = {}) {
|
|
|
63
76
|
return { kind: "skip" };
|
|
64
77
|
const e = ev;
|
|
65
78
|
const evType = e["type"];
|
|
79
|
+
// Inbound 👍/👎 reaction on one of the bot's messages → feedback. Only
|
|
80
|
+
// reaction_added produces feedback (a removed reaction does not retract
|
|
81
|
+
// an append-only event-log line). Non-vote emojis (incl. the bot's own
|
|
82
|
+
// 👀/✅/⚠️ status reactions) map to no vote and are skipped.
|
|
83
|
+
if (evType === "reaction_added") {
|
|
84
|
+
const vote = reactionToVote(e["reaction"]);
|
|
85
|
+
if (vote === undefined)
|
|
86
|
+
return { kind: "skip" };
|
|
87
|
+
const item = e["item"];
|
|
88
|
+
if (typeof item !== "object" || item === null)
|
|
89
|
+
return { kind: "skip" };
|
|
90
|
+
const it = item;
|
|
91
|
+
if (it["type"] !== "message")
|
|
92
|
+
return { kind: "skip" };
|
|
93
|
+
const rChannelId = typeof it["channel"] === "string" ? it["channel"] : undefined;
|
|
94
|
+
const rMessageTs = typeof it["ts"] === "string" ? it["ts"] : undefined;
|
|
95
|
+
const rUserId = typeof e["user"] === "string" ? e["user"] : undefined;
|
|
96
|
+
const rIdemp = typeof p["event_id"] === "string" ? p["event_id"] : undefined;
|
|
97
|
+
const rWorkspace = typeof p["team_id"] === "string" ? p["team_id"] : undefined;
|
|
98
|
+
if (!rChannelId || !rMessageTs || !rUserId || !rIdemp || !rWorkspace) {
|
|
99
|
+
return { kind: "skip" };
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
kind: "reaction",
|
|
103
|
+
reaction: {
|
|
104
|
+
idempotencyKey: rIdemp,
|
|
105
|
+
workspaceId: rWorkspace,
|
|
106
|
+
channelId: rChannelId,
|
|
107
|
+
messageTs: rMessageTs,
|
|
108
|
+
userId: rUserId,
|
|
109
|
+
vote,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
66
113
|
if (evType !== "app_mention" && evType !== "message")
|
|
67
114
|
return { kind: "skip" };
|
|
68
115
|
// Skip self/bot loops. `bot_id` is present on Slack's bot-authored
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewhaus/channel-adapter-slack",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Slack channel adapter: webhook signature verification, event parsing, reply-to-thread (Section 12)",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test": "bun test src"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@crewhaus/errors": "0.
|
|
18
|
+
"@crewhaus/errors": "0.2.0"
|
|
19
19
|
},
|
|
20
20
|
"license": "Apache-2.0",
|
|
21
21
|
"author": {
|