this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add /me support

+82
+1
README.md
··· 100 100 - Image URLs are automatically displayed as inline attachments 101 101 - IRC mentions (`@nick` or `nick:`) are converted to Slack mentions for mapped users 102 102 - IRC formatting codes are converted to Slack markdown 103 + - IRC `/me` actions are displayed in a context block with the user's avatar 103 104 - **Slack → IRC**: Messages from mapped Slack channels are sent to their corresponding IRC channels 104 105 - Slack mentions are converted to mapped IRC nicks, or the display name from `<@U123|name>` format 105 106 - Slack markdown is converted to IRC formatting codes
+81
src/index.ts
··· 247 247 console.error("IRC error:", error); 248 248 }); 249 249 250 + // 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; 256 + 257 + // Find Slack channel mapping for this IRC channel 258 + const mapping = channelMappings.getByIrcChannel(to); 259 + if (!mapping) return; 260 + 261 + // Check if this IRC nick is mapped to a Slack user 262 + const userMapping = userMappings.getByIrcNick(nick); 263 + 264 + let iconUrl: string; 265 + if (userMapping) { 266 + iconUrl = `https://cachet.dunkirk.sh/users/${userMapping.slack_user_id}/r`; 267 + } else { 268 + iconUrl = getAvatarForNick(nick); 269 + } 270 + 271 + // Parse IRC formatting and mentions 272 + let messageText = parseIRCFormatting(text); 273 + 274 + // Find all @mentions and nick: mentions in the IRC message 275 + const atMentionPattern = /@(\w+)/g; 276 + const nickMentionPattern = /(\w+):/g; 277 + 278 + const atMentions = Array.from(messageText.matchAll(atMentionPattern)); 279 + const nickMentions = Array.from(messageText.matchAll(nickMentionPattern)); 280 + 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( 286 + match[0], 287 + `<@${mentionedUserMapping.slack_user_id}>`, 288 + ); 289 + } 290 + } 291 + 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( 297 + match[0], 298 + `<@${mentionedUserMapping.slack_user_id}>:`, 299 + ); 300 + } 301 + } 302 + 303 + // Format as action message with context block 304 + const actionText = `${nick} ${messageText}`; 305 + 306 + await slackClient.chat.postMessage({ 307 + token: process.env.SLACK_BOT_TOKEN, 308 + channel: mapping.slack_channel_id, 309 + text: actionText, 310 + blocks: [ 311 + { 312 + type: "context", 313 + elements: [ 314 + { 315 + type: "image", 316 + image_url: iconUrl, 317 + alt_text: nick, 318 + }, 319 + { 320 + type: "mrkdwn", 321 + text: actionText, 322 + }, 323 + ], 324 + }, 325 + ], 326 + }); 327 + 328 + console.log(`IRC → Slack (action): ${actionText}`); 329 + }); 330 + 250 331 // Slack event handlers 251 332 slackApp.event("message", async ({ payload, context }) => { 252 333 // Ignore bot messages and threaded messages