Merge branch 'master' into dependabot/npm_and_yarn/sqlite3-5.1.5

This commit is contained in:
Bryan Ashby 2023-08-23 21:47:47 -06:00 committed by GitHub
commit 18678d3e4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 723 additions and 489 deletions

View File

@ -8,6 +8,7 @@ This document attempts to track **major** changes and additions in ENiGMA½. For
* Routes for the file base now default to `/_f/` prefixed instead of just `/f/`. If `/f/` is in your `config.hjson` you are encouraged to update it! * Routes for the file base now default to `/_f/` prefixed instead of just `/f/`. If `/f/` is in your `config.hjson` you are encouraged to update it!
* Finally, the system will search for `index.html` and `index.htm` in that order, if another suitable route cannot be established. * Finally, the system will search for `index.html` and `index.htm` in that order, if another suitable route cannot be established.
* CombatNet has shut down, so the module (`combatnet.js`) has been removed. * CombatNet has shut down, so the module (`combatnet.js`) has been removed.
* The Menu Flag `popParent` has been removed and `noHistory` has been updated to work as expected. In general things should "Just Work", but check your `menu.hjson` entries if you see menu stack issues.
## 0.0.13-beta ## 0.0.13-beta
* **Note for contributors**: ENiGMA has switched to [Prettier](https://prettier.io) for formatting/style. Please see [CONTRIBUTING](CONTRIBUTING.md) and the Prettier website for more information. * **Note for contributors**: ENiGMA has switched to [Prettier](https://prettier.io) for formatting/style. Please see [CONTRIBUTING](CONTRIBUTING.md) and the Prettier website for more information.

View File

@ -595,7 +595,7 @@
} }
} }
messageBaseSearchMessageList: { messageBaseSearchResultsMessageList: {
config: { config: {
allViewsInfoFormat10: "|00|15{msgNumSelected:>4.4} |08/ |15{msgNumTotal:<4.4}" allViewsInfoFormat10: "|00|15{msgNumSelected:>4.4} |08/ |15{msgNumTotal:<4.4}"
// Fri Sep 25th // Fri Sep 25th

View File

@ -2,10 +2,8 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module.js');
const ViewController = require('./view_controller.js').ViewController;
const ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
const theme = require('./theme.js');
const FileEntry = require('./file_entry.js'); const FileEntry = require('./file_entry.js');
const stringFormat = require('./string_format.js'); const stringFormat = require('./string_format.js');
const FileArea = require('./file_base_area.js'); const FileArea = require('./file_base_area.js');
@ -77,6 +75,8 @@ exports.getModule = class FileAreaList extends MenuModule {
this.fileList = _.get(options, 'extraArgs.fileList'); this.fileList = _.get(options, 'extraArgs.fileList');
this.lastFileNextExit = _.get(options, 'extraArgs.lastFileNextExit', true); this.lastFileNextExit = _.get(options, 'extraArgs.lastFileNextExit', true);
this.setMergedFlag(MenuFlags.NoHistory);
if (this.fileList) { if (this.fileList) {
// we'll need to adjust position as well! // we'll need to adjust position as well!
this.fileListPosition = 0; this.fileListPosition = 0;

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// enigma-bbs // enigma-bbs
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module.js');
const { getSortedAvailableFileAreas } = require('./file_base_area.js'); const { getSortedAvailableFileAreas } = require('./file_base_area.js');
const StatLog = require('./stat_log.js'); const StatLog = require('./stat_log.js');
const SysProps = require('./system_property.js'); const SysProps = require('./system_property.js');
@ -24,6 +24,8 @@ exports.getModule = class FileAreaSelectModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
this.menuMethods = { this.menuMethods = {
selectArea: (formData, extraArgs, cb) => { selectArea: (formData, extraArgs, cb) => {
const filterCriteria = { const filterCriteria = {
@ -34,7 +36,7 @@ exports.getModule = class FileAreaSelectModule extends MenuModule {
extraArgs: { extraArgs: {
filterCriteria: filterCriteria, filterCriteria: filterCriteria,
}, },
menuFlags: ['popParent', 'mergeFlags'], menuFlags: [ MenuFlags.NoHistory ],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -2,10 +2,8 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module.js');
const ViewController = require('./view_controller.js').ViewController;
const DownloadQueue = require('./download_queue.js'); const DownloadQueue = require('./download_queue.js');
const theme = require('./theme.js');
const ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
const Errors = require('./enig_error.js').Errors; const Errors = require('./enig_error.js').Errors;
const FileAreaWeb = require('./file_area_web.js'); const FileAreaWeb = require('./file_area_web.js');
@ -38,6 +36,8 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
this.dlQueue = new DownloadQueue(this.client); this.dlQueue = new DownloadQueue(this.client);
if (_.has(options, 'lastMenuResult.sentFileIds')) { if (_.has(options, 'lastMenuResult.sentFileIds')) {

View File

@ -121,7 +121,6 @@ exports.getModule = class FileBaseSearch extends MenuModule {
extraArgs: { extraArgs: {
filterCriteria: filterCriteria, filterCriteria: filterCriteria,
}, },
menuFlags: ['popParent'],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const { MenuModule } = require('./menu_module.js'); const { MenuModule, MenuFlags } = require('./menu_module.js');
const FileEntry = require('./file_entry.js'); const FileEntry = require('./file_entry.js');
const FileArea = require('./file_base_area.js'); const FileArea = require('./file_base_area.js');
const { renderSubstr } = require('./string_util.js'); const { renderSubstr } = require('./string_util.js');
@ -65,6 +65,9 @@ const MciViewIds = {
exports.getModule = class FileBaseListExport extends MenuModule { exports.getModule = class FileBaseListExport extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
this.config = Object.assign( this.config = Object.assign(
{}, {},
_.get(options, 'menuConfig.config'), _.get(options, 'menuConfig.config'),

View File

@ -2,10 +2,8 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module.js');
const ViewController = require('./view_controller.js').ViewController;
const DownloadQueue = require('./download_queue.js'); const DownloadQueue = require('./download_queue.js');
const theme = require('./theme.js');
const ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
const Errors = require('./enig_error.js').Errors; const Errors = require('./enig_error.js').Errors;
const FileAreaWeb = require('./file_area_web.js'); const FileAreaWeb = require('./file_area_web.js');
@ -40,6 +38,8 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
this.dlQueue = new DownloadQueue(this.client); this.dlQueue = new DownloadQueue(this.client);
this.menuMethods = { this.menuMethods = {

View File

@ -20,6 +20,23 @@ const assert = require('assert');
const _ = require('lodash'); const _ = require('lodash');
const iconvDecode = require('iconv-lite').decode; const iconvDecode = require('iconv-lite').decode;
const MenuFlags = {
// When leaving this menu to load/chain to another, remove this
// menu from history. In other words, the fallback from
// the next menu would *not* be this one, but the previous.
NoHistory: 'noHistory',
// Generally used in code only: Request that any flags from menu.hjson
// are merged in to the total set of flags vs overriding the default.
MergeFlags: 'mergeFlags',
// Forward this menu's 'extraArgs' to the next.
ForwardArgs: 'forwardArgs',
};
exports.MenuFlags = MenuFlags;
exports.MenuModule = class MenuModule extends PluginModule { exports.MenuModule = class MenuModule extends PluginModule {
constructor(options) { constructor(options) {
super(options); super(options);
@ -48,6 +65,11 @@ exports.MenuModule = class MenuModule extends PluginModule {
}); });
} }
setMergedFlag(flag) {
this.menuConfig.config.menuFlags.push(flag);
this.menuConfig.config.menuFlags = [...new Set([...this.menuConfig.config.menuFlags, MenuFlags.MergeFlags])];
}
static get InterruptTypes() { static get InterruptTypes() {
return { return {
Never: 'never', Never: 'never',

View File

@ -5,12 +5,12 @@
const loadMenu = require('./menu_util.js').loadMenu; const loadMenu = require('./menu_util.js').loadMenu;
const { Errors, ErrorReasons } = require('./enig_error.js'); const { Errors, ErrorReasons } = require('./enig_error.js');
const { getResolvedSpec } = require('./menu_util.js'); const { getResolvedSpec } = require('./menu_util.js');
const { MenuFlags } = require('./menu_module.js');
// deps // deps
const _ = require('lodash'); const _ = require('lodash');
const assert = require('assert'); const assert = require('assert');
const bunyan = require('bunyan');
// :TODO: Stack is backwards.... top should be most recent! :)
module.exports = class MenuStack { module.exports = class MenuStack {
constructor(client) { constructor(client) {
@ -27,20 +27,12 @@ module.exports = class MenuStack {
} }
peekPrev() { peekPrev() {
if (this.stackSize > 1) {
return this.stack[this.stack.length - 2]; return this.stack[this.stack.length - 2];
} }
}
top() { top() {
if (this.stackSize > 0) {
return this.stack[this.stack.length - 1]; return this.stack[this.stack.length - 1];
} }
}
get stackSize() {
return this.stack.length;
}
get currentModule() { get currentModule() {
const top = this.top(); const top = this.top();
@ -81,7 +73,6 @@ module.exports = class MenuStack {
prev(cb) { prev(cb) {
const menuResult = this.top().instance.getMenuResult(); const menuResult = this.top().instance.getMenuResult();
// :TODO: leave() should really take a cb...
this.pop().instance.leave(); // leave & remove current this.pop().instance.leave(); // leave & remove current
const previousModuleInfo = this.pop(); // get previous const previousModuleInfo = this.pop(); // get previous
@ -129,7 +120,7 @@ module.exports = class MenuStack {
client: self.client, client: self.client,
}; };
if (currentModuleInfo && currentModuleInfo.menuFlags.includes('forwardArgs')) { if (currentModuleInfo && currentModuleInfo.menuFlags.includes(MenuFlags.ForwardArgs)) {
loadOpts.extraArgs = currentModuleInfo.extraArgs; loadOpts.extraArgs = currentModuleInfo.extraArgs;
} else { } else {
loadOpts.extraArgs = options.extraArgs || _.get(options, 'formData.value'); loadOpts.extraArgs = options.extraArgs || _.get(options, 'formData.value');
@ -138,7 +129,6 @@ module.exports = class MenuStack {
loadMenu(loadOpts, (err, modInst) => { loadMenu(loadOpts, (err, modInst) => {
if (err) { if (err) {
// :TODO: probably should just require a cb...
const errCb = cb || self.client.defaultHandlerMissingMod(); const errCb = cb || self.client.defaultHandlerMissingMod();
errCb(err); errCb(err);
} else { } else {
@ -151,22 +141,6 @@ module.exports = class MenuStack {
return; return;
} }
//
// Handle deprecated 'options' block by merging to config and warning user.
// :TODO: Remove in 0.0.10+
//
if (modInst.menuConfig.options) {
self.client.log.warn(
{ options: modInst.menuConfig.options },
'Use of "options" is deprecated. Move relevant members to "config" block! Support will be fully removed in future versions'
);
Object.assign(
modInst.menuConfig.config || {},
modInst.menuConfig.options
);
delete modInst.menuConfig.options;
}
// //
// If menuFlags were supplied in menu.hjson, they should win over // If menuFlags were supplied in menu.hjson, they should win over
// anything supplied in code. // anything supplied in code.
@ -180,9 +154,9 @@ module.exports = class MenuStack {
// in code we can ask to merge in // in code we can ask to merge in
if ( if (
Array.isArray(options.menuFlags) && Array.isArray(options.menuFlags) &&
options.menuFlags.includes('mergeFlags') options.menuFlags.includes(MenuFlags.MergeFlags)
) { ) {
menuFlags = _.uniq(menuFlags.concat(options.menuFlags)); menuFlags = [...new Set(options.menuFlags)]; // make unique
} }
} }
@ -193,12 +167,8 @@ module.exports = class MenuStack {
currentModuleInfo.instance.leave(); currentModuleInfo.instance.leave();
if (currentModuleInfo.menuFlags.includes('noHistory')) { if (currentModuleInfo.menuFlags.includes(MenuFlags.NoHistory)) {
this.pop(); this.pop().instance.leave(); // leave & remove current from stack
}
if (menuFlags.includes('popParent')) {
this.pop().instance.leave(); // leave & remove current
} }
} }
@ -214,6 +184,7 @@ module.exports = class MenuStack {
modInst.restoreSavedState(options.savedState); modInst.restoreSavedState(options.savedState);
} }
if (self.client.log.level() <= bunyan.TRACE) {
const stackEntries = self.stack.map(stackEntry => { const stackEntries = self.stack.map(stackEntry => {
let name = stackEntry.name; let name = stackEntry.name;
if (stackEntry.instance.menuConfig.config.menuFlags.length > 0) { if (stackEntry.instance.menuConfig.config.menuFlags.length > 0) {
@ -225,6 +196,7 @@ module.exports = class MenuStack {
}); });
self.client.log.trace({ stack: stackEntries }, 'Updated menu stack'); self.client.log.trace({ stack: stackEntries }, 'Updated menu stack');
}
modInst.enter(); modInst.enter();

View File

@ -113,7 +113,6 @@ exports.getModule = class MessageBaseSearch extends MenuModule {
const returnNoResults = () => { const returnNoResults = () => {
return this.gotoMenu( return this.gotoMenu(
this.menuConfig.config.noResultsMenu || 'messageSearchNoResults', this.menuConfig.config.noResultsMenu || 'messageSearchNoResults',
{ menuFlags: ['popParent'] },
cb cb
); );
}; };
@ -160,7 +159,6 @@ exports.getModule = class MessageBaseSearch extends MenuModule {
messageList, messageList,
noUpdateLastReadId: true, noUpdateLastReadId: true,
}, },
menuFlags: ['popParent'],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -55,6 +55,8 @@ const helpText = `
|03/|11topic |03<message> |08- |07Set the room topic |03/|11topic |03<message> |08- |07Set the room topic
|03/|11bbses |08& |03/|11info <id> |08- |07Info about BBS's connected |03/|11bbses |08& |03/|11info <id> |08- |07Info about BBS's connected
|03/|11meetups |08- |07Info about MRC MeetUps |03/|11meetups |08- |07Info about MRC MeetUps
|03/|11quote |08- |07Send raw command to server
|03/|11help |08- |07Server-side commands help
--- ---
|03/|11l33t |03<your message> |08- |07l337 5p34k |03/|11l33t |03<your message> |08- |07l337 5p34k
|03/|11kewl |03<your message> |08- |07BBS KeWL SPeaK |03/|11kewl |03<your message> |08- |07BBS KeWL SPeaK
@ -375,6 +377,18 @@ exports.getModule = class mrcModule extends MenuModule {
'|08' + currentTime + '|00 ' + message.body + '|00' '|08' + currentTime + '|00 ' + message.body + '|00'
); );
} }
// Deliver PrivMsg
else if (
message.to_user.toLowerCase() == this.state.alias.toLowerCase()
) {
const currentTime = moment().format(
this.client.currentTheme.helpers.getTimeFormat()
);
this.addMessageToChatLog(
'|08' + currentTime + '|00 ' + message.body + '|00'
);
}
} }
this.viewControllers.mrcChat.switchFocus(MciViewIds.mrcChat.inputArea); this.viewControllers.mrcChat.switchFocus(MciViewIds.mrcChat.inputArea);
@ -540,6 +554,46 @@ exports.getModule = class mrcModule extends MenuModule {
this.sendServerMessage('LIST'); this.sendServerMessage('LIST');
break; break;
// Allow support for new server commands without change to client
case 'quote':
this.sendServerMessage(`${message.substr(7)}`);
break;
/**
* Process known additional server commands directly
*/
case 'afk':
this.sendServerMessage(`AFK ${message.substr(5)}`);
break;
case 'roomconfig':
this.sendServerMessage(`ROOMCONFIG ${message.substr(12)}`);
break;
case 'roompass':
this.sendServerMessage(`ROOMPASS ${message.substr(12)}`);
break;
case 'status':
this.sendServerMessage(`STATUS ${message.substr(8)}`);
break;
case 'lastseen':
this.sendServerMessage(`LASTSEEN ${message.substr(10)}`);
break;
case 'help':
this.sendServerMessage(`HELP ${message.substr(6)}`);
break;
case 'statistics':
case 'changelog':
case 'listbans':
case 'listmutes':
case 'routing':
this.sendServerMessage(cmd[0].toUpperCase());
break;
case 'quit': case 'quit':
return this.prevMenu(); return this.prevMenu();

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const { MenuModule } = require('./menu_module.js'); const { MenuModule, MenuFlags } = require('./menu_module.js');
const messageArea = require('./message_area.js'); const messageArea = require('./message_area.js');
const { Errors } = require('./enig_error.js'); const { Errors } = require('./enig_error.js');
const UserProps = require('./user_property.js'); const UserProps = require('./user_property.js');
@ -29,6 +29,9 @@ exports.getModule = class MessageAreaListModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
// always include noHistory flag
this.setMergedFlag(MenuFlags.NoHistory);
this.initList(); this.initList();
this.menuMethods = { this.menuMethods = {
@ -49,7 +52,7 @@ exports.getModule = class MessageAreaListModule extends MenuModule {
extraArgs: { extraArgs: {
areaTag: area.areaTag, areaTag: area.areaTag,
}, },
menuFlags: ['popParent', 'noHistory'], menuFlags: [ MenuFlags.NoHistory ],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const { MenuModule } = require('./menu_module.js'); const { MenuModule, MenuFlags } = require('./menu_module.js');
const messageArea = require('./message_area.js'); const messageArea = require('./message_area.js');
const { Errors } = require('./enig_error.js'); const { Errors } = require('./enig_error.js');
@ -26,6 +26,9 @@ exports.getModule = class MessageConfListModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
// always include noHistory flag
this.setMergedFlag(MenuFlags.NoHistory);
this.initList(); this.initList();
this.menuMethods = { this.menuMethods = {
@ -49,7 +52,7 @@ exports.getModule = class MessageConfListModule extends MenuModule {
extraArgs: { extraArgs: {
confTag: conf.confTag, confTag: conf.confTag,
}, },
menuFlags: ['popParent', 'noHistory'], menuFlags: [ MenuFlags.NoHistory ],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// ENiGMA½ // ENiGMA½
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module');
const Message = require('./message.js'); const Message = require('./message.js');
const UserProps = require('./user_property.js'); const UserProps = require('./user_property.js');
const { filterMessageListByReadACS } = require('./message_area.js'); const { filterMessageListByReadACS } = require('./message_area.js');
@ -16,6 +16,7 @@ exports.moduleInfo = {
exports.getModule = class MyMessagesModule extends MenuModule { exports.getModule = class MyMessagesModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
} }
initSequence() { initSequence() {
@ -49,7 +50,6 @@ exports.getModule = class MyMessagesModule extends MenuModule {
if (!this.messageList || 0 === this.messageList.length) { if (!this.messageList || 0 === this.messageList.length) {
return this.gotoMenu( return this.gotoMenu(
this.menuConfig.config.noResultsMenu || 'messageSearchNoResults', this.menuConfig.config.noResultsMenu || 'messageSearchNoResults',
{ menuFlags: ['popParent'] }
); );
} }
@ -58,7 +58,6 @@ exports.getModule = class MyMessagesModule extends MenuModule {
messageList: this.messageList, messageList: this.messageList,
noUpdateLastReadId: true, noUpdateLastReadId: true,
}, },
menuFlags: ['popParent'],
}; };
return this.gotoMenu( return this.gotoMenu(

View File

@ -182,6 +182,14 @@ General Information:
MessageBase: `usage: oputil.js mb <action> [<arguments>] MessageBase: `usage: oputil.js mb <action> [<arguments>]
Actions: Actions:
list-confs List conferences and areas
post PATH Posts a message file specified in PATH.
PATH must point to a UTF-8 encoded JSON file
containing 'to', 'from', 'subject', 'areaTag', and
'body'. If 'timestamp' is present, the system will
attempt to use it.
areafix CMD1 CMD2 ... ADDR Sends an AreaFix NetMail areafix CMD1 CMD2 ... ADDR Sends an AreaFix NetMail
NetMail is sent to supplied address with the supplied command(s). Multi-part commands NetMail is sent to supplied address with the supplied command(s). Multi-part commands
@ -194,6 +202,9 @@ Actions:
packet in the directory specified by PATH. The QWK packet in the directory specified by PATH. The QWK
BBS ID will be obtained by the final component of PATH. BBS ID will be obtained by the final component of PATH.
list-confs arguments:
--areas Include areas within each message conference.
import-areas arguments: import-areas arguments:
--conf CONF_TAG Conference tag in which to import areas --conf CONF_TAG Conference tag in which to import areas
--network NETWORK Network name/key to associate FTN areas --network NETWORK Network name/key to associate FTN areas

View File

@ -692,6 +692,162 @@ function exportQWKPacket() {
); );
} }
const listConferences = () => {
initConfigAndDatabases(err => {
if (err) {
return console.error(err.reason ? err.reason : err.message);
}
const { getSortedAvailMessageConferences } = require('../../core/message_area');
const conferences = getSortedAvailMessageConferences(null, { noClient: true });
for (let conf of conferences) {
console.info(`${conf.confTag} - ${conf.conf.name}`);
if (!argv.areas) {
continue;
}
for (let areaTag of Object.keys(conf.conf.areas)) {
console.info(` ${areaTag} - ${conf.conf.areas[areaTag].name}`);
}
}
});
};
const postMessage = () => {
const inputFile = argv._[argv._.length - 1];
if (argv._.length < 3 || !inputFile || 0 === inputFile.length) {
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
}
async.waterfall(
[
callback => {
return initConfigAndDatabases(callback);
},
callback => {
fs.readFile(inputFile, { encoding: 'utf-8' }, (err, jsonData) => {
if (err) {
return callback(err);
}
let messageJson;
try {
messageJson = JSON.parse(jsonData);
} catch (e) {
return callback(e);
}
for (let f of ['to', 'from', 'subject', 'body', 'areaTag']) {
if (!_.isString(messageJson[f])) {
return callback(
Errors.MissingConfig(
`Missing "${f}" field in message JSON`
)
);
}
messageJson[f] = messageJson[f].trim();
if (messageJson[f].length === 0 && f !== 'subject') {
return callback(
Errors.Invalid(
`"${messageJson[f]}" is not a valid value for the "${f}" field`
)
);
}
}
const { getMessageAreaByTag } = require('../../core/message_area');
const area = getMessageAreaByTag(messageJson.areaTag);
if (!area) {
return callback(
Errors.DoesNotExist(
`Area "${messageJson.areaTag}" does not exist`
)
);
}
const { getAddressedToInfo } = require('../../core/mail_util');
const Message = require('../../core/message');
const toInfo = getAddressedToInfo(messageJson.to);
const fromInfo = getAddressedToInfo(messageJson.from);
if (fromInfo.flavor !== Message.AddressFlavor.Local) {
return callback(
Errors.Invalid(
'Only local "from" users are currently supported'
)
);
}
let modTimestamp;
if (_.isString(messageJson.timestamp)) {
modTimestamp = moment(messageJson.timestamp);
}
if (!modTimestamp || !modTimestamp.isValid()) {
modTimestamp = moment();
}
const message = new Message({
toUserName: messageJson.to,
fromUserName: messageJson.from,
subject: messageJson.subject,
message: messageJson.body,
areaTag: messageJson.areaTag,
modTimestamp,
});
if (toInfo.flavor !== Message.AddressFlavor.Local) {
message.setExternalFlavor(toInfo.flavor);
message.setRemoteToUser(toInfo.remote);
return callback(null, area, message);
}
const User = require('../../core/user');
User.getUserIdAndNameByLookup(
message.toUserName,
(err, toUserId, toUserName) => {
if (err) {
return callback(
Errors.DoesNotExist(
`User "${message.toUserName}" does not exist.`
)
);
}
message.to = toUserName; // adjust case/etc.
message.setLocalToUserId(toUserId);
return callback(null, area, message);
}
);
});
},
(area, message, callback) => {
message.persist(err => {
if (!err) {
console.info(
`Message from ${message.fromUserName} to ${message.toUserName}: "${message.subject}" in ${area.name}`
);
}
return callback(err);
});
},
],
err => {
if (err) {
return console.error(err.reason ? err.reason : err.message);
}
}
);
};
function handleMessageBaseCommand() { function handleMessageBaseCommand() {
function errUsage() { function errUsage() {
return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR); return printUsageAndSetExitCode(getHelpFor('MessageBase'), ExitCodes.ERROR);
@ -709,6 +865,8 @@ function handleMessageBaseCommand() {
'import-areas': importAreas, 'import-areas': importAreas,
'qwk-dump': dumpQWKPacket, 'qwk-dump': dumpQWKPacket,
'qwk-export': exportQWKPacket, 'qwk-export': exportQWKPacket,
'list-confs': listConferences,
post: postMessage,
}[action] || errUsage }[action] || errUsage
)(); )();
} }

View File

@ -172,7 +172,7 @@ exports.getModule = class RumorzModule extends MenuModule {
StatLog.getSystemLogEntries( StatLog.getSystemLogEntries(
SystemLogKeys.UserAddedRumorz, SystemLogKeys.UserAddedRumorz,
StatLog.Order.Timestamp, StatLog.Order.TimestampDesc,
(err, entries) => { (err, entries) => {
return callback(err, entriesView, entries); return callback(err, entriesView, entries);
} }

View File

@ -219,7 +219,8 @@ exports.getModule = class MrcModule extends ServerModule {
connectedSockets.forEach(client => { connectedSockets.forEach(client => {
if ( if (
message.to_user == '' || message.to_user == '' ||
message.to_user == client.username || // Fix PrivMSG delivery on case mismatch
message.to_user.toUpperCase() == client.username.toUpperCase() ||
message.to_user == 'CLIENT' || message.to_user == 'CLIENT' ||
message.from_user == client.username || message.from_user == client.username ||
message.to_user == 'NOTME' message.to_user == 'NOTME'

View File

@ -557,17 +557,24 @@ exports.getModule = class GopherModule extends ServerModule {
this.makeItem(ItemTypes.InfoMessage, `Messages in ${area.name}`), this.makeItem(ItemTypes.InfoMessage, `Messages in ${area.name}`),
this.makeItem(ItemTypes.InfoMessage, '(newest first)'), this.makeItem(ItemTypes.InfoMessage, '(newest first)'),
this.makeItem(ItemTypes.InfoMessage, '-'.repeat(70)), this.makeItem(ItemTypes.InfoMessage, '-'.repeat(70)),
...msgList.map(msg => ...msgList.map(msg => {
this.makeItem( let m;
try {
m = moment(msg.modTimestamp);
} catch (e) {
this.log.warn(
`Error parsing "${msg.modTimestamp}"; expected timestamp: ${e.message}`
);
m = moment();
}
return this.makeItem(
ItemTypes.TextFile, ItemTypes.TextFile,
`${moment(msg.modTimestamp).format( `${m.format('YYYY-MM-DD hh:mma')}: ${this.shortenSubject(
'YYYY-MM-DD hh:mma' msg.subject
)}: ${this.shortenSubject(msg.subject)} (${ )} (${msg.fromUserName} to ${msg.toUserName})`,
msg.fromUserName
} to ${msg.toUserName})`,
`/msgarea/${confTag}/${areaTag}/${msg.messageUuid}` `/msgarea/${confTag}/${areaTag}/${msg.messageUuid}`
) );
), }),
].join(''); ].join('');
this.log.debug({ confTag, areaTag }, 'Gopher serving message list'); this.log.debug({ confTag, areaTag }, 'Gopher serving message list');

View File

@ -4,7 +4,6 @@
// ENiGMA½ // ENiGMA½
const MenuModule = require('./menu_module.js').MenuModule; const MenuModule = require('./menu_module.js').MenuModule;
const Errors = require('../core/enig_error.js').Errors; const Errors = require('../core/enig_error.js').Errors;
const ANSI = require('./ansi_term.js');
const Config = require('./config.js').get; const Config = require('./config.js').get;
const { getMessageAreaByTag } = require('./message_area.js'); const { getMessageAreaByTag } = require('./message_area.js');
@ -21,6 +20,7 @@ exports.moduleInfo = {
exports.getModule = class ShowArtModule extends MenuModule { exports.getModule = class ShowArtModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.config = Object.assign({}, _.get(options, 'menuConfig.config'), { this.config = Object.assign({}, _.get(options, 'menuConfig.config'), {
extraArgs: options.extraArgs, extraArgs: options.extraArgs,
}); });

View File

@ -2,7 +2,7 @@
'use strict'; 'use strict';
// enigma-bbs // enigma-bbs
const MenuModule = require('./menu_module.js').MenuModule; const { MenuModule, MenuFlags } = require('./menu_module');
const stringFormat = require('./string_format.js'); const stringFormat = require('./string_format.js');
const getSortedAvailableFileAreas = const getSortedAvailableFileAreas =
require('./file_base_area.js').getSortedAvailableFileAreas; require('./file_base_area.js').getSortedAvailableFileAreas;
@ -76,6 +76,8 @@ exports.getModule = class UploadModule extends MenuModule {
constructor(options) { constructor(options) {
super(options); super(options);
this.setMergedFlag(MenuFlags.NoHistory);
this.interrupt = MenuModule.InterruptTypes.Never; this.interrupt = MenuModule.InterruptTypes.Never;
if (_.has(options, 'lastMenuResult.recvFilePaths')) { if (_.has(options, 'lastMenuResult.recvFilePaths')) {

View File

@ -8,10 +8,10 @@ const theme = require('./theme.js');
const sysValidate = require('./system_view_validate.js'); const sysValidate = require('./system_view_validate.js');
const UserProps = require('./user_property.js'); const UserProps = require('./user_property.js');
const { getISOTimestampString } = require('./database.js'); const { getISOTimestampString } = require('./database.js');
const EnigAssert = require('./enigma_assert');
// deps // deps
const async = require('async'); const async = require('async');
const assert = require('assert');
const _ = require('lodash'); const _ = require('lodash');
const moment = require('moment'); const moment = require('moment');
@ -109,7 +109,10 @@ exports.getModule = class UserConfigModule extends MenuModule {
// Handlers // Handlers
// //
saveChanges: function (formData, extraArgs, cb) { saveChanges: function (formData, extraArgs, cb) {
assert(formData.value.password === formData.value.passwordConfirm); EnigAssert(formData.value.password === formData.value.passwordConfirm);
// cache a copy of |formData| as changing a theme below can invalidate it
formData = _.clone(formData);
const newProperties = { const newProperties = {
[UserProps.RealName]: formData.value.realName, [UserProps.RealName]: formData.value.realName,
@ -126,7 +129,7 @@ exports.getModule = class UserConfigModule extends MenuModule {
self.availThemeInfo[formData.value.theme].themeId, self.availThemeInfo[formData.value.theme].themeId,
}; };
// runtime set theme // Runtime set theme
theme.setClientTheme(self.client, newProperties.theme_id); theme.setClientTheme(self.client, newProperties.theme_id);
// persist all changes // persist all changes

View File

@ -1,7 +1,7 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activesupport (7.0.4.1) activesupport (7.0.7.2)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -9,7 +9,7 @@ GEM
addressable (2.8.0) addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
colorator (1.1.0) colorator (1.1.0)
concurrent-ruby (1.1.10) concurrent-ruby (1.2.2)
cssminify2 (2.0.1) cssminify2 (2.0.1)
em-websocket (0.5.3) em-websocket (0.5.3)
eventmachine (>= 0.12.9) eventmachine (>= 0.12.9)
@ -24,7 +24,7 @@ GEM
nokogiri (>= 1.4) nokogiri (>= 1.4)
htmlcompressor (0.4.0) htmlcompressor (0.4.0)
http_parser.rb (0.8.0) http_parser.rb (0.8.0)
i18n (1.12.0) i18n (1.14.1)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
jekyll (4.2.1) jekyll (4.2.1)
addressable (~> 2.4) addressable (~> 2.4)
@ -76,13 +76,13 @@ GEM
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
mercenary (0.4.0) mercenary (0.4.0)
minitest (5.17.0) minitest (5.19.0)
nokogiri (1.13.6-x86_64-linux) nokogiri (1.14.3-x86_64-linux)
racc (~> 1.4) racc (~> 1.4)
pathutil (0.16.2) pathutil (0.16.2)
forwardable-extended (~> 2.6) forwardable-extended (~> 2.6)
public_suffix (4.0.6) public_suffix (4.0.6)
racc (1.6.0) racc (1.6.2)
rb-fsevent (0.11.0) rb-fsevent (0.11.0)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
@ -93,7 +93,7 @@ GEM
ffi (~> 1.9) ffi (~> 1.9)
terminal-table (2.0.0) terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1) unicode-display_width (~> 1.1, >= 1.1.1)
tzinfo (2.0.5) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uglifier (4.2.0) uglifier (4.2.0)
execjs (>= 0.3.0, < 3) execjs (>= 0.3.0, < 3)

View File

@ -323,7 +323,7 @@ qwk-export arguments:
| Action | Description | Examples | | Action | Description | Examples |
|-----------|-------------------|---------------------------------------| |-----------|-------------------|---------------------------------------|
| `import-areas` | Imports areas using a FidoNet style *.NA or AREAS.BBS formatted file. Optionally maps areas to FTN networks. | `./oputil.js config import-areas /some/path/l33tnet.na` | | `import-areas` | Imports areas using a FidoNet style *.NA or AREAS.BBS formatted file. Optionally maps areas to FTN networks. | `./oputil.js mb import-areas /some/path/l33tnet.na` |
| `areafix` | Utility for sending AreaFix mails without logging into the system | | | `areafix` | Utility for sending AreaFix mails without logging into the system | |
| `qwk-dump` | Dump a QWK packet to stdout | `./oputil.js mb qwk-dump /path/to/XIBALBA.QWK` | | `qwk-dump` | Dump a QWK packet to stdout | `./oputil.js mb qwk-dump /path/to/XIBALBA.QWK` |
| `qwk-export` | Export messages to a QWK packet | `./oputil.js mb qwk-export /path/to/XIBALBA.QWK` | | `qwk-export` | Export messages to a QWK packet | `./oputil.js mb qwk-export /path/to/XIBALBA.QWK` |

View File

@ -59,13 +59,15 @@ The `config` block for a menu entry can contain common members as well as a per-
| `menuFlags` | An array of menu flag(s) controlling menu behavior. See **Menu Flags** below. | `menuFlags` | An array of menu flag(s) controlling menu behavior. See **Menu Flags** below.
#### Menu Flags #### Menu Flags
The `menuFlags` field of a `config` block can change default behavior of a particular menu. The `menuFlags` field of a `config` block can change default behavior of a particular menu:
| Flag | Description | | Flag | Description |
|------|-------------| |------|-------------|
| `noHistory` | Prevents the menu from remaining in the menu stack / history. When this flag is set, when the **next** menu falls back, this menu will be skipped and the previous menu again displayed instead. Example: menuA -> menuB(noHistory) -> menuC: Exiting menuC returns the user to menuA. | | `noHistory` | When leaving the current menu to load/chain to another, remove this menu from history. In other words, the fallback from the next menu would *not* be this one, but the previous. |
| `popParent` | When *this* menu is exited, fall back beyond the parent as well. Often used in combination with `noHistory`. | | `mergeFlags` | Generally used in code only: Request that any flags from `menu.hjson` |
| `forwardArgs` | If set, when the next menu is entered, forward any `extraArgs` arguments to *this* menu on to it. | | `forwardArgs` | Forward this menu's `extraArgs` to the next. |
> 💡 In JavaScript code, `MenuFlags` from `menu_module.js` contains constants for these flags.
## Forms ## Forms

View File

@ -58,7 +58,6 @@ showFileBaseAreaArt: {
method: fileBaseArea method: fileBaseArea
cls: true cls: true
pause: true pause: true
menuFlags: [ "popParent", "noHistory" ]
} }
} }
``` ```

View File

@ -101,6 +101,6 @@ webserver, and unpack it to a temporary directory.
otherwise. otherwise.
9. If you navigate to http://your-hostname.here/vtx.html, you should see a splash screen like the following: 9. If you navigate to http://your-hostname.here/vtx.html, you should see a splash screen like the following:
![VTXClient](../assets/images/vtxclient.png "VTXClient") ![VTXClient](../../assets/images/vtxclient.png "VTXClient")

View File

@ -315,6 +315,8 @@
chatServers: { chatServers: {
// multi relay chat settings. No need to sign up, just enable it. // multi relay chat settings. No need to sign up, just enable it.
// More info: https://bbswiki.bottomlessabyss.net/index.php?title=MRC_Chat_platform // More info: https://bbswiki.bottomlessabyss.net/index.php?title=MRC_Chat_platform
// Make sure to adjust 'prettyBoardName' to your liking in your config before enabling
mrc: { mrc: {
enabled : false enabled : false
serverHostname : 'mrc.bottomlessabyss.net' serverHostname : 'mrc.bottomlessabyss.net'

View File

@ -388,7 +388,7 @@
art: FEMPTYQ art: FEMPTYQ
config: { config: {
pause: true pause: true
menuFlags: [ "noHistory", "popParent" ] menuFlags: [ "noHistory" ]
} }
} }
@ -779,7 +779,7 @@
art: FBNORES art: FBNORES
config: { config: {
pause: true pause: true
menuFlags: [ "noHistory", "popParent" ] menuFlags: [ "noHistory" ]
} }
} }
@ -807,7 +807,7 @@
art: FBNORES art: FBNORES
config: { config: {
pause: true pause: true
menuFlags: [ "noHistory", "popParent" ] menuFlags: [ "noHistory" ]
} }
} }
@ -852,7 +852,7 @@
art: ULNOAREA art: ULNOAREA
config: { config: {
pause: true pause: true
menuFlags: [ "noHistory", "popParent" ] menuFlags: [ "noHistory" ]
} }
} }

View File

@ -776,7 +776,7 @@
key: confTag key: confTag
pause: true pause: true
cls: true cls: true
menuFlags: [ "popParent", "noHistory" ] menuFlags: [ "noHistory" ]
} }
} }
@ -794,7 +794,7 @@
key: areaTag key: areaTag
pause: true pause: true
cls: true cls: true
menuFlags: [ "popParent", "noHistory" ] menuFlags: [ "noHistory" ]
} }
} }
} }

View File

@ -57,7 +57,7 @@
"rlogin": "^1.0.0", "rlogin": "^1.0.0",
"sane": "5.0.1", "sane": "5.0.1",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"sqlite3": "5.1.5", "sqlite3": "5.1.6",
"sqlite3-trans": "1.3.0", "sqlite3-trans": "1.3.0",
"ssh2": "1.11.0", "ssh2": "1.11.0",
"systeminformation": "5.12.3", "systeminformation": "5.12.3",

739
yarn.lock

File diff suppressed because it is too large Load Diff