@effect-uai/slack 0.14.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/LICENSE +21 -0
- package/README.md +51 -0
- package/dist/Slack.d.mts +43 -0
- package/dist/Slack.d.mts.map +1 -0
- package/dist/Slack.mjs +215 -0
- package/dist/Slack.mjs.map +1 -0
- package/dist/api-BBgliKo7.d.mts +51 -0
- package/dist/api-BBgliKo7.d.mts.map +1 -0
- package/dist/events-Cj5RRjK1.d.mts +317 -0
- package/dist/events-Cj5RRjK1.d.mts.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/internal/api.d.mts +2 -0
- package/dist/internal/api.mjs +79 -0
- package/dist/internal/api.mjs.map +1 -0
- package/dist/internal/events.d.mts +2 -0
- package/dist/internal/events.mjs +184 -0
- package/dist/internal/events.mjs.map +1 -0
- package/dist/internal/events.test.d.mts +1 -0
- package/dist/internal/events.test.mjs +174 -0
- package/dist/internal/events.test.mjs.map +1 -0
- package/dist/internal/socket.d.mts +2 -0
- package/dist/internal/socket.mjs +109 -0
- package/dist/internal/socket.mjs.map +1 -0
- package/dist/internal/socket.test.d.mts +1 -0
- package/dist/internal/socket.test.mjs +25 -0
- package/dist/internal/socket.test.mjs.map +1 -0
- package/dist/magic-string.es-CUAnjKYC.mjs +1014 -0
- package/dist/magic-string.es-CUAnjKYC.mjs.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/dist/socket-DJgdt_ka.d.mts +54 -0
- package/dist/socket-DJgdt_ka.d.mts.map +1 -0
- package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs +13658 -0
- package/dist/test.DNmyFkvJ-N3QqMRwZ.mjs.map +1 -0
- package/package.json +59 -0
- package/src/Slack.ts +419 -0
- package/src/index.ts +1 -0
- package/src/internal/api.ts +171 -0
- package/src/internal/events.test.ts +121 -0
- package/src/internal/events.ts +330 -0
- package/src/internal/socket.test.ts +25 -0
- package/src/internal/socket.ts +238 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import { InboundEvent } from "@effect-uai/core/Messenger";
|
|
3
|
+
//#region src/internal/events.d.ts
|
|
4
|
+
declare const AppMention: Schema.Struct<{
|
|
5
|
+
readonly type: Schema.Literal<"app_mention">;
|
|
6
|
+
readonly channel: Schema.String;
|
|
7
|
+
readonly user: Schema.optional<Schema.String>;
|
|
8
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
9
|
+
readonly text: Schema.optional<Schema.String>;
|
|
10
|
+
readonly ts: Schema.String;
|
|
11
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
12
|
+
}>;
|
|
13
|
+
type AppMention = typeof AppMention.Type;
|
|
14
|
+
declare const Message: Schema.Struct<{
|
|
15
|
+
readonly type: Schema.Literal<"message">;
|
|
16
|
+
readonly channel: Schema.String;
|
|
17
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
18
|
+
readonly channel_type: Schema.optional<Schema.String>;
|
|
19
|
+
readonly user: Schema.optional<Schema.String>;
|
|
20
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
21
|
+
readonly subtype: Schema.optional<Schema.String>;
|
|
22
|
+
readonly text: Schema.optional<Schema.String>;
|
|
23
|
+
readonly ts: Schema.String;
|
|
24
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
25
|
+
}>;
|
|
26
|
+
type Message = typeof Message.Type;
|
|
27
|
+
declare const ReactionAdded: Schema.Struct<{
|
|
28
|
+
readonly type: Schema.Literal<"reaction_added">;
|
|
29
|
+
readonly user: Schema.String;
|
|
30
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
31
|
+
readonly reaction: Schema.String;
|
|
32
|
+
readonly item: Schema.Struct<{
|
|
33
|
+
readonly channel: Schema.String;
|
|
34
|
+
readonly ts: Schema.String;
|
|
35
|
+
}>;
|
|
36
|
+
}>;
|
|
37
|
+
type ReactionAdded = typeof ReactionAdded.Type;
|
|
38
|
+
/** The events this adapter subscribes to. Anything else fails to decode and is dropped. */
|
|
39
|
+
declare const Event: Schema.Union<readonly [Schema.Struct<{
|
|
40
|
+
readonly type: Schema.Literal<"app_mention">;
|
|
41
|
+
readonly channel: Schema.String;
|
|
42
|
+
readonly user: Schema.optional<Schema.String>;
|
|
43
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
44
|
+
readonly text: Schema.optional<Schema.String>;
|
|
45
|
+
readonly ts: Schema.String;
|
|
46
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
47
|
+
}>, Schema.Struct<{
|
|
48
|
+
readonly type: Schema.Literal<"message">;
|
|
49
|
+
readonly channel: Schema.String;
|
|
50
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
51
|
+
readonly channel_type: Schema.optional<Schema.String>;
|
|
52
|
+
readonly user: Schema.optional<Schema.String>;
|
|
53
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
54
|
+
readonly subtype: Schema.optional<Schema.String>;
|
|
55
|
+
readonly text: Schema.optional<Schema.String>;
|
|
56
|
+
readonly ts: Schema.String;
|
|
57
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
58
|
+
}>, Schema.Struct<{
|
|
59
|
+
readonly type: Schema.Literal<"reaction_added">;
|
|
60
|
+
readonly user: Schema.String;
|
|
61
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
62
|
+
readonly reaction: Schema.String;
|
|
63
|
+
readonly item: Schema.Struct<{
|
|
64
|
+
readonly channel: Schema.String;
|
|
65
|
+
readonly ts: Schema.String;
|
|
66
|
+
}>;
|
|
67
|
+
}>]>;
|
|
68
|
+
type Event = typeof Event.Type;
|
|
69
|
+
declare const EventsApi: Schema.Struct<{
|
|
70
|
+
readonly type: Schema.Literal<"events_api">;
|
|
71
|
+
readonly envelope_id: Schema.String;
|
|
72
|
+
readonly payload: Schema.Struct<{
|
|
73
|
+
/** Stable across a redelivery, which is what the socket dedupes on. */
|
|
74
|
+
readonly event_id: Schema.optional<Schema.String>;
|
|
75
|
+
readonly event: Schema.Union<readonly [Schema.Struct<{
|
|
76
|
+
readonly type: Schema.Literal<"app_mention">;
|
|
77
|
+
readonly channel: Schema.String;
|
|
78
|
+
readonly user: Schema.optional<Schema.String>;
|
|
79
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
80
|
+
readonly text: Schema.optional<Schema.String>;
|
|
81
|
+
readonly ts: Schema.String;
|
|
82
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
83
|
+
}>, Schema.Struct<{
|
|
84
|
+
readonly type: Schema.Literal<"message">;
|
|
85
|
+
readonly channel: Schema.String;
|
|
86
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
87
|
+
readonly channel_type: Schema.optional<Schema.String>;
|
|
88
|
+
readonly user: Schema.optional<Schema.String>;
|
|
89
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
90
|
+
readonly subtype: Schema.optional<Schema.String>;
|
|
91
|
+
readonly text: Schema.optional<Schema.String>;
|
|
92
|
+
readonly ts: Schema.String;
|
|
93
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
94
|
+
}>, Schema.Struct<{
|
|
95
|
+
readonly type: Schema.Literal<"reaction_added">;
|
|
96
|
+
readonly user: Schema.String;
|
|
97
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
98
|
+
readonly reaction: Schema.String;
|
|
99
|
+
readonly item: Schema.Struct<{
|
|
100
|
+
readonly channel: Schema.String;
|
|
101
|
+
readonly ts: Schema.String;
|
|
102
|
+
}>;
|
|
103
|
+
}>]>;
|
|
104
|
+
}>;
|
|
105
|
+
}>;
|
|
106
|
+
type EventsApi = typeof EventsApi.Type;
|
|
107
|
+
declare const BlockAction: Schema.Struct<{
|
|
108
|
+
readonly action_id: Schema.optional<Schema.String>;
|
|
109
|
+
readonly value: Schema.optional<Schema.String>;
|
|
110
|
+
}>;
|
|
111
|
+
type BlockAction = typeof BlockAction.Type;
|
|
112
|
+
declare const Interactive: Schema.Struct<{
|
|
113
|
+
readonly type: Schema.Literal<"interactive">;
|
|
114
|
+
readonly envelope_id: Schema.String;
|
|
115
|
+
readonly payload: Schema.Struct<{
|
|
116
|
+
/** `block_actions`, `view_submission`, `shortcut`; only the first maps. */
|
|
117
|
+
readonly type: Schema.String;
|
|
118
|
+
readonly user: Schema.Struct<{
|
|
119
|
+
readonly id: Schema.String;
|
|
120
|
+
}>;
|
|
121
|
+
readonly container: Schema.optional<Schema.Struct<{
|
|
122
|
+
readonly channel_id: Schema.optional<Schema.String>;
|
|
123
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
124
|
+
}>>;
|
|
125
|
+
readonly channel: Schema.optional<Schema.Struct<{
|
|
126
|
+
readonly id: Schema.String;
|
|
127
|
+
}>>;
|
|
128
|
+
readonly actions: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
129
|
+
readonly action_id: Schema.optional<Schema.String>;
|
|
130
|
+
readonly value: Schema.optional<Schema.String>;
|
|
131
|
+
}>>>;
|
|
132
|
+
}>;
|
|
133
|
+
}>;
|
|
134
|
+
type Interactive = typeof Interactive.Type;
|
|
135
|
+
declare const SlashCommand: Schema.Struct<{
|
|
136
|
+
readonly type: Schema.Literal<"slash_commands">;
|
|
137
|
+
readonly envelope_id: Schema.String;
|
|
138
|
+
readonly payload: Schema.Struct<{
|
|
139
|
+
readonly channel_id: Schema.String;
|
|
140
|
+
readonly user_id: Schema.String;
|
|
141
|
+
readonly command: Schema.String;
|
|
142
|
+
readonly text: Schema.optional<Schema.String>;
|
|
143
|
+
}>;
|
|
144
|
+
}>;
|
|
145
|
+
type SlashCommand = typeof SlashCommand.Type;
|
|
146
|
+
/** A frame carrying work, and so an `envelope_id` that has to be acknowledged. */
|
|
147
|
+
declare const Envelope: Schema.Union<readonly [Schema.Struct<{
|
|
148
|
+
readonly type: Schema.Literal<"events_api">;
|
|
149
|
+
readonly envelope_id: Schema.String;
|
|
150
|
+
readonly payload: Schema.Struct<{
|
|
151
|
+
/** Stable across a redelivery, which is what the socket dedupes on. */
|
|
152
|
+
readonly event_id: Schema.optional<Schema.String>;
|
|
153
|
+
readonly event: Schema.Union<readonly [Schema.Struct<{
|
|
154
|
+
readonly type: Schema.Literal<"app_mention">;
|
|
155
|
+
readonly channel: Schema.String;
|
|
156
|
+
readonly user: Schema.optional<Schema.String>;
|
|
157
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
158
|
+
readonly text: Schema.optional<Schema.String>;
|
|
159
|
+
readonly ts: Schema.String;
|
|
160
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
161
|
+
}>, Schema.Struct<{
|
|
162
|
+
readonly type: Schema.Literal<"message">;
|
|
163
|
+
readonly channel: Schema.String;
|
|
164
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
165
|
+
readonly channel_type: Schema.optional<Schema.String>;
|
|
166
|
+
readonly user: Schema.optional<Schema.String>;
|
|
167
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
168
|
+
readonly subtype: Schema.optional<Schema.String>;
|
|
169
|
+
readonly text: Schema.optional<Schema.String>;
|
|
170
|
+
readonly ts: Schema.String;
|
|
171
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
172
|
+
}>, Schema.Struct<{
|
|
173
|
+
readonly type: Schema.Literal<"reaction_added">;
|
|
174
|
+
readonly user: Schema.String;
|
|
175
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
176
|
+
readonly reaction: Schema.String;
|
|
177
|
+
readonly item: Schema.Struct<{
|
|
178
|
+
readonly channel: Schema.String;
|
|
179
|
+
readonly ts: Schema.String;
|
|
180
|
+
}>;
|
|
181
|
+
}>]>;
|
|
182
|
+
}>;
|
|
183
|
+
}>, Schema.Struct<{
|
|
184
|
+
readonly type: Schema.Literal<"interactive">;
|
|
185
|
+
readonly envelope_id: Schema.String;
|
|
186
|
+
readonly payload: Schema.Struct<{
|
|
187
|
+
/** `block_actions`, `view_submission`, `shortcut`; only the first maps. */
|
|
188
|
+
readonly type: Schema.String;
|
|
189
|
+
readonly user: Schema.Struct<{
|
|
190
|
+
readonly id: Schema.String;
|
|
191
|
+
}>;
|
|
192
|
+
readonly container: Schema.optional<Schema.Struct<{
|
|
193
|
+
readonly channel_id: Schema.optional<Schema.String>;
|
|
194
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
195
|
+
}>>;
|
|
196
|
+
readonly channel: Schema.optional<Schema.Struct<{
|
|
197
|
+
readonly id: Schema.String;
|
|
198
|
+
}>>;
|
|
199
|
+
readonly actions: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
200
|
+
readonly action_id: Schema.optional<Schema.String>;
|
|
201
|
+
readonly value: Schema.optional<Schema.String>;
|
|
202
|
+
}>>>;
|
|
203
|
+
}>;
|
|
204
|
+
}>, Schema.Struct<{
|
|
205
|
+
readonly type: Schema.Literal<"slash_commands">;
|
|
206
|
+
readonly envelope_id: Schema.String;
|
|
207
|
+
readonly payload: Schema.Struct<{
|
|
208
|
+
readonly channel_id: Schema.String;
|
|
209
|
+
readonly user_id: Schema.String;
|
|
210
|
+
readonly command: Schema.String;
|
|
211
|
+
readonly text: Schema.optional<Schema.String>;
|
|
212
|
+
}>;
|
|
213
|
+
}>]>;
|
|
214
|
+
type Envelope = typeof Envelope.Type;
|
|
215
|
+
declare const Hello: Schema.Struct<{
|
|
216
|
+
readonly type: Schema.Literal<"hello">;
|
|
217
|
+
}>;
|
|
218
|
+
declare const Disconnect: Schema.Struct<{
|
|
219
|
+
readonly type: Schema.Literal<"disconnect">;
|
|
220
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
221
|
+
}>;
|
|
222
|
+
/** Everything Socket Mode sends down the wire. */
|
|
223
|
+
declare const Frame: Schema.Union<readonly [Schema.Struct<{
|
|
224
|
+
readonly type: Schema.Literal<"hello">;
|
|
225
|
+
}>, Schema.Struct<{
|
|
226
|
+
readonly type: Schema.Literal<"disconnect">;
|
|
227
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
228
|
+
}>, Schema.Struct<{
|
|
229
|
+
readonly type: Schema.Literal<"events_api">;
|
|
230
|
+
readonly envelope_id: Schema.String;
|
|
231
|
+
readonly payload: Schema.Struct<{
|
|
232
|
+
/** Stable across a redelivery, which is what the socket dedupes on. */
|
|
233
|
+
readonly event_id: Schema.optional<Schema.String>;
|
|
234
|
+
readonly event: Schema.Union<readonly [Schema.Struct<{
|
|
235
|
+
readonly type: Schema.Literal<"app_mention">;
|
|
236
|
+
readonly channel: Schema.String;
|
|
237
|
+
readonly user: Schema.optional<Schema.String>;
|
|
238
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
239
|
+
readonly text: Schema.optional<Schema.String>;
|
|
240
|
+
readonly ts: Schema.String;
|
|
241
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
242
|
+
}>, Schema.Struct<{
|
|
243
|
+
readonly type: Schema.Literal<"message">;
|
|
244
|
+
readonly channel: Schema.String;
|
|
245
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
246
|
+
readonly channel_type: Schema.optional<Schema.String>;
|
|
247
|
+
readonly user: Schema.optional<Schema.String>;
|
|
248
|
+
readonly bot_id: Schema.optional<Schema.String>;
|
|
249
|
+
readonly subtype: Schema.optional<Schema.String>;
|
|
250
|
+
readonly text: Schema.optional<Schema.String>;
|
|
251
|
+
readonly ts: Schema.String;
|
|
252
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
253
|
+
}>, Schema.Struct<{
|
|
254
|
+
readonly type: Schema.Literal<"reaction_added">;
|
|
255
|
+
readonly user: Schema.String;
|
|
256
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
257
|
+
readonly reaction: Schema.String;
|
|
258
|
+
readonly item: Schema.Struct<{
|
|
259
|
+
readonly channel: Schema.String;
|
|
260
|
+
readonly ts: Schema.String;
|
|
261
|
+
}>;
|
|
262
|
+
}>]>;
|
|
263
|
+
}>;
|
|
264
|
+
}>, Schema.Struct<{
|
|
265
|
+
readonly type: Schema.Literal<"interactive">;
|
|
266
|
+
readonly envelope_id: Schema.String;
|
|
267
|
+
readonly payload: Schema.Struct<{
|
|
268
|
+
/** `block_actions`, `view_submission`, `shortcut`; only the first maps. */
|
|
269
|
+
readonly type: Schema.String;
|
|
270
|
+
readonly user: Schema.Struct<{
|
|
271
|
+
readonly id: Schema.String;
|
|
272
|
+
}>;
|
|
273
|
+
readonly container: Schema.optional<Schema.Struct<{
|
|
274
|
+
readonly channel_id: Schema.optional<Schema.String>;
|
|
275
|
+
readonly thread_ts: Schema.optional<Schema.String>;
|
|
276
|
+
}>>;
|
|
277
|
+
readonly channel: Schema.optional<Schema.Struct<{
|
|
278
|
+
readonly id: Schema.String;
|
|
279
|
+
}>>;
|
|
280
|
+
readonly actions: Schema.optional<Schema.$Array<Schema.Struct<{
|
|
281
|
+
readonly action_id: Schema.optional<Schema.String>;
|
|
282
|
+
readonly value: Schema.optional<Schema.String>;
|
|
283
|
+
}>>>;
|
|
284
|
+
}>;
|
|
285
|
+
}>, Schema.Struct<{
|
|
286
|
+
readonly type: Schema.Literal<"slash_commands">;
|
|
287
|
+
readonly envelope_id: Schema.String;
|
|
288
|
+
readonly payload: Schema.Struct<{
|
|
289
|
+
readonly channel_id: Schema.String;
|
|
290
|
+
readonly user_id: Schema.String;
|
|
291
|
+
readonly command: Schema.String;
|
|
292
|
+
readonly text: Schema.optional<Schema.String>;
|
|
293
|
+
}>;
|
|
294
|
+
}>]>;
|
|
295
|
+
type Frame = typeof Frame.Type;
|
|
296
|
+
/** From `auth.test` at layer build: the identity behind `addressed`. */
|
|
297
|
+
type BotIdentity = {
|
|
298
|
+
readonly userId: string;
|
|
299
|
+
readonly botId: string;
|
|
300
|
+
readonly teamId: string;
|
|
301
|
+
};
|
|
302
|
+
/** Where a top-level mention is answered: in a thread under it, or in the channel. */
|
|
303
|
+
type ReplyIn = "thread" | "channel";
|
|
304
|
+
type Options = {
|
|
305
|
+
readonly bot: BotIdentity;
|
|
306
|
+
readonly replyIn: ReplyIn;
|
|
307
|
+
};
|
|
308
|
+
/** The id Slack repeats when it redelivers. Only an `events_api` frame carries one. */
|
|
309
|
+
declare const eventId: (envelope: Envelope) => Option.Option<string>;
|
|
310
|
+
/**
|
|
311
|
+
* Everything one envelope says to the agent. `raw` is the frame as it came off
|
|
312
|
+
* the wire, since decoding keeps only the fields above.
|
|
313
|
+
*/
|
|
314
|
+
declare const toEvents: (options: Options) => (envelope: Envelope, raw: unknown) => ReadonlyArray<InboundEvent>;
|
|
315
|
+
//#endregion
|
|
316
|
+
export { toEvents as _, Envelope as a, Frame as c, Message as d, Options as f, eventId as g, SlashCommand as h, Disconnect as i, Hello as l, ReplyIn as m, BlockAction as n, Event as o, ReactionAdded as p, BotIdentity as r, EventsApi as s, AppMention as t, Interactive as u };
|
|
317
|
+
//# sourceMappingURL=events-Cj5RRjK1.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-Cj5RRjK1.d.mts","names":[],"sources":["../src/internal/events.ts"],"mappings":";;;cAca,YAAU,OAAA;;;;;;;;;KASX,oBAAoB,WAAW;cAE9B,SAAO,OAAA;;;;;;;;;;;;KAYR,iBAAiB,QAAQ;cAExB,eAAa,OAAA;;;;;;;;;;KAOd,uBAAuB,cAAc;;cAGpC,OAAK,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KACN,eAAe,MAAM;cAEpB,WAAS,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KASV,mBAAmB,UAAU;cAE5B,aAAW,OAAA;;;;KAIZ,qBAAqB,YAAY;cAEhC,aAAW,OAAA;;;;;;;;;;;;;;;;;;;;;;KAiBZ,qBAAqB,YAAY;cAEhC,cAAY,OAAA;;;;;;;;;;KAUb,sBAAsB,aAAa;;cAGlC,UAAQ,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KACT,kBAAkB,SAAS;cAE1B,OAAK,OAAA;iBAAL,OAAA;;cAEA,YAAU,OAAA;;;;;cAMV,OAAK,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KACN,eAAe,MAAM;;KAGrB;WACD;WACA;WACA;;;KAIC;KAEA;WACD,KAAK;WACL,SAAS;;;cAIP,UAAO,UAAc,aAAW,OAAO;;;;;cA4LvC,WAAQ,SACT,aAAO,UACN,UAAQ,iBAAiB,cAAc"}
|
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as Upload, c as decoded, d as upload, i as Params, l as provider, n as ApiFailure, o as call, r as Config, s as callAsApp, t as ApiError, u as query } from "../api-BBgliKo7.mjs";
|
|
2
|
+
export { ApiError, ApiFailure, Config, Params, Upload, call, callAsApp, decoded, provider, query, upload };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Data, Duration, Effect, Option, Redacted, Schema } from "effect";
|
|
2
|
+
import { Headers, HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
3
|
+
import * as MessengerError from "@effect-uai/core/MessengerError";
|
|
4
|
+
//#region src/internal/api.ts
|
|
5
|
+
const provider = "slack";
|
|
6
|
+
/**
|
|
7
|
+
* A Web API call that came back `ok: false` or never came back at all.
|
|
8
|
+
* `error` is Slack's own error string (`channel_not_found`, `invalid_auth`);
|
|
9
|
+
* each verb maps this onto its own `MessengerError`.
|
|
10
|
+
*/
|
|
11
|
+
var ApiFailure = class extends Data.TaggedError("SlackApiFailure") {};
|
|
12
|
+
const Envelope = Schema.Struct({
|
|
13
|
+
ok: Schema.Boolean,
|
|
14
|
+
error: Schema.optional(Schema.String)
|
|
15
|
+
});
|
|
16
|
+
const endpoint = (cfg, method) => `${cfg.baseUrl ?? "https://slack.com/api"}/${method}`;
|
|
17
|
+
const authorized = (request, token) => HttpClientRequest.setHeader(request, "Authorization", `Bearer ${Redacted.value(token)}`);
|
|
18
|
+
const transportFailure = (method) => (raw) => new ApiFailure({
|
|
19
|
+
method,
|
|
20
|
+
error: "transport failure",
|
|
21
|
+
raw
|
|
22
|
+
});
|
|
23
|
+
const retryAfter = (headers) => Headers.get(headers, "retry-after").pipe(Option.map(Number), Option.filter(Number.isFinite), Option.getOrElse(() => 60), Duration.seconds);
|
|
24
|
+
const send = (method, request) => Effect.gen(function* () {
|
|
25
|
+
const response = yield* (yield* HttpClient.HttpClient).execute(request).pipe(Effect.mapError(transportFailure(method)));
|
|
26
|
+
if (response.status === 429) return yield* new MessengerError.MessengerRateLimited({
|
|
27
|
+
provider,
|
|
28
|
+
retryAfter: retryAfter(response.headers)
|
|
29
|
+
});
|
|
30
|
+
const body = yield* response.json.pipe(Effect.mapError(transportFailure(method)));
|
|
31
|
+
const envelope = yield* Schema.decodeUnknownEffect(Envelope)(body).pipe(Effect.mapError(() => new ApiFailure({
|
|
32
|
+
method,
|
|
33
|
+
error: "malformed envelope",
|
|
34
|
+
raw: body
|
|
35
|
+
})));
|
|
36
|
+
return envelope.ok ? body : yield* new ApiFailure({
|
|
37
|
+
method,
|
|
38
|
+
status: response.status,
|
|
39
|
+
error: envelope.error ?? "unknown error",
|
|
40
|
+
raw: body
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
const post = (cfg, method, params) => {
|
|
44
|
+
const request = HttpClientRequest.post(endpoint(cfg, method));
|
|
45
|
+
return params === void 0 ? request : HttpClientRequest.bodyJsonUnsafe(request, params);
|
|
46
|
+
};
|
|
47
|
+
/** One JSON method call with the bot token. The body comes back undecoded. */
|
|
48
|
+
const call = (cfg) => (method, params) => send(method, authorized(post(cfg, method, params), cfg.botToken));
|
|
49
|
+
/** `apps.connections.open`, the only call the app token is for. */
|
|
50
|
+
const callAsApp = (cfg) => (method) => send(method, authorized(post(cfg, method, void 0), cfg.appToken));
|
|
51
|
+
/** One method called with query parameters, for the few that take no JSON body. */
|
|
52
|
+
const query = (cfg) => (method, params) => send(method, authorized(HttpClientRequest.get(endpoint(cfg, method)).pipe(HttpClientRequest.setUrlParams(Object.entries(params).map(([name, value]) => [name, String(value)]))), cfg.botToken));
|
|
53
|
+
const uploadRejected = (response) => new ApiFailure({
|
|
54
|
+
method: "files.upload",
|
|
55
|
+
status: response.status,
|
|
56
|
+
error: `upload rejected with HTTP ${response.status}`,
|
|
57
|
+
raw: response.status
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* Raw bytes to the URL `files.getUploadURLExternal` handed out. Not a Web API
|
|
61
|
+
* call: no token, no `ok` envelope, and the response body is plain text.
|
|
62
|
+
*/
|
|
63
|
+
const upload = (url, file) => Effect.gen(function* () {
|
|
64
|
+
const response = yield* (yield* HttpClient.HttpClient).execute(HttpClientRequest.post(url).pipe(HttpClientRequest.bodyUint8Array(file.bytes, file.mimeType))).pipe(Effect.mapError(transportFailure("files.upload")));
|
|
65
|
+
if (response.status >= 400) return yield* uploadRejected(response);
|
|
66
|
+
});
|
|
67
|
+
/** Decode a response body; a shape mismatch is a malformed response, not a defect. */
|
|
68
|
+
const decoded = (method, schema) => (result) => Effect.gen(function* () {
|
|
69
|
+
const raw = yield* result;
|
|
70
|
+
return yield* Schema.decodeUnknownEffect(schema)(raw).pipe(Effect.mapError(() => new ApiFailure({
|
|
71
|
+
method,
|
|
72
|
+
error: "malformed response",
|
|
73
|
+
raw
|
|
74
|
+
})));
|
|
75
|
+
});
|
|
76
|
+
//#endregion
|
|
77
|
+
export { ApiFailure, call, callAsApp, decoded, provider, query, upload };
|
|
78
|
+
|
|
79
|
+
//# sourceMappingURL=api.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.mjs","names":[],"sources":["../../src/internal/api.ts"],"sourcesContent":["import { Data, Duration, Effect, Option, Redacted, Schema } from \"effect\"\nimport {\n Headers,\n HttpClient,\n HttpClientRequest,\n type HttpClientResponse,\n} from \"effect/unstable/http\"\nimport * as MessengerError from \"@effect-uai/core/MessengerError\"\n\nexport const provider = \"slack\"\n\nexport type Config = {\n /** `xoxb-`, for every Web API call. */\n readonly botToken: Redacted.Redacted\n /** `xapp-`, for `apps.connections.open` and nothing else. */\n readonly appToken: Redacted.Redacted\n /** Defaults to `https://slack.com/api`. */\n readonly baseUrl?: string\n}\n\n/**\n * A Web API call that came back `ok: false` or never came back at all.\n * `error` is Slack's own error string (`channel_not_found`, `invalid_auth`);\n * each verb maps this onto its own `MessengerError`.\n */\nexport class ApiFailure extends Data.TaggedError(\"SlackApiFailure\")<{\n readonly method: string\n readonly status?: number\n readonly error: string\n readonly raw: unknown\n}> {}\n\nexport type ApiError = ApiFailure | MessengerError.MessengerRateLimited\n\n// Slack answers every method with `ok`, then the result fields beside it.\nconst Envelope = Schema.Struct({\n ok: Schema.Boolean,\n error: Schema.optional(Schema.String),\n})\n\nexport type Params = Readonly<Record<string, unknown>>\n\n/** The bytes behind a `files.getUploadURLExternal` URL. */\nexport type Upload = {\n readonly bytes: Uint8Array\n readonly filename: string\n readonly mimeType: string\n}\n\nconst endpoint = (cfg: Config, method: string): string =>\n `${cfg.baseUrl ?? \"https://slack.com/api\"}/${method}`\n\nconst authorized = (\n request: HttpClientRequest.HttpClientRequest,\n token: Redacted.Redacted,\n): HttpClientRequest.HttpClientRequest =>\n HttpClientRequest.setHeader(request, \"Authorization\", `Bearer ${Redacted.value(token)}`)\n\nconst transportFailure = (method: string) => (raw: unknown) =>\n new ApiFailure({ method, error: \"transport failure\", raw })\n\n// A 429 carries the wait in a header rather than the body, and Slack's own\n// floor for a missing one is a minute.\nconst retryAfter = (headers: Headers.Headers): Duration.Duration =>\n Headers.get(headers, \"retry-after\").pipe(\n Option.map(Number),\n Option.filter(Number.isFinite),\n Option.getOrElse(() => 60),\n Duration.seconds,\n )\n\nconst send = (\n method: string,\n request: HttpClientRequest.HttpClientRequest,\n): Effect.Effect<unknown, ApiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client.execute(request).pipe(Effect.mapError(transportFailure(method)))\n if (response.status === 429) {\n return yield* new MessengerError.MessengerRateLimited({\n provider,\n retryAfter: retryAfter(response.headers),\n })\n }\n const body = yield* response.json.pipe(Effect.mapError(transportFailure(method)))\n const envelope = yield* Schema.decodeUnknownEffect(Envelope)(body).pipe(\n Effect.mapError(() => new ApiFailure({ method, error: \"malformed envelope\", raw: body })),\n )\n // The result fields sit beside `ok`, so the whole body is the result.\n return envelope.ok\n ? body\n : yield* new ApiFailure({\n method,\n status: response.status,\n error: envelope.error ?? \"unknown error\",\n raw: body,\n })\n })\n\nconst post = (cfg: Config, method: string, params: Params | undefined) => {\n const request = HttpClientRequest.post(endpoint(cfg, method))\n // A method with no arguments takes no body at all; Slack rejects an empty one.\n return params === undefined ? request : HttpClientRequest.bodyJsonUnsafe(request, params)\n}\n\n/** One JSON method call with the bot token. The body comes back undecoded. */\nexport const call =\n (cfg: Config) =>\n (method: string, params?: Params): Effect.Effect<unknown, ApiError, HttpClient.HttpClient> =>\n send(method, authorized(post(cfg, method, params), cfg.botToken))\n\n/** `apps.connections.open`, the only call the app token is for. */\nexport const callAsApp =\n (cfg: Config) =>\n (method: string): Effect.Effect<unknown, ApiError, HttpClient.HttpClient> =>\n send(method, authorized(post(cfg, method, undefined), cfg.appToken))\n\n/** One method called with query parameters, for the few that take no JSON body. */\nexport const query =\n (cfg: Config) =>\n (method: string, params: Params): Effect.Effect<unknown, ApiError, HttpClient.HttpClient> =>\n send(\n method,\n authorized(\n HttpClientRequest.get(endpoint(cfg, method)).pipe(\n HttpClientRequest.setUrlParams(\n Object.entries(params).map(([name, value]) => [name, String(value)] as const),\n ),\n ),\n cfg.botToken,\n ),\n )\n\nconst uploadRejected = (response: HttpClientResponse.HttpClientResponse) =>\n new ApiFailure({\n method: \"files.upload\",\n status: response.status,\n error: `upload rejected with HTTP ${response.status}`,\n raw: response.status,\n })\n\n/**\n * Raw bytes to the URL `files.getUploadURLExternal` handed out. Not a Web API\n * call: no token, no `ok` envelope, and the response body is plain text.\n */\nexport const upload = (\n url: string,\n file: Upload,\n): Effect.Effect<void, ApiFailure, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const response = yield* client\n .execute(\n HttpClientRequest.post(url).pipe(\n HttpClientRequest.bodyUint8Array(file.bytes as Uint8Array<ArrayBuffer>, file.mimeType),\n ),\n )\n .pipe(Effect.mapError(transportFailure(\"files.upload\")))\n if (response.status >= 400) return yield* uploadRejected(response)\n })\n\n/** Decode a response body; a shape mismatch is a malformed response, not a defect. */\nexport const decoded =\n <A>(method: string, schema: Schema.Decoder<A>) =>\n <E, R>(result: Effect.Effect<unknown, E, R>): Effect.Effect<A, E | ApiFailure, R> =>\n Effect.gen(function* () {\n const raw = yield* result\n return yield* Schema.decodeUnknownEffect(schema)(raw).pipe(\n Effect.mapError(() => new ApiFailure({ method, error: \"malformed response\", raw })),\n )\n })\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as toEvents, a as Envelope, c as Frame, d as Message, f as Options, g as eventId, h as SlashCommand, i as Disconnect, l as Hello, m as ReplyIn, n as BlockAction, o as Event, p as ReactionAdded, r as BotIdentity, s as EventsApi, t as AppMention, u as Interactive } from "../events-Cj5RRjK1.mjs";
|
|
2
|
+
export { AppMention, BlockAction, BotIdentity, Disconnect, Envelope, Event, EventsApi, Frame, Hello, Interactive, Message, Options, ReactionAdded, ReplyIn, SlashCommand, eventId, toEvents };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Array, Match, Option, Schema } from "effect";
|
|
2
|
+
import { ChannelId, InboundEvent, MessageId, ThreadId, UserId } from "@effect-uai/core/Messenger";
|
|
3
|
+
//#region src/internal/events.ts
|
|
4
|
+
const AppMention = Schema.Struct({
|
|
5
|
+
type: Schema.Literal("app_mention"),
|
|
6
|
+
channel: Schema.String,
|
|
7
|
+
user: Schema.optional(Schema.String),
|
|
8
|
+
bot_id: Schema.optional(Schema.String),
|
|
9
|
+
text: Schema.optional(Schema.String),
|
|
10
|
+
ts: Schema.String,
|
|
11
|
+
thread_ts: Schema.optional(Schema.String)
|
|
12
|
+
});
|
|
13
|
+
const Message = Schema.Struct({
|
|
14
|
+
type: Schema.Literal("message"),
|
|
15
|
+
channel: Schema.String,
|
|
16
|
+
/** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */
|
|
17
|
+
channel_type: Schema.optional(Schema.String),
|
|
18
|
+
user: Schema.optional(Schema.String),
|
|
19
|
+
bot_id: Schema.optional(Schema.String),
|
|
20
|
+
subtype: Schema.optional(Schema.String),
|
|
21
|
+
text: Schema.optional(Schema.String),
|
|
22
|
+
ts: Schema.String,
|
|
23
|
+
thread_ts: Schema.optional(Schema.String)
|
|
24
|
+
});
|
|
25
|
+
const ReactionAdded = Schema.Struct({
|
|
26
|
+
type: Schema.Literal("reaction_added"),
|
|
27
|
+
user: Schema.String,
|
|
28
|
+
/** A shortcode without colons, which is also what `reactions.add` takes. */
|
|
29
|
+
reaction: Schema.String,
|
|
30
|
+
item: Schema.Struct({
|
|
31
|
+
channel: Schema.String,
|
|
32
|
+
ts: Schema.String
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
/** The events this adapter subscribes to. Anything else fails to decode and is dropped. */
|
|
36
|
+
const Event = Schema.Union([
|
|
37
|
+
AppMention,
|
|
38
|
+
Message,
|
|
39
|
+
ReactionAdded
|
|
40
|
+
]);
|
|
41
|
+
const EventsApi = Schema.Struct({
|
|
42
|
+
type: Schema.Literal("events_api"),
|
|
43
|
+
envelope_id: Schema.String,
|
|
44
|
+
payload: Schema.Struct({
|
|
45
|
+
/** Stable across a redelivery, which is what the socket dedupes on. */
|
|
46
|
+
event_id: Schema.optional(Schema.String),
|
|
47
|
+
event: Event
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
const BlockAction = Schema.Struct({
|
|
51
|
+
action_id: Schema.optional(Schema.String),
|
|
52
|
+
value: Schema.optional(Schema.String)
|
|
53
|
+
});
|
|
54
|
+
const Interactive = Schema.Struct({
|
|
55
|
+
type: Schema.Literal("interactive"),
|
|
56
|
+
envelope_id: Schema.String,
|
|
57
|
+
payload: Schema.Struct({
|
|
58
|
+
/** `block_actions`, `view_submission`, `shortcut`; only the first maps. */
|
|
59
|
+
type: Schema.String,
|
|
60
|
+
user: Schema.Struct({ id: Schema.String }),
|
|
61
|
+
container: Schema.optional(Schema.Struct({
|
|
62
|
+
channel_id: Schema.optional(Schema.String),
|
|
63
|
+
thread_ts: Schema.optional(Schema.String)
|
|
64
|
+
})),
|
|
65
|
+
channel: Schema.optional(Schema.Struct({ id: Schema.String })),
|
|
66
|
+
actions: Schema.optional(Schema.Array(BlockAction))
|
|
67
|
+
})
|
|
68
|
+
});
|
|
69
|
+
const SlashCommand = Schema.Struct({
|
|
70
|
+
type: Schema.Literal("slash_commands"),
|
|
71
|
+
envelope_id: Schema.String,
|
|
72
|
+
payload: Schema.Struct({
|
|
73
|
+
channel_id: Schema.String,
|
|
74
|
+
user_id: Schema.String,
|
|
75
|
+
command: Schema.String,
|
|
76
|
+
text: Schema.optional(Schema.String)
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
/** A frame carrying work, and so an `envelope_id` that has to be acknowledged. */
|
|
80
|
+
const Envelope = Schema.Union([
|
|
81
|
+
EventsApi,
|
|
82
|
+
Interactive,
|
|
83
|
+
SlashCommand
|
|
84
|
+
]);
|
|
85
|
+
const Hello = Schema.Struct({ type: Schema.Literal("hello") });
|
|
86
|
+
const Disconnect = Schema.Struct({
|
|
87
|
+
type: Schema.Literal("disconnect"),
|
|
88
|
+
reason: Schema.optional(Schema.String)
|
|
89
|
+
});
|
|
90
|
+
/** Everything Socket Mode sends down the wire. */
|
|
91
|
+
const Frame = Schema.Union([
|
|
92
|
+
Hello,
|
|
93
|
+
Disconnect,
|
|
94
|
+
EventsApi,
|
|
95
|
+
Interactive,
|
|
96
|
+
SlashCommand
|
|
97
|
+
]);
|
|
98
|
+
/** The id Slack repeats when it redelivers. Only an `events_api` frame carries one. */
|
|
99
|
+
const eventId = (envelope) => envelope.type === "events_api" ? Option.fromNullishOr(envelope.payload.event_id) : Option.none();
|
|
100
|
+
const isDirect = (channel, channelType) => channelType === "im" || channelType === void 0 && channel.startsWith("D");
|
|
101
|
+
/**
|
|
102
|
+
* Under `replyIn: "thread"` a channel message with no thread of its own gets
|
|
103
|
+
* one minted from its own `ts`, so the bot answers under the mention and a
|
|
104
|
+
* follow-up inside that thread is the same conversation. A DM never does.
|
|
105
|
+
*/
|
|
106
|
+
const conversation = (options, channel, ts, threadTs, direct) => {
|
|
107
|
+
const thread = threadTs ?? (options.replyIn === "thread" && !direct ? ts : void 0);
|
|
108
|
+
return {
|
|
109
|
+
channel: ChannelId(channel),
|
|
110
|
+
...thread !== void 0 && { thread: ThreadId(thread) }
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
const mention = (userId) => new RegExp(`<@${userId}(\\|[^>]*)?>`, "g");
|
|
114
|
+
const mentionsBot = (bot, text) => mention(bot.userId).test(text);
|
|
115
|
+
/** The bot's own mention removed; other users' mentions stay as Slack wrote them. */
|
|
116
|
+
const withoutMention = (bot, text) => text.replace(mention(bot.userId), "").trim();
|
|
117
|
+
const fromBot = (bot, event) => event.bot_id !== void 0 || event.user === bot.userId;
|
|
118
|
+
const droppedSubtypes = [
|
|
119
|
+
"bot_message",
|
|
120
|
+
"message_changed",
|
|
121
|
+
"message_deleted",
|
|
122
|
+
"thread_broadcast",
|
|
123
|
+
"file_share"
|
|
124
|
+
];
|
|
125
|
+
const fromMessage = (options, event, raw) => {
|
|
126
|
+
const direct = isDirect(event.channel, event.channel_type);
|
|
127
|
+
const text = event.text ?? "";
|
|
128
|
+
return !droppedSubtypes.includes(event.subtype ?? "") && !fromBot(options.bot, event) && (direct || !mentionsBot(options.bot, text)) ? Option.map(Option.fromNullishOr(event.user), (user) => InboundEvent.Message({
|
|
129
|
+
conversation: conversation(options, event.channel, event.ts, event.thread_ts, direct),
|
|
130
|
+
id: MessageId(event.ts),
|
|
131
|
+
author: UserId(user),
|
|
132
|
+
text: withoutMention(options.bot, text),
|
|
133
|
+
addressed: direct,
|
|
134
|
+
raw
|
|
135
|
+
})) : Option.none();
|
|
136
|
+
};
|
|
137
|
+
const fromMention = (options, event, raw) => Option.liftPredicate(event, (e) => !isDirect(e.channel) && !fromBot(options.bot, e)).pipe(Option.flatMap((e) => Option.map(Option.fromNullishOr(e.user), (user) => InboundEvent.Message({
|
|
138
|
+
conversation: conversation(options, e.channel, e.ts, e.thread_ts, false),
|
|
139
|
+
id: MessageId(e.ts),
|
|
140
|
+
author: UserId(user),
|
|
141
|
+
text: withoutMention(options.bot, e.text ?? ""),
|
|
142
|
+
addressed: true,
|
|
143
|
+
raw
|
|
144
|
+
}))));
|
|
145
|
+
const fromReaction = (options, event, raw) => Option.liftPredicate(event, (e) => e.user !== options.bot.userId).pipe(Option.map((e) => InboundEvent.Reaction({
|
|
146
|
+
conversation: conversation(options, e.item.channel, e.item.ts, void 0, isDirect(e.item.channel)),
|
|
147
|
+
message: MessageId(e.item.ts),
|
|
148
|
+
emoji: e.reaction,
|
|
149
|
+
author: UserId(e.user),
|
|
150
|
+
raw
|
|
151
|
+
})));
|
|
152
|
+
const fromEvent = (options, event, raw) => Match.value(event).pipe(Match.when({ type: "app_mention" }, (e) => fromMention(options, e, raw)), Match.when({ type: "message" }, (e) => fromMessage(options, e, raw)), Match.when({ type: "reaction_added" }, (e) => fromReaction(options, e, raw)), Match.exhaustive);
|
|
153
|
+
const BLOCK_ACTIONS = "block_actions";
|
|
154
|
+
const actionConversation = (payload) => Option.map(Option.fromNullishOr(payload.container?.channel_id ?? payload.channel?.id), (channel) => ({
|
|
155
|
+
channel: ChannelId(channel),
|
|
156
|
+
...payload.container?.thread_ts !== void 0 && { thread: ThreadId(payload.container.thread_ts) }
|
|
157
|
+
}));
|
|
158
|
+
/** One `Action` per pressed element; a view submission or shortcut is ignored. */
|
|
159
|
+
const fromInteractive = (payload, raw) => payload.type !== BLOCK_ACTIONS ? [] : Option.match(actionConversation(payload), {
|
|
160
|
+
onNone: () => [],
|
|
161
|
+
onSome: (at) => (payload.actions ?? []).map((action) => InboundEvent.Action({
|
|
162
|
+
conversation: at,
|
|
163
|
+
actionId: action.action_id ?? "",
|
|
164
|
+
...action.value !== void 0 && { value: action.value },
|
|
165
|
+
author: UserId(payload.user.id),
|
|
166
|
+
raw
|
|
167
|
+
}))
|
|
168
|
+
});
|
|
169
|
+
const fromCommand = (payload, raw) => InboundEvent.Command({
|
|
170
|
+
conversation: { channel: ChannelId(payload.channel_id) },
|
|
171
|
+
name: payload.command.startsWith("/") ? payload.command.slice(1) : payload.command,
|
|
172
|
+
args: (payload.text ?? "").trim(),
|
|
173
|
+
author: UserId(payload.user_id),
|
|
174
|
+
raw
|
|
175
|
+
});
|
|
176
|
+
/**
|
|
177
|
+
* Everything one envelope says to the agent. `raw` is the frame as it came off
|
|
178
|
+
* the wire, since decoding keeps only the fields above.
|
|
179
|
+
*/
|
|
180
|
+
const toEvents = (options) => (envelope, raw) => Match.value(envelope).pipe(Match.when({ type: "events_api" }, ({ payload }) => Array.fromOption(fromEvent(options, payload.event, raw))), Match.when({ type: "interactive" }, ({ payload }) => fromInteractive(payload, raw)), Match.when({ type: "slash_commands" }, ({ payload }) => [fromCommand(payload, raw)]), Match.exhaustive);
|
|
181
|
+
//#endregion
|
|
182
|
+
export { AppMention, BlockAction, Disconnect, Envelope, Event, EventsApi, Frame, Hello, Interactive, Message, ReactionAdded, SlashCommand, eventId, toEvents };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=events.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.mjs","names":["Arr"],"sources":["../../src/internal/events.ts"],"sourcesContent":["import { Array as Arr, Match, Option, Schema } from \"effect\"\nimport {\n ChannelId,\n type ConversationRef,\n InboundEvent,\n MessageId,\n ThreadId,\n UserId,\n} from \"@effect-uai/core/Messenger\"\n\n// ---------------------------------------------------------------------------\n// Wire shapes: the fields this adapter reads. The rest survives on `raw`.\n// ---------------------------------------------------------------------------\n\nexport const AppMention = Schema.Struct({\n type: Schema.Literal(\"app_mention\"),\n channel: Schema.String,\n user: Schema.optional(Schema.String),\n bot_id: Schema.optional(Schema.String),\n text: Schema.optional(Schema.String),\n ts: Schema.String,\n thread_ts: Schema.optional(Schema.String),\n})\nexport type AppMention = typeof AppMention.Type\n\nexport const Message = Schema.Struct({\n type: Schema.Literal(\"message\"),\n channel: Schema.String,\n /** `channel`, `group`, `im` or `mpim`. Authoritative for what kind of chat this is. */\n channel_type: Schema.optional(Schema.String),\n user: Schema.optional(Schema.String),\n bot_id: Schema.optional(Schema.String),\n subtype: Schema.optional(Schema.String),\n text: Schema.optional(Schema.String),\n ts: Schema.String,\n thread_ts: Schema.optional(Schema.String),\n})\nexport type Message = typeof Message.Type\n\nexport const ReactionAdded = Schema.Struct({\n type: Schema.Literal(\"reaction_added\"),\n user: Schema.String,\n /** A shortcode without colons, which is also what `reactions.add` takes. */\n reaction: Schema.String,\n item: Schema.Struct({ channel: Schema.String, ts: Schema.String }),\n})\nexport type ReactionAdded = typeof ReactionAdded.Type\n\n/** The events this adapter subscribes to. Anything else fails to decode and is dropped. */\nexport const Event = Schema.Union([AppMention, Message, ReactionAdded])\nexport type Event = typeof Event.Type\n\nexport const EventsApi = Schema.Struct({\n type: Schema.Literal(\"events_api\"),\n envelope_id: Schema.String,\n payload: Schema.Struct({\n /** Stable across a redelivery, which is what the socket dedupes on. */\n event_id: Schema.optional(Schema.String),\n event: Event,\n }),\n})\nexport type EventsApi = typeof EventsApi.Type\n\nexport const BlockAction = Schema.Struct({\n action_id: Schema.optional(Schema.String),\n value: Schema.optional(Schema.String),\n})\nexport type BlockAction = typeof BlockAction.Type\n\nexport const Interactive = Schema.Struct({\n type: Schema.Literal(\"interactive\"),\n envelope_id: Schema.String,\n payload: Schema.Struct({\n /** `block_actions`, `view_submission`, `shortcut`; only the first maps. */\n type: Schema.String,\n user: Schema.Struct({ id: Schema.String }),\n container: Schema.optional(\n Schema.Struct({\n channel_id: Schema.optional(Schema.String),\n thread_ts: Schema.optional(Schema.String),\n }),\n ),\n channel: Schema.optional(Schema.Struct({ id: Schema.String })),\n actions: Schema.optional(Schema.Array(BlockAction)),\n }),\n})\nexport type Interactive = typeof Interactive.Type\n\nexport const SlashCommand = Schema.Struct({\n type: Schema.Literal(\"slash_commands\"),\n envelope_id: Schema.String,\n payload: Schema.Struct({\n channel_id: Schema.String,\n user_id: Schema.String,\n command: Schema.String,\n text: Schema.optional(Schema.String),\n }),\n})\nexport type SlashCommand = typeof SlashCommand.Type\n\n/** A frame carrying work, and so an `envelope_id` that has to be acknowledged. */\nexport const Envelope = Schema.Union([EventsApi, Interactive, SlashCommand])\nexport type Envelope = typeof Envelope.Type\n\nexport const Hello = Schema.Struct({ type: Schema.Literal(\"hello\") })\n\nexport const Disconnect = Schema.Struct({\n type: Schema.Literal(\"disconnect\"),\n reason: Schema.optional(Schema.String),\n})\n\n/** Everything Socket Mode sends down the wire. */\nexport const Frame = Schema.Union([Hello, Disconnect, EventsApi, Interactive, SlashCommand])\nexport type Frame = typeof Frame.Type\n\n/** From `auth.test` at layer build: the identity behind `addressed`. */\nexport type BotIdentity = {\n readonly userId: string\n readonly botId: string\n readonly teamId: string\n}\n\n/** Where a top-level mention is answered: in a thread under it, or in the channel. */\nexport type ReplyIn = \"thread\" | \"channel\"\n\nexport type Options = {\n readonly bot: BotIdentity\n readonly replyIn: ReplyIn\n}\n\n/** The id Slack repeats when it redelivers. Only an `events_api` frame carries one. */\nexport const eventId = (envelope: Envelope): Option.Option<string> =>\n envelope.type === \"events_api\" ? Option.fromNullishOr(envelope.payload.event_id) : Option.none()\n\n// ---------------------------------------------------------------------------\n// Mapping\n// ---------------------------------------------------------------------------\n\n// Slack ids name their own kind, and `channel_type` says so outright where the\n// payload has one. Group DMs are `mpim` and are treated as a channel.\nconst isDirect = (channel: string, channelType?: string): boolean =>\n channelType === \"im\" || (channelType === undefined && channel.startsWith(\"D\"))\n\n/**\n * Under `replyIn: \"thread\"` a channel message with no thread of its own gets\n * one minted from its own `ts`, so the bot answers under the mention and a\n * follow-up inside that thread is the same conversation. A DM never does.\n */\nconst conversation = (\n options: Options,\n channel: string,\n ts: string,\n threadTs: string | undefined,\n direct: boolean,\n): ConversationRef => {\n const thread = threadTs ?? (options.replyIn === \"thread\" && !direct ? ts : undefined)\n return {\n channel: ChannelId(channel),\n ...(thread !== undefined && { thread: ThreadId(thread) }),\n }\n}\n\n// Slack writes a mention as `<@U123>`, or `<@U123|name>` in older payloads.\nconst mention = (userId: string): RegExp => new RegExp(`<@${userId}(\\\\|[^>]*)?>`, \"g\")\n\nconst mentionsBot = (bot: BotIdentity, text: string): boolean => mention(bot.userId).test(text)\n\n/** The bot's own mention removed; other users' mentions stay as Slack wrote them. */\nconst withoutMention = (bot: BotIdentity, text: string): string =>\n text.replace(mention(bot.userId), \"\").trim()\n\n// Anything the bot itself or another app posted, so two bots cannot answer\n// each other.\nconst fromBot = (\n bot: BotIdentity,\n event: { readonly bot_id?: string | undefined; readonly user?: string | undefined },\n): boolean => event.bot_id !== undefined || event.user === bot.userId\n\n// Subtypes that are not a person saying something new.\nconst droppedSubtypes = [\n \"bot_message\",\n \"message_changed\",\n \"message_deleted\",\n \"thread_broadcast\",\n \"file_share\",\n]\n\nconst fromMessage = (\n options: Options,\n event: Message,\n raw: unknown,\n): Option.Option<InboundEvent> => {\n const direct = isDirect(event.channel, event.channel_type)\n const text = event.text ?? \"\"\n const keep =\n !droppedSubtypes.includes(event.subtype ?? \"\") &&\n !fromBot(options.bot, event) &&\n // In a channel the very same message also arrives as `app_mention`, and\n // that is the copy this adapter keeps.\n (direct || !mentionsBot(options.bot, text))\n return keep\n ? Option.map(Option.fromNullishOr(event.user), (user) =>\n InboundEvent.Message({\n conversation: conversation(options, event.channel, event.ts, event.thread_ts, direct),\n id: MessageId(event.ts),\n author: UserId(user),\n text: withoutMention(options.bot, text),\n // A thread follow-up without a mention is not addressed: the payload\n // does not say who opened the thread and this adapter keeps no memory.\n addressed: direct,\n raw,\n }),\n )\n : Option.none()\n}\n\nconst fromMention = (\n options: Options,\n event: AppMention,\n raw: unknown,\n): Option.Option<InboundEvent> =>\n // A mention inside a DM already arrived as `message.im`.\n Option.liftPredicate(event, (e) => !isDirect(e.channel) && !fromBot(options.bot, e)).pipe(\n Option.flatMap((e) =>\n Option.map(Option.fromNullishOr(e.user), (user) =>\n InboundEvent.Message({\n conversation: conversation(options, e.channel, e.ts, e.thread_ts, false),\n id: MessageId(e.ts),\n author: UserId(user),\n text: withoutMention(options.bot, e.text ?? \"\"),\n addressed: true,\n raw,\n }),\n ),\n ),\n )\n\nconst fromReaction = (\n options: Options,\n event: ReactionAdded,\n raw: unknown,\n): Option.Option<InboundEvent> =>\n Option.liftPredicate(event, (e) => e.user !== options.bot.userId).pipe(\n Option.map((e) =>\n InboundEvent.Reaction({\n // `reaction_added` names the message but not the thread it sits in, so\n // under `replyIn: \"thread\"` this matches a reaction on the message that\n // opened the thread and nothing deeper.\n conversation: conversation(\n options,\n e.item.channel,\n e.item.ts,\n undefined,\n isDirect(e.item.channel),\n ),\n message: MessageId(e.item.ts),\n emoji: e.reaction,\n author: UserId(e.user),\n raw,\n }),\n ),\n )\n\nconst fromEvent = (options: Options, event: Event, raw: unknown): Option.Option<InboundEvent> =>\n Match.value(event).pipe(\n Match.when({ type: \"app_mention\" }, (e) => fromMention(options, e, raw)),\n Match.when({ type: \"message\" }, (e) => fromMessage(options, e, raw)),\n Match.when({ type: \"reaction_added\" }, (e) => fromReaction(options, e, raw)),\n Match.exhaustive,\n )\n\nconst BLOCK_ACTIONS = \"block_actions\"\n\nconst actionConversation = (payload: Interactive[\"payload\"]): Option.Option<ConversationRef> =>\n Option.map(\n Option.fromNullishOr(payload.container?.channel_id ?? payload.channel?.id),\n (channel) => ({\n channel: ChannelId(channel),\n ...(payload.container?.thread_ts !== undefined && {\n thread: ThreadId(payload.container.thread_ts),\n }),\n }),\n )\n\n/** One `Action` per pressed element; a view submission or shortcut is ignored. */\nconst fromInteractive = (\n payload: Interactive[\"payload\"],\n raw: unknown,\n): ReadonlyArray<InboundEvent> =>\n payload.type !== BLOCK_ACTIONS\n ? []\n : Option.match(actionConversation(payload), {\n onNone: () => [],\n onSome: (at) =>\n (payload.actions ?? []).map((action) =>\n InboundEvent.Action({\n conversation: at,\n actionId: action.action_id ?? \"\",\n ...(action.value !== undefined && { value: action.value }),\n author: UserId(payload.user.id),\n raw,\n }),\n ),\n })\n\nconst fromCommand = (payload: SlashCommand[\"payload\"], raw: unknown): InboundEvent =>\n InboundEvent.Command({\n // A slash command names no thread, so it starts at channel level.\n conversation: { channel: ChannelId(payload.channel_id) },\n name: payload.command.startsWith(\"/\") ? payload.command.slice(1) : payload.command,\n args: (payload.text ?? \"\").trim(),\n author: UserId(payload.user_id),\n raw,\n })\n\n/**\n * Everything one envelope says to the agent. `raw` is the frame as it came off\n * the wire, since decoding keeps only the fields above.\n */\nexport const toEvents =\n (options: Options) =>\n (envelope: Envelope, raw: unknown): ReadonlyArray<InboundEvent> =>\n Match.value(envelope).pipe(\n Match.when({ type: \"events_api\" }, ({ payload }) =>\n Arr.fromOption(fromEvent(options, payload.event, raw)),\n ),\n Match.when({ type: \"interactive\" }, ({ payload }) => fromInteractive(payload, raw)),\n Match.when({ type: \"slash_commands\" }, ({ payload }) => [fromCommand(payload, raw)]),\n Match.exhaustive,\n )\n"],"mappings":"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"}
|