@hasna/connectors 0.3.16 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +71 -1
- package/bin/mcp.js +71 -1
- package/bin/serve.js +70 -0
- package/connectors/connect-asana/.env.example +11 -0
- package/connectors/connect-asana/CLAUDE.md +128 -0
- package/connectors/connect-asana/README.md +193 -0
- package/connectors/connect-asana/package.json +52 -0
- package/connectors/connect-asana/src/api/client.ts +119 -0
- package/connectors/connect-asana/src/api/index.ts +319 -0
- package/connectors/connect-asana/src/cli/index.ts +731 -0
- package/connectors/connect-asana/src/index.ts +19 -0
- package/connectors/connect-asana/src/types/index.ts +270 -0
- package/connectors/connect-asana/src/utils/config.ts +171 -0
- package/connectors/connect-asana/src/utils/output.ts +119 -0
- package/connectors/connect-asana/tsconfig.json +16 -0
- package/connectors/connect-clickup/.env.example +11 -0
- package/connectors/connect-clickup/CLAUDE.md +128 -0
- package/connectors/connect-clickup/README.md +193 -0
- package/connectors/connect-clickup/package.json +52 -0
- package/connectors/connect-clickup/src/api/client.ts +116 -0
- package/connectors/connect-clickup/src/api/index.ts +400 -0
- package/connectors/connect-clickup/src/cli/index.ts +625 -0
- package/connectors/connect-clickup/src/index.ts +19 -0
- package/connectors/connect-clickup/src/types/index.ts +591 -0
- package/connectors/connect-clickup/src/utils/config.ts +157 -0
- package/connectors/connect-clickup/src/utils/output.ts +119 -0
- package/connectors/connect-clickup/tsconfig.json +16 -0
- package/connectors/connect-confluence/.env.example +11 -0
- package/connectors/connect-confluence/CLAUDE.md +272 -0
- package/connectors/connect-confluence/README.md +193 -0
- package/connectors/connect-confluence/package.json +53 -0
- package/connectors/connect-confluence/scripts/release.ts +179 -0
- package/connectors/connect-confluence/src/api/client.ts +213 -0
- package/connectors/connect-confluence/src/api/example.ts +48 -0
- package/connectors/connect-confluence/src/api/index.ts +51 -0
- package/connectors/connect-confluence/src/cli/index.ts +254 -0
- package/connectors/connect-confluence/src/index.ts +103 -0
- package/connectors/connect-confluence/src/types/index.ts +237 -0
- package/connectors/connect-confluence/src/utils/auth.ts +274 -0
- package/connectors/connect-confluence/src/utils/bulk.ts +212 -0
- package/connectors/connect-confluence/src/utils/config.ts +326 -0
- package/connectors/connect-confluence/src/utils/output.ts +175 -0
- package/connectors/connect-confluence/src/utils/settings.ts +114 -0
- package/connectors/connect-confluence/src/utils/storage.ts +198 -0
- package/connectors/connect-confluence/tsconfig.json +16 -0
- package/connectors/connect-jira/.env.example +11 -0
- package/connectors/connect-jira/CLAUDE.md +128 -0
- package/connectors/connect-jira/README.md +193 -0
- package/connectors/connect-jira/package.json +53 -0
- package/connectors/connect-jira/src/api/client.ts +131 -0
- package/connectors/connect-jira/src/api/index.ts +266 -0
- package/connectors/connect-jira/src/cli/index.ts +653 -0
- package/connectors/connect-jira/src/index.ts +23 -0
- package/connectors/connect-jira/src/types/index.ts +448 -0
- package/connectors/connect-jira/src/utils/config.ts +179 -0
- package/connectors/connect-jira/src/utils/output.ts +119 -0
- package/connectors/connect-jira/tsconfig.json +16 -0
- package/connectors/connect-linear/CLAUDE.md +88 -0
- package/connectors/connect-linear/README.md +201 -0
- package/connectors/connect-linear/package.json +45 -0
- package/connectors/connect-linear/src/api/client.ts +62 -0
- package/connectors/connect-linear/src/api/index.ts +46 -0
- package/connectors/connect-linear/src/api/issues.ts +247 -0
- package/connectors/connect-linear/src/api/projects.ts +179 -0
- package/connectors/connect-linear/src/api/teams.ts +125 -0
- package/connectors/connect-linear/src/api/users.ts +112 -0
- package/connectors/connect-linear/src/cli/index.ts +560 -0
- package/connectors/connect-linear/src/index.ts +27 -0
- package/connectors/connect-linear/src/types/index.ts +275 -0
- package/connectors/connect-linear/src/utils/config.ts +249 -0
- package/connectors/connect-linear/src/utils/output.ts +119 -0
- package/connectors/connect-linear/tsconfig.json +16 -0
- package/connectors/connect-slack/.env.example +7 -0
- package/connectors/connect-slack/CLAUDE.md +69 -0
- package/connectors/connect-slack/README.md +150 -0
- package/connectors/connect-slack/package.json +44 -0
- package/connectors/connect-slack/src/api/channels.ts +112 -0
- package/connectors/connect-slack/src/api/client.ts +97 -0
- package/connectors/connect-slack/src/api/index.ts +42 -0
- package/connectors/connect-slack/src/api/messages.ts +127 -0
- package/connectors/connect-slack/src/api/users.ts +110 -0
- package/connectors/connect-slack/src/cli/index.ts +494 -0
- package/connectors/connect-slack/src/index.ts +21 -0
- package/connectors/connect-slack/src/types/index.ts +263 -0
- package/connectors/connect-slack/src/utils/config.ts +297 -0
- package/connectors/connect-slack/src/utils/output.ts +119 -0
- package/connectors/connect-slack/tsconfig.json +16 -0
- package/connectors/connect-telegram/.env.example +2 -0
- package/connectors/connect-telegram/package.json +49 -0
- package/connectors/connect-todoist/.env.example +11 -0
- package/connectors/connect-todoist/CLAUDE.md +104 -0
- package/connectors/connect-todoist/README.md +193 -0
- package/connectors/connect-todoist/package.json +52 -0
- package/connectors/connect-todoist/src/api/client.ts +117 -0
- package/connectors/connect-todoist/src/api/index.ts +188 -0
- package/connectors/connect-todoist/src/cli/index.ts +990 -0
- package/connectors/connect-todoist/src/index.ts +21 -0
- package/connectors/connect-todoist/src/types/index.ts +240 -0
- package/connectors/connect-todoist/src/utils/config.ts +157 -0
- package/connectors/connect-todoist/src/utils/output.ts +119 -0
- package/connectors/connect-todoist/tsconfig.json +16 -0
- package/connectors/connect-trello/.env.example +11 -0
- package/connectors/connect-trello/CLAUDE.md +128 -0
- package/connectors/connect-trello/README.md +193 -0
- package/connectors/connect-trello/package.json +53 -0
- package/connectors/connect-trello/src/api/client.ts +128 -0
- package/connectors/connect-trello/src/api/index.ts +278 -0
- package/connectors/connect-trello/src/cli/index.ts +737 -0
- package/connectors/connect-trello/src/index.ts +21 -0
- package/connectors/connect-trello/src/types/index.ts +314 -0
- package/connectors/connect-trello/src/utils/config.ts +182 -0
- package/connectors/connect-trello/src/utils/output.ts +119 -0
- package/connectors/connect-trello/tsconfig.json +16 -0
- package/connectors/connect-whatsapp/.env.example +11 -0
- package/connectors/connect-whatsapp/CLAUDE.md +113 -0
- package/connectors/connect-whatsapp/README.md +193 -0
- package/connectors/connect-whatsapp/package.json +53 -0
- package/connectors/connect-whatsapp/src/api/client.ts +133 -0
- package/connectors/connect-whatsapp/src/api/index.ts +365 -0
- package/connectors/connect-whatsapp/src/cli/index.ts +686 -0
- package/connectors/connect-whatsapp/src/index.ts +25 -0
- package/connectors/connect-whatsapp/src/types/index.ts +502 -0
- package/connectors/connect-whatsapp/src/utils/config.ts +179 -0
- package/connectors/connect-whatsapp/src/utils/output.ts +119 -0
- package/connectors/connect-whatsapp/tsconfig.json +16 -0
- package/dist/index.js +70 -0
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code when working with this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
connect-slack is a TypeScript CLI and library for Slack's API. It provides channel management, messaging, and user operations with multi-profile support for managing multiple workspaces.
|
|
8
|
+
|
|
9
|
+
## Build & Run Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun install # Install dependencies
|
|
13
|
+
bun run dev # Run CLI in development
|
|
14
|
+
bun run build # Build for distribution
|
|
15
|
+
bun run typecheck # Type check
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Code Style
|
|
19
|
+
|
|
20
|
+
- TypeScript with strict mode
|
|
21
|
+
- ESM modules (type: module)
|
|
22
|
+
- Async/await for all async operations
|
|
23
|
+
- Minimal dependencies: commander, chalk
|
|
24
|
+
|
|
25
|
+
## Project Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
src/
|
|
29
|
+
├── api/
|
|
30
|
+
│ ├── client.ts # HTTP client with Bearer auth
|
|
31
|
+
│ ├── channels.ts # Channels/Conversations API
|
|
32
|
+
│ ├── messages.ts # Messages API
|
|
33
|
+
│ ├── users.ts # Users API
|
|
34
|
+
│ └── index.ts # Main Slack class
|
|
35
|
+
├── cli/
|
|
36
|
+
│ └── index.ts # CLI commands
|
|
37
|
+
├── types/
|
|
38
|
+
│ └── index.ts # TypeScript types
|
|
39
|
+
├── utils/
|
|
40
|
+
│ ├── config.ts # Multi-profile configuration
|
|
41
|
+
│ └── output.ts # CLI output formatting
|
|
42
|
+
└── index.ts # Library exports
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Environment Variables
|
|
46
|
+
|
|
47
|
+
| Variable | Description |
|
|
48
|
+
|----------|-------------|
|
|
49
|
+
| `SLACK_BOT_TOKEN` | Bot token (xoxb-...) |
|
|
50
|
+
| `SLACK_USER_TOKEN` | User token (xoxp-...) |
|
|
51
|
+
| `SLACK_TEAM_ID` | Team/workspace ID |
|
|
52
|
+
|
|
53
|
+
## Multi-Profile Configuration
|
|
54
|
+
|
|
55
|
+
Configuration stored in `~/.connect/connect-slack/`:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
~/.connect/connect-slack/
|
|
59
|
+
├── current_profile
|
|
60
|
+
└── profiles/
|
|
61
|
+
└── default/
|
|
62
|
+
├── config.json
|
|
63
|
+
└── tokens.json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Dependencies
|
|
67
|
+
|
|
68
|
+
- commander: CLI framework
|
|
69
|
+
- chalk: Terminal styling
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @hasna/connect-slack
|
|
2
|
+
|
|
3
|
+
Slack API connector with CLI and library support. Multi-profile configuration for managing multiple workspaces.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun install -g @hasna/connect-slack
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Set your bot token
|
|
15
|
+
connect-slack config set-token xoxb-your-bot-token
|
|
16
|
+
|
|
17
|
+
# Test authentication
|
|
18
|
+
connect-slack test
|
|
19
|
+
|
|
20
|
+
# Send a message
|
|
21
|
+
connect-slack send general "Hello from CLI!"
|
|
22
|
+
|
|
23
|
+
# List channels
|
|
24
|
+
connect-slack channels list
|
|
25
|
+
|
|
26
|
+
# View message history
|
|
27
|
+
connect-slack messages history general --limit 10
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## CLI Commands
|
|
31
|
+
|
|
32
|
+
### Configuration
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
connect-slack config set-token <token> # Set bot token
|
|
36
|
+
connect-slack config set-user-token <token> # Set user token
|
|
37
|
+
connect-slack config set-channel <channel> # Set default channel
|
|
38
|
+
connect-slack config show # Show configuration
|
|
39
|
+
connect-slack config clear # Clear configuration
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Profile Management
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
connect-slack profile list # List profiles
|
|
46
|
+
connect-slack profile create <name> # Create profile
|
|
47
|
+
connect-slack profile use <name> # Switch profile
|
|
48
|
+
connect-slack profile delete <name> # Delete profile
|
|
49
|
+
connect-slack profile show # Show current profile
|
|
50
|
+
|
|
51
|
+
# Use specific profile for a command
|
|
52
|
+
connect-slack --profile work channels list
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Channels
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
connect-slack channels list # List channels
|
|
59
|
+
connect-slack channels info <channel> # Get channel info
|
|
60
|
+
connect-slack channels join <channel> # Join channel
|
|
61
|
+
connect-slack channels leave <channel> # Leave channel
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Messages
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
connect-slack send <channel> <text> # Quick send
|
|
68
|
+
connect-slack messages send <channel> <text> # Send message
|
|
69
|
+
connect-slack messages send <channel> <text> -t <ts> # Reply to thread
|
|
70
|
+
connect-slack messages history <channel> # View history
|
|
71
|
+
connect-slack messages search <query> # Search messages
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Users
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
connect-slack users list # List users
|
|
78
|
+
connect-slack users info <user> # Get user info
|
|
79
|
+
connect-slack test # Show current user
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Library Usage
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { Slack } from '@hasna/connect-slack';
|
|
86
|
+
|
|
87
|
+
const slack = new Slack({
|
|
88
|
+
accessToken: process.env.SLACK_BOT_TOKEN,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Test authentication
|
|
92
|
+
const auth = await slack.test();
|
|
93
|
+
console.log(`Logged in as ${auth.user} in ${auth.team}`);
|
|
94
|
+
|
|
95
|
+
// List channels
|
|
96
|
+
const channels = await slack.channels.list();
|
|
97
|
+
console.log(channels);
|
|
98
|
+
|
|
99
|
+
// Send a message
|
|
100
|
+
await slack.send('C1234567890', 'Hello from the library!');
|
|
101
|
+
|
|
102
|
+
// Get message history
|
|
103
|
+
const messages = await slack.messages.history({
|
|
104
|
+
channel: 'C1234567890',
|
|
105
|
+
limit: 10,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// List users
|
|
109
|
+
const users = await slack.users.list();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Environment Variables
|
|
113
|
+
|
|
114
|
+
| Variable | Description |
|
|
115
|
+
|----------|-------------|
|
|
116
|
+
| `SLACK_BOT_TOKEN` | Bot token (xoxb-...) |
|
|
117
|
+
| `SLACK_USER_TOKEN` | User token (xoxp-...) |
|
|
118
|
+
| `SLACK_TEAM_ID` | Team/workspace ID |
|
|
119
|
+
|
|
120
|
+
## Configuration Files
|
|
121
|
+
|
|
122
|
+
Configuration is stored in `~/.connect/connect-slack/`:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
~/.connect/connect-slack/
|
|
126
|
+
├── current_profile # Active profile name
|
|
127
|
+
└── profiles/
|
|
128
|
+
├── default/
|
|
129
|
+
│ ├── config.json # Profile settings
|
|
130
|
+
│ └── tokens.json # OAuth tokens
|
|
131
|
+
└── work/
|
|
132
|
+
├── config.json
|
|
133
|
+
└── tokens.json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Getting a Bot Token
|
|
137
|
+
|
|
138
|
+
1. Go to [Slack API Apps](https://api.slack.com/apps)
|
|
139
|
+
2. Create a new app or select existing
|
|
140
|
+
3. Navigate to "OAuth & Permissions"
|
|
141
|
+
4. Add required scopes:
|
|
142
|
+
- `channels:read`, `channels:history`, `channels:join`
|
|
143
|
+
- `chat:write`
|
|
144
|
+
- `users:read`
|
|
145
|
+
5. Install to your workspace
|
|
146
|
+
6. Copy the Bot User OAuth Token
|
|
147
|
+
|
|
148
|
+
## License
|
|
149
|
+
|
|
150
|
+
Apache-2.0
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hasna/connect-slack",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Slack API connector with CLI and library support",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"connect-slack": "bin/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "bun run ./src/cli/index.ts",
|
|
13
|
+
"build": "bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/cli/index.ts --outdir ./bin --target bun",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"prepublishOnly": "bun run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"slack",
|
|
19
|
+
"api",
|
|
20
|
+
"cli",
|
|
21
|
+
"connector"
|
|
22
|
+
],
|
|
23
|
+
"author": "Hasna",
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"chalk": "^5.3.0",
|
|
27
|
+
"commander": "^12.1.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"registry": "https://registry.npmjs.org",
|
|
35
|
+
"access": "restricted"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"bin",
|
|
40
|
+
"src",
|
|
41
|
+
"README.md",
|
|
42
|
+
"CLAUDE.md"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { SlackClient } from './client';
|
|
2
|
+
import type {
|
|
3
|
+
SlackChannel,
|
|
4
|
+
ConversationsListResponse,
|
|
5
|
+
ConversationsListOptions,
|
|
6
|
+
} from '../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Channels/Conversations API
|
|
10
|
+
*/
|
|
11
|
+
export class ChannelsApi {
|
|
12
|
+
constructor(private readonly client: SlackClient) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* List all conversations/channels
|
|
16
|
+
*/
|
|
17
|
+
async list(options: Omit<ConversationsListOptions, 'channel'> = {}): Promise<SlackChannel[]> {
|
|
18
|
+
const allChannels: SlackChannel[] = [];
|
|
19
|
+
let cursor: string | undefined;
|
|
20
|
+
|
|
21
|
+
do {
|
|
22
|
+
const response = await this.client.get<ConversationsListResponse>(
|
|
23
|
+
'conversations.list',
|
|
24
|
+
{
|
|
25
|
+
types: options.types || 'public_channel,private_channel',
|
|
26
|
+
exclude_archived: options.exclude_archived ?? true,
|
|
27
|
+
limit: options.limit || 200,
|
|
28
|
+
cursor,
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
allChannels.push(...response.channels);
|
|
33
|
+
cursor = response.response_metadata?.next_cursor;
|
|
34
|
+
} while (cursor && allChannels.length < (options.limit || 1000));
|
|
35
|
+
|
|
36
|
+
return allChannels;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get channel info by ID
|
|
41
|
+
*/
|
|
42
|
+
async info(channelId: string): Promise<SlackChannel> {
|
|
43
|
+
const response = await this.client.get<{ ok: boolean; channel: SlackChannel }>(
|
|
44
|
+
'conversations.info',
|
|
45
|
+
{ channel: channelId }
|
|
46
|
+
);
|
|
47
|
+
return response.channel;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Find a channel by name
|
|
52
|
+
*/
|
|
53
|
+
async findByName(name: string): Promise<SlackChannel | undefined> {
|
|
54
|
+
const channels = await this.list();
|
|
55
|
+
const normalizedName = name.replace(/^#/, '');
|
|
56
|
+
return channels.find(c => c.name === normalizedName || c.name_normalized === normalizedName);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Join a channel
|
|
61
|
+
*/
|
|
62
|
+
async join(channelId: string): Promise<SlackChannel> {
|
|
63
|
+
const response = await this.client.post<{ ok: boolean; channel: SlackChannel }>(
|
|
64
|
+
'conversations.join',
|
|
65
|
+
{ channel: channelId }
|
|
66
|
+
);
|
|
67
|
+
return response.channel;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Leave a channel
|
|
72
|
+
*/
|
|
73
|
+
async leave(channelId: string): Promise<void> {
|
|
74
|
+
await this.client.post('conversations.leave', { channel: channelId });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create a new channel
|
|
79
|
+
*/
|
|
80
|
+
async create(name: string, isPrivate = false): Promise<SlackChannel> {
|
|
81
|
+
const response = await this.client.post<{ ok: boolean; channel: SlackChannel }>(
|
|
82
|
+
'conversations.create',
|
|
83
|
+
{ name, is_private: isPrivate }
|
|
84
|
+
);
|
|
85
|
+
return response.channel;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Archive a channel
|
|
90
|
+
*/
|
|
91
|
+
async archive(channelId: string): Promise<void> {
|
|
92
|
+
await this.client.post('conversations.archive', { channel: channelId });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Unarchive a channel
|
|
97
|
+
*/
|
|
98
|
+
async unarchive(channelId: string): Promise<void> {
|
|
99
|
+
await this.client.post('conversations.unarchive', { channel: channelId });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get members of a channel
|
|
104
|
+
*/
|
|
105
|
+
async members(channelId: string, limit = 200): Promise<string[]> {
|
|
106
|
+
const response = await this.client.get<{ ok: boolean; members: string[] }>(
|
|
107
|
+
'conversations.members',
|
|
108
|
+
{ channel: channelId, limit }
|
|
109
|
+
);
|
|
110
|
+
return response.members;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { SlackConfig, SlackApiResponse, OutputFormat } from '../types';
|
|
2
|
+
import { SlackApiError } from '../types';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_BASE_URL = 'https://slack.com/api';
|
|
5
|
+
|
|
6
|
+
export interface RequestOptions {
|
|
7
|
+
method?: 'GET' | 'POST';
|
|
8
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
9
|
+
body?: Record<string, unknown>;
|
|
10
|
+
format?: OutputFormat;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class SlackClient {
|
|
14
|
+
private readonly token: string;
|
|
15
|
+
private readonly baseUrl: string;
|
|
16
|
+
|
|
17
|
+
constructor(config: SlackConfig) {
|
|
18
|
+
const token = config.accessToken || config.botToken;
|
|
19
|
+
if (!token) {
|
|
20
|
+
throw new Error('Slack token is required (accessToken or botToken)');
|
|
21
|
+
}
|
|
22
|
+
this.token = token;
|
|
23
|
+
this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {
|
|
27
|
+
const url = new URL(`${this.baseUrl}/${path}`);
|
|
28
|
+
|
|
29
|
+
if (params) {
|
|
30
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
31
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
32
|
+
url.searchParams.append(key, String(value));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return url.toString();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async request<T extends SlackApiResponse>(
|
|
41
|
+
path: string,
|
|
42
|
+
options: RequestOptions = {}
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
const { method = 'GET', params, body } = options;
|
|
45
|
+
|
|
46
|
+
const headers: Record<string, string> = {
|
|
47
|
+
'Authorization': `Bearer ${this.token}`,
|
|
48
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const url = this.buildUrl(path, method === 'GET' ? params : undefined);
|
|
52
|
+
|
|
53
|
+
const fetchOptions: RequestInit = {
|
|
54
|
+
method,
|
|
55
|
+
headers,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (body && method === 'POST') {
|
|
59
|
+
fetchOptions.body = JSON.stringify(body);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const response = await fetch(url, fetchOptions);
|
|
63
|
+
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
throw new SlackApiError(
|
|
66
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
67
|
+
'http_error',
|
|
68
|
+
response.status
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const data = await response.json() as T;
|
|
73
|
+
|
|
74
|
+
if (!data.ok) {
|
|
75
|
+
throw new SlackApiError(
|
|
76
|
+
data.error || 'Unknown Slack API error',
|
|
77
|
+
data.error || 'unknown_error'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async get<T extends SlackApiResponse>(
|
|
85
|
+
path: string,
|
|
86
|
+
params?: Record<string, string | number | boolean | undefined>
|
|
87
|
+
): Promise<T> {
|
|
88
|
+
return this.request<T>(path, { method: 'GET', params });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async post<T extends SlackApiResponse>(
|
|
92
|
+
path: string,
|
|
93
|
+
body?: Record<string, unknown>
|
|
94
|
+
): Promise<T> {
|
|
95
|
+
return this.request<T>(path, { method: 'POST', body });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { SlackConfig } from '../types';
|
|
2
|
+
import { SlackClient } from './client';
|
|
3
|
+
import { ChannelsApi } from './channels';
|
|
4
|
+
import { MessagesApi } from './messages';
|
|
5
|
+
import { UsersApi } from './users';
|
|
6
|
+
|
|
7
|
+
export { SlackClient } from './client';
|
|
8
|
+
export { ChannelsApi } from './channels';
|
|
9
|
+
export { MessagesApi } from './messages';
|
|
10
|
+
export { UsersApi } from './users';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Main Slack API class
|
|
14
|
+
*/
|
|
15
|
+
export class Slack {
|
|
16
|
+
private readonly client: SlackClient;
|
|
17
|
+
|
|
18
|
+
public readonly channels: ChannelsApi;
|
|
19
|
+
public readonly messages: MessagesApi;
|
|
20
|
+
public readonly users: UsersApi;
|
|
21
|
+
|
|
22
|
+
constructor(config: SlackConfig) {
|
|
23
|
+
this.client = new SlackClient(config);
|
|
24
|
+
this.channels = new ChannelsApi(this.client);
|
|
25
|
+
this.messages = new MessagesApi(this.client);
|
|
26
|
+
this.users = new UsersApi(this.client);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Test authentication
|
|
31
|
+
*/
|
|
32
|
+
async test() {
|
|
33
|
+
return this.users.me();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Send a message to a channel (convenience method)
|
|
38
|
+
*/
|
|
39
|
+
async send(channel: string, text: string) {
|
|
40
|
+
return this.messages.sendText(channel, text);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { SlackClient } from './client';
|
|
2
|
+
import type {
|
|
3
|
+
SlackMessage,
|
|
4
|
+
ConversationsHistoryResponse,
|
|
5
|
+
ConversationsHistoryOptions,
|
|
6
|
+
ChatPostMessageResponse,
|
|
7
|
+
ChatPostMessageOptions,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Messages API
|
|
12
|
+
*/
|
|
13
|
+
export class MessagesApi {
|
|
14
|
+
constructor(private readonly client: SlackClient) {}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get message history for a channel
|
|
18
|
+
*/
|
|
19
|
+
async history(options: ConversationsHistoryOptions): Promise<SlackMessage[]> {
|
|
20
|
+
const response = await this.client.get<ConversationsHistoryResponse>(
|
|
21
|
+
'conversations.history',
|
|
22
|
+
{
|
|
23
|
+
channel: options.channel,
|
|
24
|
+
limit: options.limit || 100,
|
|
25
|
+
cursor: options.cursor,
|
|
26
|
+
oldest: options.oldest,
|
|
27
|
+
latest: options.latest,
|
|
28
|
+
inclusive: options.inclusive,
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return response.messages;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Send a message to a channel
|
|
37
|
+
*/
|
|
38
|
+
async send(options: ChatPostMessageOptions): Promise<ChatPostMessageResponse> {
|
|
39
|
+
return this.client.post<ChatPostMessageResponse>('chat.postMessage', {
|
|
40
|
+
channel: options.channel,
|
|
41
|
+
text: options.text,
|
|
42
|
+
blocks: options.blocks,
|
|
43
|
+
attachments: options.attachments,
|
|
44
|
+
thread_ts: options.thread_ts,
|
|
45
|
+
reply_broadcast: options.reply_broadcast,
|
|
46
|
+
unfurl_links: options.unfurl_links,
|
|
47
|
+
unfurl_media: options.unfurl_media,
|
|
48
|
+
mrkdwn: options.mrkdwn ?? true,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Send a simple text message
|
|
54
|
+
*/
|
|
55
|
+
async sendText(channel: string, text: string): Promise<ChatPostMessageResponse> {
|
|
56
|
+
return this.send({ channel, text });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Reply in a thread
|
|
61
|
+
*/
|
|
62
|
+
async reply(channel: string, threadTs: string, text: string): Promise<ChatPostMessageResponse> {
|
|
63
|
+
return this.send({ channel, text, thread_ts: threadTs });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Update a message
|
|
68
|
+
*/
|
|
69
|
+
async update(channel: string, ts: string, text: string): Promise<SlackMessage> {
|
|
70
|
+
const response = await this.client.post<{ ok: boolean; message: SlackMessage }>(
|
|
71
|
+
'chat.update',
|
|
72
|
+
{ channel, ts, text }
|
|
73
|
+
);
|
|
74
|
+
return response.message;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Delete a message
|
|
79
|
+
*/
|
|
80
|
+
async delete(channel: string, ts: string): Promise<void> {
|
|
81
|
+
await this.client.post('chat.delete', { channel, ts });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Add a reaction to a message
|
|
86
|
+
*/
|
|
87
|
+
async addReaction(channel: string, timestamp: string, emoji: string): Promise<void> {
|
|
88
|
+
await this.client.post('reactions.add', {
|
|
89
|
+
channel,
|
|
90
|
+
timestamp,
|
|
91
|
+
name: emoji.replace(/:/g, ''),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Remove a reaction from a message
|
|
97
|
+
*/
|
|
98
|
+
async removeReaction(channel: string, timestamp: string, emoji: string): Promise<void> {
|
|
99
|
+
await this.client.post('reactions.remove', {
|
|
100
|
+
channel,
|
|
101
|
+
timestamp,
|
|
102
|
+
name: emoji.replace(/:/g, ''),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get thread replies
|
|
108
|
+
*/
|
|
109
|
+
async thread(channel: string, ts: string, limit = 100): Promise<SlackMessage[]> {
|
|
110
|
+
const response = await this.client.get<{ ok: boolean; messages: SlackMessage[] }>(
|
|
111
|
+
'conversations.replies',
|
|
112
|
+
{ channel, ts, limit }
|
|
113
|
+
);
|
|
114
|
+
return response.messages;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Search messages
|
|
119
|
+
*/
|
|
120
|
+
async search(query: string, count = 20): Promise<SlackMessage[]> {
|
|
121
|
+
const response = await this.client.get<{
|
|
122
|
+
ok: boolean;
|
|
123
|
+
messages: { matches: SlackMessage[] };
|
|
124
|
+
}>('search.messages', { query, count });
|
|
125
|
+
return response.messages.matches;
|
|
126
|
+
}
|
|
127
|
+
}
|