brainlet-irc/bots/ChanServ.js
2021-01-25 00:23:34 -05:00

154 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);
}
}