this repo has no description

feat: move everything to lib and add some more libs

dunkirk.sh 7515505d 3ef74b0c

verified
+2 -2
src/commands.ts
···
import type { AnyMessageBlock } from "slack-edge";
-
import { channelMappings, userMappings } from "./db";
import { ircClient, slackApp } from "./index";
-
import { canManageChannel } from "./permissions";
export function registerCommands() {
// Link Slack channel to IRC channel
···
import type { AnyMessageBlock } from "slack-edge";
+
import { channelMappings, userMappings } from "./lib/db";
import { ircClient, slackApp } from "./index";
+
import { canManageChannel } from "./lib/permissions";
export function registerCommands() {
// Link Slack channel to IRC channel
src/db.ts src/lib/db.ts
+10 -115
src/index.ts
···
import { SlackApp } from "slack-edge";
import { version } from "../package.json";
import { registerCommands } from "./commands";
-
import { channelMappings, userMappings } from "./db";
import { uploadToCDN } from "./lib/cdn";
-
import { parseIRCFormatting, parseSlackMarkdown } from "./parser";
-
import type { CachetUser } from "./types";
-
-
// Default profile pictures for unmapped IRC users
-
const DEFAULT_AVATARS = [
-
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/4183627c4d26c56c915e104a8a7374f43acd1733_pfp__1_.png",
-
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/389b1e6bd4248a7e5dd88e14c1adb8eb01267080_pfp__2_.png",
-
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/03011a5e59548191de058f33ccd1d1cb1d64f2a0_pfp__3_.png",
-
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/f9c57b88fbd4633114c1864bcc2968db555dbd2a_pfp__4_.png",
-
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/e61a8cabee5a749588125242747b65122fb94205_pfp.png",
-
];
-
-
// Hash function for stable avatar selection
-
function getAvatarForNick(nick: string): string {
-
let hash = 0;
-
for (let i = 0; i < nick.length; i++) {
-
hash = (hash << 5) - hash + nick.charCodeAt(i);
-
hash = hash & hash; // Convert to 32bit integer
-
}
-
return DEFAULT_AVATARS[Math.abs(hash) % DEFAULT_AVATARS.length] as string;
-
}
const missingEnvVars = [];
if (!process.env.SLACK_BOT_TOKEN) missingEnvVars.push("SLACK_BOT_TOKEN");
···
/https?:\/\/[^\s]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg)(?:\?[^\s]*)?/gi;
const imageUrls = Array.from(messageText.matchAll(imagePattern));
-
// Find all @mentions and nick: mentions in the IRC message
-
const atMentionPattern = /@(\w+)/g;
-
const nickMentionPattern = /(\w+):/g;
-
-
const atMentions = Array.from(messageText.matchAll(atMentionPattern));
-
const nickMentions = Array.from(messageText.matchAll(nickMentionPattern));
-
-
for (const match of atMentions) {
-
const mentionedNick = match[1] as string;
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
-
if (mentionedUserMapping) {
-
messageText = messageText.replace(
-
match[0],
-
`<@${mentionedUserMapping.slack_user_id}>`,
-
);
-
}
-
}
-
-
for (const match of nickMentions) {
-
const mentionedNick = match[1] as string;
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
-
if (mentionedUserMapping) {
-
messageText = messageText.replace(
-
match[0],
-
`<@${mentionedUserMapping.slack_user_id}>:`,
-
);
-
}
-
}
try {
// If there are image URLs, send them as attachments
···
// Parse IRC formatting and mentions
let messageText = parseIRCFormatting(text);
-
-
// Find all @mentions and nick: mentions in the IRC message
-
const atMentionPattern = /@(\w+)/g;
-
const nickMentionPattern = /(\w+):/g;
-
-
const atMentions = Array.from(messageText.matchAll(atMentionPattern));
-
const nickMentions = Array.from(messageText.matchAll(nickMentionPattern));
-
-
for (const match of atMentions) {
-
const mentionedNick = match[1] as string;
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
-
if (mentionedUserMapping) {
-
messageText = messageText.replace(
-
match[0],
-
`<@${mentionedUserMapping.slack_user_id}>`,
-
);
-
}
-
}
-
-
for (const match of nickMentions) {
-
const mentionedNick = match[1] as string;
-
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
-
if (mentionedUserMapping) {
-
messageText = messageText.replace(
-
match[0],
-
`<@${mentionedUserMapping.slack_user_id}>:`,
-
);
-
}
-
}
// Format as action message with context block
const actionText = `${nick} ${messageText}`;
···
"Unknown";
// Parse Slack mentions and replace with IRC nicks or display names
-
let messageText = payload.text;
-
const mentionRegex = /<@(U[A-Z0-9]+)(\|([^>]+))?>/g;
-
const mentions = Array.from(messageText.matchAll(mentionRegex));
-
-
for (const match of mentions) {
-
const userId = match[1] as string;
-
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) {
-
messageText = messageText.replace(
-
match[0],
-
`@${mentionedUserMapping.irc_nick}`,
-
);
-
} else if (displayName) {
-
// Use the display name from the mention format <@U123|name>
-
messageText = messageText.replace(match[0], `@${displayName}`);
-
} else {
-
// Fallback to Cachet lookup
-
try {
-
const response = await fetch(
-
`https://cachet.dunkirk.sh/users/${userId}`,
-
{
-
tls: { rejectUnauthorized: false },
-
},
-
);
-
if (response.ok) {
-
const data = (await response.json()) as CachetUser;
-
messageText = messageText.replace(match[0], `@${data.displayName}`);
-
}
-
} catch (error) {
-
console.error(`Error fetching user ${userId} from cachet:`, error);
-
}
-
}
-
}
// Parse Slack markdown formatting
messageText = parseSlackMarkdown(messageText);
···
import { SlackApp } from "slack-edge";
import { version } from "../package.json";
import { registerCommands } from "./commands";
+
import { channelMappings, userMappings } from "./lib/db";
+
import { getAvatarForNick } from "./lib/avatars";
import { uploadToCDN } from "./lib/cdn";
+
import {
+
convertIrcMentionsToSlack,
+
convertSlackMentionsToIrc,
+
} from "./lib/mentions";
+
import { parseIRCFormatting, parseSlackMarkdown } from "./lib/parser";
const missingEnvVars = [];
if (!process.env.SLACK_BOT_TOKEN) missingEnvVars.push("SLACK_BOT_TOKEN");
···
/https?:\/\/[^\s]+\.(?:png|jpg|jpeg|gif|webp|bmp|svg)(?:\?[^\s]*)?/gi;
const imageUrls = Array.from(messageText.matchAll(imagePattern));
+
messageText = convertIrcMentionsToSlack(messageText);
try {
// If there are image URLs, send them as attachments
···
// Parse IRC formatting and mentions
let messageText = parseIRCFormatting(text);
+
messageText = convertIrcMentionsToSlack(messageText);
// Format as action message with context block
const actionText = `${nick} ${messageText}`;
···
"Unknown";
// Parse Slack mentions and replace with IRC nicks or display names
+
let messageText = await convertSlackMentionsToIrc(payload.text);
// Parse Slack markdown formatting
messageText = parseSlackMarkdown(messageText);
+19
src/lib/avatars.ts
···
···
+
const DEFAULT_AVATARS = [
+
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/4183627c4d26c56c915e104a8a7374f43acd1733_pfp__1_.png",
+
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/389b1e6bd4248a7e5dd88e14c1adb8eb01267080_pfp__2_.png",
+
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/03011a5e59548191de058f33ccd1d1cb1d64f2a0_pfp__3_.png",
+
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/f9c57b88fbd4633114c1864bcc2968db555dbd2a_pfp__4_.png",
+
"https://hc-cdn.hel1.your-objectstorage.com/s/v3/e61a8cabee5a749588125242747b65122fb94205_pfp.png",
+
];
+
+
/**
+
* Returns a stable avatar URL for an IRC nick based on hash
+
*/
+
export function getAvatarForNick(nick: string): string {
+
let hash = 0;
+
for (let i = 0; i < nick.length; i++) {
+
hash = (hash << 5) - hash + nick.charCodeAt(i);
+
hash = hash & hash; // Convert to 32bit integer
+
}
+
return DEFAULT_AVATARS[Math.abs(hash) % DEFAULT_AVATARS.length] as string;
+
}
+21
src/lib/cachet.ts
···
···
+
import type { CachetUser } from "../types";
+
+
/**
+
* Fetches user information from Cachet API
+
*/
+
export async function getCachetUser(
+
userId: string,
+
): Promise<CachetUser | null> {
+
try {
+
const response = await fetch(`https://cachet.dunkirk.sh/users/${userId}`, {
+
tls: { rejectUnauthorized: false },
+
});
+
if (response.ok) {
+
return (await response.json()) as CachetUser;
+
}
+
return null;
+
} catch (error) {
+
console.error(`Error fetching user ${userId} from cachet:`, error);
+
return null;
+
}
+
}
+73
src/lib/mentions.ts
···
···
+
import { userMappings } from "./db";
+
import { getCachetUser } from "./cachet";
+
+
/**
+
* Converts IRC @mentions and nick: mentions to Slack user mentions
+
*/
+
export function convertIrcMentionsToSlack(messageText: string): string {
+
let result = messageText;
+
+
// Find all @mentions and nick: mentions in the IRC message
+
const atMentionPattern = /@(\w+)/g;
+
const nickMentionPattern = /(\w+):/g;
+
+
const atMentions = Array.from(result.matchAll(atMentionPattern));
+
const nickMentions = Array.from(result.matchAll(nickMentionPattern));
+
+
for (const match of atMentions) {
+
const mentionedNick = match[1] as string;
+
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
+
if (mentionedUserMapping) {
+
result = result.replace(
+
match[0],
+
`<@${mentionedUserMapping.slack_user_id}>`,
+
);
+
}
+
}
+
+
for (const match of nickMentions) {
+
const mentionedNick = match[1] as string;
+
const mentionedUserMapping = userMappings.getByIrcNick(mentionedNick);
+
if (mentionedUserMapping) {
+
result = result.replace(
+
match[0],
+
`<@${mentionedUserMapping.slack_user_id}>:`,
+
);
+
}
+
}
+
+
return result;
+
}
+
+
/**
+
* Converts Slack user mentions to IRC @mentions, with Cachet fallback
+
*/
+
export async function convertSlackMentionsToIrc(
+
messageText: string,
+
): Promise<string> {
+
let result = messageText;
+
const mentionRegex = /<@(U[A-Z0-9]+)(\|([^>]+))?>/g;
+
const mentions = Array.from(result.matchAll(mentionRegex));
+
+
for (const match of mentions) {
+
const userId = match[1] as string;
+
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) {
+
result = result.replace(match[0], `@${mentionedUserMapping.irc_nick}`);
+
} else if (displayName) {
+
// Use the display name from the mention format <@U123|name>
+
result = result.replace(match[0], `@${displayName}`);
+
} else {
+
// Fallback to Cachet lookup
+
const data = await getCachetUser(userId);
+
if (data) {
+
result = result.replace(match[0], `@${data.displayName}`);
+
}
+
}
+
}
+
+
return result;
+
}
src/parser.ts src/lib/parser.ts
src/permissions.ts src/lib/permissions.ts