enigma-bbs/core/predefined_mci.js

554 lines
18 KiB
JavaScript

/* jslint node: true */
'use strict';
// ENiGMA½
const Config = require('./config.js').get;
const Log = require('./logger.js').log;
const { getMessageAreaByTag, getMessageConferenceByTag } = require('./message_area.js');
const clientConnections = require('./client_connections.js');
const StatLog = require('./stat_log.js');
const FileBaseFilters = require('./file_base_filter.js');
const { formatByteSize } = require('./string_util.js');
const ANSI = require('./ansi_term.js');
const UserProps = require('./user_property.js');
const SysProps = require('./system_property.js');
const SysLogKeys = require('./system_log.js');
// deps
const packageJson = require('../package.json');
const os = require('os');
const _ = require('lodash');
const moment = require('moment');
const async = require('async');
exports.getPredefinedMCIValue = getPredefinedMCIValue;
exports.getPredefinedMCIFormatObject = getPredefinedMCIFormatObject;
exports.init = init;
function init(cb) {
async.series(
[
callback => {
return setNextRandomRumor(callback);
},
callback => {
// by fetching a memory or load we'll force a refresh now
StatLog.getSystemStat(SysProps.SystemMemoryStats);
return callback(null);
},
],
err => {
return cb(err);
}
);
}
// :TODO: move this to stat_log.js like system memory is handled
function setNextRandomRumor(cb) {
StatLog.getSystemLogEntries(
SysLogKeys.UserAddedRumorz,
StatLog.Order.Random,
1,
(err, entry) => {
if (entry) {
entry = entry[0];
}
const randRumor = entry && entry.log_value ? entry.log_value : '';
StatLog.setNonPersistentSystemStat(SysProps.NextRandomRumor, randRumor);
if (cb) {
return cb(null);
}
}
);
}
function getUserRatio(client, propA, propB) {
const a = StatLog.getUserStatNum(client.user, propA);
const b = StatLog.getUserStatNum(client.user, propB);
const ratio = ~~((a / b) * 100);
return `${ratio}%`;
}
function userStatAsString(client, statName, defaultValue) {
return (StatLog.getUserStat(client.user, statName) || defaultValue).toLocaleString();
}
function toNumberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
function userStatAsCountString(client, statName, defaultValue) {
const value = StatLog.getUserStatNum(client.user, statName) || defaultValue;
return toNumberWithCommas(value);
}
const PREDEFINED_MCI_GENERATORS = {
//
// Board
//
BN: function boardName() {
return Config().general.boardName;
},
// ENiGMA
VL: function versionLabel() {
return 'ENiGMA½ v' + packageJson.version;
},
VN: function version() {
return packageJson.version;
},
// +op info
SN: function opUserName() {
return StatLog.getSystemStat(SysProps.SysOpUsername);
},
SR: function opRealName() {
return StatLog.getSystemStat(SysProps.SysOpRealName);
},
SL: function opLocation() {
return StatLog.getSystemStat(SysProps.SysOpLocation);
},
SA: function opAffils() {
return StatLog.getSystemStat(SysProps.SysOpAffiliations);
},
SS: function opSex() {
return StatLog.getSystemStat(SysProps.SysOpSex);
},
SE: function opEmail() {
return StatLog.getSystemStat(SysProps.SysOpEmailAddress);
},
//
// Current user / session
//
UN: function userName(client) {
return client.user.username;
},
UI: function userId(client) {
return client.user.userId.toString();
},
UG: function groups(client) {
return _.values(client.user.groups).join(', ');
},
UR: function realName(client) {
return userStatAsString(client, UserProps.RealName, '');
},
LO: function location(client) {
return userStatAsString(client, UserProps.Location, '');
},
UA: function age(client) {
return client.user.getAge().toString();
},
BD: function birthdate(client) {
// iNiQUiTY
return moment(client.user.properties[UserProps.Birthdate]).format(
client.currentTheme.helpers.getDateFormat()
);
},
US: function sex(client) {
return userStatAsString(client, UserProps.Sex, '');
},
UE: function emailAddress(client) {
return userStatAsString(client, UserProps.EmailAddress, '');
},
UW: function webAddress(client) {
return userStatAsString(client, UserProps.WebAddress, '');
},
UF: function affils(client) {
return userStatAsString(client, UserProps.Affiliations, '');
},
UT: function themeName(client) {
return _.get(
client,
'currentTheme.info.name',
userStatAsString(client, UserProps.ThemeId, '')
);
},
UD: function themeId(client) {
return userStatAsString(client, UserProps.ThemeId, '');
},
UC: function loginCount(client) {
return userStatAsCountString(client, UserProps.LoginCount, 0);
},
ND: function connectedNode(client) {
return client.node.toString();
},
IP: function clientIpAddress(client) {
return client.friendlyRemoteAddress();
},
ST: function serverName(client) {
return client.session.serverName;
},
FN: function activeFileBaseFilterName(client) {
const activeFilter = FileBaseFilters.getActiveFilter(client);
return activeFilter ? activeFilter.name : '(Unknown)';
},
DN: function userNumDownloads(client) {
return userStatAsCountString(client, UserProps.FileDlTotalCount, 0);
}, // Obv/2
DK: function userByteDownload(client) {
// Obv/2 uses DK=downloaded Kbytes
const byteSize = StatLog.getUserStatNum(client.user, UserProps.FileDlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
UP: function userNumUploads(client) {
return userStatAsCountString(client, UserProps.FileUlTotalCount, 0);
}, // Obv/2
UK: function userByteUpload(client) {
// Obv/2 uses UK=uploaded Kbytes
const byteSize = StatLog.getUserStatNum(client.user, UserProps.FileUlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
NR: function userUpDownRatio(client) {
// Obv/2
return getUserRatio(
client,
UserProps.FileUlTotalCount,
UserProps.FileDlTotalCount
);
},
KR: function userUpDownByteRatio(client) {
// Obv/2 uses KR=upload/download Kbyte ratio
return getUserRatio(
client,
UserProps.FileUlTotalBytes,
UserProps.FileDlTotalBytes
);
},
MS: function accountCreated(client) {
return moment(client.user.properties[UserProps.AccountCreated]).format(
client.currentTheme.helpers.getDateFormat()
);
},
PS: function userPostCount(client) {
return userStatAsCountString(client, UserProps.MessagePostCount, 0);
},
PC: function userPostCallRatio(client) {
return getUserRatio(client, UserProps.MessagePostCount, UserProps.LoginCount);
},
MD: function currentMenuDescription(client) {
return _.has(client, 'currentMenuModule.menuConfig.desc')
? client.currentMenuModule.menuConfig.desc
: '';
},
MA: function messageAreaName(client) {
const area = getMessageAreaByTag(
client.user.properties[UserProps.MessageAreaTag]
);
return area ? area.name : '';
},
MC: function messageConfName(client) {
const conf = getMessageConferenceByTag(
client.user.properties[UserProps.MessageConfTag]
);
return conf ? conf.name : '';
},
ML: function messageAreaDescription(client) {
const area = getMessageAreaByTag(
client.user.properties[UserProps.MessageAreaTag]
);
return area ? area.desc : '';
},
CM: function messageConfDescription(client) {
const conf = getMessageConferenceByTag(
client.user.properties[UserProps.MessageConfTag]
);
return conf ? conf.desc : '';
},
SH: function termHeight(client) {
return client.term.termHeight.toString();
},
SW: function termWidth(client) {
return client.term.termWidth.toString();
},
AC: function achievementCount(client) {
return userStatAsCountString(client, UserProps.AchievementTotalCount, 0);
},
AP: function achievementPoints(client) {
return userStatAsCountString(client, UserProps.AchievementTotalPoints, 0);
},
DR: function doorRuns(client) {
return userStatAsCountString(client, UserProps.DoorRunTotalCount, 0);
},
DM: function doorFriendlyRunTime(client) {
const minutes = client.user.properties[UserProps.DoorRunTotalMinutes] || 0;
return moment.duration(minutes, 'minutes').humanize();
},
TO: function friendlyTotalTimeOnSystem(client) {
const minutes = client.user.properties[UserProps.MinutesOnlineTotalCount] || 0;
return moment.duration(minutes, 'minutes').humanize();
},
NM: function userNewMessagesAddressedToCount(client) {
return StatLog.getUserStatNumByClient(
client,
UserProps.NewAddressedToMessageCount
);
},
NP: function userNewPrivateMailCount(client) {
return StatLog.getUserStatNumByClient(client, UserProps.NewPrivateMailCount);
},
IA: function userStatusAvailableIndicator(client) {
const indicators = client.currentTheme.helpers.getStatusAvailIndicators();
return client.user.isAvailable() ? indicators[0] || 'Y' : indicators[1] || 'N';
},
IV: function userStatusVisibleIndicator(client) {
const indicators = client.currentTheme.helpers.getStatusVisibleIndicators();
return client.user.isVisible() ? indicators[0] || 'Y' : indicators[1] || 'N';
},
//
// Date/Time
//
DT: function date(client) {
return moment().format(client.currentTheme.helpers.getDateFormat());
},
CT: function time(client) {
return moment().format(client.currentTheme.helpers.getTimeFormat());
},
//
// OS/System Info
//
// https://github.com/nodejs/node-v0.x-archive/issues/25769
//
OS: function operatingSystem() {
return (
{
linux: 'Linux',
darwin: 'OS X',
win32: 'Windows',
sunos: 'SunOS',
freebsd: 'FreeBSD',
android: 'Android',
openbsd: 'OpenBSD',
aix: 'IBM AIX',
}[os.platform()] || os.type()
);
},
OA: function systemArchitecture() {
return os.arch();
},
SC: function systemCpuModel() {
//
// Clean up CPU strings a bit for better display
//
return os
.cpus()[0]
.model.replace(/\(R\)|\(TM\)|processor|CPU/gi, '')
.replace(/\s+(?= )/g, '')
.trim();
},
MB: function totalMemoryBytes() {
const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {
totalBytes: 0,
};
return formatByteSize(stats.totalBytes, true); // true=withAbbr
},
MF: function totalMemoryFreeBytes() {
const stats = StatLog.getSystemStat(SysProps.SystemMemoryStats) || {
freeBytes: 0,
};
return formatByteSize(stats.freeBytes, true); // true=withAbbr
},
LA: function systemLoadAverage() {
const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { average: 0.0 };
return stats.average.toLocaleString();
},
CL: function systemCurrentLoad() {
const stats = StatLog.getSystemStat(SysProps.SystemLoadStats) || { current: 0 };
return `${stats.current}%`;
},
UU: function systemUptime() {
return moment.duration(process.uptime(), 'seconds').humanize();
},
NV: function nodeVersion() {
return process.version;
},
AN: function activeNodes() {
return clientConnections
.getActiveConnections(clientConnections.UserVisibleConnections)
.length.toString();
},
TC: function totalCalls() {
return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString();
},
TT: function totalCallsToday() {
return StatLog.getSystemStat(SysProps.LoginsToday).toLocaleString();
},
PI: function processBytesIngress() {
const stats = StatLog.getSystemStat(SysProps.ProcessTrafficStats) || {
ingress: 0,
};
return stats.ingress.toLocaleString();
},
PE: function processBytesEgress() {
const stats = StatLog.getSystemStat(SysProps.ProcessTrafficStats) || {
egress: 0,
};
return stats.ingress.toLocaleString();
},
RR: function randomRumor() {
// start the process of picking another random one
setNextRandomRumor();
return StatLog.getSystemStat('random_rumor');
},
//
// System File Base, Up/Download Info
//
SD: function systemNumDownloads() {
return StatLog.getFriendlySystemStat(SysProps.FileDlTotalCount, 0);
},
SO: function systemByteDownload() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
SU: function systemNumUploads() {
return StatLog.getFriendlySystemStat(SysProps.FileUlTotalCount, 0);
},
SP: function systemByteUpload() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTotalBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
TF: function totalFilesOnSystem() {
const areaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats);
return _.get(areaStats, 'totalFiles', 0).toLocaleString();
},
TB: function totalBytesOnSystem() {
const areaStats = StatLog.getSystemStat(SysProps.FileBaseAreaStats);
const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0));
return formatByteSize(totalBytes, true); // true=withAbbr
},
PT: function messagesPostedToday() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.MessagesToday, 0);
},
TP: function totalMessagesOnSystem() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.MessageTotalCount, 0);
},
FT: function totalUploadsToday() {
// Obv/2
return StatLog.getFriendlySystemStat(SysProps.FileUlTodayCount, 0);
},
FB: function totalUploadBytesToday() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileUlTodayBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
DD: function totalDownloadsToday() {
// iNiQUiTY
return StatLog.getFriendlySystemStat(SysProps.FileDlTodayCount, 0);
},
DB: function totalDownloadBytesToday() {
const byteSize = StatLog.getSystemStatNum(SysProps.FileDlTodayBytes);
return formatByteSize(byteSize, true); // true=withAbbr
},
NT: function totalNewUsersToday() {
// Obv/2
return StatLog.getSystemStatNum(SysProps.NewUsersTodayCount);
},
// :TODO: TZ - Average *system* post/call ratio (iNiQUiTY)
// :TODO: ?? - Total users on system
TU: function totalSystemUsers() {
return StatLog.getSystemStatNum(SysProps.TotalUserCount) || 1;
},
LC: function lastCallerUserName() {
// Obv/2
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
return lastLogin.userName || 'N/A';
},
LD: function lastCallerDate(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) {
return 'N/A';
}
return lastLogin.timestamp.format(client.currentTheme.helpers.getDateFormat());
},
LT: function lastCallerTime(client) {
const lastLogin = StatLog.getSystemStat(SysProps.LastLogin) || {};
if (!lastLogin.timestamp) {
return 'N/A';
}
return lastLogin.timestamp.format(client.currentTheme.helpers.getTimeFormat());
},
//
// Special handling for XY
//
XY: function xyHack() {
return; /* nothing */
},
//
// Various movement by N
//
CF: function cursorForwardBy(client, n = 1) {
return ANSI.forward(n);
},
CB: function cursorBackBy(client, n = 1) {
return ANSI.back(n);
},
CU: function cursorUpBy(client, n = 1) {
return ANSI.up(n);
},
CD: function cursorDownBy(client, n = 1) {
return ANSI.down(n);
},
};
function getPredefinedMCIValue(client, code, extra) {
if (!client || !code) {
return;
}
const generator = PREDEFINED_MCI_GENERATORS[code];
if (generator) {
let value;
try {
value = generator(client, extra);
} catch (e) {
Log.error(
{ code: code, exception: e.message },
`Failed generating predefined MCI value (${code})`
);
}
return value;
}
}
function getPredefinedMCIFormatObject(client, text) {
const re = /\{([A-Z]{2})(?:[!:][^}]+)?\}/g;
let m;
const formatObj = {};
while ((m = re.exec(text))) {
const v = getPredefinedMCIValue(client, m[1]);
if (v) {
if (!isNaN(v)) {
formatObj[m[1]] = parseInt(v);
} else {
formatObj[m[1]] = v;
}
}
}
return _.isEmpty(formatObj) ? null : formatObj;
}