···
const missingEnvVars = [];
if (!process.env.SLACK_BOT_TOKEN) missingEnvVars.push("SLACK_BOT_TOKEN");
if (!process.env.SLACK_SIGNING_SECRET)
12
-
missingEnvVars.push("SLACK_SIGNING_SECRET");
12
+
missingEnvVars.push("SLACK_SIGNING_SECRET");
if (!process.env.ADMINS) missingEnvVars.push("ADMINS");
if (!process.env.IRC_NICK) missingEnvVars.push("IRC_NICK");
if (missingEnvVars.length > 0) {
18
-
`Missing required environment variables: ${missingEnvVars.join(", ")}`,
18
+
`Missing required environment variables: ${missingEnvVars.join(", ")}`,
const slackApp = new SlackApp({
24
-
SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN as string,
25
-
SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET as string,
26
-
SLACK_LOGGING_LEVEL: "INFO",
28
-
startLazyListenerAfterAck: true,
24
+
SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN as string,
25
+
SLACK_SIGNING_SECRET: process.env.SLACK_SIGNING_SECRET as string,
26
+
SLACK_LOGGING_LEVEL: "INFO",
28
+
startLazyListenerAfterAck: true,
const slackClient = slackApp.client;
let botUserId: string | undefined;
36
-
token: process.env.SLACK_BOT_TOKEN,
39
-
botUserId = result.user_id;
40
-
console.log(`Bot user ID: ${botUserId}`);
36
+
token: process.env.SLACK_BOT_TOKEN,
39
+
botUserId = result.user_id;
40
+
console.log(`Bot user ID: ${botUserId}`);
const ircClient = new irc.Client(
46
-
process.env.IRC_NICK || "slackbridge",
53
-
userName: process.env.IRC_NICK,
54
-
realName: "Slack IRC Bridge",
46
+
process.env.IRC_NICK || "slackbridge",
53
+
userName: process.env.IRC_NICK,
54
+
realName: "Slack IRC Bridge",
// Clean up IRC connection on hot reload or exit
process.on("beforeExit", () => {
60
-
ircClient.disconnect("Reloading", () => {
61
-
console.log("IRC client disconnected");
60
+
ircClient.disconnect("Reloading", () => {
61
+
console.log("IRC client disconnected");
// Register slash commands
···
// Join all mapped IRC channels on connect
ircClient.addListener("registered", async () => {
70
-
console.log("Connected to IRC server");
71
-
const mappings = channelMappings.getAll();
72
-
for (const mapping of mappings) {
73
-
ircClient.join(mapping.irc_channel);
70
+
console.log("Connected to IRC server");
71
+
const mappings = channelMappings.getAll();
72
+
for (const mapping of mappings) {
73
+
ircClient.join(mapping.irc_channel);
ircClient.addListener("join", (channel: string, nick: string) => {
78
-
if (nick === process.env.IRC_NICK) {
79
-
console.log(`Joined IRC channel: ${channel}`);
78
+
if (nick === process.env.IRC_NICK) {
79
+
console.log(`Joined IRC channel: ${channel}`);
85
-
async (nick: string, to: string, text: string) => {
86
-
// Ignore messages from our own bot (with or without numbers suffix)
87
-
const botNickPattern = new RegExp(`^${process.env.IRC_NICK}\\d*$`);
88
-
if (botNickPattern.test(nick)) return;
89
-
if (nick === "****") return;
85
+
async (nick: string, to: string, text: string) => {
86
+
// Ignore messages from our own bot (with or without numbers suffix)
87
+
const botNickPattern = new RegExp(`^${process.env.IRC_NICK}\\d*$`);
88
+
if (botNickPattern.test(nick)) return;
89
+
if (nick === "****") return;
91
-
// Find Slack channel mapping for this IRC channel
92
-
const mapping = channelMappings.getByIrcChannel(to);
93
-
if (!mapping) return;
91
+
// Find Slack channel mapping for this IRC channel
92
+
const mapping = channelMappings.getByIrcChannel(to);
93
+
if (!mapping) return;
95
-
// Check if this IRC nick is mapped to a Slack user
96
-
const userMapping = userMappings.getByIrcNick(nick);
95
+
// Check if this IRC nick is mapped to a Slack user
96
+
const userMapping = userMappings.getByIrcNick(nick);
98
-
const displayName = `${nick} <irc>`;
99
-
let iconUrl: string | undefined;
98
+
const displayName = `${nick} <irc>`;
99
+
let iconUrl: string | undefined;
103
-
iconUrl = `https://cachet.dunkirk.sh/users/${userMapping.slack_user_id}/r`;
105
-
console.error("Error fetching user info:", error);
103
+
iconUrl = `https://cachet.dunkirk.sh/users/${userMapping.slack_user_id}/r`;
105
+
console.error("Error fetching user info:", error);
110
-
await slackClient.chat.postMessage({
111
-
token: process.env.SLACK_BOT_TOKEN,
112
-
channel: mapping.slack_channel_id,
113
-
text: parseIRCFormatting(text),
114
-
username: displayName,
116
-
unfurl_links: false,
117
-
unfurl_media: false,
119
-
console.log(`IRC → Slack: <${nick}> ${text}`);
121
-
console.error("Error posting to Slack:", error);
110
+
await slackClient.chat.postMessage({
111
+
token: process.env.SLACK_BOT_TOKEN,
112
+
channel: mapping.slack_channel_id,
113
+
text: parseIRCFormatting(text),
114
+
username: displayName,
116
+
unfurl_links: false,
117
+
unfurl_media: false,
119
+
console.log(`IRC → Slack: <${nick}> ${text}`);
121
+
console.error("Error posting to Slack:", error);
ircClient.addListener("error", (error: string) => {
127
-
console.error("IRC error:", error);
127
+
console.error("IRC error:", error);
slackApp.event("message", async ({ payload }) => {
132
-
if (payload.subtype) return;
133
-
if (payload.bot_id) return;
134
-
if (payload.user === botUserId) return;
132
+
if (payload.subtype) return;
133
+
if (payload.bot_id) return;
134
+
if (payload.user === botUserId) return;
135
+
if (payload.thread_ts) return;
136
-
// Find IRC channel mapping for this Slack channel
137
-
const mapping = channelMappings.getBySlackChannel(payload.channel);
140
-
`No IRC channel mapping found for Slack channel ${payload.channel}`,
142
-
slackClient.conversations.leave({
143
-
channel: payload.channel,
137
+
// Find IRC channel mapping for this Slack channel
138
+
const mapping = channelMappings.getBySlackChannel(payload.channel);
141
+
`No IRC channel mapping found for Slack channel ${payload.channel}`,
143
+
slackClient.conversations.leave({
144
+
channel: payload.channel,
149
-
const userInfo = await slackClient.users.info({
150
-
token: process.env.SLACK_BOT_TOKEN,
151
-
user: payload.user,
150
+
const userInfo = await slackClient.users.info({
151
+
token: process.env.SLACK_BOT_TOKEN,
152
+
user: payload.user,
154
-
// Check for user mapping, otherwise use Slack name
155
-
const userMapping = userMappings.getBySlackUser(payload.user);
157
-
userMapping?.irc_nick ||
158
-
userInfo.user?.real_name ||
159
-
userInfo.user?.name ||
155
+
// Check for user mapping, otherwise use Slack name
156
+
const userMapping = userMappings.getBySlackUser(payload.user);
158
+
userMapping?.irc_nick ||
159
+
userInfo.user?.real_name ||
160
+
userInfo.user?.name ||
162
-
// Parse Slack mentions and replace with display names
163
-
let messageText = payload.text;
164
-
const mentionRegex = /<@(U[A-Z0-9]+)>/g;
165
-
const mentions = Array.from(messageText.matchAll(mentionRegex));
163
+
// Parse Slack mentions and replace with display names
164
+
let messageText = payload.text;
165
+
const mentionRegex = /<@(U[A-Z0-9]+)>/g;
166
+
const mentions = Array.from(messageText.matchAll(mentionRegex));
167
-
for (const match of mentions) {
168
-
const userId = match[1];
170
-
const response = await fetch(
171
-
`https://cachet.dunkirk.sh/users/${userId}`,
174
-
const data = (await response.json()) as CachetUser;
175
-
messageText = messageText.replace(match[0], `@${data.displayName}`);
178
-
console.error(`Error fetching user ${userId} from cachet:`, error);
168
+
for (const match of mentions) {
169
+
const userId = match[1];
171
+
const response = await fetch(
172
+
`https://cachet.dunkirk.sh/users/${userId}`,
175
+
const data = (await response.json()) as CachetUser;
176
+
messageText = messageText.replace(match[0], `@${data.displayName}`);
179
+
console.error(`Error fetching user ${userId} from cachet:`, error);
182
-
// Parse Slack markdown formatting
183
-
messageText = parseSlackMarkdown(messageText);
183
+
// Parse Slack markdown formatting
184
+
messageText = parseSlackMarkdown(messageText);
185
-
const message = `<${username}> ${messageText}`;
186
+
const message = `<${username}> ${messageText}`;
187
-
ircClient.say(mapping.irc_channel, message);
188
-
console.log(`Slack → IRC: ${message}`);
190
-
console.error("Error handling Slack message:", error);
188
+
ircClient.say(mapping.irc_channel, message);
189
+
console.log(`Slack → IRC: ${message}`);
191
+
console.error("Error handling Slack message:", error);
195
-
port: process.env.PORT || 3000,
196
-
async fetch(request: Request) {
197
-
const url = new URL(request.url);
198
-
const path = url.pathname;
196
+
port: process.env.PORT || 3000,
197
+
async fetch(request: Request) {
198
+
const url = new URL(request.url);
199
+
const path = url.pathname;
202
-
return new Response(`Hello World from irc-slack-bridge@${version}`);
204
-
return new Response("OK");
206
-
return slackApp.run(request);
208
-
return new Response("404 Not Found", { status: 404 });
203
+
return new Response(`Hello World from irc-slack-bridge@${version}`);
205
+
return new Response("OK");
207
+
return slackApp.run(request);
209
+
return new Response("404 Not Found", { status: 404 });
214
-
`🚀 Server Started in ${Bun.nanoseconds() / 1000000} milliseconds on version: ${version}!\n\n----------------------------------\n`,
215
+
`🚀 Server Started in ${Bun.nanoseconds() / 1000000} milliseconds on version: ${version}!\n\n----------------------------------\n`,
217
-
`Connecting to IRC: irc.hackclub.com:6667 as ${process.env.IRC_NICK}`,
218
+
`Connecting to IRC: irc.hackclub.com:6667 as ${process.env.IRC_NICK}`,
console.log(`Channel mappings: ${channelMappings.getAll().length}`);
console.log(`User mappings: ${userMappings.getAll().length}`);