155 lines
5.1 KiB
JavaScript
155 lines
5.1 KiB
JavaScript
|
/*
|
||
|
An IRC bot to sync Brainlet categories
|
||
|
Copyright (C) 2020 hiimgoodpack
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
const ircClient = require("irc");
|
||
|
// Node's replaceAll is a new feature. We'll use a package instead
|
||
|
const replaceAll = require("replaceall");
|
||
|
const brainlet = require("../brainlet-lib/index.js");
|
||
|
|
||
|
module.exports = {
|
||
|
start: (config, {tokens, sockets, availableBots, channelNameToCategoryId, categoryIdToChannelName}) => {
|
||
|
let bot = new ircClient.Client("localhost", "ChanServ", {
|
||
|
realName: "A bot to sync channels from a Brainlet instance to the IRC server"
|
||
|
});
|
||
|
|
||
|
const getRandomToken = () => {
|
||
|
const names = Object.keys(tokens);
|
||
|
let nameIndex = Math.floor(Math.random() * names.length);
|
||
|
// Floating point rounding might make nameIndex >= tokens.length, which we don't want
|
||
|
if (nameIndex >= tokens.length)
|
||
|
nameIndex = tokens.length-1;
|
||
|
|
||
|
return tokens[names[nameIndex]];
|
||
|
}
|
||
|
|
||
|
const commands = {
|
||
|
help: (from) => {
|
||
|
bot.notice(from, config.responses.ChanServ.help);
|
||
|
},
|
||
|
invite: (from) => {
|
||
|
if (!tokens[from]) {
|
||
|
bot.notice(from, config.responses.ChanServ.accessDenied);
|
||
|
return;
|
||
|
}
|
||
|
Object.keys(channelNameToCategoryId).forEach((channelName) => {
|
||
|
bot.send("INVITE", from, channelName);
|
||
|
})
|
||
|
},
|
||
|
create: (from, args) => {
|
||
|
if (!tokens[from]) {
|
||
|
bot.notice(from, config.responses.ChanServ.accessDenied);
|
||
|
return;
|
||
|
}
|
||
|
const categoryTitle = args.splice(1).join(" ");
|
||
|
|
||
|
bot.notice(from, `Creating category ${categoryTitle}...`);
|
||
|
brainlet.categories.create(config.server, tokens[from], categoryTitle).then(() => {
|
||
|
bot.notice(from, "Successfully created category on Brainlet\n" +
|
||
|
"You may need to wait a few seconds for a category sync to occur to use the channel");
|
||
|
}).catch((error) => {
|
||
|
bot.notice(from, `Failed to create category: ${error}`);
|
||
|
});
|
||
|
}
|
||
|
};
|
||
|
|
||
|
bot.addListener("error", console.error);
|
||
|
bot.addListener("message", (from, channel, text, message) => {
|
||
|
// Make sure it's a private message channel the bot
|
||
|
if (channel !== bot.opt.nick)
|
||
|
return;
|
||
|
|
||
|
const args = text.split(" ");
|
||
|
if (args.length == 0) {
|
||
|
bot.notice(from, config.responses.ChanServ.invalid);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const command = commands[args[0].toLowerCase()];
|
||
|
if (!command) {
|
||
|
bot.notice(from, config.responses.ChanServ.invalid);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
command(from, args);
|
||
|
});
|
||
|
bot.addListener("join", (channel, nick) => {
|
||
|
bot.send("MODE", channel, "+v", nick);
|
||
|
|
||
|
// Is it a bot?
|
||
|
if (availableBots[nick]) {
|
||
|
// Give it channel op
|
||
|
bot.send("MODE", channel, "+o", nick);
|
||
|
} else {
|
||
|
const socket = sockets[nick];
|
||
|
const categoryId = channelNameToCategoryId[channel];
|
||
|
console.log(socket);
|
||
|
socket.connect(categoryId).then(() => {
|
||
|
console.log("Successfully joined channel");
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// We need 2 hexadecimal digits for a buffer to work
|
||
|
let nextId = 0xF+1;
|
||
|
const categoryToChannelName = (name) => {
|
||
|
return `#${replaceAll(" ", "_", name)}_${Buffer.from((nextId++).toString(16), "hex").toString("base64")}`;
|
||
|
};
|
||
|
// Update category list
|
||
|
setInterval(() => {
|
||
|
const token = getRandomToken();
|
||
|
if (!token)
|
||
|
return;
|
||
|
// TODO: Handle having more than 100 categories
|
||
|
// Alternatively, send a notice if there is more than 100 categories
|
||
|
const maxCategories = 100;
|
||
|
brainlet.categories.list(config.server, token, maxCategories).then((categories) => {
|
||
|
for (const {_id: categoryId, title: categoryTitle, creator: {username: creatorUsername}} of categories) {
|
||
|
// Make sure this is a new channel
|
||
|
if (categoryIdToChannelName[categoryId])
|
||
|
continue;
|
||
|
|
||
|
const channelName = categoryToChannelName(categoryTitle);
|
||
|
|
||
|
bot.join(channelName, () => {
|
||
|
channelNameToCategoryId[channelName] = categoryId;
|
||
|
categoryIdToChannelName[categoryId] = channelName;
|
||
|
bot.send("TOPIC", channelName, `${ircClient.colors.wrap("light_blue", categoryTitle)} by ${ircClient.colors.wrap("light_red", creatorUsername)}`);
|
||
|
|
||
|
// Invite only
|
||
|
bot.send("MODE", channelName, "+i");
|
||
|
// Allow mutes
|
||
|
bot.send("MODE", channelName, "+m");
|
||
|
|
||
|
// Invite everyone who is logged in
|
||
|
Object.keys(tokens).forEach((nick) => {
|
||
|
bot.send("INVITE", nick, channelName);
|
||
|
});
|
||
|
Object.keys(availableBots).forEach((nick) => {
|
||
|
if (nick === "ChanServ")
|
||
|
return;
|
||
|
bot.send("INVITE", nick, channelName);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}).catch((error) => {
|
||
|
console.error(`An error occured when updating category list: ${error}`);
|
||
|
});
|
||
|
}, 5000);
|
||
|
}
|
||
|
}
|