this repo has no description

feat: add nick serv support

dunkirk.sh 869ae2fa ac1f8287

verified
Changed files
+58 -4
src
+2
.env.example
···
# IRC Configuration
IRC_NICK=slackbridge
# Admin users (comma-separated Slack user IDs)
ADMINS=U1234567890,U0987654321
···
# IRC Configuration
IRC_NICK=slackbridge
+
NICKSERV_PASSWORD=your-nickserv-password-here
+
NICKSERV_EMAIL=your-email@example.com
# Admin users (comma-separated Slack user IDs)
ADMINS=U1234567890,U0987654321
+7 -1
README.md
···
# IRC Configuration
IRC_NICK=slackbridge
# Admin users (comma-separated Slack user IDs)
ADMINS=U1234567890,U0987654321
···
The bridge connects to `irc.hackclub.com:6667` (no TLS) and forwards messages bidirectionally based on channel mappings:
- **IRC → Slack**: Messages from mapped IRC channels appear in their corresponding Slack channels
- Image URLs are automatically displayed as inline attachments
- IRC mentions (`@nick` or `nick:`) are converted to Slack mentions for mapped users
- IRC formatting codes are converted to Slack markdown
- **Slack → IRC**: Messages from mapped Slack channels are sent to their corresponding IRC channels
-
- Slack mentions are converted to `@displayName` format using Cachet
- Slack markdown is converted to IRC formatting codes
- File attachments are uploaded to Hack Club CDN and URLs are shared
- **User mappings** allow custom IRC nicknames for specific Slack users and enable proper mentions both ways
···
# IRC Configuration
IRC_NICK=slackbridge
+
NICKSERV_PASSWORD=your-nickserv-password-here
+
NICKSERV_EMAIL=your-email@example.com
# Admin users (comma-separated Slack user IDs)
ADMINS=U1234567890,U0987654321
···
The bridge connects to `irc.hackclub.com:6667` (no TLS) and forwards messages bidirectionally based on channel mappings:
+
- **NickServ Authentication**: If `NICKSERV_PASSWORD` is configured, the bridge authenticates on connect
+
- Waits for NickServ confirmation before joining channels
+
- Auto-registers the nick if not registered (requires `NICKSERV_EMAIL`)
+
- Prevents "No external channel messages" errors by ensuring proper authentication
- **IRC → Slack**: Messages from mapped IRC channels appear in their corresponding Slack channels
- Image URLs are automatically displayed as inline attachments
- IRC mentions (`@nick` or `nick:`) are converted to Slack mentions for mapped users
- IRC formatting codes are converted to Slack markdown
- **Slack → IRC**: Messages from mapped Slack channels are sent to their corresponding IRC channels
+
- Slack mentions are converted to mapped IRC nicks, or the display name from `<@U123|name>` format
- Slack markdown is converted to IRC formatting codes
- File attachments are uploaded to Hack Club CDN and URLs are shared
- **User mappings** allow custom IRC nicknames for specific Slack users and enable proper mentions both ways
+49 -3
src/index.ts
···
// Register slash commands
registerCommands();
// Join all mapped IRC channels on connect
ircClient.addListener("registered", async () => {
console.log("Connected to IRC server");
-
const mappings = channelMappings.getAll();
-
for (const mapping of mappings) {
-
ircClient.join(mapping.irc_channel);
}
});
ircClient.addListener("join", (channel: string, nick: string) => {
if (nick === process.env.IRC_NICK) {
console.log(`Joined IRC channel: ${channel}`);
}
});
···
// Register slash commands
registerCommands();
+
// Track NickServ authentication state
+
let nickServAuthAttempted = false;
+
let isAuthenticated = false;
+
// 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;
+
console.log("Authenticating with NickServ...");
+
ircClient.say("NickServ", `IDENTIFY ${process.env.NICKSERV_PASSWORD}`);
+
// Don't join channels yet - wait for NickServ response
+
} else if (!process.env.NICKSERV_PASSWORD) {
+
// No auth needed, join immediately
+
const mappings = channelMappings.getAll();
+
for (const mapping of mappings) {
+
ircClient.join(mapping.irc_channel);
+
}
}
});
ircClient.addListener("join", (channel: string, nick: string) => {
if (nick === process.env.IRC_NICK) {
console.log(`Joined IRC channel: ${channel}`);
+
}
+
});
+
+
// Handle NickServ notices
+
ircClient.addListener("notice", async (nick: string, to: string, text: string) => {
+
if (nick !== "NickServ") return;
+
+
console.log(`NickServ: ${text}`);
+
+
// Check for successful authentication
+
if (text.includes("You are now identified") || text.includes("Password accepted")) {
+
console.log("✓ Successfully authenticated with NickServ");
+
isAuthenticated = true;
+
+
// Join channels after successful auth
+
const mappings = channelMappings.getAll();
+
for (const mapping of mappings) {
+
ircClient.join(mapping.irc_channel);
+
}
+
}
+
// Check if nick is not registered
+
else if (text.includes("isn't registered") || text.includes("not registered")) {
+
console.log("Nick not registered, registering with NickServ...");
+
if (process.env.NICKSERV_PASSWORD && process.env.NICKSERV_EMAIL) {
+
ircClient.say("NickServ", `REGISTER ${process.env.NICKSERV_PASSWORD} ${process.env.NICKSERV_EMAIL}`);
+
} else {
+
console.error("Cannot register: NICKSERV_EMAIL not configured");
+
}
+
}
+
// Check for failed authentication
+
else if (text.includes("Invalid password") || text.includes("Access denied")) {
+
console.error("✗ NickServ authentication failed: Invalid password");
}
});