···
1
+
import { createHmac, timingSafeEqual } from "node:crypto";
3
+
SlackEmojiListResponse,
5
+
SlackUserInfoResponse,
9
+
* Interface for mapping emoji names to their URLs
11
+
interface SlackEmoji {
12
+
[key: string]: string;
16
+
* Configuration options for initializing the SlackWrapper
18
+
interface SlackConfig {
19
+
/** Slack signing secret for request verification */
20
+
signingSecret?: string;
21
+
/** Slack bot user OAuth token */
26
+
* Wrapper class for Slack API interactions
28
+
class SlackWrapper {
29
+
private signingSecret: string;
30
+
private botToken: string;
33
+
* Creates a new SlackWrapper instance
34
+
* @param config Optional configuration object containing signing secret and bot token
35
+
* @throws Error if required credentials are missing
37
+
constructor(config?: SlackConfig) {
38
+
this.signingSecret =
39
+
config?.signingSecret || process.env.SLACK_SIGNING_SECRET || "";
40
+
this.botToken = config?.botToken || process.env.SLACK_BOT_TOKEN || "";
42
+
const missingFields = [];
43
+
if (!this.signingSecret) missingFields.push("signing secret");
44
+
if (!this.botToken) missingFields.push("bot token");
46
+
if (missingFields.length > 0) {
48
+
`Missing required Slack credentials: ${missingFields.join(" and ")} either pass them to the class or set them as environment variables`,
54
+
* Tests authentication with current credentials
55
+
* @returns Promise resolving to true if auth is valid
56
+
* @throws Error if authentication fails
58
+
async testAuth(): Promise<boolean> {
59
+
const response = await fetch("https://slack.com/api/auth.test", {
61
+
Authorization: `Bearer ${this.botToken}`,
62
+
"Content-Type": "application/json",
66
+
const data = await response.json();
68
+
throw new Error(`Authentication failed: ${data.error}`);
75
+
* Retrieves information about a Slack user
76
+
* @param userId The ID of the user to look up
77
+
* @returns Promise resolving to the user's information
78
+
* @throws Error if the API request fails
80
+
async getUserInfo(userId: string): Promise<SlackUser> {
81
+
const response = await fetch(
82
+
`https://slack.com/api/users.info?user=${userId}`,
86
+
Authorization: `Bearer ${this.botToken}`,
87
+
"Content-Type": "application/json",
89
+
body: JSON.stringify({ user: userId }),
93
+
const data: SlackUserInfoResponse = await response.json();
94
+
if ((!data.ok && data.error !== "user_not_found") || !data.user) {
95
+
throw new Error(data.error);
102
+
* Retrieves the list of custom emojis from the Slack workspace
103
+
* @returns Promise resolving to the emoji list
104
+
* @throws Error if the API request fails
106
+
async getEmojiList(): Promise<Record<string, string>> {
107
+
const response = await fetch("https://slack.com/api/emoji.list", {
109
+
Authorization: `Bearer ${this.botToken}`,
110
+
"Content-Type": "application/json",
114
+
const data: SlackEmojiListResponse = await response.json();
115
+
if (!data.ok || !data.emoji) {
116
+
throw new Error(`Failed to get emoji list: ${data.error}`);
123
+
* Verifies a Slack request signature
124
+
* @param signature The signature from the request header
125
+
* @param timestamp The timestamp from the request header
126
+
* @param body The raw request body
127
+
* @returns boolean indicating if the signature is valid
129
+
verifySignature(signature: string, timestamp: string, body: string): boolean {
130
+
const baseString = `v0:${timestamp}:${body}`;
131
+
const hmac = createHmac("sha256", this.signingSecret);
132
+
const computedSignature = `v0=${hmac.update(baseString).digest("hex")}`;
133
+
return timingSafeEqual(
134
+
Buffer.from(signature).valueOf() as Uint8Array,
135
+
Buffer.from(computedSignature).valueOf() as Uint8Array,
140
+
export { SlackWrapper };