···
// Join all mapped IRC channels on connect
ircClient.addListener("registered", async () => {
console.log("Connected to IRC server");
// Authenticate with NickServ if password is provided
if (process.env.NICKSERV_PASSWORD && !nickServAuthAttempted) {
nickServAuthAttempted = true;
···
// Handle NickServ notices
117
-
ircClient.addListener("notice", async (nick: string, to: string, text: string) => {
118
-
if (nick !== "NickServ") return;
120
-
console.log(`NickServ: ${text}`);
122
-
// Check for successful authentication
123
-
if (text.includes("You are now identified") || text.includes("Password accepted")) {
124
-
console.log("✓ Successfully authenticated with NickServ");
125
-
isAuthenticated = true;
127
-
// Join channels after successful auth
128
-
const mappings = channelMappings.getAll();
129
-
for (const mapping of mappings) {
130
-
ircClient.join(mapping.irc_channel);
117
+
ircClient.addListener(
119
+
async (nick: string, to: string, text: string) => {
120
+
if (nick !== "NickServ") return;
122
+
console.log(`NickServ: ${text}`);
124
+
// Check for successful authentication
126
+
text.includes("You are now identified") ||
127
+
text.includes("Password accepted")
129
+
console.log("✓ Successfully authenticated with NickServ");
130
+
isAuthenticated = true;
132
+
// Join channels after successful auth
133
+
const mappings = channelMappings.getAll();
134
+
for (const mapping of mappings) {
135
+
ircClient.join(mapping.irc_channel);
133
-
// Check if nick is not registered
134
-
else if (text.includes("isn't registered") || text.includes("not registered")) {
135
-
console.log("Nick not registered, registering with NickServ...");
136
-
if (process.env.NICKSERV_PASSWORD && process.env.NICKSERV_EMAIL) {
137
-
ircClient.say("NickServ", `REGISTER ${process.env.NICKSERV_PASSWORD} ${process.env.NICKSERV_EMAIL}`);
139
-
console.error("Cannot register: NICKSERV_EMAIL not configured");
138
+
// Check if nick is not registered
140
+
text.includes("isn't registered") ||
141
+
text.includes("not registered")
143
+
console.log("Nick not registered, registering with NickServ...");
144
+
if (process.env.NICKSERV_PASSWORD && process.env.NICKSERV_EMAIL) {
147
+
`REGISTER ${process.env.NICKSERV_PASSWORD} ${process.env.NICKSERV_EMAIL}`,
150
+
console.error("Cannot register: NICKSERV_EMAIL not configured");
142
-
// Check for failed authentication
143
-
else if (text.includes("Invalid password") || text.includes("Access denied")) {
144
-
console.error("✗ NickServ authentication failed: Invalid password");
153
+
// Check for failed authentication
155
+
text.includes("Invalid password") ||
156
+
text.includes("Access denied")
158
+
console.error("✗ NickServ authentication failed: Invalid password");
···
attachments: attachments,
225
-
unfurl_links: false,
226
-
unfurl_media: false,
240
+
unfurl_links: true,
241
+
unfurl_media: true,
await slackClient.chat.postMessage({
···
235
-
unfurl_links: false,
236
-
unfurl_media: false,
250
+
unfurl_links: true,
251
+
unfurl_media: true,
console.log(`IRC → Slack: <${nick}> ${text}`);
···
// Handle IRC /me actions
251
-
ircClient.addListener("action", async (nick: string, to: string, text: string) => {
252
-
// Ignore messages from our own bot
253
-
const botNickPattern = new RegExp(`^${process.env.IRC_NICK}\\d*$`);
254
-
if (botNickPattern.test(nick)) return;
255
-
if (nick === "****") return;
266
+
ircClient.addListener(
268
+
async (nick: string, to: string, text: string) => {
269
+
// Ignore messages from our own bot
270
+
const botNickPattern = new RegExp(`^${process.env.IRC_NICK}\\d*$`);
271
+
if (botNickPattern.test(nick)) return;
272
+
if (nick === "****") return;
257
-
// Find Slack channel mapping for this IRC channel
258
-
const mapping = channelMappings.getByIrcChannel(to);
259
-
if (!mapping) return;
274
+
// Find Slack channel mapping for this IRC channel
275
+
const mapping = channelMappings.getByIrcChannel(to);
276
+
if (!mapping) return;
261
-
// Check if this IRC nick is mapped to a Slack user
262
-
const userMapping = userMappings.getByIrcNick(nick);
278
+
// Check if this IRC nick is mapped to a Slack user
279
+
const userMapping = userMappings.getByIrcNick(nick);
264
-
let iconUrl: string;
266
-
iconUrl = `https://cachet.dunkirk.sh/users/${userMapping.slack_user_id}/r`;
268
-
iconUrl = getAvatarForNick(nick);
281
+
let iconUrl: string;
283
+
iconUrl = `https://cachet.dunkirk.sh/users/${userMapping.slack_user_id}/r`;
285
+
iconUrl = getAvatarForNick(nick);
271
-
// Parse IRC formatting and mentions
272
-
let messageText = parseIRCFormatting(text);
288
+
// Parse IRC formatting and mentions
289
+
let messageText = parseIRCFormatting(text);
274
-
// Find all @mentions and nick: mentions in the IRC message
275
-
const atMentionPattern = /@(\w+)/g;
276
-
const nickMentionPattern = /(\w+):/g;
291
+
// Find all @mentions and nick: mentions in the IRC message
292
+
const atMentionPattern = /@(\w+)/g;
293
+
const nickMentionPattern = /(\w+):/g;
278
-
const atMentions = Array.from(messageText.matchAll(atMentionPattern));
279
-
const nickMentions = Array.from(messageText.matchAll(nickMentionPattern));
295
+
const atMentions = Array.from(messageText.matchAll(atMentionPattern));
296
+
const nickMentions = Array.from(messageText.matchAll(nickMentionPattern));
281
-
for (const match of atMentions) {
282
-
const mentionedNick = match[1] as string;
283
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
284
-
if (mentionedUserMapping) {
285
-
messageText = messageText.replace(
287
-
`<@${mentionedUserMapping.slack_user_id}>`,
298
+
for (const match of atMentions) {
299
+
const mentionedNick = match[1] as string;
300
+
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
301
+
if (mentionedUserMapping) {
302
+
messageText = messageText.replace(
304
+
`<@${mentionedUserMapping.slack_user_id}>`,
292
-
for (const match of nickMentions) {
293
-
const mentionedNick = match[1] as string;
294
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
295
-
if (mentionedUserMapping) {
296
-
messageText = messageText.replace(
298
-
`<@${mentionedUserMapping.slack_user_id}>:`,
309
+
for (const match of nickMentions) {
310
+
const mentionedNick = match[1] as string;
311
+
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
312
+
if (mentionedUserMapping) {
313
+
messageText = messageText.replace(
315
+
`<@${mentionedUserMapping.slack_user_id}>:`,
303
-
// Format as action message with context block
304
-
const actionText = `${nick} ${messageText}`;
320
+
// Format as action message with context block
321
+
const actionText = `${nick} ${messageText}`;
306
-
await slackClient.chat.postMessage({
307
-
token: process.env.SLACK_BOT_TOKEN,
308
-
channel: mapping.slack_channel_id,
316
-
image_url: iconUrl,
323
+
await slackClient.chat.postMessage({
324
+
token: process.env.SLACK_BOT_TOKEN,
325
+
channel: mapping.slack_channel_id,
333
+
image_url: iconUrl,
328
-
console.log(`IRC → Slack (action): ${actionText}`);
345
+
console.log(`IRC → Slack (action): ${actionText}`);
slackApp.event("message", async ({ payload, context }) => {
···
const mentions = Array.from(messageText.matchAll(mentionRegex));
for (const match of mentions) {
371
-
const userId = match[1];
372
-
const displayName = match[3]; // The name part after |
389
+
const userId = match[1] as string;
390
+
const displayName = match[3] as string; // The name part after |
// Check if user has a mapped IRC nick
const mentionedUserMapping = userMappings.getBySlackUser(userId);
if (mentionedUserMapping) {
377
-
messageText = messageText.replace(match[0], `@${mentionedUserMapping.irc_nick}`);
395
+
messageText = messageText.replace(
397
+
`@${mentionedUserMapping.irc_nick}`,
} else if (displayName) {
// Use the display name from the mention format <@U123|name>
messageText = messageText.replace(match[0], `@${displayName}`);
···
const response = await fetch(
`https://cachet.dunkirk.sh/users/${userId}`,
387
-
// @ts-ignore - Bun specific option
tls: { rejectUnauthorized: false },