init
This commit is contained in:
commit
23dd42c963
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
config/config.json
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM node:18-alpine3.17
|
||||
RUN mkdir -p /home/node/app/node_modules \
|
||||
&& chown -R node:node /home/node/app
|
||||
WORKDIR /home/node/app
|
||||
USER node
|
||||
COPY --chown=node:node package*.json ./
|
||||
RUN npm i
|
||||
COPY --chown=node:node . .
|
||||
CMD [ "node", "bot.js" ]
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2023 hgw7
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
22
README.md
Normal file
22
README.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Bones
|
||||
|
||||
#### node.js IRC bot framework
|
||||
|
||||
Bones is the base framework I use for creating multithreaded IRC bots, originally developed as a way to hugely improve on the speeds of earlier versions of mercury (which were not multithreaded and was very slow)
|
||||
|
||||
## Deployment
|
||||
|
||||
Instructions are general and assume you have already developed something with this framework, not a whole lot of point trying to run this as it is.
|
||||
|
||||
1. Have Docker (required) and Docker Compose (optional, but strongly recommended, this guide assumes you have it) installed already.
|
||||
2. Rename `config/example.default.json` to `config/default.json` and modify it accordingly. A list of variables and their descriptions can be found in this repos wiki. You do not need to do anything with `example.usersettings.json` unless you wish to predefine settings prior to the bots first start, the usersettings file will be made on the first run if it does not exist.
|
||||
3. You may also want to edit the container names in the `docker-compose.yml` file accordingly. (Optional but recommended)
|
||||
4. Run `docker compose up` to begin. Append `-d` to start in the background and `--build` for the first run and subsequent starts after edits have been made. If you begin the bot with `-d` you can run `docker compose logs -f` to see live logs.
|
||||
|
||||
## Support
|
||||
|
||||
If you need assistance with installation or usage, you are more than welcome to contact me in #5000 on `irc.supernets.org`
|
||||
|
||||
## License
|
||||
|
||||
This framework is licensed under the ISC License
|
118
bot.js
Normal file
118
bot.js
Normal file
@ -0,0 +1,118 @@
|
||||
// Bones node.js IRC bot framework - git.supernets.org/hgw/bones
|
||||
// __
|
||||
// / /_ ____ ____ ___ _____
|
||||
// / __ \/ __ \/ __ \/ _ \/ ___/
|
||||
// / /_/ / /_/ / / / / __(__ )
|
||||
// /_.___/\____/_/ /_/\___/____/
|
||||
//
|
||||
var config = require('./config/default.json');
|
||||
var irc = require("irc");
|
||||
var fs = require("fs");
|
||||
const { Worker } = require('worker_threads');
|
||||
|
||||
warningMsg = ''+config.colours.brackets+'['+config.colours.warning+'WARNING'+config.colours.brackets+']'
|
||||
errorMsg = ''+config.colours.brackets+'['+config.colours.error+'ERROR'+config.colours.brackets+']'
|
||||
const msgTimeout = new Set();
|
||||
const msgTimeoutMsg = new Set();
|
||||
const timer = ms => new Promise(res => setTimeout(res, ms))
|
||||
var hostmask = null
|
||||
|
||||
var bot = new irc.Client(config.irc.server, config.irc.nickname, {
|
||||
channels: config.irc.channels,
|
||||
secure: config.irc.ssl,
|
||||
port: config.irc.port,
|
||||
autoRejoin: config.irc.autorejoin,
|
||||
userName: config.irc.username,
|
||||
realName: config.irc.realname,
|
||||
floodProtection: config.floodprotect.flood_protection,
|
||||
floodProtectionDelay: config.floodprotect.flood_protection_delay
|
||||
});
|
||||
|
||||
// We use consoleLog instead of console.log() as it allows togglable logging through the config file.
|
||||
// If you have things you definitely do not want to be togglable then you can continue using console.log()
|
||||
function consoleLog(log) {
|
||||
if (config.misc.logging === "true") {
|
||||
console.log(log)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function openPostWorker(chan, command, d1, d2, d3, d4, d5, d6) {
|
||||
consoleLog(`[bot.openPostWorker] Opening ${command} worker`)
|
||||
const worker = new Worker(`./commands/${command}.js`, {
|
||||
workerData: {
|
||||
d1, d2, d3, d4, d5, d6 //d1-d6 equate to variables you can pass in to a worker, see the example1 block below for an example (var1 there is d1 here). Further defined in individual command files.
|
||||
}
|
||||
});
|
||||
worker.once('message', (string) => {
|
||||
consoleLog(`[bot.openPostWorker.finalising] Got output from ${command}, sending to `+chan);
|
||||
bot.say(chan, string);
|
||||
});
|
||||
}
|
||||
|
||||
// Example command function.
|
||||
// In this example "channel" is the channel the prompt came from and "var1" is the contents of
|
||||
// that message after the !example1 part, as called in the listener further down
|
||||
async function example1(channel, var1) {
|
||||
if (sub != undefined ) {
|
||||
var sub = var1.toLowerCase() //transform variables to lowercase, this is not a requirement of course if you have case-sensitive inputs
|
||||
}
|
||||
openPostWorker(channel, 'example1', var1) //Opens commands/example1.js as a seperate process, much faster than just running the command in the one file
|
||||
}
|
||||
|
||||
// ###################################
|
||||
// Put your bot command functions here
|
||||
// ###################################
|
||||
|
||||
bot.addListener('message', function(nick, to, text, from) {
|
||||
// nick = Nickname of the message sender
|
||||
// to = Channel the message came from
|
||||
// text = Contents of the message received by the bot
|
||||
if (text.startsWith(config.irc.prefix)) {
|
||||
if (msgTimeout.has(to)) {
|
||||
if (msgTimeoutMsg.has("yes")) {
|
||||
return;
|
||||
} else {
|
||||
bot.say(to, errorMsg+" You are sending commands too quickly")
|
||||
msgTimeoutMsg.add("yes");
|
||||
setTimeout(() => {
|
||||
msgTimeoutMsg.delete("yes");
|
||||
}, config.floodprotect.command_listen_timeout)
|
||||
}
|
||||
} else {
|
||||
var args = text.split(' ');
|
||||
var command = args[0].toLowerCase()
|
||||
if (command === config.irc.prefix+'example1') {
|
||||
example1(to, text)
|
||||
} else if (command === config.irc.prefix+'COMMAND2') {
|
||||
if (args[1] == undefined ) {
|
||||
//If you have a help function you can call it here, otherwise catch a no input error
|
||||
} else {
|
||||
//Call your command here
|
||||
}
|
||||
} else if (command === config.irc.prefix+'COMMAND2') {
|
||||
if (args[1] == undefined ) {
|
||||
//If you have a help function you can call it here, otherwise catch a no input error
|
||||
} else {
|
||||
//Call your command here
|
||||
}
|
||||
}
|
||||
msgTimeout.add(to);
|
||||
setTimeout(() => {
|
||||
msgTimeout.delete(to);
|
||||
}, config.floodprotect.command_listen_timeout)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bot.addListener('error', function(message) {
|
||||
consoleLog('[ERROR]' +message) //Dump errors to console
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.error(err);
|
||||
if (config.errorhandling.log_errors_to_irc == 'true') { //If logging errors to IRC is enabled then we send the error to that, otherwise we only consoleLog it
|
||||
bot.say(config.errorhandling.error_channel, errorMsg+" "+err.stack.split('\n',1).join(" "))
|
||||
}
|
||||
});
|
43
commands/example1.js
Normal file
43
commands/example1.js
Normal file
@ -0,0 +1,43 @@
|
||||
const config = require('../config/default.json')
|
||||
const { parentPort, workerData } = require('worker_threads');
|
||||
const { d1, d2 } = workerData; //Declare all used variables here (if you only pass 1 variable to this command you only really need d1 in here, but it doesnt matter)
|
||||
var var1 = d1; // Declaring d1 as var1, just for consistancy with the last file but again, this may not be necessary in all cases.
|
||||
var val2 = d2; // Further variables declared like so, fairly simple
|
||||
const timer = ms => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
warningMsg = ''+config.colours.brackets+'['+config.colours.warning+'WARNING'+config.colours.brackets+']'
|
||||
errorMsg = ''+config.colours.brackets+'['+config.colours.error+'ERROR'+config.colours.brackets+']'
|
||||
|
||||
// We use consoleLog instead of console.log() as it allows togglable logging through the config file.
|
||||
// If you have things you definitely do not want to be togglable then you can continue using console.log()
|
||||
function consoleLog(log) {
|
||||
if (config.misc.logging === "true") {
|
||||
console.log(log)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// the errorMessage function is just a nice-to-have if you want to use error codes internally to just push set messages to console/IRC
|
||||
// You can also just sendUpstream('ERROR_MESSAGE_HERE') to handle errors as well if wanted,
|
||||
function errorMessage(error, code, extra) {
|
||||
consoleLog('[example1.errorMessage] '+error.code)
|
||||
if (code == "BAD") {
|
||||
var error = errorMsg+" SHITS_FUCKED_MAN: " + extra + " not found"
|
||||
} else {
|
||||
var error = errorMsg+" Unknown error"
|
||||
}
|
||||
parentPort.postMessage(error);
|
||||
process.exit()
|
||||
}
|
||||
|
||||
// All code must end with either a sendUpstream or errorMessage call, otherwise this subprocess will remain running indefinitely.
|
||||
// sendUpstream takes the content provided to it and sends it in whatever channel the original command prompt came from.
|
||||
async function sendUpstream(content) {
|
||||
parentPort.postMessage(content);
|
||||
process.exit()
|
||||
}
|
||||
|
||||
// ######################################################################################
|
||||
// Put your commands code here, remember to always end with sendUpstream or errorMessage.
|
||||
// ######################################################################################
|
32
config/example.config.json
Normal file
32
config/example.config.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"irc": {
|
||||
"server": "irc.supernets.org",
|
||||
"port": 6697,
|
||||
"ssl": "true",
|
||||
"channels": [
|
||||
"#5000"
|
||||
],
|
||||
"nickname": "bones-dev",
|
||||
"username": "bones-dev",
|
||||
"realname": "git.supernets.org/hgw/bones",
|
||||
"autorejoin": "true",
|
||||
"prefix": "!"
|
||||
},
|
||||
"floodprotect": {
|
||||
"flood_protection": "false",
|
||||
"flood_protection_delay": "0",
|
||||
"command_listen_timeout": "2500"
|
||||
},
|
||||
"errorhandling": {
|
||||
"error_logging": "true",
|
||||
"error_channel": "#CHANNELTOSENDERRORSTO",
|
||||
"error_channel_pass": ""
|
||||
},
|
||||
"colours": {
|
||||
"warning": "08",
|
||||
"error": "04"
|
||||
},
|
||||
"misc": {
|
||||
"logging": "true"
|
||||
}
|
||||
}
|
8
docker-compose.yml
Normal file
8
docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
||||
version: '3.3'
|
||||
services:
|
||||
bones:
|
||||
container_name: bones
|
||||
build: .
|
||||
restart: always
|
||||
volumes:
|
||||
- './config:/home/node/app/config'
|
52
package-lock.json
generated
Normal file
52
package-lock.json
generated
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "bones",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"fs": {
|
||||
"version": "0.0.1-security",
|
||||
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
|
||||
"integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
|
||||
},
|
||||
"iconv": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv/-/iconv-2.2.3.tgz",
|
||||
"integrity": "sha512-evIiYeKdt5nEGYKNkQcGPQy781sYgbBKi3gEkt1s4CwteCdOHSjGGRyyp6lP8inYFZwvzG3lgjXEvGUC8nqQ5A==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.3.5"
|
||||
}
|
||||
},
|
||||
"irc": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/irc/-/irc-0.5.2.tgz",
|
||||
"integrity": "sha512-KnrvkV05Y71SWmRWHtnlWEIH7LA/YeDul6l7tncCGLNEw4B6Obtmkatb3ACnSLj0kOJ6UBiuhss9e+eRG3zlxw==",
|
||||
"requires": {
|
||||
"iconv": "~2.2.1",
|
||||
"irc-colors": "^1.1.0",
|
||||
"node-icu-charset-detector": "~0.2.0"
|
||||
}
|
||||
},
|
||||
"irc-colors": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/irc-colors/-/irc-colors-1.5.0.tgz",
|
||||
"integrity": "sha512-HtszKchBQTcqw1DC09uD7i7vvMayHGM1OCo6AHt5pkgZEyo99ClhHTMJdf+Ezc9ovuNNxcH89QfyclGthjZJOw=="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.18.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
|
||||
"integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
|
||||
"optional": true
|
||||
},
|
||||
"node-icu-charset-detector": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/node-icu-charset-detector/-/node-icu-charset-detector-0.2.0.tgz",
|
||||
"integrity": "sha512-DYOFJ3NfKdxEi9hPbmoCss6WydGhJsxpSleUlZfAWEbZt3AU7JuxailgA9tnqQdsHiujfUY9VtDfWD9m0+ThtQ==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "^2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "bones",
|
||||
"version": "1.0.0",
|
||||
"description": "node.js IRC bot framework",
|
||||
"main": "bot.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.supernets.org/hgw/bones"
|
||||
},
|
||||
"author": "hgw7",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"fs": "0.0.1-security",
|
||||
"irc": "^0.5.2"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user