@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,110 @@
|
|
|
1
|
+
import type { SlackClient } from './client';
|
|
2
|
+
import type { SlackUser, UsersListResponse, AuthTestResponse } from '../types';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Users API
|
|
6
|
+
*/
|
|
7
|
+
export class UsersApi {
|
|
8
|
+
constructor(private readonly client: SlackClient) {}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* List all users in the workspace
|
|
12
|
+
*/
|
|
13
|
+
async list(limit = 200): Promise<SlackUser[]> {
|
|
14
|
+
const allUsers: SlackUser[] = [];
|
|
15
|
+
let cursor: string | undefined;
|
|
16
|
+
|
|
17
|
+
do {
|
|
18
|
+
const response = await this.client.get<UsersListResponse>('users.list', {
|
|
19
|
+
limit,
|
|
20
|
+
cursor,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
allUsers.push(...response.members);
|
|
24
|
+
cursor = response.response_metadata?.next_cursor;
|
|
25
|
+
} while (cursor && allUsers.length < 10000);
|
|
26
|
+
|
|
27
|
+
return allUsers;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get user info by ID
|
|
32
|
+
*/
|
|
33
|
+
async info(userId: string): Promise<SlackUser> {
|
|
34
|
+
const response = await this.client.get<{ ok: boolean; user: SlackUser }>(
|
|
35
|
+
'users.info',
|
|
36
|
+
{ user: userId }
|
|
37
|
+
);
|
|
38
|
+
return response.user;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get current user's identity
|
|
43
|
+
*/
|
|
44
|
+
async me(): Promise<AuthTestResponse> {
|
|
45
|
+
return this.client.get<AuthTestResponse>('auth.test');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find a user by email
|
|
50
|
+
*/
|
|
51
|
+
async findByEmail(email: string): Promise<SlackUser> {
|
|
52
|
+
const response = await this.client.get<{ ok: boolean; user: SlackUser }>(
|
|
53
|
+
'users.lookupByEmail',
|
|
54
|
+
{ email }
|
|
55
|
+
);
|
|
56
|
+
return response.user;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Find a user by username/display name
|
|
61
|
+
*/
|
|
62
|
+
async findByName(name: string): Promise<SlackUser | undefined> {
|
|
63
|
+
const users = await this.list();
|
|
64
|
+
const normalizedName = name.replace(/^@/, '').toLowerCase();
|
|
65
|
+
|
|
66
|
+
return users.find(u =>
|
|
67
|
+
u.name.toLowerCase() === normalizedName ||
|
|
68
|
+
u.profile.display_name?.toLowerCase() === normalizedName ||
|
|
69
|
+
u.real_name?.toLowerCase() === normalizedName
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get user's presence status
|
|
75
|
+
*/
|
|
76
|
+
async presence(userId: string): Promise<string> {
|
|
77
|
+
const response = await this.client.get<{ ok: boolean; presence: string }>(
|
|
78
|
+
'users.getPresence',
|
|
79
|
+
{ user: userId }
|
|
80
|
+
);
|
|
81
|
+
return response.presence;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Set current user's presence
|
|
86
|
+
*/
|
|
87
|
+
async setPresence(presence: 'auto' | 'away'): Promise<void> {
|
|
88
|
+
await this.client.post('users.setPresence', { presence });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Set current user's status
|
|
93
|
+
*/
|
|
94
|
+
async setStatus(text: string, emoji?: string, expiration?: number): Promise<void> {
|
|
95
|
+
await this.client.post('users.profile.set', {
|
|
96
|
+
profile: {
|
|
97
|
+
status_text: text,
|
|
98
|
+
status_emoji: emoji || '',
|
|
99
|
+
status_expiration: expiration || 0,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Clear current user's status
|
|
106
|
+
*/
|
|
107
|
+
async clearStatus(): Promise<void> {
|
|
108
|
+
await this.setStatus('', '', 0);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { Slack } from '../api';
|
|
5
|
+
import type { OutputFormat } from '../types';
|
|
6
|
+
import {
|
|
7
|
+
getBotToken,
|
|
8
|
+
setBotToken,
|
|
9
|
+
setUserToken,
|
|
10
|
+
getDefaultChannel,
|
|
11
|
+
setDefaultChannel,
|
|
12
|
+
getCurrentProfile,
|
|
13
|
+
setCurrentProfile,
|
|
14
|
+
listProfiles,
|
|
15
|
+
createProfile,
|
|
16
|
+
deleteProfile,
|
|
17
|
+
profileExists,
|
|
18
|
+
clearConfig,
|
|
19
|
+
isAuthenticated,
|
|
20
|
+
setProfileOverride,
|
|
21
|
+
} from '../utils/config';
|
|
22
|
+
import { print, success, error, info, heading } from '../utils/output';
|
|
23
|
+
|
|
24
|
+
const program = new Command();
|
|
25
|
+
|
|
26
|
+
// Helper to get authenticated client
|
|
27
|
+
function getClient(): Slack {
|
|
28
|
+
const token = getBotToken();
|
|
29
|
+
if (!token) {
|
|
30
|
+
console.error(chalk.red('Error: No Slack token configured.'));
|
|
31
|
+
console.error(chalk.yellow('Set token with: connect-slack config set-token <token>'));
|
|
32
|
+
console.error(chalk.yellow('Or set SLACK_BOT_TOKEN environment variable'));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
return new Slack({ accessToken: token });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Global options
|
|
39
|
+
program
|
|
40
|
+
.name('connect-slack')
|
|
41
|
+
.description('Slack API CLI')
|
|
42
|
+
.version('0.0.1')
|
|
43
|
+
.option('-p, --profile <name>', 'Use specific profile')
|
|
44
|
+
.option('-f, --format <format>', 'Output format: json, table, pretty', 'pretty')
|
|
45
|
+
.hook('preAction', (thisCommand) => {
|
|
46
|
+
const opts = thisCommand.opts();
|
|
47
|
+
if (opts.profile) {
|
|
48
|
+
setProfileOverride(opts.profile);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// ============================================
|
|
53
|
+
// Auth/Config Commands
|
|
54
|
+
// ============================================
|
|
55
|
+
|
|
56
|
+
const configCmd = program
|
|
57
|
+
.command('config')
|
|
58
|
+
.description('Configuration commands');
|
|
59
|
+
|
|
60
|
+
configCmd
|
|
61
|
+
.command('set-token <token>')
|
|
62
|
+
.description('Set bot token for current profile')
|
|
63
|
+
.action((token: string) => {
|
|
64
|
+
setBotToken(token);
|
|
65
|
+
success(`Bot token saved to profile "${getCurrentProfile()}"`);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
configCmd
|
|
69
|
+
.command('set-user-token <token>')
|
|
70
|
+
.description('Set user token for current profile')
|
|
71
|
+
.action((token: string) => {
|
|
72
|
+
setUserToken(token);
|
|
73
|
+
success(`User token saved to profile "${getCurrentProfile()}"`);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
configCmd
|
|
77
|
+
.command('set-channel <channel>')
|
|
78
|
+
.description('Set default channel')
|
|
79
|
+
.action((channel: string) => {
|
|
80
|
+
setDefaultChannel(channel);
|
|
81
|
+
success(`Default channel set to "${channel}"`);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
configCmd
|
|
85
|
+
.command('show')
|
|
86
|
+
.description('Show current configuration')
|
|
87
|
+
.action(() => {
|
|
88
|
+
const profile = getCurrentProfile();
|
|
89
|
+
const token = getBotToken();
|
|
90
|
+
const channel = getDefaultChannel();
|
|
91
|
+
|
|
92
|
+
heading('Current Configuration');
|
|
93
|
+
print({
|
|
94
|
+
profile,
|
|
95
|
+
authenticated: isAuthenticated(),
|
|
96
|
+
token: token ? `${token.substring(0, 10)}...` : 'Not set',
|
|
97
|
+
defaultChannel: channel || 'Not set',
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
configCmd
|
|
102
|
+
.command('clear')
|
|
103
|
+
.description('Clear configuration for current profile')
|
|
104
|
+
.action(() => {
|
|
105
|
+
clearConfig();
|
|
106
|
+
success('Configuration cleared');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ============================================
|
|
110
|
+
// Profile Commands
|
|
111
|
+
// ============================================
|
|
112
|
+
|
|
113
|
+
const profileCmd = program
|
|
114
|
+
.command('profile')
|
|
115
|
+
.description('Profile management');
|
|
116
|
+
|
|
117
|
+
profileCmd
|
|
118
|
+
.command('list')
|
|
119
|
+
.description('List all profiles')
|
|
120
|
+
.action(() => {
|
|
121
|
+
const profiles = listProfiles();
|
|
122
|
+
const current = getCurrentProfile();
|
|
123
|
+
|
|
124
|
+
if (profiles.length === 0) {
|
|
125
|
+
info('No profiles found. Using default.');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
heading('Profiles');
|
|
130
|
+
profiles.forEach(p => {
|
|
131
|
+
const marker = p === current ? chalk.green(' (active)') : '';
|
|
132
|
+
console.log(` ${p}${marker}`);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
profileCmd
|
|
137
|
+
.command('use <name>')
|
|
138
|
+
.description('Switch to a profile')
|
|
139
|
+
.action((name: string) => {
|
|
140
|
+
if (!profileExists(name)) {
|
|
141
|
+
error(`Profile "${name}" does not exist`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
setCurrentProfile(name);
|
|
145
|
+
success(`Switched to profile "${name}"`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
profileCmd
|
|
149
|
+
.command('create <name>')
|
|
150
|
+
.description('Create a new profile')
|
|
151
|
+
.action((name: string) => {
|
|
152
|
+
try {
|
|
153
|
+
createProfile(name);
|
|
154
|
+
success(`Profile "${name}" created`);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
error((e as Error).message);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
profileCmd
|
|
162
|
+
.command('delete <name>')
|
|
163
|
+
.description('Delete a profile')
|
|
164
|
+
.action((name: string) => {
|
|
165
|
+
try {
|
|
166
|
+
deleteProfile(name);
|
|
167
|
+
success(`Profile "${name}" deleted`);
|
|
168
|
+
} catch (e) {
|
|
169
|
+
error((e as Error).message);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
profileCmd
|
|
175
|
+
.command('show')
|
|
176
|
+
.description('Show current profile name')
|
|
177
|
+
.action(() => {
|
|
178
|
+
console.log(getCurrentProfile());
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// ============================================
|
|
182
|
+
// Test/Auth Commands
|
|
183
|
+
// ============================================
|
|
184
|
+
|
|
185
|
+
program
|
|
186
|
+
.command('test')
|
|
187
|
+
.alias('whoami')
|
|
188
|
+
.description('Test authentication and show current user')
|
|
189
|
+
.action(async () => {
|
|
190
|
+
try {
|
|
191
|
+
const client = getClient();
|
|
192
|
+
const result = await client.test();
|
|
193
|
+
print({
|
|
194
|
+
user: result.user,
|
|
195
|
+
userId: result.user_id,
|
|
196
|
+
team: result.team,
|
|
197
|
+
teamId: result.team_id,
|
|
198
|
+
url: result.url,
|
|
199
|
+
});
|
|
200
|
+
} catch (e) {
|
|
201
|
+
error((e as Error).message);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ============================================
|
|
207
|
+
// Channel Commands
|
|
208
|
+
// ============================================
|
|
209
|
+
|
|
210
|
+
const channelsCmd = program
|
|
211
|
+
.command('channels')
|
|
212
|
+
.description('Channel commands');
|
|
213
|
+
|
|
214
|
+
channelsCmd
|
|
215
|
+
.command('list')
|
|
216
|
+
.description('List channels')
|
|
217
|
+
.option('-t, --types <types>', 'Channel types (public_channel,private_channel)', 'public_channel,private_channel')
|
|
218
|
+
.option('-a, --all', 'Include archived channels')
|
|
219
|
+
.option('-l, --limit <n>', 'Maximum number to return', '100')
|
|
220
|
+
.action(async (opts) => {
|
|
221
|
+
try {
|
|
222
|
+
const client = getClient();
|
|
223
|
+
const format = program.opts().format as OutputFormat;
|
|
224
|
+
const channels = await client.channels.list({
|
|
225
|
+
types: opts.types,
|
|
226
|
+
exclude_archived: !opts.all,
|
|
227
|
+
limit: parseInt(opts.limit, 10),
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
if (format === 'json') {
|
|
231
|
+
print(channels, format);
|
|
232
|
+
} else {
|
|
233
|
+
print(channels.map(c => ({
|
|
234
|
+
id: c.id,
|
|
235
|
+
name: c.name,
|
|
236
|
+
private: c.is_private ? 'yes' : 'no',
|
|
237
|
+
members: c.num_members,
|
|
238
|
+
archived: c.is_archived ? 'yes' : 'no',
|
|
239
|
+
})), format);
|
|
240
|
+
}
|
|
241
|
+
} catch (e) {
|
|
242
|
+
error((e as Error).message);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
channelsCmd
|
|
248
|
+
.command('info <channel>')
|
|
249
|
+
.description('Get channel info')
|
|
250
|
+
.action(async (channel: string) => {
|
|
251
|
+
try {
|
|
252
|
+
const client = getClient();
|
|
253
|
+
const format = program.opts().format as OutputFormat;
|
|
254
|
+
|
|
255
|
+
// Try to find by name if not an ID
|
|
256
|
+
let channelId = channel;
|
|
257
|
+
if (!channel.startsWith('C')) {
|
|
258
|
+
const found = await client.channels.findByName(channel);
|
|
259
|
+
if (!found) {
|
|
260
|
+
error(`Channel "${channel}" not found`);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
channelId = found.id;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const info = await client.channels.info(channelId);
|
|
267
|
+
print(info, format);
|
|
268
|
+
} catch (e) {
|
|
269
|
+
error((e as Error).message);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
channelsCmd
|
|
275
|
+
.command('join <channel>')
|
|
276
|
+
.description('Join a channel')
|
|
277
|
+
.action(async (channel: string) => {
|
|
278
|
+
try {
|
|
279
|
+
const client = getClient();
|
|
280
|
+
const result = await client.channels.join(channel);
|
|
281
|
+
success(`Joined channel #${result.name}`);
|
|
282
|
+
} catch (e) {
|
|
283
|
+
error((e as Error).message);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
channelsCmd
|
|
289
|
+
.command('leave <channel>')
|
|
290
|
+
.description('Leave a channel')
|
|
291
|
+
.action(async (channel: string) => {
|
|
292
|
+
try {
|
|
293
|
+
const client = getClient();
|
|
294
|
+
await client.channels.leave(channel);
|
|
295
|
+
success('Left channel');
|
|
296
|
+
} catch (e) {
|
|
297
|
+
error((e as Error).message);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// ============================================
|
|
303
|
+
// Message Commands
|
|
304
|
+
// ============================================
|
|
305
|
+
|
|
306
|
+
const messagesCmd = program
|
|
307
|
+
.command('messages')
|
|
308
|
+
.alias('msg')
|
|
309
|
+
.description('Message commands');
|
|
310
|
+
|
|
311
|
+
messagesCmd
|
|
312
|
+
.command('send <channel> <text>')
|
|
313
|
+
.description('Send a message to a channel')
|
|
314
|
+
.option('-t, --thread <ts>', 'Reply to thread')
|
|
315
|
+
.action(async (channel: string, text: string, opts) => {
|
|
316
|
+
try {
|
|
317
|
+
const client = getClient();
|
|
318
|
+
|
|
319
|
+
// Resolve channel name to ID if needed
|
|
320
|
+
let channelId = channel;
|
|
321
|
+
if (!channel.startsWith('C') && !channel.startsWith('D')) {
|
|
322
|
+
const found = await client.channels.findByName(channel);
|
|
323
|
+
if (!found) {
|
|
324
|
+
error(`Channel "${channel}" not found`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
channelId = found.id;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const result = await client.messages.send({
|
|
331
|
+
channel: channelId,
|
|
332
|
+
text,
|
|
333
|
+
thread_ts: opts.thread,
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
success(`Message sent (ts: ${result.ts})`);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
error((e as Error).message);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
messagesCmd
|
|
344
|
+
.command('history <channel>')
|
|
345
|
+
.description('Get message history')
|
|
346
|
+
.option('-l, --limit <n>', 'Number of messages', '20')
|
|
347
|
+
.action(async (channel: string, opts) => {
|
|
348
|
+
try {
|
|
349
|
+
const client = getClient();
|
|
350
|
+
const format = program.opts().format as OutputFormat;
|
|
351
|
+
|
|
352
|
+
// Resolve channel name to ID if needed
|
|
353
|
+
let channelId = channel;
|
|
354
|
+
if (!channel.startsWith('C') && !channel.startsWith('D')) {
|
|
355
|
+
const found = await client.channels.findByName(channel);
|
|
356
|
+
if (!found) {
|
|
357
|
+
error(`Channel "${channel}" not found`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
channelId = found.id;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const messages = await client.messages.history({
|
|
364
|
+
channel: channelId,
|
|
365
|
+
limit: parseInt(opts.limit, 10),
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (format === 'json') {
|
|
369
|
+
print(messages, format);
|
|
370
|
+
} else {
|
|
371
|
+
print(messages.map(m => ({
|
|
372
|
+
ts: m.ts,
|
|
373
|
+
user: m.user || m.bot_id || 'unknown',
|
|
374
|
+
text: m.text?.substring(0, 100) + (m.text && m.text.length > 100 ? '...' : ''),
|
|
375
|
+
})), format);
|
|
376
|
+
}
|
|
377
|
+
} catch (e) {
|
|
378
|
+
error((e as Error).message);
|
|
379
|
+
process.exit(1);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
messagesCmd
|
|
384
|
+
.command('search <query>')
|
|
385
|
+
.description('Search messages')
|
|
386
|
+
.option('-l, --limit <n>', 'Number of results', '20')
|
|
387
|
+
.action(async (query: string, opts) => {
|
|
388
|
+
try {
|
|
389
|
+
const client = getClient();
|
|
390
|
+
const format = program.opts().format as OutputFormat;
|
|
391
|
+
const messages = await client.messages.search(query, parseInt(opts.limit, 10));
|
|
392
|
+
print(messages, format);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
error((e as Error).message);
|
|
395
|
+
process.exit(1);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// ============================================
|
|
400
|
+
// User Commands
|
|
401
|
+
// ============================================
|
|
402
|
+
|
|
403
|
+
const usersCmd = program
|
|
404
|
+
.command('users')
|
|
405
|
+
.description('User commands');
|
|
406
|
+
|
|
407
|
+
usersCmd
|
|
408
|
+
.command('list')
|
|
409
|
+
.description('List users')
|
|
410
|
+
.option('-l, --limit <n>', 'Maximum number to return', '100')
|
|
411
|
+
.action(async (opts) => {
|
|
412
|
+
try {
|
|
413
|
+
const client = getClient();
|
|
414
|
+
const format = program.opts().format as OutputFormat;
|
|
415
|
+
const users = await client.users.list(parseInt(opts.limit, 10));
|
|
416
|
+
|
|
417
|
+
// Filter out bots and deleted users by default
|
|
418
|
+
const activeUsers = users.filter(u => !u.is_bot && !u.deleted);
|
|
419
|
+
|
|
420
|
+
if (format === 'json') {
|
|
421
|
+
print(activeUsers, format);
|
|
422
|
+
} else {
|
|
423
|
+
print(activeUsers.map(u => ({
|
|
424
|
+
id: u.id,
|
|
425
|
+
name: u.name,
|
|
426
|
+
realName: u.real_name,
|
|
427
|
+
email: u.profile.email,
|
|
428
|
+
admin: u.is_admin ? 'yes' : 'no',
|
|
429
|
+
})), format);
|
|
430
|
+
}
|
|
431
|
+
} catch (e) {
|
|
432
|
+
error((e as Error).message);
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
usersCmd
|
|
438
|
+
.command('info <user>')
|
|
439
|
+
.description('Get user info')
|
|
440
|
+
.action(async (user: string) => {
|
|
441
|
+
try {
|
|
442
|
+
const client = getClient();
|
|
443
|
+
const format = program.opts().format as OutputFormat;
|
|
444
|
+
|
|
445
|
+
// Try to find by name if not an ID
|
|
446
|
+
let userInfo;
|
|
447
|
+
if (user.startsWith('U')) {
|
|
448
|
+
userInfo = await client.users.info(user);
|
|
449
|
+
} else {
|
|
450
|
+
userInfo = await client.users.findByName(user);
|
|
451
|
+
if (!userInfo) {
|
|
452
|
+
error(`User "${user}" not found`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
print(userInfo, format);
|
|
458
|
+
} catch (e) {
|
|
459
|
+
error((e as Error).message);
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
// ============================================
|
|
465
|
+
// Quick Send Command
|
|
466
|
+
// ============================================
|
|
467
|
+
|
|
468
|
+
program
|
|
469
|
+
.command('send <channel> <text>')
|
|
470
|
+
.description('Quick send a message')
|
|
471
|
+
.action(async (channel: string, text: string) => {
|
|
472
|
+
try {
|
|
473
|
+
const client = getClient();
|
|
474
|
+
|
|
475
|
+
// Resolve channel name to ID if needed
|
|
476
|
+
let channelId = channel;
|
|
477
|
+
if (!channel.startsWith('C') && !channel.startsWith('D')) {
|
|
478
|
+
const found = await client.channels.findByName(channel);
|
|
479
|
+
if (!found) {
|
|
480
|
+
error(`Channel "${channel}" not found`);
|
|
481
|
+
process.exit(1);
|
|
482
|
+
}
|
|
483
|
+
channelId = found.id;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const result = await client.send(channelId, text);
|
|
487
|
+
success(`Message sent to #${channel} (ts: ${result.ts})`);
|
|
488
|
+
} catch (e) {
|
|
489
|
+
error((e as Error).message);
|
|
490
|
+
process.exit(1);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
program.parse();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Main library exports
|
|
2
|
+
export { Slack, SlackClient, ChannelsApi, MessagesApi, UsersApi } from './api';
|
|
3
|
+
|
|
4
|
+
// Type exports
|
|
5
|
+
export type {
|
|
6
|
+
SlackConfig,
|
|
7
|
+
SlackUser,
|
|
8
|
+
SlackUserProfile,
|
|
9
|
+
SlackChannel,
|
|
10
|
+
SlackMessage,
|
|
11
|
+
SlackBlock,
|
|
12
|
+
SlackAttachment,
|
|
13
|
+
SlackApiResponse,
|
|
14
|
+
ChatPostMessageOptions,
|
|
15
|
+
ConversationsListOptions,
|
|
16
|
+
ConversationsHistoryOptions,
|
|
17
|
+
OAuth2Tokens,
|
|
18
|
+
OutputFormat,
|
|
19
|
+
} from './types';
|
|
20
|
+
|
|
21
|
+
export { SlackApiError } from './types';
|