@hardlydifficult/chat 1.0.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 +117 -0
- package/dist/Channel.d.ts +41 -0
- package/dist/Channel.d.ts.map +1 -0
- package/dist/Channel.js +105 -0
- package/dist/Channel.js.map +1 -0
- package/dist/ChatClient.d.ts +21 -0
- package/dist/ChatClient.d.ts.map +1 -0
- package/dist/ChatClient.js +11 -0
- package/dist/ChatClient.js.map +1 -0
- package/dist/Message.d.ts +29 -0
- package/dist/Message.d.ts.map +1 -0
- package/dist/Message.js +38 -0
- package/dist/Message.js.map +1 -0
- package/dist/discord/DiscordChatClient.d.ts +49 -0
- package/dist/discord/DiscordChatClient.d.ts.map +1 -0
- package/dist/discord/DiscordChatClient.js +140 -0
- package/dist/discord/DiscordChatClient.js.map +1 -0
- package/dist/discord/index.d.ts +2 -0
- package/dist/discord/index.d.ts.map +1 -0
- package/dist/discord/index.js +2 -0
- package/dist/discord/index.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/slack/SlackChatClient.d.ts +32 -0
- package/dist/slack/SlackChatClient.d.ts.map +1 -0
- package/dist/slack/SlackChatClient.js +109 -0
- package/dist/slack/SlackChatClient.js.map +1 -0
- package/dist/slack/index.d.ts +2 -0
- package/dist/slack/index.d.ts.map +1 -0
- package/dist/slack/index.js +2 -0
- package/dist/slack/index.js.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @hardlydifficult/chat
|
|
2
|
+
|
|
3
|
+
Unified API for posting messages with emoji reactions to Discord and Slack.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hardlydifficult/chat
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createChatClient } from '@hardlydifficult/chat';
|
|
15
|
+
|
|
16
|
+
// Uses DISCORD_TOKEN and DISCORD_GUILD_ID env vars
|
|
17
|
+
const client = createChatClient({ type: 'discord' });
|
|
18
|
+
|
|
19
|
+
// Or for Slack: uses SLACK_BOT_TOKEN and SLACK_APP_TOKEN env vars
|
|
20
|
+
const client = createChatClient({ type: 'slack' });
|
|
21
|
+
|
|
22
|
+
const channel = await client.connect(channelId);
|
|
23
|
+
|
|
24
|
+
await channel
|
|
25
|
+
.postMessage('Vote: (1) Pizza, (2) Burgers, (3) Salad')
|
|
26
|
+
.addReactions(['1️⃣', '2️⃣', '3️⃣']);
|
|
27
|
+
|
|
28
|
+
channel.onReaction((event) => {
|
|
29
|
+
console.log(`${event.user.username} voted ${event.emoji}`);
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## API
|
|
34
|
+
|
|
35
|
+
### `createChatClient(config)`
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// Discord - env vars: DISCORD_TOKEN, DISCORD_GUILD_ID
|
|
39
|
+
createChatClient({ type: 'discord' });
|
|
40
|
+
createChatClient({ type: 'discord', token: '...', guildId: '...' }); // override
|
|
41
|
+
|
|
42
|
+
// Slack - env vars: SLACK_BOT_TOKEN, SLACK_APP_TOKEN
|
|
43
|
+
createChatClient({ type: 'slack' });
|
|
44
|
+
createChatClient({ type: 'slack', token: '...', appToken: '...' }); // override
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### `client.connect(channelId): Promise<Channel>`
|
|
48
|
+
|
|
49
|
+
Connect to a channel.
|
|
50
|
+
|
|
51
|
+
### `channel.postMessage(text): Message`
|
|
52
|
+
|
|
53
|
+
Post a message. Returns a chainable `Message`.
|
|
54
|
+
|
|
55
|
+
### `message.addReactions(emojis): Message`
|
|
56
|
+
|
|
57
|
+
Add reactions. Chainable and awaitable.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
await channel.postMessage('Pick one').addReactions(['1️⃣', '2️⃣']);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `channel.onReaction(callback): () => void`
|
|
64
|
+
|
|
65
|
+
Listen for reactions. Returns unsubscribe function.
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const unsubscribe = channel.onReaction((event) => {
|
|
69
|
+
// event.emoji, event.user.id, event.user.username, event.messageId
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `client.disconnect(): Promise<void>`
|
|
74
|
+
|
|
75
|
+
Disconnect from the platform.
|
|
76
|
+
|
|
77
|
+
## Example: Poll
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { createChatClient } from '@hardlydifficult/chat';
|
|
81
|
+
|
|
82
|
+
const client = createChatClient({ type: 'discord' });
|
|
83
|
+
const channel = await client.connect(process.env.CHANNEL_ID);
|
|
84
|
+
|
|
85
|
+
const options = ['Pizza', 'Burgers', 'Salad'];
|
|
86
|
+
const emojis = ['1️⃣', '2️⃣', '3️⃣'];
|
|
87
|
+
|
|
88
|
+
const message = await channel
|
|
89
|
+
.postMessage(options.map((o, i) => `${emojis[i]} ${o}`).join('\n'))
|
|
90
|
+
.addReactions(emojis);
|
|
91
|
+
|
|
92
|
+
channel.onReaction((event) => {
|
|
93
|
+
if (event.messageId !== message.id) return;
|
|
94
|
+
|
|
95
|
+
const choice = options[emojis.indexOf(event.emoji)];
|
|
96
|
+
if (choice) {
|
|
97
|
+
console.log(`${event.user.username} voted for ${choice}`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Platform Setup
|
|
103
|
+
|
|
104
|
+
### Discord
|
|
105
|
+
|
|
106
|
+
1. Create bot at [Discord Developer Portal](https://discord.com/developers/applications)
|
|
107
|
+
2. Enable Gateway Intents: `GUILDS`, `GUILD_MESSAGES`, `GUILD_MESSAGE_REACTIONS`
|
|
108
|
+
3. Bot permissions: `Send Messages`, `Add Reactions`, `Read Message History`
|
|
109
|
+
4. Set `DISCORD_TOKEN` and `DISCORD_GUILD_ID` env vars
|
|
110
|
+
|
|
111
|
+
### Slack
|
|
112
|
+
|
|
113
|
+
1. Create app at [Slack API](https://api.slack.com/apps)
|
|
114
|
+
2. Enable Socket Mode, generate App Token
|
|
115
|
+
3. Bot scopes: `chat:write`, `reactions:write`, `reactions:read`
|
|
116
|
+
4. Subscribe to: `reaction_added`
|
|
117
|
+
5. Set `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN` env vars
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Platform, ReactionCallback, MessageData } from './types.js';
|
|
2
|
+
import { Message, type ReactionAdder } from './Message.js';
|
|
3
|
+
/**
|
|
4
|
+
* Interface for platform-specific channel operations
|
|
5
|
+
*/
|
|
6
|
+
export interface ChannelOperations extends ReactionAdder {
|
|
7
|
+
postMessage(channelId: string, text: string): Promise<MessageData>;
|
|
8
|
+
subscribeToReactions(channelId: string, callback: ReactionCallback): () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Represents a connected channel with messaging capabilities
|
|
12
|
+
*/
|
|
13
|
+
export declare class Channel {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly platform: Platform;
|
|
16
|
+
private operations;
|
|
17
|
+
private reactionCallbacks;
|
|
18
|
+
private unsubscribeFromPlatform;
|
|
19
|
+
constructor(id: string, platform: Platform, operations: ChannelOperations);
|
|
20
|
+
/**
|
|
21
|
+
* Post a message to this channel
|
|
22
|
+
* @param text - Message content
|
|
23
|
+
* @returns Message object with chainable reaction methods
|
|
24
|
+
*/
|
|
25
|
+
postMessage(text: string): Message;
|
|
26
|
+
/**
|
|
27
|
+
* Register a callback for reaction events on this channel
|
|
28
|
+
* @param callback - Function called when users add reactions
|
|
29
|
+
* @returns Unsubscribe function
|
|
30
|
+
*/
|
|
31
|
+
onReaction(callback: ReactionCallback): () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Emit a reaction event to all registered callbacks
|
|
34
|
+
*/
|
|
35
|
+
private emitReaction;
|
|
36
|
+
/**
|
|
37
|
+
* Disconnect from this channel (cleanup)
|
|
38
|
+
*/
|
|
39
|
+
disconnect(): void;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=Channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Channel.d.ts","sourceRoot":"","sources":["../src/Channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACnE,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAAC;CACjF;AAED;;GAEG;AACH,qBAAa,OAAO;IAClB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IAEnC,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,uBAAuB,CAA6B;gBAEhD,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB;IAWzE;;;;OAIG;IACH,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAQlC;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAOlD;;OAEG;YACW,YAAY;IAS1B;;OAEG;IACH,UAAU,IAAI,IAAI;CAOnB"}
|
package/dist/Channel.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Message } from './Message.js';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a connected channel with messaging capabilities
|
|
4
|
+
*/
|
|
5
|
+
export class Channel {
|
|
6
|
+
id;
|
|
7
|
+
platform;
|
|
8
|
+
operations;
|
|
9
|
+
reactionCallbacks = new Set();
|
|
10
|
+
unsubscribeFromPlatform = null;
|
|
11
|
+
constructor(id, platform, operations) {
|
|
12
|
+
this.id = id;
|
|
13
|
+
this.platform = platform;
|
|
14
|
+
this.operations = operations;
|
|
15
|
+
// Subscribe to platform reactions and forward to our callbacks
|
|
16
|
+
this.unsubscribeFromPlatform = this.operations.subscribeToReactions(id, (event) => this.emitReaction(event));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Post a message to this channel
|
|
20
|
+
* @param text - Message content
|
|
21
|
+
* @returns Message object with chainable reaction methods
|
|
22
|
+
*/
|
|
23
|
+
postMessage(text) {
|
|
24
|
+
const messagePromise = this.operations.postMessage(this.id, text);
|
|
25
|
+
// Create a Message that will resolve once the post completes
|
|
26
|
+
const pendingMessage = new PendingMessage(messagePromise, this.operations);
|
|
27
|
+
return pendingMessage;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Register a callback for reaction events on this channel
|
|
31
|
+
* @param callback - Function called when users add reactions
|
|
32
|
+
* @returns Unsubscribe function
|
|
33
|
+
*/
|
|
34
|
+
onReaction(callback) {
|
|
35
|
+
this.reactionCallbacks.add(callback);
|
|
36
|
+
return () => {
|
|
37
|
+
this.reactionCallbacks.delete(callback);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Emit a reaction event to all registered callbacks
|
|
42
|
+
*/
|
|
43
|
+
async emitReaction(event) {
|
|
44
|
+
const promises = Array.from(this.reactionCallbacks).map((cb) => Promise.resolve(cb(event)).catch((err) => {
|
|
45
|
+
console.error('Reaction callback error:', err);
|
|
46
|
+
}));
|
|
47
|
+
await Promise.all(promises);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Disconnect from this channel (cleanup)
|
|
51
|
+
*/
|
|
52
|
+
disconnect() {
|
|
53
|
+
if (this.unsubscribeFromPlatform) {
|
|
54
|
+
this.unsubscribeFromPlatform();
|
|
55
|
+
this.unsubscribeFromPlatform = null;
|
|
56
|
+
}
|
|
57
|
+
this.reactionCallbacks.clear();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A Message that is still being posted - supports the same chainable API
|
|
62
|
+
*/
|
|
63
|
+
class PendingMessage extends Message {
|
|
64
|
+
postPromise;
|
|
65
|
+
resolvedData = null;
|
|
66
|
+
constructor(postPromise, reactionAdder) {
|
|
67
|
+
// Initialize with placeholder data
|
|
68
|
+
super({ id: '', channelId: '', platform: 'discord' }, reactionAdder);
|
|
69
|
+
this.postPromise = postPromise;
|
|
70
|
+
// Update our data when the post resolves
|
|
71
|
+
void this.postPromise.then((data) => {
|
|
72
|
+
this.resolvedData = data;
|
|
73
|
+
// Update the readonly properties via Object.defineProperty
|
|
74
|
+
Object.defineProperty(this, 'id', { value: data.id });
|
|
75
|
+
Object.defineProperty(this, 'channelId', { value: data.channelId });
|
|
76
|
+
Object.defineProperty(this, 'platform', { value: data.platform });
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Override addReactions to wait for post to complete first
|
|
81
|
+
*/
|
|
82
|
+
addReactions(emojis) {
|
|
83
|
+
// Chain after the post completes
|
|
84
|
+
void this.postPromise.then(() => {
|
|
85
|
+
super.addReactions(emojis);
|
|
86
|
+
});
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Wait for post and all reactions to complete
|
|
91
|
+
*/
|
|
92
|
+
async then(onFulfilled, onRejected) {
|
|
93
|
+
try {
|
|
94
|
+
await this.postPromise;
|
|
95
|
+
return await super.then(onFulfilled, onRejected);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (onRejected) {
|
|
99
|
+
return onRejected(err);
|
|
100
|
+
}
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=Channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Channel.js","sourceRoot":"","sources":["../src/Channel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAsB,MAAM,cAAc,CAAC;AAU3D;;GAEG;AACH,MAAM,OAAO,OAAO;IACF,EAAE,CAAS;IACX,QAAQ,CAAW;IAE3B,UAAU,CAAoB;IAC9B,iBAAiB,GAAG,IAAI,GAAG,EAAoB,CAAC;IAChD,uBAAuB,GAAwB,IAAI,CAAC;IAE5D,YAAY,EAAU,EAAE,QAAkB,EAAE,UAA6B;QACvE,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,+DAA+D;QAC/D,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAChF,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CACzB,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,IAAY;QACtB,MAAM,cAAc,GAAG,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAElE,6DAA6D;QAC7D,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3E,OAAO,cAAc,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,QAA0B;QACnC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,KAAsC;QAC/D,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAC7D,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YAChD,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,GAAG,CAAC,CAAC;QACjD,CAAC,CAAC,CACH,CAAC;QACF,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,IAAI,CAAC,uBAAuB,EAAE,CAAC;YACjC,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC/B,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACtC,CAAC;QACD,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,cAAe,SAAQ,OAAO;IAC1B,WAAW,CAAuB;IAClC,YAAY,GAAuB,IAAI,CAAC;IAEhD,YAAY,WAAiC,EAAE,aAA4B;QACzE,mCAAmC;QACnC,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,CAAC,CAAC;QACrE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAE/B,yCAAyC;QACzC,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;YAClC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,2DAA2D;YAC3D,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;YACpE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACM,YAAY,CAAC,MAAgB;QACpC,iCAAiC;QACjC,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE;YAC9B,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACM,KAAK,CAAC,IAAI,CACjB,WAA6D,EAC7D,UAA6D;QAE7D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC;YACvB,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACnD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ChatConfig } from './types.js';
|
|
2
|
+
import type { Channel } from './Channel.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for chat platform clients
|
|
5
|
+
* Provides a unified API for Discord and Slack
|
|
6
|
+
*/
|
|
7
|
+
export declare abstract class ChatClient {
|
|
8
|
+
protected readonly config: ChatConfig;
|
|
9
|
+
constructor(config: ChatConfig);
|
|
10
|
+
/**
|
|
11
|
+
* Connect to the chat platform and return a channel object
|
|
12
|
+
* @param channelId - Platform-specific channel identifier
|
|
13
|
+
* @returns Channel object for interacting with the channel
|
|
14
|
+
*/
|
|
15
|
+
abstract connect(channelId: string): Promise<Channel>;
|
|
16
|
+
/**
|
|
17
|
+
* Disconnect from the chat platform
|
|
18
|
+
*/
|
|
19
|
+
abstract disconnect(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=ChatClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatClient.d.ts","sourceRoot":"","sources":["../src/ChatClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;GAGG;AACH,8BAAsB,UAAU;IAClB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,UAAU;gBAAlB,MAAM,EAAE,UAAU;IAEjD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAErD;;OAEG;IACH,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatClient.js","sourceRoot":"","sources":["../src/ChatClient.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,OAAgB,UAAU;IACC;IAA/B,YAA+B,MAAkB;QAAlB,WAAM,GAAN,MAAM,CAAY;IAAG,CAAC;CAatD"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { MessageData, Platform } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Interface for the platform-specific reaction adder
|
|
4
|
+
*/
|
|
5
|
+
export interface ReactionAdder {
|
|
6
|
+
addReaction(messageId: string, channelId: string, emoji: string): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Represents a posted message with chainable reaction methods
|
|
10
|
+
*/
|
|
11
|
+
export declare class Message {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly channelId: string;
|
|
14
|
+
readonly platform: Platform;
|
|
15
|
+
private pendingReactions;
|
|
16
|
+
private reactionAdder;
|
|
17
|
+
constructor(data: MessageData, reactionAdder: ReactionAdder);
|
|
18
|
+
/**
|
|
19
|
+
* Add multiple emoji reactions to this message
|
|
20
|
+
* @param emojis - Array of emojis to add
|
|
21
|
+
* @returns this for chaining
|
|
22
|
+
*/
|
|
23
|
+
addReactions(emojis: string[]): this;
|
|
24
|
+
/**
|
|
25
|
+
* Wait for all pending reactions to complete
|
|
26
|
+
*/
|
|
27
|
+
then<T>(onFulfilled?: ((value: Message) => T | PromiseLike<T>) | null, _onRejected?: ((reason: unknown) => T | PromiseLike<T>) | null): Promise<T>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=Message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../src/Message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjF;AAED;;GAEG;AACH,qBAAa,OAAO;IAClB,SAAgB,EAAE,EAAE,MAAM,CAAC;IAC3B,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IAEnC,OAAO,CAAC,gBAAgB,CAAoC;IAC5D,OAAO,CAAC,aAAa,CAAgB;gBAEzB,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa;IAO3D;;;;OAIG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI;IASpC;;OAEG;IACG,IAAI,CAAC,CAAC,EACV,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAC7D,WAAW,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,OAAO,KAAK,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAC7D,OAAO,CAAC,CAAC,CAAC;CAOd"}
|
package/dist/Message.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a posted message with chainable reaction methods
|
|
3
|
+
*/
|
|
4
|
+
export class Message {
|
|
5
|
+
id;
|
|
6
|
+
channelId;
|
|
7
|
+
platform;
|
|
8
|
+
pendingReactions = Promise.resolve();
|
|
9
|
+
reactionAdder;
|
|
10
|
+
constructor(data, reactionAdder) {
|
|
11
|
+
this.id = data.id;
|
|
12
|
+
this.channelId = data.channelId;
|
|
13
|
+
this.platform = data.platform;
|
|
14
|
+
this.reactionAdder = reactionAdder;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Add multiple emoji reactions to this message
|
|
18
|
+
* @param emojis - Array of emojis to add
|
|
19
|
+
* @returns this for chaining
|
|
20
|
+
*/
|
|
21
|
+
addReactions(emojis) {
|
|
22
|
+
for (const emoji of emojis) {
|
|
23
|
+
this.pendingReactions = this.pendingReactions.then(() => this.reactionAdder.addReaction(this.id, this.channelId, emoji));
|
|
24
|
+
}
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wait for all pending reactions to complete
|
|
29
|
+
*/
|
|
30
|
+
async then(onFulfilled, _onRejected) {
|
|
31
|
+
await this.pendingReactions;
|
|
32
|
+
if (onFulfilled) {
|
|
33
|
+
return onFulfilled(this);
|
|
34
|
+
}
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=Message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Message.js","sourceRoot":"","sources":["../src/Message.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,OAAO,OAAO;IACF,EAAE,CAAS;IACX,SAAS,CAAS;IAClB,QAAQ,CAAW;IAE3B,gBAAgB,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IACpD,aAAa,CAAgB;IAErC,YAAY,IAAiB,EAAE,aAA4B;QACzD,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QAClB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,MAAgB;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,CACtD,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAC/D,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CACR,WAA6D,EAC7D,WAA8D;QAE9D,MAAM,IAAI,CAAC,gBAAgB,CAAC;QAC5B,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,IAAoB,CAAC;IAC9B,CAAC;CACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ChatClient } from '../ChatClient.js';
|
|
2
|
+
import { Channel, type ChannelOperations } from '../Channel.js';
|
|
3
|
+
import type { DiscordConfig, ReactionCallback, MessageData } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Discord chat client implementation using discord.js
|
|
6
|
+
*/
|
|
7
|
+
export declare class DiscordChatClient extends ChatClient implements ChannelOperations {
|
|
8
|
+
private client;
|
|
9
|
+
private reactionListeners;
|
|
10
|
+
private readonly token;
|
|
11
|
+
private readonly guildId;
|
|
12
|
+
constructor(config: DiscordConfig);
|
|
13
|
+
/**
|
|
14
|
+
* Set up the global reaction listener that routes events to channel-specific callbacks
|
|
15
|
+
*/
|
|
16
|
+
private setupReactionListener;
|
|
17
|
+
/**
|
|
18
|
+
* Connect to Discord and return a channel object
|
|
19
|
+
* @param channelId - Discord channel ID
|
|
20
|
+
* @returns Channel object for interacting with the channel
|
|
21
|
+
*/
|
|
22
|
+
connect(channelId: string): Promise<Channel>;
|
|
23
|
+
/**
|
|
24
|
+
* Disconnect from Discord
|
|
25
|
+
*/
|
|
26
|
+
disconnect(): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Post a message to a Discord channel
|
|
29
|
+
* @param channelId - Channel to post to
|
|
30
|
+
* @param text - Message content
|
|
31
|
+
* @returns Message data with ID
|
|
32
|
+
*/
|
|
33
|
+
postMessage(channelId: string, text: string): Promise<MessageData>;
|
|
34
|
+
/**
|
|
35
|
+
* Add a reaction to a message
|
|
36
|
+
* @param messageId - Message to react to
|
|
37
|
+
* @param channelId - Channel containing the message
|
|
38
|
+
* @param emoji - Emoji to add
|
|
39
|
+
*/
|
|
40
|
+
addReaction(messageId: string, channelId: string, emoji: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Subscribe to reaction events on a channel
|
|
43
|
+
* @param channelId - Channel to monitor
|
|
44
|
+
* @param callback - Function to call when reactions are added
|
|
45
|
+
* @returns Unsubscribe function
|
|
46
|
+
*/
|
|
47
|
+
subscribeToReactions(channelId: string, callback: ReactionCallback): () => void;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=DiscordChatClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiscordChatClient.d.ts","sourceRoot":"","sources":["../../src/discord/DiscordChatClient.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,WAAW,EAGZ,MAAM,aAAa,CAAC;AAErB;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAW,YAAW,iBAAiB;IAC5E,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,iBAAiB,CAA4C;IACrE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,EAAE,aAAa;IAgBjC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA+C7B;;;;OAIG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAYlD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC;;;;;OAKG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBxE;;;;;OAKG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrF;;;;;OAKG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;CAkBhF"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Client, GatewayIntentBits, TextChannel, } from 'discord.js';
|
|
2
|
+
import { ChatClient } from '../ChatClient.js';
|
|
3
|
+
import { Channel } from '../Channel.js';
|
|
4
|
+
/**
|
|
5
|
+
* Discord chat client implementation using discord.js
|
|
6
|
+
*/
|
|
7
|
+
export class DiscordChatClient extends ChatClient {
|
|
8
|
+
client;
|
|
9
|
+
reactionListeners = new Map();
|
|
10
|
+
token;
|
|
11
|
+
guildId;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super(config);
|
|
14
|
+
this.token = config.token ?? process.env.DISCORD_TOKEN ?? '';
|
|
15
|
+
this.guildId = config.guildId ?? process.env.DISCORD_GUILD_ID ?? '';
|
|
16
|
+
this.client = new Client({
|
|
17
|
+
intents: [
|
|
18
|
+
GatewayIntentBits.Guilds,
|
|
19
|
+
GatewayIntentBits.GuildMessages,
|
|
20
|
+
GatewayIntentBits.GuildMessageReactions,
|
|
21
|
+
],
|
|
22
|
+
});
|
|
23
|
+
this.setupReactionListener();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set up the global reaction listener that routes events to channel-specific callbacks
|
|
27
|
+
*/
|
|
28
|
+
setupReactionListener() {
|
|
29
|
+
this.client.on('messageReactionAdd', (reaction, user) => {
|
|
30
|
+
void (async () => {
|
|
31
|
+
// Handle partial reactions
|
|
32
|
+
if (reaction.partial) {
|
|
33
|
+
try {
|
|
34
|
+
await reaction.fetch();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error('Failed to fetch partial reaction:', error);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const channelId = reaction.message.channelId;
|
|
42
|
+
const callbacks = this.reactionListeners.get(channelId);
|
|
43
|
+
if (!callbacks || callbacks.size === 0) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const reactionUser = { id: user.id, username: user.username ?? undefined };
|
|
47
|
+
const event = {
|
|
48
|
+
emoji: reaction.emoji.name ?? reaction.emoji.id ?? '',
|
|
49
|
+
user: reactionUser,
|
|
50
|
+
messageId: reaction.message.id,
|
|
51
|
+
channelId: channelId,
|
|
52
|
+
timestamp: new Date(),
|
|
53
|
+
};
|
|
54
|
+
for (const callback of callbacks) {
|
|
55
|
+
try {
|
|
56
|
+
await callback(event);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error('Reaction callback error:', error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
})();
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Connect to Discord and return a channel object
|
|
67
|
+
* @param channelId - Discord channel ID
|
|
68
|
+
* @returns Channel object for interacting with the channel
|
|
69
|
+
*/
|
|
70
|
+
async connect(channelId) {
|
|
71
|
+
await this.client.login(this.token);
|
|
72
|
+
const discordChannel = await this.client.channels.fetch(channelId);
|
|
73
|
+
if (!discordChannel || !(discordChannel instanceof TextChannel)) {
|
|
74
|
+
throw new Error(`Channel ${channelId} not found or is not a text channel`);
|
|
75
|
+
}
|
|
76
|
+
return new Channel(channelId, 'discord', this);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Disconnect from Discord
|
|
80
|
+
*/
|
|
81
|
+
async disconnect() {
|
|
82
|
+
this.reactionListeners.clear();
|
|
83
|
+
await this.client.destroy();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Post a message to a Discord channel
|
|
87
|
+
* @param channelId - Channel to post to
|
|
88
|
+
* @param text - Message content
|
|
89
|
+
* @returns Message data with ID
|
|
90
|
+
*/
|
|
91
|
+
async postMessage(channelId, text) {
|
|
92
|
+
const channel = await this.client.channels.fetch(channelId);
|
|
93
|
+
if (!channel || !(channel instanceof TextChannel)) {
|
|
94
|
+
throw new Error(`Channel ${channelId} not found or is not a text channel`);
|
|
95
|
+
}
|
|
96
|
+
const message = await channel.send({ content: text });
|
|
97
|
+
return {
|
|
98
|
+
id: message.id,
|
|
99
|
+
channelId: channelId,
|
|
100
|
+
platform: 'discord',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Add a reaction to a message
|
|
105
|
+
* @param messageId - Message to react to
|
|
106
|
+
* @param channelId - Channel containing the message
|
|
107
|
+
* @param emoji - Emoji to add
|
|
108
|
+
*/
|
|
109
|
+
async addReaction(messageId, channelId, emoji) {
|
|
110
|
+
const channel = await this.client.channels.fetch(channelId);
|
|
111
|
+
if (!channel || !(channel instanceof TextChannel)) {
|
|
112
|
+
throw new Error(`Channel ${channelId} not found or is not a text channel`);
|
|
113
|
+
}
|
|
114
|
+
const message = await channel.messages.fetch(messageId);
|
|
115
|
+
await message.react(emoji);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Subscribe to reaction events on a channel
|
|
119
|
+
* @param channelId - Channel to monitor
|
|
120
|
+
* @param callback - Function to call when reactions are added
|
|
121
|
+
* @returns Unsubscribe function
|
|
122
|
+
*/
|
|
123
|
+
subscribeToReactions(channelId, callback) {
|
|
124
|
+
if (!this.reactionListeners.has(channelId)) {
|
|
125
|
+
this.reactionListeners.set(channelId, new Set());
|
|
126
|
+
}
|
|
127
|
+
const callbacks = this.reactionListeners.get(channelId);
|
|
128
|
+
if (!callbacks) {
|
|
129
|
+
throw new Error(`Callbacks not found for channel ${channelId}`);
|
|
130
|
+
}
|
|
131
|
+
callbacks.add(callback);
|
|
132
|
+
return () => {
|
|
133
|
+
callbacks.delete(callback);
|
|
134
|
+
if (callbacks.size === 0) {
|
|
135
|
+
this.reactionListeners.delete(channelId);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=DiscordChatClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DiscordChatClient.js","sourceRoot":"","sources":["../../src/discord/DiscordChatClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { type User, type DiscordConfig, type SlackConfig, type ChatConfig, type Platform, type ReactionEvent, type ReactionCallback, } from './types.js';
|
|
2
|
+
export { ChatClient } from './ChatClient.js';
|
|
3
|
+
export { Channel } from './Channel.js';
|
|
4
|
+
export { Message } from './Message.js';
|
|
5
|
+
export { DiscordChatClient } from './discord/index.js';
|
|
6
|
+
export { SlackChatClient } from './slack/index.js';
|
|
7
|
+
import type { ChatConfig } from './types.js';
|
|
8
|
+
import { ChatClient } from './ChatClient.js';
|
|
9
|
+
/**
|
|
10
|
+
* Factory function to create a chat client based on config type
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Discord (uses env vars by default)
|
|
15
|
+
* const client = createChatClient({ type: 'discord' });
|
|
16
|
+
*
|
|
17
|
+
* // Slack (uses env vars by default)
|
|
18
|
+
* const client = createChatClient({ type: 'slack' });
|
|
19
|
+
*
|
|
20
|
+
* // Usage
|
|
21
|
+
* const channel = await client.connect(channelId);
|
|
22
|
+
* await channel.postMessage('Vote: 1, 2, or 3').addReactions(['1️⃣', '2️⃣', '3️⃣']);
|
|
23
|
+
*
|
|
24
|
+
* channel.onReaction((event) => {
|
|
25
|
+
* console.log(`${event.user.username ?? event.user.id} voted ${event.emoji}`);
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function createChatClient(config: ChatConfig): ChatClient;
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,IAAI,EACT,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAI7C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAS/D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Core classes
|
|
2
|
+
export { ChatClient } from './ChatClient.js';
|
|
3
|
+
export { Channel } from './Channel.js';
|
|
4
|
+
export { Message } from './Message.js';
|
|
5
|
+
// Platform implementations
|
|
6
|
+
export { DiscordChatClient } from './discord/index.js';
|
|
7
|
+
export { SlackChatClient } from './slack/index.js';
|
|
8
|
+
import { DiscordChatClient } from './discord/index.js';
|
|
9
|
+
import { SlackChatClient } from './slack/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Factory function to create a chat client based on config type
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Discord (uses env vars by default)
|
|
16
|
+
* const client = createChatClient({ type: 'discord' });
|
|
17
|
+
*
|
|
18
|
+
* // Slack (uses env vars by default)
|
|
19
|
+
* const client = createChatClient({ type: 'slack' });
|
|
20
|
+
*
|
|
21
|
+
* // Usage
|
|
22
|
+
* const channel = await client.connect(channelId);
|
|
23
|
+
* await channel.postMessage('Vote: 1, 2, or 3').addReactions(['1️⃣', '2️⃣', '3️⃣']);
|
|
24
|
+
*
|
|
25
|
+
* channel.onReaction((event) => {
|
|
26
|
+
* console.log(`${event.user.username ?? event.user.id} voted ${event.emoji}`);
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function createChatClient(config) {
|
|
31
|
+
switch (config.type) {
|
|
32
|
+
case 'discord':
|
|
33
|
+
return new DiscordChatClient(config);
|
|
34
|
+
case 'slack':
|
|
35
|
+
return new SlackChatClient(config);
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`Unknown chat platform: ${config.type}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,eAAe;AACf,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2BAA2B;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAKnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAkB;IACjD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACvC,KAAK,OAAO;YACV,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QACrC;YACE,MAAM,IAAI,KAAK,CAAC,0BAA2B,MAA2B,CAAC,IAAI,EAAE,CAAC,CAAC;IACnF,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ChatClient } from '../ChatClient.js';
|
|
2
|
+
import { Channel, type ChannelOperations } from '../Channel.js';
|
|
3
|
+
import type { SlackConfig, MessageData, ReactionCallback } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Slack chat client implementation using @slack/bolt
|
|
6
|
+
*/
|
|
7
|
+
export declare class SlackChatClient extends ChatClient implements ChannelOperations {
|
|
8
|
+
private app;
|
|
9
|
+
private reactionCallbacks;
|
|
10
|
+
constructor(config: SlackConfig);
|
|
11
|
+
/**
|
|
12
|
+
* Connect to Slack and return a channel object
|
|
13
|
+
*/
|
|
14
|
+
connect(channelId: string): Promise<Channel>;
|
|
15
|
+
/**
|
|
16
|
+
* Disconnect from Slack
|
|
17
|
+
*/
|
|
18
|
+
disconnect(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Post a message to a Slack channel
|
|
21
|
+
*/
|
|
22
|
+
postMessage(channelId: string, text: string): Promise<MessageData>;
|
|
23
|
+
/**
|
|
24
|
+
* Add a reaction to a message
|
|
25
|
+
*/
|
|
26
|
+
addReaction(messageId: string, channelId: string, emoji: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Subscribe to reaction events for a specific channel
|
|
29
|
+
*/
|
|
30
|
+
subscribeToReactions(channelId: string, callback: ReactionCallback): () => void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=SlackChatClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackChatClient.d.ts","sourceRoot":"","sources":["../../src/slack/SlackChatClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAuB,MAAM,aAAa,CAAC;AAEnG;;GAEG;AACH,qBAAa,eAAgB,SAAQ,UAAW,YAAW,iBAAiB;IAC1E,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,iBAAiB,CAA4C;gBAEzD,MAAM,EAAE,WAAW;IAyC/B;;OAEG;IACG,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBxE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrF;;OAEG;IACH,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;CAmBhF"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { App } from '@slack/bolt';
|
|
2
|
+
import { ChatClient } from '../ChatClient.js';
|
|
3
|
+
import { Channel } from '../Channel.js';
|
|
4
|
+
/**
|
|
5
|
+
* Slack chat client implementation using @slack/bolt
|
|
6
|
+
*/
|
|
7
|
+
export class SlackChatClient extends ChatClient {
|
|
8
|
+
app;
|
|
9
|
+
reactionCallbacks = new Map();
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super(config);
|
|
12
|
+
const token = config.token ?? process.env.SLACK_BOT_TOKEN;
|
|
13
|
+
const appToken = config.appToken ?? process.env.SLACK_APP_TOKEN;
|
|
14
|
+
this.app = new App({
|
|
15
|
+
token,
|
|
16
|
+
appToken,
|
|
17
|
+
socketMode: config.socketMode ?? true,
|
|
18
|
+
});
|
|
19
|
+
// Set up global reaction event listener
|
|
20
|
+
this.app.event('reaction_added', async ({ event }) => {
|
|
21
|
+
const channelId = event.item.channel;
|
|
22
|
+
const callbacks = this.reactionCallbacks.get(channelId);
|
|
23
|
+
if (!callbacks || callbacks.size === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const user = { id: event.user, username: undefined };
|
|
27
|
+
const reactionEvent = {
|
|
28
|
+
emoji: event.reaction,
|
|
29
|
+
user,
|
|
30
|
+
messageId: event.item.ts,
|
|
31
|
+
channelId: channelId,
|
|
32
|
+
timestamp: new Date(parseFloat(event.event_ts) * 1000),
|
|
33
|
+
};
|
|
34
|
+
for (const callback of callbacks) {
|
|
35
|
+
try {
|
|
36
|
+
await Promise.resolve(callback(reactionEvent));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
console.error('Reaction callback error:', err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Connect to Slack and return a channel object
|
|
46
|
+
*/
|
|
47
|
+
async connect(channelId) {
|
|
48
|
+
await this.app.start();
|
|
49
|
+
return new Channel(channelId, 'slack', this);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Disconnect from Slack
|
|
53
|
+
*/
|
|
54
|
+
async disconnect() {
|
|
55
|
+
await this.app.stop();
|
|
56
|
+
this.reactionCallbacks.clear();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Post a message to a Slack channel
|
|
60
|
+
*/
|
|
61
|
+
async postMessage(channelId, text) {
|
|
62
|
+
const result = await this.app.client.chat.postMessage({
|
|
63
|
+
channel: channelId,
|
|
64
|
+
text: text,
|
|
65
|
+
});
|
|
66
|
+
if (result.ts === undefined) {
|
|
67
|
+
throw new Error('Slack API did not return a message timestamp');
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
id: result.ts,
|
|
71
|
+
channelId: channelId,
|
|
72
|
+
platform: 'slack',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Add a reaction to a message
|
|
77
|
+
*/
|
|
78
|
+
async addReaction(messageId, channelId, emoji) {
|
|
79
|
+
// Strip colons from emoji name (e.g., ":thumbsup:" -> "thumbsup")
|
|
80
|
+
const emojiName = emoji.replace(/^:|:$/g, '');
|
|
81
|
+
await this.app.client.reactions.add({
|
|
82
|
+
channel: channelId,
|
|
83
|
+
timestamp: messageId,
|
|
84
|
+
name: emojiName,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Subscribe to reaction events for a specific channel
|
|
89
|
+
*/
|
|
90
|
+
subscribeToReactions(channelId, callback) {
|
|
91
|
+
let callbacks = this.reactionCallbacks.get(channelId);
|
|
92
|
+
if (!callbacks) {
|
|
93
|
+
callbacks = new Set();
|
|
94
|
+
this.reactionCallbacks.set(channelId, callbacks);
|
|
95
|
+
}
|
|
96
|
+
callbacks.add(callback);
|
|
97
|
+
// Return unsubscribe function
|
|
98
|
+
return () => {
|
|
99
|
+
const channelCallbacks = this.reactionCallbacks.get(channelId);
|
|
100
|
+
if (channelCallbacks) {
|
|
101
|
+
channelCallbacks.delete(callback);
|
|
102
|
+
if (channelCallbacks.size === 0) {
|
|
103
|
+
this.reactionCallbacks.delete(channelId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=SlackChatClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackChatClient.js","sourceRoot":"","sources":["../../src/slack/SlackChatClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/slack/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/slack/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for Discord client
|
|
3
|
+
*/
|
|
4
|
+
export interface DiscordConfig {
|
|
5
|
+
type: 'discord';
|
|
6
|
+
token?: string;
|
|
7
|
+
guildId?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for Slack client
|
|
11
|
+
*/
|
|
12
|
+
export interface SlackConfig {
|
|
13
|
+
type: 'slack';
|
|
14
|
+
token?: string;
|
|
15
|
+
appToken?: string;
|
|
16
|
+
socketMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export type ChatConfig = DiscordConfig | SlackConfig;
|
|
19
|
+
/**
|
|
20
|
+
* Platform identifier
|
|
21
|
+
*/
|
|
22
|
+
export type Platform = 'discord' | 'slack';
|
|
23
|
+
/**
|
|
24
|
+
* User who performed an action (e.g., added a reaction)
|
|
25
|
+
*/
|
|
26
|
+
export interface User {
|
|
27
|
+
id: string;
|
|
28
|
+
username?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Data provided to reaction callbacks
|
|
32
|
+
*/
|
|
33
|
+
export interface ReactionEvent {
|
|
34
|
+
/** The emoji that was added */
|
|
35
|
+
emoji: string;
|
|
36
|
+
/** User who added the reaction */
|
|
37
|
+
user: User;
|
|
38
|
+
/** ID of the message that received the reaction */
|
|
39
|
+
messageId: string;
|
|
40
|
+
/** ID of the channel containing the message */
|
|
41
|
+
channelId: string;
|
|
42
|
+
/** Timestamp of the reaction */
|
|
43
|
+
timestamp: Date;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Callback function type for reaction events
|
|
47
|
+
*/
|
|
48
|
+
export type ReactionCallback = (event: ReactionEvent) => void | Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Internal message data passed between classes
|
|
51
|
+
*/
|
|
52
|
+
export interface MessageData {
|
|
53
|
+
id: string;
|
|
54
|
+
channelId: string;
|
|
55
|
+
platform: Platform;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3C;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;CACpB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hardlydifficult/chat",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"test:coverage": "vitest run --coverage",
|
|
15
|
+
"lint": "tsc --noEmit",
|
|
16
|
+
"clean": "rm -rf dist"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@slack/bolt": "3.22.0",
|
|
20
|
+
"discord.js": "14.25.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "20.19.31",
|
|
24
|
+
"typescript": "5.8.3",
|
|
25
|
+
"vitest": "1.6.1"
|
|
26
|
+
}
|
|
27
|
+
}
|