fore.st/src/channel/tokebot.js
rainbownapkin cb4c99727f Autobump backend finished. Tweaks may be made during creation of
configuration frontend, but for the most part should be set in stone.
2022-08-13 04:50:09 +00:00

309 lines
9.3 KiB
JavaScript

/*
fore.st is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
fore.st 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with fore.st. If not, see < http://www.gnu.org/licenses/ >.
(C) 2022- by rainbownapkin, <ourforest@420blaze.it>
*/
import fs from 'fs';
var ChannelModule = require("./module");
//global vars
var tokes = loadTokes();
var solotokes = ["", "https://ourfore.st/img/femotes/onetoker.jpg","https://ourfore.st/img/femotes/solotoke.jpg","https://ourfore.st/img/femotes/1toker.gif"];
//global functions
function loadTokes(){//load tokes as array from file
const rawContents = fs.readFileSync("tokebot/tokes").toString('utf8');
var spcReg = /^\s*$/g;
var t = rawContents.split("\n").filter(function(i){
return !spcReg.test(i);
});
return t;
}
//used to store ES6 maps as arrays in JSON
function mapReplacer(key, value) {
if(value instanceof Map) {
return {
dataType: 'Map',
value: Array.from(value.entries()), // or with spread: value: [...value]
};
} else {
return value;
}
}
function mapReviver(key, value) {
if(typeof value === 'object' && value !== null) {
if (value.dataType === 'Map') {
return new Map(value.value);
}
}
return value;
}
function randi(len) {//get random number from zero to len, meant for use to pull random items from an array
return Math.floor(Math.random() * len); //The maximum is exclusive and the minimum is inclusive
}
//constructor
function TokebotModule(_channel){
ChannelModule.apply(this, arguments);
//mod command registration
this.channel.modules.chat.registerCommand("!resettoke", this.resettoke.bind(this));
//admin command registration
this.channel.modules.chat.registerCommand("!updatetokes", this.updatetokesCmd.bind(this));
this.channel.modules.chat.registerCommand("!reloadtokes", this.updatetokesCmd.bind(this));
this.channel.modules.chat.registerCommand("!tokesay", this.tokesayCmd.bind(this));
this.channel.modules.chat.registerCommand("!tokeannounce", this.tokeyellCmd.bind(this));
this.channel.modules.chat.registerCommand("!tokeyell", this.tokeyellCmd.bind(this));
this.channel.modules.chat.registerCommand("!tokewhisper", this.tokewhisperCmd.bind(this));
//!toke command registration
this.updatetokes();
this.channel.modules.chat.registerCommand("!r", this.randotoke.bind(this));
this.loadtfile();//load up toke stats from toke file.
}
//protoype definition
TokebotModule.prototype = Object.create(ChannelModule.prototype);
//tokebot object properties
TokebotModule.prototype.toking = 0;
TokebotModule.prototype.tokers = [];
TokebotModule.prototype.cdown = 3;
TokebotModule.prototype.cdel = 120;
TokebotModule.prototype.ctime = 120;
TokebotModule.prototype.statmap = null;
//mod commands
TokebotModule.prototype.resettoke = function(user, msg, _meta){
if(user.account.effectiveRank >= 2 && this.toking == 2){
this.ctime = 0;
this.tokewhisper("!toke cooldown reset.", user.account.name);
}
}
//siteowner commands
TokebotModule.prototype.updatetokesCmd = function(user, msg, _meta){
if(user.account.effectiveRank >= 256){
this.updatetokes();
this.tokewhisper("Reloading !toke commands...", user.account.name);
}
}
TokebotModule.prototype.tokesayCmd = function(user, msg, _meta){
if(user.account.effectiveRank >= 256){
var fmsg = msg.split(" ");
fmsg.shift();
this.tokesay(fmsg.join(' '), true);
}
}
TokebotModule.prototype.tokeyellCmd = function(user, msg, _meta){
if(user.account.effectiveRank >= 256){
var fmsg = msg.split(" ");
fmsg.shift();
this.tokesay(fmsg.join(' '), false);
}
}
TokebotModule.prototype.tokewhisperCmd = function(user, msg, _meta){
if(user.account.effectiveRank >= 256){
var fmsg = msg.split(" ");
fmsg.shift();
this.tokewhisper(fmsg.join(' '));
}
}
//extra user commands
TokebotModule.prototype.randotoke = function(user, msg, _meta){
this.toke(user, '!' + tokes[randi(tokes.length)],_meta);
}
//main toke logic (adapted from chozobot implementation)
TokebotModule.prototype.toke = function (user, msg, _meta){
var name = user.getName()
/*if(name === "Ten"){//use in case of anger
bot.sendChatMsg(">:^(");
return;
}*/
switch (this.toking){
case 0://ready to start toke
this.tokesay("A group toke has been started by " + name + "! We'll be taking a toke in 60 seconds - join in by posting " + msg);
this.cdown = 3;
this.toking = 1; this.tokers.push(name);
setTimeout(this.countdown, 57000, this);
break;
case 1://taking toke
if(this.tokers.includes(name)){
this.tokewhisper(" You're already taking part in this toke!", name);
}else{
this.tokesay(name + " joined the toke! Post " + msg + " to take part!");
this.tokers.push(name);
this.cdown = 3;
}
break;
case 2://cooldown
this.tokewhisper(" Please wait " + this.ctime + " seconds before starting a new group toke.", name);
break;
}
};
TokebotModule.prototype.countdown = function (tb){
tb.toking = 1;//set toking mode
tb.tokesay(tb.cdown + "...");//send countdown msg
--tb.cdown;//count down
if(tb.cdown <= 0){//if cdown hits 0
setTimeout(tb.endtoke, 1000, tb);
}else{
setTimeout(tb.countdown, 1000, tb);//call endtoke
}
};
TokebotModule.prototype.endtoke = function (tb){
if(tb.cdown != 0){
setTimeout(tb.countdown, 1000, tb);
return;
}
if(tb.tokers.length > 1){
let callstring = '';
for(let i = 0; i < tb.tokers.length - 1; i++){
callstring += tb.tokers[i] + ', ';
}
callstring += tb.tokers[tb.tokers.length - 1];
tb.tokesay("Take a toke " + callstring + "! " + tb.tokers.length + " tokers!");
}else{
tb.tokesay("Take a toke " + tb.tokers.toString() + ". " + (solotokes[randi(solotokes.length)]));
}
tb.logtoke();
tb.tokers = [];
tb.toking = 2;//reset toking mode
setTimeout(tb.cooldown, 1000, tb);
};
TokebotModule.prototype.cooldown = function (tb){
if(tb.ctime > 0){
tb.toking = 2;
--tb.ctime;
setTimeout(tb.cooldown, 1000, tb);
}else{
tb.toking = 0;
tb.ctime = tb.cdel;
}
};
//helper functions(mostly just syntactic sugar)
TokebotModule.prototype.updatetokes = function (){
tokes = loadTokes();
if(this.channel.modules.chat){//register !toke commands
if(tokes == null){//if for some reason tokes file couldnt be loaded this.channel.modules.chat.registerCommand("!toke", this.toke.bind(this)); console.log("[tokebot] Unable to load toke commands from ./tokebot/tokes, defaulting to !toke definition");
}else{//if we we're able to pull toke commands
var _this = this;//we need to use this, might put this up higher to replace the tb parameter in other member functions
tokes.forEach(function(tokec){
_this.channel.modules.chat.registerCommand("!" + tokec, _this.toke.bind(_this));
});
}
}
}
TokebotModule.prototype.tokesay = function (msg,quiet){
var msgobj = {
username: "tokebot",
msg: msg,
meta:{
addClass: (quiet ? null : "shout"),
addClassToNameAndTimestamp: true,
forceShowName: (quiet ? true : false), //It's loud enough when announcing. Toke chats are rare enouhg to be more prominent :P
modflair: 3
},
time: Date.now()
}
this.channel.users.forEach(function (u) {
u.socket.emit("chatMsg",msgobj);
});
};
TokebotModule.prototype.tokewhisper = function (msg, usr){//(msg, username)
if(this.channel.modules.chat != null){
if(usr != null){
this.channel.modules.chat.sendModMessage(msg,-1,"tokebot",usr);
}else{
var _this = this
this.channel.users.forEach(function(u){
_this.channel.modules.chat.sendModMessage(msg,-1,"tokebot",u.account.name);
});
}
}
}
//filesystem manipulation functions
TokebotModule.prototype.writetokelog = function(){//append a toke to current channels toke log
var _this = this;
fs.appendFile("tokebot/" + this.channel.name + "_toke.log" ,('[' + this.tokers.toString() + '],' + this.tokers.length + ',' + new Date().getTime() + "\n"), function(err){
if(err){
console.log("[chan: " + _this.channel.name + "] TOKE LOG WRITE ERROR: " + err);
}
});
}
TokebotModule.prototype.loadtfile = function(){//load tokefile into statmap(default to new Map() on error)
var _this = this;
fs.readFile("tokebot/" + this.channel.name + "_tokefile", function(err,data){
if(err){
console.log("[chan: " + _this.channel.name + "] TOKE FILE READ ERROR (may overwrite existing stat file!): " + err);
_this.statmap = new Map();
}else{
_this.statmap = JSON.parse(data, mapReviver);
}
});
}
TokebotModule.prototype.writetfile = function(){//write statmap to tokefile
var _this = this;
fs.writeFile("tokebot/" + this.channel.name + "_tokefile", JSON.stringify(_this.statmap, mapReplacer), function(err,data){
if(err){
console.log("[chan: " + _this.channel.name + "] TOKE FILE WRITE ERROR: " + err);
}
});
}
TokebotModule.prototype.logtoke = function(){
this.writetokelog();//save toke to toke log
var _this = this;
this.tokers.forEach(function (u){//for all tokers
var ct = _this.statmap.get(u);//lets make this a lil prettier :P
_this.statmap.set(u,(ct == null ? 1 : ++ct));//increment toke counter
});
this.writetfile();//write statmap to tfile
}
module.exports = TokebotModule;