Create bots
This commit is contained in:
parent
0972fb4bde
commit
60b564a154
6 changed files with 379 additions and 64 deletions
154
bots/ChanServ.js
Normal file
154
bots/ChanServ.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
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);
|
||||
}
|
||||
}
|
47
bots/DinoServ.js
Normal file
47
bots/DinoServ.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
An IRC bot to annoy people
|
||||
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");
|
||||
const brainlet = require("../brainlet-lib/index.js");
|
||||
|
||||
module.exports = {
|
||||
start: (config, {tokens}) => {
|
||||
let bot = new ircClient.Client("localhost", "DinoServ", {
|
||||
realName: "A bot to annoy users"
|
||||
});
|
||||
|
||||
bot.addListener("error", console.error);
|
||||
bot.addListener("message", (from, channel, text, message) => {
|
||||
if (text.match(/apt-get/))
|
||||
bot.say(channel, `Hello, annoying bot reporting in, just notifying @${from} that the apt-get command on debian is deprecated. Please use apt instead.
|
||||
|
||||
Thank you for attending my TED-talk.`);
|
||||
if (text.match(/reverse engineer/i)) {
|
||||
bot.say(channel, `@${from} Watch your language.`);
|
||||
bot.send("MODE", channel, "-v", from);
|
||||
bot.notice(from, "You have been muted for 10 seconds.");
|
||||
setTimeout(() => {
|
||||
bot.send("MODE", channel, "+v", from);
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
bot.addListener("invite", (channel) => {
|
||||
bot.join(channel);
|
||||
});
|
||||
}
|
||||
}
|
31
bots/MsgServ.js
Normal file
31
bots/MsgServ.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
An IRC bot manager to sync Brainlet messages
|
||||
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, channelNameToCategoryId, categoryIdToChannelName}) => {
|
||||
let bot = new ircClient.Client("localhost", "MsgServ", {
|
||||
realName: "A bot to sync messages from a Brainlet instance to the IRC server"
|
||||
});
|
||||
// TODO: Actually do stuff
|
||||
}
|
||||
};
|
100
bots/NickServ.js
Normal file
100
bots/NickServ.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
An IRC bot to manage Brainlet accounts
|
||||
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");
|
||||
const brainlet = require("../brainlet-lib/index.js");
|
||||
|
||||
module.exports = {
|
||||
start: (config, {tokens, sockets}) => {
|
||||
let bot = new ircClient.Client("localhost", "NickServ", {
|
||||
realName: "A bot to let users manage Brainlet accounts from an IRC server"
|
||||
});
|
||||
|
||||
const commands = {
|
||||
help: (from) => {
|
||||
bot.notice(from, config.responses.NickServ.help);
|
||||
},
|
||||
register: (from, [command, password, email]) => {
|
||||
if (!password || !email) {
|
||||
bot.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
bot.notice(from, `Registering account ${from}...`);
|
||||
brainlet.users.create(config.server, {
|
||||
name: from,
|
||||
email: email,
|
||||
password: password
|
||||
}).then(() => {
|
||||
bot.notice(from, "Successfully registered account");
|
||||
commands.identify(from, ["identify", from, password]);
|
||||
}).catch((error) => {
|
||||
bot.notice(from, `Failed to register account: ${error}`);
|
||||
});
|
||||
},
|
||||
identify: (from, [command, name, password]) => {
|
||||
if (!name || !password) {
|
||||
bot.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
bot.notice(from, `Logging into account ${name}...`);
|
||||
brainlet.users.login(config.server, {
|
||||
name: name,
|
||||
password: password
|
||||
}).then(({token = ""}) => {
|
||||
bot.notice(from, "Successfully logged into account");
|
||||
tokens[from] = token;
|
||||
|
||||
bot.notice(from, "Creating websocket connection...");
|
||||
brainlet.categories.client(config.server, token).then((client) => {
|
||||
sockets[from] = client;
|
||||
bot.notice(from, "Successfully connected to server\n" +
|
||||
"You will likely want to use \"/msg ChanServ INVITE\" now\n" +
|
||||
"Use \"/msg ChanServ HELP\" for more information");
|
||||
}).catch((error) => {
|
||||
bot.notice(from, `Failed to create websocket connection: ${error}`);
|
||||
});
|
||||
}).catch((error) => {
|
||||
bot.notice(from, `Failed to log into account: ${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.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
const command = commands[args[0].toLowerCase()];
|
||||
if (!command) {
|
||||
bot.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
command(from, args);
|
||||
});
|
||||
}
|
||||
};
|
43
config.js
43
config.js
|
@ -1,33 +1,42 @@
|
|||
module.exports = {
|
||||
server: "127.0.0.1:3005",
|
||||
spectator: {
|
||||
name: "BrainletToIrcSpectator",
|
||||
password: "Put a secure password here. See the README for more info"
|
||||
},
|
||||
|
||||
motd: "A Brainlet to IRC translation layer.\n" +
|
||||
"SECURITY WARNING: The traffic to the Brainlet server is not encrypted.\n" +
|
||||
"Use \"/msg NickServ help\" to see how to use an account",
|
||||
motd: "A Brainlet to IRC translation layer\n" +
|
||||
"SECURITY WARNING: The traffic to the Brainlet server is not encrypted\n" +
|
||||
"Use \"/msg NickServ help\" to see how to use an account\n" +
|
||||
"Use \"/msg ChanServ help\" to see how to join and manage channels",
|
||||
|
||||
responses: {
|
||||
NickServ: {
|
||||
help: `NickServ allows you to use and create a Brainlet account from IRC.
|
||||
help: `NickServ allows you to use and create a Brainlet account from the IRC server
|
||||
You can use a command with "/msg NickServ <command> <arguments>"
|
||||
|
||||
Commands:
|
||||
HELP - Shows this message
|
||||
REGISTER <password> <email> - Creates an account, using your current nick as the username.
|
||||
Note: The email is not directly used by IRC, but may be used by the Brainlet host.
|
||||
IDENTIFY <name> <password> - Logs into an account.`,
|
||||
REGISTER <password> <email> - Creates an account, using your current nick as the username
|
||||
Note: The email is not directly used by the IRC server, but could be used by the Brainlet host
|
||||
IDENTIFY <name> <password> - Logs into an account`,
|
||||
|
||||
// Sent on invalid command
|
||||
invalid: "Invalid command. Use \"/msg NickServ help\" for help",
|
||||
},
|
||||
ChanServ: {
|
||||
help: `ChanServ syncs channel information between a Brainlet instance and the IRC server
|
||||
You may get channel invites from ChanServ, as each channel is invite-only to require you to log in
|
||||
You can use a command with "/msg ChanServ <command> <arguments>
|
||||
|
||||
// Sent on an invalid entry.
|
||||
// %s is replaced with how it's invalid.
|
||||
invalidName: "Invalid username specified. %s",
|
||||
invalidEmail: "Invalid email specified. %s",
|
||||
invalidPassword: "Invalid password specified. %s"
|
||||
Commands:
|
||||
HELP - Shows this message
|
||||
INVITE - Get invited to every channel, as each channel is invite only
|
||||
CREATE <category name> - Creates a category`,
|
||||
|
||||
// Sent on invalid command
|
||||
invalid: "Invalid command. Use \"/msg ChanServ help\" for help",
|
||||
|
||||
// Sent when using a command requiring authentication without being logged in
|
||||
accessDenied: "You must log in before using this command. Use \"/msg NickServ help\" to see how to log in"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
bots: ["NickServ", "ChanServ", "MsgServ"]
|
||||
}
|
||||
|
|
68
index.js
68
index.js
|
@ -19,6 +19,7 @@ const fs = require("fs");
|
|||
const ircServer = require("./ircd.js/lib/server.js");
|
||||
const ircClient = require("irc");
|
||||
const crypto = require("crypto");
|
||||
const brainlet = require("./brainlet-lib/index.js");
|
||||
|
||||
// Set up IRC server
|
||||
const config = require(`${__dirname}/config.js`)
|
||||
|
@ -28,11 +29,19 @@ const genPass = () => {
|
|||
return crypto.randomBytes(66).toString("base64");
|
||||
}
|
||||
|
||||
// NOTE: You don't actually need the password to log into the account
|
||||
let systemUsers = {
|
||||
NickServ: {
|
||||
password: "12345"
|
||||
},
|
||||
ChanServ: {
|
||||
password: genPass()
|
||||
},
|
||||
DinoServ: {
|
||||
password: genPass()
|
||||
}
|
||||
}
|
||||
|
||||
const ircConfig = {
|
||||
network: "BrainletToIRC",
|
||||
hostname: "localhost",
|
||||
|
@ -61,51 +70,16 @@ fs.writeFileSync(`${__dirname}/ircd.js/config/config.json`, JSON.stringify(ircCo
|
|||
ircServer.Server.boot();
|
||||
|
||||
// Set up system users
|
||||
const ircServerAddress = ircConfig.links.server.host;
|
||||
const ircServerPort = ircConfig.links.server.port;
|
||||
let tokens = {};
|
||||
let sockets = {};
|
||||
let availableBots = {};
|
||||
// Maps channel names to category ids
|
||||
let channelNameToCategoryId = {};
|
||||
// Maps category ids to channel names
|
||||
let categoryIdToChannelName = {};
|
||||
|
||||
let NickServ = new ircClient.Client("localhost", "NickServ");
|
||||
console.log(NickServ)
|
||||
NickServ.addListener("error", console.error);
|
||||
NickServ.addListener("message", (from, to, text, message) => {
|
||||
// Make sure it's a private message to NickServ
|
||||
if (to !== NickServ.opt.nick)
|
||||
return;
|
||||
|
||||
const args = text.split(" ");
|
||||
if (args.length == 0) {
|
||||
NickServ.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "help": {
|
||||
NickServ.notice(from, config.responses.NickServ.help);
|
||||
break;
|
||||
}
|
||||
case "register": {
|
||||
const password = args[1];
|
||||
const email = args[2];
|
||||
if (!password || !email) {
|
||||
NickServ.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
NickServ.notice(from, "Account registration is currently not implemented");
|
||||
break;
|
||||
}
|
||||
case "identify": {
|
||||
const name = args[1];
|
||||
const password = args[2];
|
||||
if (!name || !password) {
|
||||
NickServ.notice(from, config.responses.NickServ.invalid);
|
||||
return;
|
||||
}
|
||||
NickServ.notice(from, "Account login is currently not implemented");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NickServ.notice(from, config.responses.NickServ.invalid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
for (const botIndex in config.bots) {
|
||||
const bot = config.bots[botIndex];
|
||||
require(`./bots/${bot}.js`).start(config, {tokens, sockets, availableBots, channelNameToCategoryId, categoryIdToChannelName});
|
||||
availableBots[bot] = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue