//Load required classes. const { Servers } = require('alta-jsapi'); const { WebsocketBot } = require('att-bot-core'); const { BasicWrapper } = require('att-websockets'); const Discord = require('discord.js'); const moment = require('moment'); const sha512 = require('crypto-js/sha512'); const fs = require('fs'); const alta_jsapi = require('alta-jsapi'); //Local classes const Player = require('./src/player.js'); const Subscriptions = require('./src/subscriptions.js'); const commands = require('./src/discordcmds.js') const managers = require('./src/managers.js'); //Load information from credentials and config const { username, password, botToken } = require("./credentials"); const { targetServers, showAllServers, blacklistServers, discordPrefix, discordChannels, discordRoles } = require("./config"); //readline stuffs const readline = require('readline'); const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); //NeDB var Datastore = require('nedb'); var players = new Datastore({ filename : 'data/players.db', autoload: true }); var kills = new Datastore({ filename : 'data/playerkills.db', autoload: true }); var chunkHistory = new Datastore({ filename : 'data/chunkhistory.db', autoload: true }); var spawnables = new Datastore({ filename : 'data/spawnables.db', autoload: true }); players.ensureIndex({ fieldName: 'id', unique: 'true' }); players.persistence.setAutocompactionInterval( 129600 ); spawnables.ensureIndex({ fieldName: 'hash', unique: 'true' }); //Vars var playername = username; var botConnection; var pendingCommandList = []; //Some utility helper functions and prototypes function ts() { return "["+ moment().format("h:mm:ss A") +"] " } function strrep( str, n ) { if ( n < 1 ) return ''; var result = str; while( n-- > 0 ) { result += str; } return result; } // Database helpers function insertHandler( err, doc ) { if ( err ) { console.log( err ); } } function updateHandler( err, rows ) { if ( err ) { console.log( err ); } } function convertPassToHash( username, password, botToken ) { // The SHA512 hash generated by crypto-js/sha512 will be 128 characters if ( password.length !== 128 ) { console.log( "Plaintext password encountered, converting to SHA512 hash for permanent storage" ); newPass = alta_jsapi.Sessions.hashPassword(password) newFile = { "username" : username, "password" : newPass, "botToken": botToken }; fs.writeFile('./credentials.json', JSON.stringify( newFile, null, 4 ), function( err ) { if ( err ) { console.log( err ); } else { console.log( "New credentials.json saved" ); } }); password = newPass; } return password; } //Run the program main(); async function main() { console.log( ts() + "[BorisBot: Discord Edition] is Starting" ); // Convert the password to a hash if necessary var mpassword = convertPassToHash( username, password, botToken ); // Players in database //players.find({}).exec( function( err, docs ) { console.log( docs ); }); //Connect to discord const discord = new Discord.Client(); await new Promise( resolve => { discord.on('ready', resolve); discord.login(botToken); }); //Discord command and message management (todo: move to own lib) discord.on('message', message => { if ( message.content.length > 0 && message.content.startsWith( discordPrefix ) ) { var tmessage = message.content.substring(discordPrefix.length).trim(); var args = splitArgs( tmessage ); if ( args && args.length >= 1 ) { var command = args.shift(); var commandFunction = commands[command]; if (!!commandFunction) { commandFunction(message, args, tmessage); } } } }); //Alta Login const bot = new WebsocketBot(); //Use a hashed password, SHA512 await bot.loginWithHash(username, mpassword); //Run the bot. //When any of the 'targetServers' are available, a connection is automatically created. var subs = new Subscriptions( discordChannels, players, kills, chunkHistory ); alta_jsapi.Sessions.loginWithUsername(username, mpassword); await bot.run(test => targetServers.includes(test.id), async (server, connection) => { var wrapper = new BasicWrapper(connection); botConnection = { "connection" : connection, "wrapper" : wrapper } console.log( ts() +"loading inital players" ); for ( var i in server.online_players ) { let oplayer = server.online_players[i]; subs.PlayerJoined( discord, { "user": { "id": oplayer.id, "username": oplayer.username } }); } // Subscriptions await wrapper.subscribe("PlayerJoined", data => { subs.PlayerJoined( discord, data ) }); await wrapper.subscribe("PlayerLeft", data => { subs.PlayerLeft( discord, data ); }); await wrapper.subscribe("PlayerKilled", data => { subs.PlayerKilled( discord, data ); }); await wrapper.subscribe("TradeDeckUsed", data => { subs.TradeDeckUsed( discord, data ); }); await wrapper.subscribe("CreatureKilled", data => { subs.CreatureKilled( discord, data ); }); await wrapper.subscribe("PlayerMovedChunk", data => { subs.PlayerMovedChunk( discord, data ); }); await wrapper.subscribe("TraceLog", data => { if ( pendingCommandList.length && data.logger === pendingCommandList[0].module ) { console.log( "the command is a module match" ) // TODO: add a 'type' to pending commands to better match response items let command = pendingCommandList.shift(); if ( command ) { console.log( "executing handler" ) command.handler( data.message ); } } } ); } ) }