diff --git a/art/themes/luciano_blocktronics/mrc.ans b/art/themes/luciano_blocktronics/mrc.ans new file mode 100644 index 00000000..a1bbd030 Binary files /dev/null and b/art/themes/luciano_blocktronics/mrc.ans differ diff --git a/core/mrc.js b/core/mrc.js index b6e68463..681944ab 100644 --- a/core/mrc.js +++ b/core/mrc.js @@ -6,10 +6,10 @@ const Log = require('./logger.js').log; const { MenuModule } = require('./menu_module.js'); const { Errors } = require('./enig_error.js'); const { - pipeToAnsi, - stripMciColorCodes + pipeToAnsi } = require('./color_codes.js'); const stringFormat = require('./string_format.js'); +const StringUtil = require('./string_util.js') const { getThemeArt } = require('./theme.js'); @@ -24,8 +24,13 @@ exports.moduleInfo = { desc : 'Connects to an MRC chat server', author : 'RiPuk', packageName : 'codes.l33t.enigma.mrc.client', + + // Whilst this module was put together by me (RiPuk), it should be noted that a lot of the ideas (and even some code snippets) were + // borrowed from the Synchronet implementation of MRC by echicken. So...thanks, your code was very helpful in putting this together. + // Source at http://cvs.synchro.net/cgi-bin/viewcvs.cgi/xtrn/mrc/. }; + const FormIds = { mrcChat : 0, }; @@ -36,7 +41,8 @@ var MciViewIds = { inputArea : 2, roomName : 3, roomTopic : 4, - + mrcUsers : 5, + mrcBbses : 6, customRangeStart : 10, // 10+ = customs } }; @@ -61,29 +67,31 @@ exports.getModule = class mrcModule extends MenuModule { this.menuMethods = { + sendChatMessage : (formData, extraArgs, cb) => { - // const message = _.get(formData.value, 'inputArea', '').trim(); const inputAreaView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.inputArea); const inputData = inputAreaView.getData(); - const textFormatObj = { - fromUserName : state.alias, - message : inputData - }; - - const messageFormat = - this.config.messageFormat || - '|00|10<|02{fromUserName}|10>|00 |03{message}|00'; - - try { - sendChat(stringFormat(messageFormat, textFormatObj)); - } catch(e) { - self.client.log.warn( { error : e.message }, 'MRC error'); - } + + this.processSentMessage(inputData); inputAreaView.clearText(); return cb(null); - + }, + + movementKeyPressed : (formData, extraArgs, cb) => { + const bodyView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.chatLog); // :TODO: use const here vs magic # + console.log("got arrow key"); + switch(formData.key.name) { + case 'down arrow' : bodyView.scrollDocumentUp(); break; + case 'up arrow' : bodyView.scrollDocumentDown(); break; + case 'page up' : bodyView.keyPressPageUp(); break; + case 'page down' : bodyView.keyPressPageDown(); break; + } + + this.viewControllers.mrcChat.switchFocus(MciViewIds.mrcChat.inputArea); + + return cb(null); } } } @@ -117,13 +125,13 @@ exports.getModule = class mrcModule extends MenuModule { state.socket = net.createConnection(connectOpts, () => { // handshake with multiplexer state.socket.write(`--DUDE-ITS--|${state.alias}\n`); - sendClientConnect() - // send register to central MRC every 60s + // send register to central MRC and get stats every 60s setInterval(function () { sendHeartbeat(state.socket) + sendServerCommand('STATS') }, 60000); }); @@ -131,7 +139,6 @@ exports.getModule = class mrcModule extends MenuModule { state.socket.on('data', data => { data = data.toString(); this.processReceivedMessage(data); - this.viewControllers.mrcChat.switchFocus(MciViewIds.mrcChat.inputArea); }); return(callback); @@ -153,37 +160,146 @@ exports.getModule = class mrcModule extends MenuModule { return } + const chatMessageView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.chatLog); + if (message.from_user == 'SERVER') { const params = message.body.split(':'); switch (params[0]) { case 'BANNER': - const chatMessageView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.chatLog); chatMessageView.addText(pipeToAnsi(params[1].replace(/^\s+/, ''))); chatMessageView.redraw(); + break; case 'ROOMTOPIC': - this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.roomName).setText(params[1]); - this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.roomTopic).setText(params[2]); - + this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.roomName).setText(`#${params[1]}`); + this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.roomTopic).setText(pipeToAnsi(params[2])); + state.room = params[1] + break; + case 'USERLIST': state.nicks = params[1].split(','); + break; + + case 'STATS': + const stats = params[1].split(' '); + this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.mrcUsers).setText(stats[2]); + this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.mrcBbses).setText(stats[0]); - break; + break; + + default: + chatMessageView.addText(pipeToAnsi(message.body)); + break; } } else { - // if we're here then we want to show it to the user - const chatMessageView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.chatLog); - const currentTime = moment().format(this.client.currentTheme.helpers.getTimeFormat()); - chatMessageView.addText(pipeToAnsi("|08" + currentTime + "|00 " + message.body)); - chatMessageView.redraw(); + if (message.from_user == state.alias && message.to_user == "NOTME") { + // don't deliver NOTME messages + return; + } else { + // if we're here then we want to show it to the user + const currentTime = moment().format(this.client.currentTheme.helpers.getTimeFormat()); + chatMessageView.addText(pipeToAnsi("|08" + currentTime + "|00 " + message.body + "|00")); + chatMessageView.redraw(); + } } + this.viewControllers.mrcChat.switchFocus(MciViewIds.mrcChat.inputArea); return; - }); } + + processSentMessage(message) { + + if (message.startsWith('/')) { + const cmd = message.split(' '); + cmd[0] = cmd[0].substr(1).toLowerCase(); + + switch (cmd[0]) { + case 'rainbow': + const line = message.replace(/^\/rainbow\s/, '').split(' ').reduce(function (a, c) { + var cc = Math.floor((Math.random() * 31) + 1).toString().padStart(2, '0'); + a += `|${cc}${c}|00 ` + return a; + }, '').substr(0, 140).replace(/\\s\|\d*$/, ''); + + this.processSentMessage(line) + break; + + case 'l33t': + this.processSentMessage(StringUtil.stylizeString(message.substr(5), 'l33t')); + break; + + case 'kewl': + const text_modes = Array('f','v','V','i','M'); + const mode = text_modes[Math.floor(Math.random() * text_modes.length)]; + this.processSentMessage(StringUtil.stylizeString(message.substr(5), mode)); + break; + + case 'whoon': + sendServerCommand('WHOON'); + break; + + case 'motd': + sendServerCommand('MOTD'); + break; + + case 'meetups': + sendServerCommand('MEETUPS'); + break; + + case 'bbses': + sendServerCommand('CONNECTED'); + break; + + case 'topic': + sendServerCommand(`NEWTOPIC:${state.room}:${message.substr(7)}`) + break; + + case 'join': + joinRoom(cmd[1]); + break; + + case 'chatters': + sendServerCommand('CHATTERS'); + break; + + case 'rooms': + sendServerCommand('LIST'); + break; + + case 'clear': + const chatLogView = this.viewControllers.mrcChat.getView(MciViewIds.mrcChat.chatLog) + chatLogView.setText(''); + sendServerCommand('STATS'); + // chatLogView.redraw(); + break; + + default: + break; + } + + } else { + // just format and send + const textFormatObj = { + fromUserName : state.alias, + message : message + }; + + const messageFormat = + this.config.messageFormat || + '|00|10<|02{fromUserName}|10>|00 |03{message}|00'; + + try { + sendChat(stringFormat(messageFormat, textFormatObj)); + } catch(e) { + self.client.log.warn( { error : e.message }, 'MRC error'); + } + } + + return; + } }; @@ -205,7 +321,7 @@ function sendMessage(to_user, to_site, to_room, body) { state.socket.write(JSON.stringify(message) + '\n'); } -function sendChat(message,to_user) { +function sendChat(message, to_user) { sendMessage(to_user || '', '', state.room, message) } @@ -222,12 +338,15 @@ function sendHeartbeat() { function sendClientConnect() { sendHeartbeat(); - joinRoom('lobby'); - sendServerCommand('BANNERS'); sendServerCommand('MOTD'); + sendServerCommand('STATS'); + joinRoom('lobby'); return; } function joinRoom(room) { + // room names are displayed with a # but referred to without. confusing. + room = room.replace(/^#/, ''); sendServerCommand(`NEWROOM:${state.room}:${room}`); + sendServerCommand('USERLIST') } diff --git a/core/servers/chat/mrc_multiplexer.js b/core/servers/chat/mrc_multiplexer.js index 5419088a..87f78a33 100644 --- a/core/servers/chat/mrc_multiplexer.js +++ b/core/servers/chat/mrc_multiplexer.js @@ -46,7 +46,7 @@ exports.getModule = class MrcModule extends ServerModule { const enigmaVersion = "ENiGMA-BBS_" + require('../../../package.json').version const mrcConnectOpts = { - port : 5000, + port : 50000, host : "mrc.bottomlessabyss.net" }; @@ -65,6 +65,9 @@ exports.getModule = class MrcModule extends ServerModule { // split on \n to deal with getting messages in batches data.toString().split('\n').forEach( item => { if (item == '') return; + console.log('start') + console.log(item) + console.log('end') this.log.debug( { data : item } , `Received data`); let message = this.parseMessage(item);