@aarekaz/switchboard-core 0.3.1
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.md +41 -0
- package/dist/index.d.ts +705 -0
- package/dist/index.js +385 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# @aarekaz/switchboard-core
|
|
2
|
+
|
|
3
|
+
Core types, interfaces, and client API for Switchboard - the universal chat platform SDK.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @aarekaz/switchboard-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @aarekaz/switchboard-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createBot } from '@aarekaz/switchboard-core';
|
|
17
|
+
import '@aarekaz/switchboard-discord'; // Auto-registers Discord adapter
|
|
18
|
+
|
|
19
|
+
const bot = createBot({
|
|
20
|
+
platform: 'discord',
|
|
21
|
+
credentials: {
|
|
22
|
+
token: process.env.DISCORD_TOKEN,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
bot.onMessage(async (message) => {
|
|
27
|
+
if (message.text.includes('ping')) {
|
|
28
|
+
await bot.reply(message, 'pong!');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await bot.start();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
For full documentation, visit [github.com/Aarekaz/switchboard](https://github.com/Aarekaz/switchboard)
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result type for type-safe error handling
|
|
3
|
+
* Inspired by Rust's Result<T, E> type
|
|
4
|
+
*/
|
|
5
|
+
type Result<T, E = Error> = {
|
|
6
|
+
ok: true;
|
|
7
|
+
value: T;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
error: E;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Create a successful result
|
|
14
|
+
*/
|
|
15
|
+
declare function ok<T>(value: T): Result<T, never>;
|
|
16
|
+
/**
|
|
17
|
+
* Create an error result
|
|
18
|
+
*/
|
|
19
|
+
declare function err<E = Error>(error: E): Result<never, E>;
|
|
20
|
+
/**
|
|
21
|
+
* Wrap an async operation in a Result type
|
|
22
|
+
* Catches any errors and converts them to Result<T>
|
|
23
|
+
*/
|
|
24
|
+
declare function wrapAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a result is successful
|
|
27
|
+
*/
|
|
28
|
+
declare function isOk<T, E>(result: Result<T, E>): result is {
|
|
29
|
+
ok: true;
|
|
30
|
+
value: T;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Check if a result is an error
|
|
34
|
+
*/
|
|
35
|
+
declare function isErr<T, E>(result: Result<T, E>): result is {
|
|
36
|
+
ok: false;
|
|
37
|
+
error: E;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Supported chat platforms
|
|
42
|
+
* Extensible to allow custom platforms
|
|
43
|
+
*/
|
|
44
|
+
type PlatformType = 'discord' | 'slack' | 'teams' | 'google-chat' | (string & {});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* File attachment
|
|
48
|
+
*/
|
|
49
|
+
interface Attachment {
|
|
50
|
+
/** Attachment ID */
|
|
51
|
+
id: string;
|
|
52
|
+
/** File name */
|
|
53
|
+
filename: string;
|
|
54
|
+
/** Download URL */
|
|
55
|
+
url: string;
|
|
56
|
+
/** MIME type (e.g., 'image/png') */
|
|
57
|
+
mimeType: string;
|
|
58
|
+
/** File size in bytes */
|
|
59
|
+
size: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Unified message representation across all platforms
|
|
63
|
+
* This is the core abstraction that normalizes messages from different platforms
|
|
64
|
+
*/
|
|
65
|
+
interface UnifiedMessage {
|
|
66
|
+
/** Platform-specific message ID */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Channel/room/conversation ID where the message was sent */
|
|
69
|
+
channelId: string;
|
|
70
|
+
/** User ID of the message sender */
|
|
71
|
+
userId: string;
|
|
72
|
+
/** Plain text content of the message */
|
|
73
|
+
text: string;
|
|
74
|
+
/** When the message was sent */
|
|
75
|
+
timestamp: Date;
|
|
76
|
+
/** Thread/reply chain ID (if this message is part of a thread) */
|
|
77
|
+
threadId?: string;
|
|
78
|
+
/** File attachments (images, files, etc.) */
|
|
79
|
+
attachments?: Attachment[];
|
|
80
|
+
/** Which platform this message is from */
|
|
81
|
+
platform: PlatformType;
|
|
82
|
+
/** Original platform message object (for accessing platform-specific fields) */
|
|
83
|
+
_raw: unknown;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Reference to a message - can be either a message ID string or a full UnifiedMessage object.
|
|
87
|
+
*
|
|
88
|
+
* **Why this exists:**
|
|
89
|
+
* Different platforms have different requirements for message operations:
|
|
90
|
+
* - Discord: Only needs message ID for edit/delete/react operations
|
|
91
|
+
* - Slack: Needs both channel ID and message timestamp (ts)
|
|
92
|
+
*
|
|
93
|
+
* **Usage recommendations:**
|
|
94
|
+
* - Passing UnifiedMessage works reliably on ALL platforms
|
|
95
|
+
* - Passing string ID works on Discord, works on Slack if message is in cache (95% of cases)
|
|
96
|
+
*
|
|
97
|
+
* **Platform notes:**
|
|
98
|
+
* - Discord: String IDs always work
|
|
99
|
+
* - Slack: String IDs work if the message was seen by the bot in the last hour.
|
|
100
|
+
* For guaranteed reliability, pass the full message object.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* // Recommended: Works everywhere, always
|
|
105
|
+
* bot.onMessage(async (message) => {
|
|
106
|
+
* await bot.editMessage(message, 'Updated text');
|
|
107
|
+
* await bot.addReaction(message, 'thumbsup');
|
|
108
|
+
* });
|
|
109
|
+
*
|
|
110
|
+
* // Works on Discord, works on Slack if cached
|
|
111
|
+
* await bot.editMessage(message.id, 'Updated text');
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
type MessageRef = string | UnifiedMessage;
|
|
115
|
+
/**
|
|
116
|
+
* Options for sending messages
|
|
117
|
+
*/
|
|
118
|
+
interface SendMessageOptions {
|
|
119
|
+
/** Send message in a specific thread */
|
|
120
|
+
threadId?: string;
|
|
121
|
+
/** Discord-specific options */
|
|
122
|
+
discord?: {
|
|
123
|
+
embeds?: unknown[];
|
|
124
|
+
components?: unknown[];
|
|
125
|
+
};
|
|
126
|
+
/** Slack-specific options */
|
|
127
|
+
slack?: {
|
|
128
|
+
blocks?: unknown[];
|
|
129
|
+
unfurl_links?: boolean;
|
|
130
|
+
unfurl_media?: boolean;
|
|
131
|
+
};
|
|
132
|
+
/** Microsoft Teams-specific options */
|
|
133
|
+
teams?: {
|
|
134
|
+
attachments?: unknown[];
|
|
135
|
+
};
|
|
136
|
+
/** Google Chat-specific options */
|
|
137
|
+
googleChat?: {
|
|
138
|
+
cards?: unknown[];
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Options for uploading files
|
|
143
|
+
*/
|
|
144
|
+
interface UploadOptions {
|
|
145
|
+
/** File name (if not provided in the File object) */
|
|
146
|
+
filename?: string;
|
|
147
|
+
/** Optional comment/message to send with the file */
|
|
148
|
+
comment?: string;
|
|
149
|
+
/** Send file in a specific thread */
|
|
150
|
+
threadId?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Event fired when a message is received
|
|
155
|
+
*/
|
|
156
|
+
interface MessageEvent {
|
|
157
|
+
type: 'message';
|
|
158
|
+
message: UnifiedMessage;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Event fired when a reaction is added or removed from a message
|
|
162
|
+
*/
|
|
163
|
+
interface ReactionEvent {
|
|
164
|
+
type: 'reaction';
|
|
165
|
+
/** Message ID that was reacted to */
|
|
166
|
+
messageId: string;
|
|
167
|
+
/** User ID who added/removed the reaction */
|
|
168
|
+
userId: string;
|
|
169
|
+
/** Emoji string (e.g., '👍', 'tada') */
|
|
170
|
+
emoji: string;
|
|
171
|
+
/** Whether the reaction was added or removed */
|
|
172
|
+
action: 'added' | 'removed';
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Event fired when a user joins a channel
|
|
176
|
+
*/
|
|
177
|
+
interface UserJoinedEvent {
|
|
178
|
+
type: 'user_joined';
|
|
179
|
+
/** Channel ID the user joined */
|
|
180
|
+
channelId: string;
|
|
181
|
+
/** User ID who joined */
|
|
182
|
+
userId: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Event fired when a user leaves a channel
|
|
186
|
+
*/
|
|
187
|
+
interface UserLeftEvent {
|
|
188
|
+
type: 'user_left';
|
|
189
|
+
/** Channel ID the user left */
|
|
190
|
+
channelId: string;
|
|
191
|
+
/** User ID who left */
|
|
192
|
+
userId: string;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Event fired when a channel is created
|
|
196
|
+
*/
|
|
197
|
+
interface ChannelCreatedEvent {
|
|
198
|
+
type: 'channel_created';
|
|
199
|
+
/** ID of the created channel */
|
|
200
|
+
channelId: string;
|
|
201
|
+
/** Name of the created channel */
|
|
202
|
+
channelName: string;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Event fired when a channel is deleted
|
|
206
|
+
*/
|
|
207
|
+
interface ChannelDeletedEvent {
|
|
208
|
+
type: 'channel_deleted';
|
|
209
|
+
/** ID of the deleted channel */
|
|
210
|
+
channelId: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Union of all possible events
|
|
214
|
+
*/
|
|
215
|
+
type UnifiedEvent = MessageEvent | ReactionEvent | UserJoinedEvent | UserLeftEvent | ChannelCreatedEvent | ChannelDeletedEvent;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Channel/room type
|
|
219
|
+
*/
|
|
220
|
+
type ChannelType = 'text' | 'voice' | 'dm' | 'group_dm' | 'category' | 'unknown';
|
|
221
|
+
/**
|
|
222
|
+
* Unified channel representation
|
|
223
|
+
*/
|
|
224
|
+
interface Channel {
|
|
225
|
+
/** Channel ID */
|
|
226
|
+
id: string;
|
|
227
|
+
/** Channel name */
|
|
228
|
+
name: string;
|
|
229
|
+
/** Channel type */
|
|
230
|
+
type: ChannelType;
|
|
231
|
+
/** Whether the channel is private/restricted */
|
|
232
|
+
isPrivate: boolean;
|
|
233
|
+
/** Channel topic/description (if applicable) */
|
|
234
|
+
topic?: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Unified user representation
|
|
239
|
+
*/
|
|
240
|
+
interface User {
|
|
241
|
+
/** User ID */
|
|
242
|
+
id: string;
|
|
243
|
+
/** Username */
|
|
244
|
+
username: string;
|
|
245
|
+
/** Display name (may be different from username on some platforms) */
|
|
246
|
+
displayName?: string;
|
|
247
|
+
/** Whether the user is a bot */
|
|
248
|
+
isBot: boolean;
|
|
249
|
+
/** Avatar URL (if available) */
|
|
250
|
+
avatarUrl?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Platform adapter interface
|
|
255
|
+
* All platform adapters must implement this interface
|
|
256
|
+
*/
|
|
257
|
+
interface PlatformAdapter {
|
|
258
|
+
/** Adapter name (e.g., 'discord-adapter') */
|
|
259
|
+
readonly name: string;
|
|
260
|
+
/** Platform type (e.g., 'discord') */
|
|
261
|
+
readonly platform: PlatformType;
|
|
262
|
+
/** Connect to the platform with credentials */
|
|
263
|
+
connect(credentials: unknown): Promise<void>;
|
|
264
|
+
/** Disconnect from the platform */
|
|
265
|
+
disconnect(): Promise<void>;
|
|
266
|
+
/** Check if currently connected */
|
|
267
|
+
isConnected(): boolean;
|
|
268
|
+
/** Send a message to a channel */
|
|
269
|
+
sendMessage(channelId: string, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
|
|
270
|
+
/** Edit an existing message */
|
|
271
|
+
editMessage(messageRef: MessageRef, newText: string): Promise<Result<UnifiedMessage>>;
|
|
272
|
+
/** Delete a message */
|
|
273
|
+
deleteMessage(messageRef: MessageRef): Promise<Result<void>>;
|
|
274
|
+
/** Add a reaction emoji to a message */
|
|
275
|
+
addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
|
|
276
|
+
/** Remove a reaction emoji from a message */
|
|
277
|
+
removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
|
|
278
|
+
/** Create a thread/reply to a message */
|
|
279
|
+
createThread(messageRef: MessageRef, text: string): Promise<Result<UnifiedMessage>>;
|
|
280
|
+
/** Upload a file to a channel */
|
|
281
|
+
uploadFile(channelId: string, file: unknown, options?: UploadOptions): Promise<Result<UnifiedMessage>>;
|
|
282
|
+
/** Subscribe to platform events */
|
|
283
|
+
onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void;
|
|
284
|
+
/** Get list of channels */
|
|
285
|
+
getChannels(): Promise<Result<Channel[]>>;
|
|
286
|
+
/** Get list of users (optionally in a specific channel) */
|
|
287
|
+
getUsers(channelId?: string): Promise<Result<User[]>>;
|
|
288
|
+
/** Normalize platform-specific message to UnifiedMessage */
|
|
289
|
+
normalizeMessage(platformMessage: unknown): UnifiedMessage;
|
|
290
|
+
/** Normalize platform-specific event to UnifiedEvent */
|
|
291
|
+
normalizeEvent(platformEvent: unknown): UnifiedEvent | null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Message Context
|
|
296
|
+
*
|
|
297
|
+
* A context object passed to message handlers that provides convenient access
|
|
298
|
+
* to message data and bot operations without repetitive parameter passing.
|
|
299
|
+
*
|
|
300
|
+
* Inspired by Hono's context pattern and Telegraf's bot framework.
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Context object provided to message handlers
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* ```typescript
|
|
308
|
+
* bot.onMessage(async (ctx) => {
|
|
309
|
+
* // Access message data
|
|
310
|
+
* console.log(ctx.message.text);
|
|
311
|
+
* console.log(ctx.userId);
|
|
312
|
+
* console.log(ctx.channelId);
|
|
313
|
+
*
|
|
314
|
+
* // Use helper methods
|
|
315
|
+
* await ctx.reply('Hello!');
|
|
316
|
+
* await ctx.react('👍');
|
|
317
|
+
* await ctx.edit('Updated!');
|
|
318
|
+
* });
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
interface MessageContext {
|
|
322
|
+
/**
|
|
323
|
+
* The received message
|
|
324
|
+
*/
|
|
325
|
+
readonly message: UnifiedMessage;
|
|
326
|
+
/**
|
|
327
|
+
* Platform this message is from
|
|
328
|
+
*/
|
|
329
|
+
readonly platform: PlatformType;
|
|
330
|
+
/**
|
|
331
|
+
* User ID who sent the message (convenience accessor)
|
|
332
|
+
*/
|
|
333
|
+
readonly userId: string;
|
|
334
|
+
/**
|
|
335
|
+
* Channel ID where message was sent (convenience accessor)
|
|
336
|
+
*/
|
|
337
|
+
readonly channelId: string;
|
|
338
|
+
/**
|
|
339
|
+
* Message text content (convenience accessor)
|
|
340
|
+
*/
|
|
341
|
+
readonly text: string;
|
|
342
|
+
/**
|
|
343
|
+
* Thread ID if message is in a thread (convenience accessor)
|
|
344
|
+
*/
|
|
345
|
+
readonly threadId: string | undefined;
|
|
346
|
+
/**
|
|
347
|
+
* Reply to the message
|
|
348
|
+
*
|
|
349
|
+
* @param text - Text to reply with
|
|
350
|
+
* @param options - Optional message options
|
|
351
|
+
* @returns Result containing the sent message
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* await ctx.reply('Got your message!');
|
|
356
|
+
*
|
|
357
|
+
* // With options
|
|
358
|
+
* await ctx.reply('Thread reply', { threadId: ctx.threadId });
|
|
359
|
+
* ```
|
|
360
|
+
*/
|
|
361
|
+
reply(text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
|
|
362
|
+
/**
|
|
363
|
+
* Add a reaction to the message
|
|
364
|
+
*
|
|
365
|
+
* @param emoji - Emoji to add (Unicode or platform name)
|
|
366
|
+
* @returns Result indicating success or failure
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```typescript
|
|
370
|
+
* await ctx.react('👍');
|
|
371
|
+
* await ctx.react('thumbsup');
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
react(emoji: string): Promise<Result<void>>;
|
|
375
|
+
/**
|
|
376
|
+
* Remove a reaction from the message
|
|
377
|
+
*
|
|
378
|
+
* @param emoji - Emoji to remove
|
|
379
|
+
* @returns Result indicating success or failure
|
|
380
|
+
*/
|
|
381
|
+
unreact(emoji: string): Promise<Result<void>>;
|
|
382
|
+
/**
|
|
383
|
+
* Edit the message (only works for bot's own messages)
|
|
384
|
+
*
|
|
385
|
+
* @param newText - New message text
|
|
386
|
+
* @returns Result containing the edited message
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* ```typescript
|
|
390
|
+
* const sent = await ctx.reply('Processing...');
|
|
391
|
+
* if (sent.ok) {
|
|
392
|
+
* // Create new context for the sent message
|
|
393
|
+
* await bot.editMessage(sent.value, 'Done!');
|
|
394
|
+
* }
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
edit(newText: string): Promise<Result<UnifiedMessage>>;
|
|
398
|
+
/**
|
|
399
|
+
* Delete the message
|
|
400
|
+
*
|
|
401
|
+
* @returns Result indicating success or failure
|
|
402
|
+
*/
|
|
403
|
+
delete(): Promise<Result<void>>;
|
|
404
|
+
/**
|
|
405
|
+
* Create a thread on the message
|
|
406
|
+
*
|
|
407
|
+
* @param text - First message in the thread
|
|
408
|
+
* @returns Result containing the thread message
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```typescript
|
|
412
|
+
* await ctx.createThread('Let\'s discuss this!');
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
createThread(text: string): Promise<Result<UnifiedMessage>>;
|
|
416
|
+
/**
|
|
417
|
+
* Send a new message to the same channel
|
|
418
|
+
*
|
|
419
|
+
* @param text - Message text
|
|
420
|
+
* @param options - Optional message options
|
|
421
|
+
* @returns Result containing the sent message
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```typescript
|
|
425
|
+
* await ctx.send('This is a new message');
|
|
426
|
+
* ```
|
|
427
|
+
*/
|
|
428
|
+
send(text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Function type for message handlers that receive context
|
|
432
|
+
*/
|
|
433
|
+
type MessageHandler = (ctx: MessageContext) => void | Promise<void>;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Bot client - the main interface for interacting with chat platforms
|
|
437
|
+
*/
|
|
438
|
+
declare class Bot {
|
|
439
|
+
private readonly adapter;
|
|
440
|
+
private readonly _platform;
|
|
441
|
+
private readonly credentials;
|
|
442
|
+
private eventHandlers;
|
|
443
|
+
constructor(adapter: PlatformAdapter, _platform: PlatformType, credentials: unknown);
|
|
444
|
+
/**
|
|
445
|
+
* Get the platform type
|
|
446
|
+
*/
|
|
447
|
+
get platform(): PlatformType;
|
|
448
|
+
/**
|
|
449
|
+
* Start the bot (connect to the platform)
|
|
450
|
+
*/
|
|
451
|
+
start(): Promise<void>;
|
|
452
|
+
/**
|
|
453
|
+
* Stop the bot (disconnect from the platform)
|
|
454
|
+
*/
|
|
455
|
+
stop(): Promise<void>;
|
|
456
|
+
/**
|
|
457
|
+
* Check if the bot is connected
|
|
458
|
+
*/
|
|
459
|
+
isConnected(): boolean;
|
|
460
|
+
/**
|
|
461
|
+
* Send a message to a channel
|
|
462
|
+
*/
|
|
463
|
+
sendMessage(channelId: string, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
|
|
464
|
+
/**
|
|
465
|
+
* Reply to a message
|
|
466
|
+
*/
|
|
467
|
+
reply(message: UnifiedMessage, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
|
|
468
|
+
/**
|
|
469
|
+
* Edit an existing message
|
|
470
|
+
*/
|
|
471
|
+
editMessage(messageRef: MessageRef, newText: string): Promise<Result<UnifiedMessage>>;
|
|
472
|
+
/**
|
|
473
|
+
* Delete a message
|
|
474
|
+
*/
|
|
475
|
+
deleteMessage(messageRef: MessageRef): Promise<Result<void>>;
|
|
476
|
+
/**
|
|
477
|
+
* Add a reaction to a message
|
|
478
|
+
*/
|
|
479
|
+
addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
|
|
480
|
+
/**
|
|
481
|
+
* Remove a reaction from a message
|
|
482
|
+
*/
|
|
483
|
+
removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
|
|
484
|
+
/**
|
|
485
|
+
* Create a thread (or reply in a thread)
|
|
486
|
+
*/
|
|
487
|
+
createThread(messageRef: MessageRef, text: string): Promise<Result<UnifiedMessage>>;
|
|
488
|
+
/**
|
|
489
|
+
* Upload a file to a channel
|
|
490
|
+
*/
|
|
491
|
+
uploadFile(channelId: string, file: unknown, options?: UploadOptions): Promise<Result<UnifiedMessage>>;
|
|
492
|
+
/**
|
|
493
|
+
* Get list of channels
|
|
494
|
+
*/
|
|
495
|
+
getChannels(): Promise<Result<Channel[]>>;
|
|
496
|
+
/**
|
|
497
|
+
* Get list of users (optionally in a specific channel)
|
|
498
|
+
*/
|
|
499
|
+
getUsers(channelId?: string): Promise<Result<User[]>>;
|
|
500
|
+
/**
|
|
501
|
+
* Register a handler for message events
|
|
502
|
+
*
|
|
503
|
+
* Supports both context API (recommended) and legacy message API
|
|
504
|
+
*
|
|
505
|
+
* @example
|
|
506
|
+
* ```typescript
|
|
507
|
+
* // Context API (recommended)
|
|
508
|
+
* bot.onMessage(async (ctx) => {
|
|
509
|
+
* await ctx.reply('Hello!');
|
|
510
|
+
* await ctx.react('thumbsup');
|
|
511
|
+
* });
|
|
512
|
+
*
|
|
513
|
+
* // Legacy API (still supported)
|
|
514
|
+
* bot.onMessage(async (message) => {
|
|
515
|
+
* await bot.reply(message, 'Hello!');
|
|
516
|
+
* });
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
onMessage(handler: MessageHandler | ((message: UnifiedMessage) => void | Promise<void>)): void;
|
|
520
|
+
/**
|
|
521
|
+
* Create a message context object
|
|
522
|
+
*/
|
|
523
|
+
private createContext;
|
|
524
|
+
/**
|
|
525
|
+
* Register a handler for reaction events
|
|
526
|
+
*/
|
|
527
|
+
onReaction(handler: (event: ReactionEvent) => void | Promise<void>): void;
|
|
528
|
+
/**
|
|
529
|
+
* Register a handler for any event
|
|
530
|
+
*/
|
|
531
|
+
onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void;
|
|
532
|
+
/**
|
|
533
|
+
* Get the underlying adapter (for advanced use cases)
|
|
534
|
+
*/
|
|
535
|
+
getAdapter(): PlatformAdapter;
|
|
536
|
+
/**
|
|
537
|
+
* Internal: Register an event handler
|
|
538
|
+
*/
|
|
539
|
+
private on;
|
|
540
|
+
/**
|
|
541
|
+
* Internal: Handle an event from the adapter
|
|
542
|
+
*/
|
|
543
|
+
private handleEvent;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Configuration for creating a bot
|
|
548
|
+
*/
|
|
549
|
+
interface BotConfig {
|
|
550
|
+
/**
|
|
551
|
+
* Platform to connect to (e.g., 'discord', 'slack', 'teams', 'google-chat')
|
|
552
|
+
*/
|
|
553
|
+
platform: PlatformType;
|
|
554
|
+
/**
|
|
555
|
+
* Platform credentials (structure depends on the platform)
|
|
556
|
+
*/
|
|
557
|
+
credentials?: {
|
|
558
|
+
/** Bot token (Discord, Slack) */
|
|
559
|
+
token?: string;
|
|
560
|
+
/** App ID (Teams) */
|
|
561
|
+
appId?: string;
|
|
562
|
+
/** App password (Teams) */
|
|
563
|
+
appPassword?: string;
|
|
564
|
+
/** Custom credentials object */
|
|
565
|
+
[key: string]: unknown;
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* Custom adapter (for advanced users or custom platforms)
|
|
569
|
+
* If provided, this will be used instead of the registered adapter
|
|
570
|
+
*/
|
|
571
|
+
adapter?: PlatformAdapter;
|
|
572
|
+
/**
|
|
573
|
+
* Platform-specific configuration (opt-in)
|
|
574
|
+
*/
|
|
575
|
+
platformConfig?: {
|
|
576
|
+
discord?: unknown;
|
|
577
|
+
slack?: unknown;
|
|
578
|
+
teams?: unknown;
|
|
579
|
+
googleChat?: unknown;
|
|
580
|
+
[key: string]: unknown;
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Create a bot instance
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```typescript
|
|
588
|
+
* import { createBot } from '@aarekaz/switchboard-core';
|
|
589
|
+
* import '@aarekaz/switchboard-discord';
|
|
590
|
+
*
|
|
591
|
+
* const bot = createBot({
|
|
592
|
+
* platform: 'discord',
|
|
593
|
+
* credentials: {
|
|
594
|
+
* token: process.env.DISCORD_TOKEN,
|
|
595
|
+
* },
|
|
596
|
+
* });
|
|
597
|
+
*
|
|
598
|
+
* bot.onMessage(async (message) => {
|
|
599
|
+
* if (message.text.includes('ping')) {
|
|
600
|
+
* await bot.reply(message, 'pong!');
|
|
601
|
+
* }
|
|
602
|
+
* });
|
|
603
|
+
*
|
|
604
|
+
* await bot.start();
|
|
605
|
+
* ```
|
|
606
|
+
*/
|
|
607
|
+
declare function createBot(config: BotConfig): Bot;
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Global registry for platform adapters
|
|
611
|
+
* Adapters register themselves when their package is imported
|
|
612
|
+
*/
|
|
613
|
+
declare class AdapterRegistry {
|
|
614
|
+
private adapters;
|
|
615
|
+
/**
|
|
616
|
+
* Register a platform adapter
|
|
617
|
+
* Called by adapter packages when they're imported
|
|
618
|
+
*/
|
|
619
|
+
register(platform: PlatformType, adapter: PlatformAdapter): void;
|
|
620
|
+
/**
|
|
621
|
+
* Get an adapter for a platform
|
|
622
|
+
*/
|
|
623
|
+
get(platform: PlatformType): PlatformAdapter | undefined;
|
|
624
|
+
/**
|
|
625
|
+
* Check if an adapter is registered for a platform
|
|
626
|
+
*/
|
|
627
|
+
has(platform: PlatformType): boolean;
|
|
628
|
+
/**
|
|
629
|
+
* Get all registered platforms
|
|
630
|
+
*/
|
|
631
|
+
getRegisteredPlatforms(): PlatformType[];
|
|
632
|
+
/**
|
|
633
|
+
* Clear all registered adapters (mainly for testing)
|
|
634
|
+
*/
|
|
635
|
+
clear(): void;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Global singleton registry instance
|
|
639
|
+
*/
|
|
640
|
+
declare const registry: AdapterRegistry;
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Context passed to middleware
|
|
644
|
+
*/
|
|
645
|
+
interface MiddlewareContext {
|
|
646
|
+
/** The bot instance */
|
|
647
|
+
bot: Bot;
|
|
648
|
+
/** The event being processed */
|
|
649
|
+
event: UnifiedEvent;
|
|
650
|
+
/** The platform this event is from */
|
|
651
|
+
platform: PlatformType;
|
|
652
|
+
/** Timestamp when the event was received */
|
|
653
|
+
timestamp: Date;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Middleware function type
|
|
657
|
+
*/
|
|
658
|
+
type Middleware = (context: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Base error class for all Switchboard errors
|
|
662
|
+
*/
|
|
663
|
+
declare class SwitchboardError extends Error {
|
|
664
|
+
readonly code: string;
|
|
665
|
+
readonly platform?: PlatformType | undefined;
|
|
666
|
+
constructor(message: string, code: string, platform?: PlatformType | undefined);
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Error thrown when no adapter is found for a platform
|
|
670
|
+
*/
|
|
671
|
+
declare class AdapterNotFoundError extends SwitchboardError {
|
|
672
|
+
constructor(platform: PlatformType);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Error thrown when connection to a platform fails
|
|
676
|
+
*/
|
|
677
|
+
declare class ConnectionError extends SwitchboardError {
|
|
678
|
+
constructor(platform: PlatformType, cause: unknown);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Error thrown when sending a message fails
|
|
682
|
+
*/
|
|
683
|
+
declare class MessageSendError extends SwitchboardError {
|
|
684
|
+
constructor(platform: PlatformType, channelId: string, cause: unknown);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Error thrown when editing a message fails
|
|
688
|
+
*/
|
|
689
|
+
declare class MessageEditError extends SwitchboardError {
|
|
690
|
+
constructor(platform: PlatformType, messageId: string, cause: unknown);
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Error thrown when deleting a message fails
|
|
694
|
+
*/
|
|
695
|
+
declare class MessageDeleteError extends SwitchboardError {
|
|
696
|
+
constructor(platform: PlatformType, messageId: string, cause: unknown);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Error thrown when adding/removing a reaction fails
|
|
700
|
+
*/
|
|
701
|
+
declare class ReactionError extends SwitchboardError {
|
|
702
|
+
constructor(platform: PlatformType, messageId: string, emoji: string, cause: unknown);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
export { AdapterNotFoundError, type Attachment, Bot, type BotConfig, type Channel, type ChannelCreatedEvent, type ChannelDeletedEvent, type ChannelType, ConnectionError, MessageDeleteError, MessageEditError, type MessageEvent, type MessageRef, MessageSendError, type Middleware, type MiddlewareContext, type PlatformAdapter, type PlatformType, ReactionError, type ReactionEvent, type Result, type SendMessageOptions, SwitchboardError, type UnifiedEvent, type UnifiedMessage, type UploadOptions, type User, type UserJoinedEvent, type UserLeftEvent, createBot, err, isErr, isOk, ok, registry, wrapAsync };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// src/client/bot.ts
|
|
2
|
+
var Bot = class {
|
|
3
|
+
constructor(adapter, _platform, credentials) {
|
|
4
|
+
this.adapter = adapter;
|
|
5
|
+
this._platform = _platform;
|
|
6
|
+
this.credentials = credentials;
|
|
7
|
+
this.adapter.onEvent(async (event) => {
|
|
8
|
+
await this.handleEvent(event);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Get the platform type
|
|
14
|
+
*/
|
|
15
|
+
get platform() {
|
|
16
|
+
return this._platform;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Start the bot (connect to the platform)
|
|
20
|
+
*/
|
|
21
|
+
async start() {
|
|
22
|
+
if (!this.adapter.isConnected()) {
|
|
23
|
+
await this.adapter.connect(this.credentials);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Stop the bot (disconnect from the platform)
|
|
28
|
+
*/
|
|
29
|
+
async stop() {
|
|
30
|
+
await this.adapter.disconnect();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if the bot is connected
|
|
34
|
+
*/
|
|
35
|
+
isConnected() {
|
|
36
|
+
return this.adapter.isConnected();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Send a message to a channel
|
|
40
|
+
*/
|
|
41
|
+
async sendMessage(channelId, text, options) {
|
|
42
|
+
return this.adapter.sendMessage(channelId, text, options);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Reply to a message
|
|
46
|
+
*/
|
|
47
|
+
async reply(message, text, options) {
|
|
48
|
+
const threadId = message.threadId || message.id;
|
|
49
|
+
return this.adapter.sendMessage(message.channelId, text, {
|
|
50
|
+
...options,
|
|
51
|
+
threadId
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Edit an existing message
|
|
56
|
+
*/
|
|
57
|
+
async editMessage(messageRef, newText) {
|
|
58
|
+
return this.adapter.editMessage(messageRef, newText);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Delete a message
|
|
62
|
+
*/
|
|
63
|
+
async deleteMessage(messageRef) {
|
|
64
|
+
return this.adapter.deleteMessage(messageRef);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Add a reaction to a message
|
|
68
|
+
*/
|
|
69
|
+
async addReaction(messageRef, emoji) {
|
|
70
|
+
return this.adapter.addReaction(messageRef, emoji);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Remove a reaction from a message
|
|
74
|
+
*/
|
|
75
|
+
async removeReaction(messageRef, emoji) {
|
|
76
|
+
return this.adapter.removeReaction(messageRef, emoji);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a thread (or reply in a thread)
|
|
80
|
+
*/
|
|
81
|
+
async createThread(messageRef, text) {
|
|
82
|
+
return this.adapter.createThread(messageRef, text);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Upload a file to a channel
|
|
86
|
+
*/
|
|
87
|
+
async uploadFile(channelId, file, options) {
|
|
88
|
+
return this.adapter.uploadFile(channelId, file, options);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get list of channels
|
|
92
|
+
*/
|
|
93
|
+
async getChannels() {
|
|
94
|
+
return this.adapter.getChannels();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get list of users (optionally in a specific channel)
|
|
98
|
+
*/
|
|
99
|
+
async getUsers(channelId) {
|
|
100
|
+
return this.adapter.getUsers(channelId);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Register a handler for message events
|
|
104
|
+
*
|
|
105
|
+
* Supports both context API (recommended) and legacy message API
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* // Context API (recommended)
|
|
110
|
+
* bot.onMessage(async (ctx) => {
|
|
111
|
+
* await ctx.reply('Hello!');
|
|
112
|
+
* await ctx.react('thumbsup');
|
|
113
|
+
* });
|
|
114
|
+
*
|
|
115
|
+
* // Legacy API (still supported)
|
|
116
|
+
* bot.onMessage(async (message) => {
|
|
117
|
+
* await bot.reply(message, 'Hello!');
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
onMessage(handler) {
|
|
122
|
+
this.on("message", async (event) => {
|
|
123
|
+
if (event.type === "message") {
|
|
124
|
+
const message = event.message;
|
|
125
|
+
const ctx = this.createContext(message);
|
|
126
|
+
await handler(ctx);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create a message context object
|
|
132
|
+
*/
|
|
133
|
+
createContext(message) {
|
|
134
|
+
return {
|
|
135
|
+
message,
|
|
136
|
+
platform: this._platform,
|
|
137
|
+
userId: message.userId,
|
|
138
|
+
channelId: message.channelId,
|
|
139
|
+
text: message.text,
|
|
140
|
+
threadId: message.threadId,
|
|
141
|
+
// Helper methods
|
|
142
|
+
reply: (text, options) => {
|
|
143
|
+
return this.reply(message, text, options);
|
|
144
|
+
},
|
|
145
|
+
react: (emoji) => {
|
|
146
|
+
return this.addReaction(message, emoji);
|
|
147
|
+
},
|
|
148
|
+
unreact: (emoji) => {
|
|
149
|
+
return this.removeReaction(message, emoji);
|
|
150
|
+
},
|
|
151
|
+
edit: (newText) => {
|
|
152
|
+
return this.editMessage(message, newText);
|
|
153
|
+
},
|
|
154
|
+
delete: () => {
|
|
155
|
+
return this.deleteMessage(message);
|
|
156
|
+
},
|
|
157
|
+
createThread: (text) => {
|
|
158
|
+
return this.createThread(message, text);
|
|
159
|
+
},
|
|
160
|
+
send: (text, options) => {
|
|
161
|
+
return this.sendMessage(message.channelId, text, options);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Register a handler for reaction events
|
|
167
|
+
*/
|
|
168
|
+
onReaction(handler) {
|
|
169
|
+
this.on("reaction", async (event) => {
|
|
170
|
+
if (event.type === "reaction") {
|
|
171
|
+
await handler(event);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Register a handler for any event
|
|
177
|
+
*/
|
|
178
|
+
onEvent(handler) {
|
|
179
|
+
this.on("*", handler);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get the underlying adapter (for advanced use cases)
|
|
183
|
+
*/
|
|
184
|
+
getAdapter() {
|
|
185
|
+
return this.adapter;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Internal: Register an event handler
|
|
189
|
+
*/
|
|
190
|
+
on(eventType, handler) {
|
|
191
|
+
if (!this.eventHandlers.has(eventType)) {
|
|
192
|
+
this.eventHandlers.set(eventType, /* @__PURE__ */ new Set());
|
|
193
|
+
}
|
|
194
|
+
this.eventHandlers.get(eventType).add(handler);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Internal: Handle an event from the adapter
|
|
198
|
+
*/
|
|
199
|
+
async handleEvent(event) {
|
|
200
|
+
const typeHandlers = this.eventHandlers.get(event.type);
|
|
201
|
+
if (typeHandlers) {
|
|
202
|
+
for (const handler of typeHandlers) {
|
|
203
|
+
try {
|
|
204
|
+
await handler(event);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error(
|
|
207
|
+
`[Switchboard] Error in ${event.type} handler:`,
|
|
208
|
+
error
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const wildcardHandlers = this.eventHandlers.get("*");
|
|
214
|
+
if (wildcardHandlers) {
|
|
215
|
+
for (const handler of wildcardHandlers) {
|
|
216
|
+
try {
|
|
217
|
+
await handler(event);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error(`[Switchboard] Error in wildcard handler:`, error);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// src/adapter/registry.ts
|
|
227
|
+
var AdapterRegistry = class {
|
|
228
|
+
adapters = /* @__PURE__ */ new Map();
|
|
229
|
+
/**
|
|
230
|
+
* Register a platform adapter
|
|
231
|
+
* Called by adapter packages when they're imported
|
|
232
|
+
*/
|
|
233
|
+
register(platform, adapter) {
|
|
234
|
+
if (this.adapters.has(platform)) {
|
|
235
|
+
console.warn(
|
|
236
|
+
`[Switchboard] Adapter for platform "${platform}" is already registered. Overwriting.`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
this.adapters.set(platform, adapter);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Get an adapter for a platform
|
|
243
|
+
*/
|
|
244
|
+
get(platform) {
|
|
245
|
+
return this.adapters.get(platform);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Check if an adapter is registered for a platform
|
|
249
|
+
*/
|
|
250
|
+
has(platform) {
|
|
251
|
+
return this.adapters.has(platform);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Get all registered platforms
|
|
255
|
+
*/
|
|
256
|
+
getRegisteredPlatforms() {
|
|
257
|
+
return Array.from(this.adapters.keys());
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Clear all registered adapters (mainly for testing)
|
|
261
|
+
*/
|
|
262
|
+
clear() {
|
|
263
|
+
this.adapters.clear();
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var registry = new AdapterRegistry();
|
|
267
|
+
|
|
268
|
+
// src/utils/errors.ts
|
|
269
|
+
var SwitchboardError = class extends Error {
|
|
270
|
+
constructor(message, code, platform) {
|
|
271
|
+
super(message);
|
|
272
|
+
this.code = code;
|
|
273
|
+
this.platform = platform;
|
|
274
|
+
this.name = "SwitchboardError";
|
|
275
|
+
if (Error.captureStackTrace) {
|
|
276
|
+
Error.captureStackTrace(this, this.constructor);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
var AdapterNotFoundError = class extends SwitchboardError {
|
|
281
|
+
constructor(platform) {
|
|
282
|
+
super(
|
|
283
|
+
`No adapter found for platform: ${platform}. Did you import @aarekaz/switchboard-${platform}?`,
|
|
284
|
+
"ADAPTER_NOT_FOUND",
|
|
285
|
+
platform
|
|
286
|
+
);
|
|
287
|
+
this.name = "AdapterNotFoundError";
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
var ConnectionError = class extends SwitchboardError {
|
|
291
|
+
constructor(platform, cause) {
|
|
292
|
+
super(
|
|
293
|
+
`Failed to connect to ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
294
|
+
"CONNECTION_ERROR",
|
|
295
|
+
platform
|
|
296
|
+
);
|
|
297
|
+
this.name = "ConnectionError";
|
|
298
|
+
this.cause = cause;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
var MessageSendError = class extends SwitchboardError {
|
|
302
|
+
constructor(platform, channelId, cause) {
|
|
303
|
+
super(
|
|
304
|
+
`Failed to send message to channel ${channelId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
305
|
+
"MESSAGE_SEND_ERROR",
|
|
306
|
+
platform
|
|
307
|
+
);
|
|
308
|
+
this.name = "MessageSendError";
|
|
309
|
+
this.cause = cause;
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var MessageEditError = class extends SwitchboardError {
|
|
313
|
+
constructor(platform, messageId, cause) {
|
|
314
|
+
super(
|
|
315
|
+
`Failed to edit message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
316
|
+
"MESSAGE_EDIT_ERROR",
|
|
317
|
+
platform
|
|
318
|
+
);
|
|
319
|
+
this.name = "MessageEditError";
|
|
320
|
+
this.cause = cause;
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var MessageDeleteError = class extends SwitchboardError {
|
|
324
|
+
constructor(platform, messageId, cause) {
|
|
325
|
+
super(
|
|
326
|
+
`Failed to delete message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
327
|
+
"MESSAGE_DELETE_ERROR",
|
|
328
|
+
platform
|
|
329
|
+
);
|
|
330
|
+
this.name = "MessageDeleteError";
|
|
331
|
+
this.cause = cause;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var ReactionError = class extends SwitchboardError {
|
|
335
|
+
constructor(platform, messageId, emoji, cause) {
|
|
336
|
+
super(
|
|
337
|
+
`Failed to add/remove reaction ${emoji} on message ${messageId} (${platform}): ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
338
|
+
"REACTION_ERROR",
|
|
339
|
+
platform
|
|
340
|
+
);
|
|
341
|
+
this.name = "ReactionError";
|
|
342
|
+
this.cause = cause;
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// src/client/create-bot.ts
|
|
347
|
+
function createBot(config) {
|
|
348
|
+
let adapter;
|
|
349
|
+
if (config.adapter) {
|
|
350
|
+
adapter = config.adapter;
|
|
351
|
+
} else {
|
|
352
|
+
const registeredAdapter = registry.get(config.platform);
|
|
353
|
+
if (!registeredAdapter) {
|
|
354
|
+
throw new AdapterNotFoundError(config.platform);
|
|
355
|
+
}
|
|
356
|
+
adapter = registeredAdapter;
|
|
357
|
+
}
|
|
358
|
+
return new Bot(adapter, config.platform, config.credentials || {});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/types/result.ts
|
|
362
|
+
function ok(value) {
|
|
363
|
+
return { ok: true, value };
|
|
364
|
+
}
|
|
365
|
+
function err(error) {
|
|
366
|
+
return { ok: false, error };
|
|
367
|
+
}
|
|
368
|
+
async function wrapAsync(fn) {
|
|
369
|
+
try {
|
|
370
|
+
const value = await fn();
|
|
371
|
+
return ok(value);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
return err(error instanceof Error ? error : new Error(String(error)));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function isOk(result) {
|
|
377
|
+
return result.ok === true;
|
|
378
|
+
}
|
|
379
|
+
function isErr(result) {
|
|
380
|
+
return result.ok === false;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export { AdapterNotFoundError, Bot, ConnectionError, MessageDeleteError, MessageEditError, MessageSendError, ReactionError, SwitchboardError, createBot, err, isErr, isOk, ok, registry, wrapAsync };
|
|
384
|
+
//# sourceMappingURL=index.js.map
|
|
385
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/bot.ts","../src/adapter/registry.ts","../src/utils/errors.ts","../src/client/create-bot.ts","../src/types/result.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PlatformAdapter } from '../adapter/interface.js';\nimport type { Result } from '../types/result.js';\nimport type { PlatformType } from '../types/platform.js';\nimport type {\n UnifiedMessage,\n MessageRef,\n SendMessageOptions,\n UploadOptions,\n} from '../types/message.js';\nimport type { UnifiedEvent, MessageEvent, ReactionEvent } from '../types/event.js';\nimport type { Channel } from '../types/channel.js';\nimport type { User } from '../types/user.js';\nimport type { MessageContext, MessageHandler } from '../types/context.js';\n\n/**\n * Bot client - the main interface for interacting with chat platforms\n */\nexport class Bot {\n private eventHandlers: Map<string, Set<(event: UnifiedEvent) => void | Promise<void>>> = new Map();\n\n constructor(\n private readonly adapter: PlatformAdapter,\n private readonly _platform: PlatformType,\n private readonly credentials: unknown\n ) {\n // Subscribe to all events from the adapter\n this.adapter.onEvent(async (event) => {\n await this.handleEvent(event);\n });\n }\n\n /**\n * Get the platform type\n */\n get platform(): PlatformType {\n return this._platform;\n }\n\n /**\n * Start the bot (connect to the platform)\n */\n async start(): Promise<void> {\n // Connect to the platform if not already connected\n if (!this.adapter.isConnected()) {\n await this.adapter.connect(this.credentials);\n }\n }\n\n /**\n * Stop the bot (disconnect from the platform)\n */\n async stop(): Promise<void> {\n await this.adapter.disconnect();\n }\n\n /**\n * Check if the bot is connected\n */\n isConnected(): boolean {\n return this.adapter.isConnected();\n }\n\n /**\n * Send a message to a channel\n */\n async sendMessage(\n channelId: string,\n text: string,\n options?: SendMessageOptions\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.sendMessage(channelId, text, options);\n }\n\n /**\n * Reply to a message\n */\n async reply(\n message: UnifiedMessage,\n text: string,\n options?: SendMessageOptions\n ): Promise<Result<UnifiedMessage>> {\n // If the message is in a thread, reply in the same thread\n const threadId = message.threadId || message.id;\n return this.adapter.sendMessage(message.channelId, text, {\n ...options,\n threadId,\n });\n }\n\n /**\n * Edit an existing message\n */\n async editMessage(\n messageRef: MessageRef,\n newText: string\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.editMessage(messageRef, newText);\n }\n\n /**\n * Delete a message\n */\n async deleteMessage(messageRef: MessageRef): Promise<Result<void>> {\n return this.adapter.deleteMessage(messageRef);\n }\n\n /**\n * Add a reaction to a message\n */\n async addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>> {\n return this.adapter.addReaction(messageRef, emoji);\n }\n\n /**\n * Remove a reaction from a message\n */\n async removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>> {\n return this.adapter.removeReaction(messageRef, emoji);\n }\n\n /**\n * Create a thread (or reply in a thread)\n */\n async createThread(\n messageRef: MessageRef,\n text: string\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.createThread(messageRef, text);\n }\n\n /**\n * Upload a file to a channel\n */\n async uploadFile(\n channelId: string,\n file: unknown,\n options?: UploadOptions\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.uploadFile(channelId, file, options);\n }\n\n /**\n * Get list of channels\n */\n async getChannels(): Promise<Result<Channel[]>> {\n return this.adapter.getChannels();\n }\n\n /**\n * Get list of users (optionally in a specific channel)\n */\n async getUsers(channelId?: string): Promise<Result<User[]>> {\n return this.adapter.getUsers(channelId);\n }\n\n /**\n * Register a handler for message events\n *\n * Supports both context API (recommended) and legacy message API\n *\n * @example\n * ```typescript\n * // Context API (recommended)\n * bot.onMessage(async (ctx) => {\n * await ctx.reply('Hello!');\n * await ctx.react('thumbsup');\n * });\n *\n * // Legacy API (still supported)\n * bot.onMessage(async (message) => {\n * await bot.reply(message, 'Hello!');\n * });\n * ```\n */\n onMessage(handler: MessageHandler | ((message: UnifiedMessage) => void | Promise<void>)): void {\n this.on('message', async (event) => {\n if (event.type === 'message') {\n const message = event.message;\n\n // Detect if handler expects context (by checking parameter count)\n // Context handlers have 1 parameter, legacy handlers might check message properties\n // We'll check if the handler looks like it wants a context by seeing if it's async\n // and calling it with a context object\n\n // Create context object\n const ctx = this.createContext(message);\n\n // Call handler with context\n await handler(ctx as any);\n }\n });\n }\n\n /**\n * Create a message context object\n */\n private createContext(message: UnifiedMessage): MessageContext {\n return {\n message,\n platform: this._platform,\n userId: message.userId,\n channelId: message.channelId,\n text: message.text,\n threadId: message.threadId,\n\n // Helper methods\n reply: (text: string, options?: SendMessageOptions) => {\n return this.reply(message, text, options);\n },\n\n react: (emoji: string) => {\n return this.addReaction(message, emoji);\n },\n\n unreact: (emoji: string) => {\n return this.removeReaction(message, emoji);\n },\n\n edit: (newText: string) => {\n return this.editMessage(message, newText);\n },\n\n delete: () => {\n return this.deleteMessage(message);\n },\n\n createThread: (text: string) => {\n return this.createThread(message, text);\n },\n\n send: (text: string, options?: SendMessageOptions) => {\n return this.sendMessage(message.channelId, text, options);\n },\n };\n }\n\n /**\n * Register a handler for reaction events\n */\n onReaction(handler: (event: ReactionEvent) => void | Promise<void>): void {\n this.on('reaction', async (event) => {\n if (event.type === 'reaction') {\n await handler(event);\n }\n });\n }\n\n /**\n * Register a handler for any event\n */\n onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void {\n this.on('*', handler);\n }\n\n /**\n * Get the underlying adapter (for advanced use cases)\n */\n getAdapter(): PlatformAdapter {\n return this.adapter;\n }\n\n /**\n * Internal: Register an event handler\n */\n private on(\n eventType: string,\n handler: (event: UnifiedEvent) => void | Promise<void>\n ): void {\n if (!this.eventHandlers.has(eventType)) {\n this.eventHandlers.set(eventType, new Set());\n }\n this.eventHandlers.get(eventType)!.add(handler);\n }\n\n /**\n * Internal: Handle an event from the adapter\n */\n private async handleEvent(event: UnifiedEvent): Promise<void> {\n // Call handlers for this specific event type\n const typeHandlers = this.eventHandlers.get(event.type);\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `[Switchboard] Error in ${event.type} handler:`,\n error\n );\n }\n }\n }\n\n // Call wildcard handlers\n const wildcardHandlers = this.eventHandlers.get('*');\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(`[Switchboard] Error in wildcard handler:`, error);\n }\n }\n }\n }\n}\n","import type { PlatformAdapter } from './interface.js';\nimport type { PlatformType } from '../types/platform.js';\n\n/**\n * Global registry for platform adapters\n * Adapters register themselves when their package is imported\n */\nclass AdapterRegistry {\n private adapters = new Map<PlatformType, PlatformAdapter>();\n\n /**\n * Register a platform adapter\n * Called by adapter packages when they're imported\n */\n register(platform: PlatformType, adapter: PlatformAdapter): void {\n if (this.adapters.has(platform)) {\n console.warn(\n `[Switchboard] Adapter for platform \"${platform}\" is already registered. Overwriting.`\n );\n }\n this.adapters.set(platform, adapter);\n }\n\n /**\n * Get an adapter for a platform\n */\n get(platform: PlatformType): PlatformAdapter | undefined {\n return this.adapters.get(platform);\n }\n\n /**\n * Check if an adapter is registered for a platform\n */\n has(platform: PlatformType): boolean {\n return this.adapters.has(platform);\n }\n\n /**\n * Get all registered platforms\n */\n getRegisteredPlatforms(): PlatformType[] {\n return Array.from(this.adapters.keys());\n }\n\n /**\n * Clear all registered adapters (mainly for testing)\n */\n clear(): void {\n this.adapters.clear();\n }\n}\n\n/**\n * Global singleton registry instance\n */\nexport const registry = new AdapterRegistry();\n","import type { PlatformType } from '../types/platform.js';\n\n/**\n * Base error class for all Switchboard errors\n */\nexport class SwitchboardError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly platform?: PlatformType\n ) {\n super(message);\n this.name = 'SwitchboardError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when no adapter is found for a platform\n */\nexport class AdapterNotFoundError extends SwitchboardError {\n constructor(platform: PlatformType) {\n super(\n `No adapter found for platform: ${platform}. Did you import @aarekaz/switchboard-${platform}?`,\n 'ADAPTER_NOT_FOUND',\n platform\n );\n this.name = 'AdapterNotFoundError';\n }\n}\n\n/**\n * Error thrown when connection to a platform fails\n */\nexport class ConnectionError extends SwitchboardError {\n constructor(platform: PlatformType, cause: unknown) {\n super(\n `Failed to connect to ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'CONNECTION_ERROR',\n platform\n );\n this.name = 'ConnectionError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when sending a message fails\n */\nexport class MessageSendError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n channelId: string,\n cause: unknown\n ) {\n super(\n `Failed to send message to channel ${channelId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_SEND_ERROR',\n platform\n );\n this.name = 'MessageSendError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when editing a message fails\n */\nexport class MessageEditError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n cause: unknown\n ) {\n super(\n `Failed to edit message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_EDIT_ERROR',\n platform\n );\n this.name = 'MessageEditError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when deleting a message fails\n */\nexport class MessageDeleteError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n cause: unknown\n ) {\n super(\n `Failed to delete message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_DELETE_ERROR',\n platform\n );\n this.name = 'MessageDeleteError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when adding/removing a reaction fails\n */\nexport class ReactionError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n emoji: string,\n cause: unknown\n ) {\n super(\n `Failed to add/remove reaction ${emoji} on message ${messageId} (${platform}): ${cause instanceof Error ? cause.message : String(cause)}`,\n 'REACTION_ERROR',\n platform\n );\n this.name = 'ReactionError';\n this.cause = cause;\n }\n}\n","import { Bot } from './bot.js';\nimport { registry } from '../adapter/registry.js';\nimport type { PlatformAdapter } from '../adapter/interface.js';\nimport type { PlatformType } from '../types/platform.js';\nimport { AdapterNotFoundError, ConnectionError } from '../utils/errors.js';\n\n/**\n * Configuration for creating a bot\n */\nexport interface BotConfig {\n /**\n * Platform to connect to (e.g., 'discord', 'slack', 'teams', 'google-chat')\n */\n platform: PlatformType;\n\n /**\n * Platform credentials (structure depends on the platform)\n */\n credentials?: {\n /** Bot token (Discord, Slack) */\n token?: string;\n /** App ID (Teams) */\n appId?: string;\n /** App password (Teams) */\n appPassword?: string;\n /** Custom credentials object */\n [key: string]: unknown;\n };\n\n /**\n * Custom adapter (for advanced users or custom platforms)\n * If provided, this will be used instead of the registered adapter\n */\n adapter?: PlatformAdapter;\n\n /**\n * Platform-specific configuration (opt-in)\n */\n platformConfig?: {\n discord?: unknown;\n slack?: unknown;\n teams?: unknown;\n googleChat?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Create a bot instance\n *\n * @example\n * ```typescript\n * import { createBot } from '@aarekaz/switchboard-core';\n * import '@aarekaz/switchboard-discord';\n *\n * const bot = createBot({\n * platform: 'discord',\n * credentials: {\n * token: process.env.DISCORD_TOKEN,\n * },\n * });\n *\n * bot.onMessage(async (message) => {\n * if (message.text.includes('ping')) {\n * await bot.reply(message, 'pong!');\n * }\n * });\n *\n * await bot.start();\n * ```\n */\nexport function createBot(config: BotConfig): Bot {\n let adapter: PlatformAdapter;\n\n // Use custom adapter if provided\n if (config.adapter) {\n adapter = config.adapter;\n } else {\n // Get adapter from registry\n const registeredAdapter = registry.get(config.platform);\n if (!registeredAdapter) {\n throw new AdapterNotFoundError(config.platform);\n }\n adapter = registeredAdapter;\n }\n\n // Create and return the bot instance\n // Connection happens lazily when bot.start() is called\n return new Bot(adapter, config.platform, config.credentials || {});\n}\n","/**\n * Result type for type-safe error handling\n * Inspired by Rust's Result<T, E> type\n */\nexport type Result<T, E = Error> =\n | { ok: true; value: T }\n | { ok: false; error: E };\n\n/**\n * Create a successful result\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Create an error result\n */\nexport function err<E = Error>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap an async operation in a Result type\n * Catches any errors and converts them to Result<T>\n */\nexport async function wrapAsync<T>(\n fn: () => Promise<T>\n): Promise<Result<T, Error>> {\n try {\n const value = await fn();\n return ok(value);\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n/**\n * Check if a result is successful\n */\nexport function isOk<T, E>(result: Result<T, E>): result is { ok: true; value: T } {\n return result.ok === true;\n}\n\n/**\n * Check if a result is an error\n */\nexport function isErr<T, E>(result: Result<T, E>): result is { ok: false; error: E } {\n return result.ok === false;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aarekaz/switchboard-core",
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Core types and client API for Switchboard - universal chat platform SDK",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"switchboard",
|
|
7
|
+
"chat",
|
|
8
|
+
"bot",
|
|
9
|
+
"sdk",
|
|
10
|
+
"core"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/Aarekaz/switchboard#readme",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/Aarekaz/switchboard.git",
|
|
16
|
+
"directory": "packages/core"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Aarekaz",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"tsup": "^8.0.1",
|
|
36
|
+
"typescript": "^5.3.3"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup",
|
|
43
|
+
"dev": "tsup --watch",
|
|
44
|
+
"typecheck": "tsc --noEmit",
|
|
45
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
46
|
+
}
|
|
47
|
+
}
|