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