-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathslack-api.ts
More file actions
123 lines (104 loc) · 3.94 KB
/
slack-api.ts
File metadata and controls
123 lines (104 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
export interface SlackMessage {
type: string;
text: string;
user: string;
ts: string;
thread_ts?: string;
channel?: { id: string; name: string };
reactions?: Array<{ name: string; users: string[] }>;
permalink?: string;
}
export interface SlackSearchResult {
messages: {
matches: SlackMessage[];
total: number;
};
}
export class SlackApiError extends Error {
constructor(public method: string, public slackError: string) {
super(`Slack API ${method}: ${slackError}`);
}
}
export class SlackApi {
private baseUrl = 'https://slack.com/api';
constructor(
private xoxc: string,
private xoxd: string,
) {}
private async call<T>(method: string, params: Record<string, string> = {}): Promise<T> {
const startTime = Date.now();
const body = new URLSearchParams({ token: this.xoxc, ...params });
console.log(`[slack-api] ${method} request`);
const res = await fetch(`${this.baseUrl}/${method}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Cookie': `d=${this.xoxd}`,
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
},
body,
});
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get('Retry-After') || '5', 10);
console.warn(`[slack-api] ${method} rate limited, retrying in ${retryAfter}s`);
await new Promise(r => setTimeout(r, retryAfter * 1000));
return this.call(method, params);
}
const data = await res.json() as { ok: boolean; error?: string } & T;
const elapsed = Date.now() - startTime;
if (!data.ok) {
console.error(`[slack-api] ${method} failed in ${elapsed}ms: ${data.error}`);
throw new SlackApiError(method, data.error || 'unknown');
}
console.log(`[slack-api] ${method} succeeded in ${elapsed}ms`);
return data;
}
async authTest(): Promise<{ user_id: string; team_id: string; user: string }> {
return this.call('auth.test');
}
async searchMessages(query: string, opts: { sort?: string; count?: string } = {}): Promise<SlackSearchResult> {
return this.call('search.messages', {
query,
sort: opts.sort || 'timestamp',
sort_dir: 'desc',
count: opts.count || '20',
});
}
async reactionsAdd(channel: string, timestamp: string, name: string): Promise<void> {
await this.call('reactions.add', { channel, timestamp, name });
}
async reactionsRemove(channel: string, timestamp: string, name: string): Promise<void> {
await this.call('reactions.remove', { channel, timestamp, name });
}
async chatPostMessage(channel: string, text: string, threadTs?: string): Promise<{ ts: string }> {
const params: Record<string, string> = { channel, text };
if (threadTs) params.thread_ts = threadTs;
return this.call('chat.postMessage', params);
}
async reactionsGet(channel: string, timestamp: string): Promise<{
message: { reactions?: Array<{ name: string; users: string[] }> };
}> {
return this.call('reactions.get', { channel, timestamp, full: 'true' });
}
async conversationsReplies(channel: string, ts: string): Promise<{
messages: SlackMessage[];
}> {
return this.call('conversations.replies', { channel, ts, limit: '100' });
}
async conversationsList(cursor?: string): Promise<{
channels: Array<{ id: string; name: string }>;
response_metadata?: { next_cursor?: string };
}> {
const params: Record<string, string> = { types: 'public_channel,private_channel', limit: '200' };
if (cursor) params.cursor = cursor;
return this.call('conversations.list', params);
}
async usersList(cursor?: string): Promise<{
members: Array<{ id: string; name: string; profile: { real_name?: string; display_name?: string } }>;
response_metadata?: { next_cursor?: string };
}> {
const params: Record<string, string> = { limit: '200' };
if (cursor) params.cursor = cursor;
return this.call('users.list', params);
}
}