Merge branch 'master' of ssh://numinibsd/git/base/enigma-bbs
This commit is contained in:
BIN
mods/art/ONEADD.ANS
Normal file
BIN
mods/art/ONEADD.ANS
Normal file
Binary file not shown.
BIN
mods/art/ONELINER.ANS
Normal file
BIN
mods/art/ONELINER.ANS
Normal file
Binary file not shown.
BIN
mods/art/erc.ans
Normal file
BIN
mods/art/erc.ans
Normal file
Binary file not shown.
174
mods/erc_client.js
Normal file
174
mods/erc_client.js
Normal file
@@ -0,0 +1,174 @@
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
var MenuModule = require('../core/menu_module.js').MenuModule;
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
const _ = require('lodash');
|
||||
const net = require('net');
|
||||
|
||||
/*
|
||||
Expected configuration block example:
|
||||
|
||||
config: {
|
||||
host: 192.168.1.171
|
||||
port: 5001
|
||||
bbsTag: SOME_TAG
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
exports.getModule = ErcClientModule;
|
||||
|
||||
exports.moduleInfo = {
|
||||
name : 'ENiGMA Relay Chat Client',
|
||||
desc : 'Chat with other ENiGMA BBSes',
|
||||
author : 'Andrew Pamment',
|
||||
};
|
||||
|
||||
var MciViewIds = {
|
||||
ChatDisplay : 1,
|
||||
InputArea : 3,
|
||||
};
|
||||
|
||||
function ErcClientModule(options) {
|
||||
MenuModule.call(this, options);
|
||||
|
||||
const self = this;
|
||||
this.config = options.menuConfig.config;
|
||||
|
||||
this.chatEntryFormat = this.config.chatEntryFormat || '[{bbsTag}] {userName}: {message}';
|
||||
this.systemEntryFormat = this.config.systemEntryFormat || '[*SYSTEM*] {message}';
|
||||
|
||||
this.finishedLoading = function() {
|
||||
async.waterfall(
|
||||
[
|
||||
function validateConfig(callback) {
|
||||
if(_.isString(self.config.host) &&
|
||||
_.isNumber(self.config.port) &&
|
||||
_.isString(self.config.bbsTag))
|
||||
{
|
||||
return callback(null);
|
||||
} else {
|
||||
return callback(new Error('Configuration is missing required option(s)'));
|
||||
}
|
||||
},
|
||||
function connectToServer(callback) {
|
||||
const connectOpts = {
|
||||
port : self.config.port,
|
||||
host : self.config.host,
|
||||
};
|
||||
|
||||
const chatMessageView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay);
|
||||
|
||||
chatMessageView.setText('Connecting to server...');
|
||||
chatMessageView.redraw();
|
||||
|
||||
self.viewControllers.menu.switchFocus(MciViewIds.InputArea);
|
||||
|
||||
// :TODO: Track actual client->enig connection for optional prevMenu @ final CB
|
||||
self.chatConnection = net.createConnection(connectOpts.port, connectOpts.host);
|
||||
|
||||
self.chatConnection.on('data', data => {
|
||||
data = data.toString();
|
||||
|
||||
if(data.startsWith('ERCHANDSHAKE')) {
|
||||
self.chatConnection.write(`ERCMAGIC|${self.config.bbsTag}|${self.client.user.username}\r\n`);
|
||||
} else if(data.startsWith('{')) {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(e) {
|
||||
return self.client.log.warn( { error : e.message }, 'ERC: Error parsing ERC data from server');
|
||||
}
|
||||
|
||||
let text;
|
||||
try {
|
||||
if(data.userName) {
|
||||
// user message
|
||||
text = self.chatEntryFormat.format(data);
|
||||
} else {
|
||||
// system message
|
||||
text = self.systemEntryFormat.format(data);
|
||||
}
|
||||
} catch(e) {
|
||||
return self.client.log.warn( { error : e.message }, 'ERC: chatEntryFormat error');
|
||||
}
|
||||
|
||||
chatMessageView.addText(text);
|
||||
|
||||
if(chatMessageView.getLineCount() > 30) { // :TODO: should probably be ChatDisplay.height?
|
||||
chatMessageView.deleteLine(0);
|
||||
chatMessageView.scrollDown();
|
||||
}
|
||||
|
||||
chatMessageView.redraw();
|
||||
self.viewControllers.menu.switchFocus(MciViewIds.InputArea);
|
||||
}
|
||||
});
|
||||
|
||||
self.chatConnection.once('end', () => {
|
||||
return callback(null);
|
||||
});
|
||||
|
||||
self.chatConnection.once('error', err => {
|
||||
self.client.log.info(`ERC connection error: ${err.message}`);
|
||||
return callback(new Error('Failed connecting to ERC server!'));
|
||||
});
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
self.client.log.warn( { error : err.message }, 'ERC error');
|
||||
}
|
||||
|
||||
self.prevMenu();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.scrollHandler = function(keyName) {
|
||||
const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea);
|
||||
const chatDisplayView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay);
|
||||
|
||||
if('up arrow' === keyName) {
|
||||
chatDisplayView.scrollUp();
|
||||
} else {
|
||||
chatDisplayView.scrollDown();
|
||||
}
|
||||
|
||||
chatDisplayView.redraw();
|
||||
inputAreaView.setFocus(true);
|
||||
};
|
||||
|
||||
|
||||
this.menuMethods = {
|
||||
inputAreaSubmit : function() {
|
||||
const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea);
|
||||
const inputData = inputAreaView.getData();
|
||||
|
||||
if('/quit' === inputData.toLowerCase()) {
|
||||
self.chatConnection.end();
|
||||
} else {
|
||||
try {
|
||||
self.chatConnection.write(`${inputData}\r\n`);
|
||||
} catch(e) {
|
||||
self.client.log.warn( { error : e.message }, 'ERC error');
|
||||
}
|
||||
inputAreaView.clearText();
|
||||
}
|
||||
},
|
||||
scrollUp : function(formData) {
|
||||
self.scrollHandler(formData.key.name);
|
||||
},
|
||||
scrollDown : function(formData) {
|
||||
self.scrollHandler(formData.key.name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
require('util').inherits(ErcClientModule, MenuModule);
|
||||
|
||||
ErcClientModule.prototype.mciReady = function(mciData, cb) {
|
||||
this.standardMCIReadyHandler(mciData, cb);
|
||||
};
|
||||
297
mods/menu.hjson
297
mods/menu.hjson
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
/*
|
||||
ENiGMA½ Menu Configuration
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
//
|
||||
// Another SSH specialization: If the user logs in with a new user
|
||||
// name (e.g. "new", "apply", ...) they will be directed to the
|
||||
// name (e.g. "new", "apply", ...) they will be directed to the
|
||||
// application process.
|
||||
//
|
||||
sshConnectedNewUser: {
|
||||
@@ -157,7 +157,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
logoff: {
|
||||
logoff: {
|
||||
art: LOGOFF
|
||||
desc: Logging Off
|
||||
next: @systemMethod:logoff
|
||||
@@ -264,7 +264,7 @@
|
||||
action: @systemMethod:prevMenu
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
action: @systemMethod:prevMenu
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +375,10 @@
|
||||
status: Feedback to SysOp
|
||||
module: msg_area_post_fse
|
||||
next: [
|
||||
{
|
||||
{
|
||||
acs: AS2
|
||||
next: fullLoginSequenceLoginArt
|
||||
}
|
||||
}
|
||||
{
|
||||
next: newUserInactiveDone
|
||||
}
|
||||
@@ -510,16 +510,103 @@
|
||||
module: last_callers
|
||||
art: LASTCALL
|
||||
options: { pause: true }
|
||||
next: fullLoginSequenceWhosOnline
|
||||
next: fullLoginSequenceWhosOnline
|
||||
}
|
||||
fullLoginSequenceWhosOnline: {
|
||||
desc: Who's Online
|
||||
module: whos_online
|
||||
art: WHOSON
|
||||
options: { pause: true }
|
||||
next: fullLoginSequenceNewScanConfirm
|
||||
next: fullLoginSequenceOnelinerz
|
||||
}
|
||||
|
||||
fullLoginSequenceOnelinerz: {
|
||||
desc: Viewing Onelinerz
|
||||
module: onelinerz
|
||||
next: fullLoginSequenceNewScanConfirm
|
||||
options: {
|
||||
cls: true
|
||||
}
|
||||
config: {
|
||||
art: {
|
||||
entries: ONELINER
|
||||
add: ONEADD
|
||||
}
|
||||
}
|
||||
form: {
|
||||
0: {
|
||||
mci: {
|
||||
VM1: {
|
||||
focus: false
|
||||
height: 10
|
||||
}
|
||||
TM2: {
|
||||
argName: addOrExit
|
||||
items: [ "yeah!", "nah" ]
|
||||
"hotKeys" : { "Y" : 0, "N" : 1, "Q" : 1 }
|
||||
submit: true
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
submit: {
|
||||
*: [
|
||||
{
|
||||
value: { addOrExit: 0 }
|
||||
action: @method:viewAddScreen
|
||||
}
|
||||
{
|
||||
value: { addOrExit: null }
|
||||
action: @systemMethod:nextMenu
|
||||
}
|
||||
]
|
||||
}
|
||||
actionKeys: [
|
||||
{
|
||||
keys: [ "escape" ]
|
||||
action: @systemMethod:nextMenu
|
||||
}
|
||||
]
|
||||
},
|
||||
1: {
|
||||
mci: {
|
||||
ET1: {
|
||||
focus: true
|
||||
maxLength: 70
|
||||
argName: oneliner
|
||||
}
|
||||
TL2: {
|
||||
width: 60
|
||||
}
|
||||
TM3: {
|
||||
argName: addOrCancel
|
||||
items: [ "add", "cancel" ]
|
||||
"hotKeys" : { "A" : 0, "C" : 1, "Q" : 1 }
|
||||
submit: true
|
||||
}
|
||||
}
|
||||
|
||||
submit: {
|
||||
*: [
|
||||
{
|
||||
value: { addOrCancel: 0 }
|
||||
action: @method:addEntry
|
||||
}
|
||||
{
|
||||
value: { addOrCancel: 1 }
|
||||
action: @method:cancelAdd
|
||||
}
|
||||
]
|
||||
}
|
||||
actionKeys: [
|
||||
{
|
||||
keys: [ "escape" ]
|
||||
action: @method:cancelAdd
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fullLoginSequenceNewScanConfirm: {
|
||||
desc: Logging In
|
||||
prompt: loginGlobalNewScan
|
||||
@@ -644,6 +731,14 @@
|
||||
value: { command: "K" }
|
||||
action: @menu:mainMenuFeedbackToSysOp
|
||||
}
|
||||
{
|
||||
value: { command: "O" }
|
||||
action: @menu:mainMenuOnelinerz
|
||||
}
|
||||
{
|
||||
value: { command: "CHAT"}
|
||||
action: @menu:ercClient
|
||||
}
|
||||
{
|
||||
value: 1
|
||||
action: @menu:mainMenu
|
||||
@@ -665,7 +760,7 @@
|
||||
mainMenuUserStats: {
|
||||
desc: User Stats
|
||||
art: STATUS
|
||||
options: { pause: true }
|
||||
options: { pause: true }
|
||||
}
|
||||
mainMenuSystemStats: {
|
||||
desc: System Stats
|
||||
@@ -907,6 +1002,144 @@
|
||||
}
|
||||
}
|
||||
|
||||
mainMenuOnelinerz: {
|
||||
desc: Viewing Onelinerz
|
||||
module: onelinerz
|
||||
options: {
|
||||
cls: true
|
||||
}
|
||||
config: {
|
||||
art: {
|
||||
entries: ONELINER
|
||||
add: ONEADD
|
||||
}
|
||||
}
|
||||
form: {
|
||||
0: {
|
||||
mci: {
|
||||
VM1: {
|
||||
focus: false
|
||||
height: 10
|
||||
}
|
||||
TM2: {
|
||||
argName: addOrExit
|
||||
items: [ "yeah!", "nah" ]
|
||||
"hotKeys" : { "Y" : 0, "N" : 1, "Q" : 1 }
|
||||
submit: true
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
submit: {
|
||||
*: [
|
||||
{
|
||||
value: { addOrExit: 0 }
|
||||
action: @method:viewAddScreen
|
||||
}
|
||||
{
|
||||
value: { addOrExit: null }
|
||||
action: @systemMethod:nextMenu
|
||||
}
|
||||
]
|
||||
}
|
||||
actionKeys: [
|
||||
{
|
||||
keys: [ "escape" ]
|
||||
action: @systemMethod:nextMenu
|
||||
}
|
||||
]
|
||||
},
|
||||
1: {
|
||||
mci: {
|
||||
ET1: {
|
||||
focus: true
|
||||
maxLength: 70
|
||||
argName: oneliner
|
||||
}
|
||||
TL2: {
|
||||
width: 60
|
||||
}
|
||||
TM3: {
|
||||
argName: addOrCancel
|
||||
items: [ "add", "cancel" ]
|
||||
"hotKeys" : { "A" : 0, "C" : 1, "Q" : 1 }
|
||||
submit: true
|
||||
}
|
||||
}
|
||||
|
||||
submit: {
|
||||
*: [
|
||||
{
|
||||
value: { addOrCancel: 0 }
|
||||
action: @method:addEntry
|
||||
}
|
||||
{
|
||||
value: { addOrCancel: 1 }
|
||||
action: @method:cancelAdd
|
||||
}
|
||||
]
|
||||
}
|
||||
actionKeys: [
|
||||
{
|
||||
keys: [ "escape" ]
|
||||
action: @method:cancelAdd
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ercClient: {
|
||||
art: erc
|
||||
module: erc_client
|
||||
config: {
|
||||
host: localhost
|
||||
port: 5001
|
||||
bbsTag: CHANGEME
|
||||
}
|
||||
|
||||
form: {
|
||||
0: {
|
||||
mci: {
|
||||
MT1: {
|
||||
width: 79
|
||||
height: 21
|
||||
mode: preview
|
||||
autoScroll: true
|
||||
}
|
||||
ET3: {
|
||||
autoScale: false
|
||||
width: 77
|
||||
argName: inputArea
|
||||
focus: true
|
||||
submit: true
|
||||
}
|
||||
}
|
||||
|
||||
submit: {
|
||||
*: [
|
||||
{
|
||||
value: { inputArea: null }
|
||||
action: @method:inputAreaSubmit
|
||||
}
|
||||
]
|
||||
}
|
||||
actionKeys: [
|
||||
{
|
||||
keys: [ "tab" ]
|
||||
}
|
||||
{
|
||||
keys: [ "up arrow" ]
|
||||
action: @method:scrollDown
|
||||
}
|
||||
{
|
||||
keys: [ "down arrow" ]
|
||||
action: @method:scrollUp
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Doors Menu
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
@@ -943,17 +1176,21 @@
|
||||
value: { command: "DP" }
|
||||
action: @menu:doorParty
|
||||
}
|
||||
{
|
||||
value: { command: "HL" }
|
||||
action: @menu:telnetBridgeHappyLand
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
doorPimpWars: {
|
||||
desc: Playing PimpWars
|
||||
module: abracadabra
|
||||
module: abracadabra
|
||||
config: {
|
||||
name: PimpWars
|
||||
dropFileType: DORINFO
|
||||
cmd: /home/nuskooler/DOS/scripts/pimpwars.sh
|
||||
args: [
|
||||
args: [
|
||||
"{node}",
|
||||
"{dropFile}",
|
||||
"{srvPort}",
|
||||
@@ -966,12 +1203,12 @@
|
||||
|
||||
doorDarkLands: {
|
||||
desc: Playing Dark Lands
|
||||
module: abracadabra
|
||||
module: abracadabra
|
||||
config: {
|
||||
name: DARKLANDS
|
||||
dropFileType: DOOR
|
||||
cmd: /home/nuskooler/dev/enigma-bbs/doors/darklands/start.sh
|
||||
args: [
|
||||
args: [
|
||||
"{node}",
|
||||
"{dropFile}",
|
||||
"{srvPort}",
|
||||
@@ -981,7 +1218,7 @@
|
||||
io: socket
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
doorLORD: {
|
||||
desc: Playing L.O.R.D.
|
||||
module: abracadabra
|
||||
@@ -1020,6 +1257,18 @@
|
||||
bbsTag: XX
|
||||
}
|
||||
}
|
||||
|
||||
telnetBridgeHappyLand: {
|
||||
desc: Connected to HappyLand BBS
|
||||
module: telnet_bridge
|
||||
config: {
|
||||
host: andrew.homeunix.org
|
||||
port: 2023
|
||||
//host: agency.bbs.geek.nz
|
||||
//port: 23
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Message Area Menu
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
@@ -1056,7 +1305,7 @@
|
||||
{
|
||||
value: 1
|
||||
action: @menu:messageArea
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1244,7 +1493,7 @@
|
||||
{
|
||||
keys: [ "n", "shift + n" ]
|
||||
action: @method:nextMessage
|
||||
}
|
||||
}
|
||||
{
|
||||
keys: [ "r", "shift + r" ]
|
||||
action: @method:replyMessage
|
||||
@@ -1259,7 +1508,7 @@
|
||||
{
|
||||
keys: [ "?" ]
|
||||
action: @method:viewModeMenuHelp
|
||||
}
|
||||
}
|
||||
{
|
||||
keys: [ "down arrow", "up arrow", "page up", "page down" ]
|
||||
action: @method:movementKeyPressed
|
||||
@@ -1295,7 +1544,7 @@
|
||||
validate: @systemMethod:validateNonEmpty
|
||||
}
|
||||
ET3: {
|
||||
argName: subject
|
||||
argName: subject
|
||||
maxLength: 72
|
||||
submit: true
|
||||
validate: @systemMethod:validateNonEmpty
|
||||
@@ -1395,7 +1644,7 @@
|
||||
width: 79
|
||||
height: 4
|
||||
argName: quote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
submit: {
|
||||
@@ -1552,7 +1801,7 @@
|
||||
"mci" : {
|
||||
"VM1" : {
|
||||
"items" : [
|
||||
"Single Line Text Editing Views",
|
||||
"Single Line Text Editing Views",
|
||||
"Spinner & Toggle Views",
|
||||
"Mask Edit Views",
|
||||
"Multi Line Text Editor",
|
||||
@@ -1735,7 +1984,7 @@
|
||||
"form" : {
|
||||
"0" : {
|
||||
"BTMT" : {
|
||||
"mci" : {
|
||||
"mci" : {
|
||||
"MT1" : {
|
||||
"width" : 70,
|
||||
"height" : 17,
|
||||
@@ -2019,6 +2268,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
315
mods/onelinerz.js
Normal file
315
mods/onelinerz.js
Normal file
@@ -0,0 +1,315 @@
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
// ENiGMA½
|
||||
const MenuModule = require('../core/menu_module.js').MenuModule;
|
||||
const getModDatabasePath = require('../core/database.js').getModDatabasePath;
|
||||
const ViewController = require('../core/view_controller.js').ViewController;
|
||||
const theme = require('../core/theme.js');
|
||||
const ansi = require('../core/ansi_term.js');
|
||||
|
||||
// deps
|
||||
const sqlite3 = require('sqlite3');
|
||||
const async = require('async');
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
|
||||
exports.moduleInfo = {
|
||||
name : 'Onelinerz',
|
||||
desc : 'Standard local onelinerz',
|
||||
author : 'NuSkooler',
|
||||
packageName : 'codes.l33t.enigma.onelinerz',
|
||||
};
|
||||
|
||||
exports.getModule = OnelinerzModule;
|
||||
|
||||
const MciCodeIds = {
|
||||
ViewForm : {
|
||||
Entries : 1,
|
||||
AddPrompt : 2,
|
||||
},
|
||||
AddForm : {
|
||||
NewEntry : 1,
|
||||
EntryPreview : 2,
|
||||
AddPrompt : 3,
|
||||
}
|
||||
};
|
||||
|
||||
const FormIds = {
|
||||
View : 0,
|
||||
Add : 1,
|
||||
};
|
||||
|
||||
function OnelinerzModule(options) {
|
||||
MenuModule.call(this, options);
|
||||
|
||||
const self = this;
|
||||
const config = this.menuConfig.config;
|
||||
|
||||
this.initSequence = function() {
|
||||
async.series(
|
||||
[
|
||||
function beforeDisplayArt(callback) {
|
||||
self.beforeArt(callback);
|
||||
},
|
||||
function display(callback) {
|
||||
self.displayViewScreen(false, callback);
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
// :TODO: Handle me -- initSequence() should really take a completion callback
|
||||
}
|
||||
self.finishedLoading();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.displayViewScreen = function(clearScreen, cb) {
|
||||
async.waterfall(
|
||||
[
|
||||
function clearAndDisplayArt(callback) {
|
||||
if(self.viewControllers.add) {
|
||||
self.viewControllers.add.setFocus(false);
|
||||
}
|
||||
|
||||
if(clearScreen) {
|
||||
self.client.term.rawWrite(ansi.resetScreen());
|
||||
}
|
||||
|
||||
theme.displayThemedAsset(
|
||||
config.art.entries,
|
||||
self.client,
|
||||
{ font : self.menuConfig.font, trailingLF : false },
|
||||
(err, artData) => {
|
||||
return callback(err, artData);
|
||||
}
|
||||
);
|
||||
},
|
||||
function initOrRedrawViewController(artData, callback) {
|
||||
if(_.isUndefined(self.viewControllers.add)) {
|
||||
const vc = self.addViewController(
|
||||
'view',
|
||||
new ViewController( { client : self.client, formId : FormIds.View } )
|
||||
);
|
||||
|
||||
const loadOpts = {
|
||||
callingMenu : self,
|
||||
mciMap : artData.mciMap,
|
||||
formId : FormIds.View,
|
||||
};
|
||||
|
||||
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||
} else {
|
||||
self.viewControllers.view.setFocus(true);
|
||||
self.viewControllers.view.getView(MciCodeIds.ViewForm.AddPrompt).redraw();
|
||||
return callback(null);
|
||||
}
|
||||
},
|
||||
function fetchEntries(callback) {
|
||||
const entriesView = self.viewControllers.view.getView(MciCodeIds.ViewForm.Entries);
|
||||
const limit = entriesView.dimens.height;
|
||||
let entries = [];
|
||||
|
||||
self.db.each(
|
||||
`SELECT *
|
||||
FROM (
|
||||
SELECT *
|
||||
FROM onelinerz
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT ${limit}
|
||||
)
|
||||
ORDER BY timestamp ASC;`,
|
||||
(err, row) => {
|
||||
if(!err) {
|
||||
row.timestamp = moment(row.timestamp); // convert -> moment
|
||||
entries.push(row);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
return callback(err, entriesView, entries);
|
||||
}
|
||||
);
|
||||
},
|
||||
function populateEntries(entriesView, entries, callback) {
|
||||
const listFormat = config.listFormat || '{username}@{ts}: {oneliner}';// :TODO: should be userName to be consistent
|
||||
const tsFormat = config.timestampFormat || 'ddd h:mma';
|
||||
|
||||
entriesView.setItems(entries.map( e => {
|
||||
return listFormat.format( {
|
||||
userId : e.user_id,
|
||||
username : e.user_name,
|
||||
oneliner : e.oneliner,
|
||||
ts : e.timestamp.format(tsFormat),
|
||||
} );
|
||||
}));
|
||||
|
||||
entriesView.focusItems = entriesView.items; // :TODO: this is a hack
|
||||
entriesView.redraw();
|
||||
|
||||
return callback(null);
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(cb) {
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.displayAddScreen = function(cb) {
|
||||
async.waterfall(
|
||||
[
|
||||
function clearAndDisplayArt(callback) {
|
||||
self.viewControllers.view.setFocus(false);
|
||||
self.client.term.rawWrite(ansi.resetScreen());
|
||||
|
||||
theme.displayThemedAsset(
|
||||
config.art.add,
|
||||
self.client,
|
||||
{ font : self.menuConfig.font },
|
||||
(err, artData) => {
|
||||
return callback(err, artData);
|
||||
}
|
||||
);
|
||||
},
|
||||
function initOrRedrawViewController(artData, callback) {
|
||||
if(_.isUndefined(self.viewControllers.add)) {
|
||||
const vc = self.addViewController(
|
||||
'add',
|
||||
new ViewController( { client : self.client, formId : FormIds.Add } )
|
||||
);
|
||||
|
||||
const loadOpts = {
|
||||
callingMenu : self,
|
||||
mciMap : artData.mciMap,
|
||||
formId : FormIds.Add,
|
||||
};
|
||||
|
||||
return vc.loadFromMenuConfig(loadOpts, callback);
|
||||
} else {
|
||||
self.viewControllers.add.setFocus(true);
|
||||
self.viewControllers.add.redrawAll();
|
||||
self.viewControllers.add.switchFocus(MciCodeIds.AddForm.NewEntry);
|
||||
return callback(null);
|
||||
}
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(cb) {
|
||||
return cb(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
this.clearAddForm = function() {
|
||||
const newEntryView = self.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry);
|
||||
const previewView = self.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview);
|
||||
|
||||
newEntryView.setText('');
|
||||
|
||||
// preview is optional
|
||||
if(previewView) {
|
||||
previewView.setText('');
|
||||
}
|
||||
};
|
||||
|
||||
this.menuMethods = {
|
||||
viewAddScreen : function() {
|
||||
self.displayAddScreen();
|
||||
},
|
||||
|
||||
addEntry : function(formData) {
|
||||
if(_.isString(formData.value.oneliner) && formData.value.oneliner.length > 0) {
|
||||
const oneliner = formData.value.oneliner.trim(); // remove any trailing ws
|
||||
|
||||
self.storeNewOneliner(oneliner, err => {
|
||||
if(err) {
|
||||
self.client.log.warn( { error : err.message }, 'Failed saving oneliner');
|
||||
}
|
||||
|
||||
self.clearAddForm();
|
||||
self.displayViewScreen(true); // true=cls
|
||||
});
|
||||
|
||||
} else {
|
||||
// empty message - treat as if cancel was hit
|
||||
self.displayViewScreen(true); // true=cls
|
||||
}
|
||||
},
|
||||
|
||||
cancelAdd : function() {
|
||||
self.clearAddForm();
|
||||
self.displayViewScreen(true); // true=cls
|
||||
}
|
||||
};
|
||||
|
||||
this.initDatabase = function(cb) {
|
||||
async.series(
|
||||
[
|
||||
function openDatabase(callback) {
|
||||
self.db = new sqlite3.Database(
|
||||
getModDatabasePath(exports.moduleInfo),
|
||||
callback
|
||||
);
|
||||
},
|
||||
function createTables(callback) {
|
||||
self.db.serialize( () => {
|
||||
self.db.run(
|
||||
`CREATE TABLE IF NOT EXISTS onelinerz (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER_NOT NULL,
|
||||
user_name VARCHAR NOT NULL,
|
||||
oneliner VARCHAR NOT NULL,
|
||||
timestamp DATETIME NOT NULL
|
||||
)`
|
||||
);
|
||||
});
|
||||
callback(null);
|
||||
}
|
||||
],
|
||||
cb
|
||||
);
|
||||
};
|
||||
|
||||
this.storeNewOneliner = function(oneliner, cb) {
|
||||
const ts = moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ');
|
||||
|
||||
async.series(
|
||||
[
|
||||
function addRec(callback) {
|
||||
self.db.run(
|
||||
`INSERT INTO onelinerz (user_id, user_name, oneliner, timestamp)
|
||||
VALUES (?, ?, ?, ?);`,
|
||||
[ self.client.user.userId, self.client.user.username, oneliner, ts ],
|
||||
callback
|
||||
);
|
||||
},
|
||||
function removeOld(callback) {
|
||||
// keep 25 max most recent items - remove the older ones
|
||||
self.db.run(
|
||||
`DELETE FROM onelinerz
|
||||
WHERE id IN (
|
||||
SELECT id
|
||||
FROM onelinerz
|
||||
ORDER BY id DESC
|
||||
LIMIT -1 OFFSET 25
|
||||
);`,
|
||||
callback
|
||||
);
|
||||
}
|
||||
],
|
||||
cb
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
require('util').inherits(OnelinerzModule, MenuModule);
|
||||
|
||||
OnelinerzModule.prototype.beforeArt = function(cb) {
|
||||
OnelinerzModule.super_.prototype.beforeArt.call(this, err => {
|
||||
return err ? cb(err) : this.initDatabase(cb);
|
||||
});
|
||||
};
|
||||
115
mods/telnet_bridge.js
Normal file
115
mods/telnet_bridge.js
Normal file
@@ -0,0 +1,115 @@
|
||||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const MenuModule = require('../core/menu_module.js').MenuModule;
|
||||
const resetScreen = require('../core/ansi_term.js').resetScreen;
|
||||
|
||||
const async = require('async');
|
||||
const _ = require('lodash');
|
||||
const net = require('net');
|
||||
|
||||
/*
|
||||
Expected configuration block:
|
||||
|
||||
{
|
||||
module: telnet_bridge
|
||||
...
|
||||
config: {
|
||||
host: somehost.net
|
||||
port: 23
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// :TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors
|
||||
// :TODO: ENH: Support nodeMax and tooManyArt
|
||||
|
||||
exports.getModule = TelnetBridgeModule;
|
||||
|
||||
exports.moduleInfo = {
|
||||
name : 'Telnet Bridge',
|
||||
desc : 'Connect to other Telnet Systems',
|
||||
author : 'Andrew Pamment',
|
||||
};
|
||||
|
||||
|
||||
function TelnetBridgeModule(options) {
|
||||
MenuModule.call(this, options);
|
||||
|
||||
const self = this;
|
||||
this.config = options.menuConfig.config;
|
||||
|
||||
this.initSequence = function() {
|
||||
let clientTerminated;
|
||||
|
||||
async.series(
|
||||
[
|
||||
function validateConfig(callback) {
|
||||
if(_.isString(self.config.host) &&
|
||||
_.isNumber(self.config.port))
|
||||
{
|
||||
callback(null);
|
||||
} else {
|
||||
callback(new Error('Configuration is missing required option(s)'));
|
||||
}
|
||||
},
|
||||
function createTelnetBridge(callback) {
|
||||
const connectOpts = {
|
||||
port : self.config.port,
|
||||
host : self.config.host,
|
||||
};
|
||||
|
||||
let clientTerminated;
|
||||
|
||||
self.client.term.write(resetScreen());
|
||||
self.client.term.write(` Connecting to ${connectOpts.host}, please wait...\n`);
|
||||
|
||||
let bridgeConnection = net.createConnection(connectOpts, () => {
|
||||
self.client.log.info(connectOpts, 'Telnet bridge connection established');
|
||||
|
||||
self.client.term.output.pipe(bridgeConnection);
|
||||
|
||||
self.client.once('end', () => {
|
||||
self.client.log.info('Connection ended. Terminating connection');
|
||||
clientTerminated = true;
|
||||
return bridgeConnection.end();
|
||||
});
|
||||
});
|
||||
|
||||
const restorePipe = function() {
|
||||
self.client.term.output.unpipe(bridgeConnection);
|
||||
self.client.term.output.resume();
|
||||
};
|
||||
|
||||
bridgeConnection.on('data', data => {
|
||||
// pass along
|
||||
// :TODO: just pipe this as well
|
||||
return self.client.term.rawWrite(data);
|
||||
});
|
||||
|
||||
bridgeConnection.once('end', () => {
|
||||
restorePipe();
|
||||
return callback(clientTerminated ? new Error('Client connection terminated') : null);
|
||||
});
|
||||
|
||||
bridgeConnection.once('error', err => {
|
||||
self.client.log.info(`Telnet bridge connection error: ${err.message}`);
|
||||
restorePipe();
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
self.client.log.warn( { error : err.message }, 'Telnet connection error');
|
||||
}
|
||||
|
||||
if(!clientTerminated) {
|
||||
self.prevMenu();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
require('util').inherits(TelnetBridgeModule, MenuModule);
|
||||
Binary file not shown.
BIN
mods/themes/luciano_blocktronics/ONELINER.ANS
Normal file
BIN
mods/themes/luciano_blocktronics/ONELINER.ANS
Normal file
Binary file not shown.
@@ -84,6 +84,29 @@
|
||||
}
|
||||
}
|
||||
|
||||
fullLoginSequenceOnelinerz: {
|
||||
config: {
|
||||
listFormat: "|00|11{username:<12}|08: |03{oneliner:<59.58}"
|
||||
}
|
||||
0: {
|
||||
mci: {
|
||||
VM1: { height: 10 }
|
||||
TM2: {
|
||||
focusTextStyle: first lower
|
||||
}
|
||||
}
|
||||
}
|
||||
1: {
|
||||
mci: {
|
||||
ET1: { width: 60 }
|
||||
TL2: { width: 60 }
|
||||
TM3: {
|
||||
focusTextStyle: first lower
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mainMenuUserStats: {
|
||||
mci: {
|
||||
UN1: { width: 17 }
|
||||
@@ -157,6 +180,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
mainMenuOnelinerz: {
|
||||
// :TODO: Need way to just duplicate entry here & in menu.hjson, e.g. use: someName + must supply next/etc. in menu
|
||||
config: {
|
||||
listFormat: "|00|11{username:<12}|08: |03{oneliner:<59.58}"
|
||||
}
|
||||
0: {
|
||||
mci: {
|
||||
VM1: { height: 10 }
|
||||
TM2: {
|
||||
focusTextStyle: first lower
|
||||
}
|
||||
}
|
||||
}
|
||||
1: {
|
||||
mci: {
|
||||
ET1: { width: 60 }
|
||||
TL2: { width: 60 }
|
||||
TM3: {
|
||||
focusTextStyle: first lower
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messageAreaMessageList: {
|
||||
config: {
|
||||
listFormat: "|00|15{msgNum:>4} |03{subj:<29.29} |11{from:<20.20} |03{ts} |01|31{newIndicator}"
|
||||
@@ -336,6 +383,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ercClient: {
|
||||
config: {
|
||||
//chatEntryFormat: "|00|08[|03{bbsTag}|08] |10{userName}|08: |02{message}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user