New MCI codes & user status flags support additions
* New MCI and WFC properties for user new private and "addressed to" mail * Additional support for user status flags in connection lists, etc.
This commit is contained in:
parent
6502f3b55e
commit
868e14aa8e
|
@ -248,10 +248,11 @@
|
|||
|
||||
mainMenuWaitingForCaller: {
|
||||
config: {
|
||||
quickLogTimestampFormat: "|01|02MM|08/|02DD hh:mm:ssa"
|
||||
nowDateTimeFormat: "|00|11dddd|08, |11MMMM Do YYYY |08/ |11h|08:|11mm|08:|11ss|03a"
|
||||
lastLoginDateTimeFormat: "|00|10ddd hh|08:|10mm|02a"
|
||||
|
||||
mainInfoFormat10: "|00|11{now} {currentUserName} |08- |03mail|08: "
|
||||
mainInfoFormat10: "|00|11{now} {currentUserName} |08- |03mail|08: |11{newPrivateMail}|03 prv|08, |11{newMessagesAddrTo}|03 addr to"
|
||||
mainInfoFormat11: "|00|10{callsToday:>5}"
|
||||
mainInfoFormat12: "|00|10{postsToday:>5}"
|
||||
mainInfoFormat13: "|00|10{uploadsToday:>2} |08/ |10{uploadBytesToday!sizeWithoutAbbr} |02{uploadBytesToday!sizeAbbr}"
|
||||
|
|
|
@ -163,6 +163,7 @@ exports.getModule = class AbracadabraModule extends MenuModule {
|
|||
this.client.term.write(ansi.resetScreen());
|
||||
|
||||
const exeInfo = {
|
||||
name : this.config.name,
|
||||
cmd : this.config.cmd,
|
||||
cwd : this.config.cwd || paths.dirname(this.config.cmd),
|
||||
args : this.config.args,
|
||||
|
|
|
@ -141,7 +141,7 @@ function shutdownSystem() {
|
|||
[
|
||||
function closeConnections(callback) {
|
||||
const ClientConns = require('./client_connections.js');
|
||||
const activeConnections = ClientConns.getActiveConnections();
|
||||
const activeConnections = ClientConns.getActiveConnections(ClientConns.AllConnections);
|
||||
let i = activeConnections.length;
|
||||
while(i--) {
|
||||
const activeTerm = activeConnections[i].term;
|
||||
|
|
|
@ -21,7 +21,16 @@ exports.getConnectionByNodeId = getConnectionByNodeId;
|
|||
const clientConnections = [];
|
||||
exports.clientConnections = clientConnections;
|
||||
|
||||
function getActiveConnections(options = { authUsersOnly: true, visibleOnly: true }) {
|
||||
const AllConnections = { authUsersOnly: false, visibleOnly: false, availOnly: false };
|
||||
exports.AllConnections = AllConnections;
|
||||
|
||||
const UserVisibleConnections = { authUsersOnly: false, visibleOnly: true, availOnly: false };
|
||||
exports.UserVisibleConnections = UserVisibleConnections;
|
||||
|
||||
const UserMessageableConnections = { authUsersOnly: true, visibleOnly: true, availOnly: true };
|
||||
exports.UserMessageableConnections = UserMessageableConnections;
|
||||
|
||||
function getActiveConnections(options = { authUsersOnly: true, visibleOnly: true, availOnly: false }) {
|
||||
return clientConnections.filter(conn => {
|
||||
if (options.authUsersOnly && !conn.user.isAuthenticated()) {
|
||||
return false;
|
||||
|
@ -29,13 +38,15 @@ function getActiveConnections(options = { authUsersOnly: true, visibleOnly: true
|
|||
if (options.visibleOnly && !conn.user.isVisible()) {
|
||||
return false;
|
||||
}
|
||||
if (options.availOnly && !conn.user.isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
//return ((options.authUsersOnly && conn.user.isAuthenticated()) || !options.authUsersOnly);
|
||||
});
|
||||
}
|
||||
|
||||
function getActiveConnectionList(options = { authUsersOnly: true, visibleOnly: true }) {
|
||||
function getActiveConnectionList(options = { authUsersOnly: true, visibleOnly: true, availOnly: false }) {
|
||||
const now = moment();
|
||||
|
||||
return _.map(getActiveConnections(options), ac => {
|
||||
|
@ -149,9 +160,9 @@ function removeClient(client) {
|
|||
}
|
||||
|
||||
function getConnectionByUserId(userId) {
|
||||
return getActiveConnections().find( ac => userId === ac.user.userId );
|
||||
return getActiveConnections(AllConnections).find( ac => userId === ac.user.userId );
|
||||
}
|
||||
|
||||
function getConnectionByNodeId(nodeId) {
|
||||
return getActiveConnections().find( ac => nodeId == ac.node );
|
||||
return getActiveConnections(AllConnections).find( ac => nodeId == ac.node );
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ module.exports = class Door {
|
|||
|
||||
this.client.log.info(
|
||||
{ cmd : exeInfo.cmd, args, io : this.io },
|
||||
'Executing external door process'
|
||||
`Executing external door (${exeInfo.name})`
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -598,7 +598,7 @@ exports.MenuModule = class MenuModule extends PluginModule {
|
|||
|
||||
if(options.appendMultiLine && (textView instanceof MultiLineEditTextView)) {
|
||||
textView.addText(text);
|
||||
} else {
|
||||
} else if (textView.getData() != text) {
|
||||
textView.setText(text);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ exports.filterMessageListByReadACS = filterMessageListByReadACS;
|
|||
exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea;
|
||||
exports.getMessageListForArea = getMessageListForArea;
|
||||
exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser;
|
||||
exports.getNewMessageCountAddressedToUser = getNewMessageCountAddressedToUser;
|
||||
exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser;
|
||||
exports.getMessageIdNewerThanTimestampByArea = getMessageIdNewerThanTimestampByArea;
|
||||
exports.getMessageAreaLastReadId = getMessageAreaLastReadId;
|
||||
|
@ -489,6 +490,26 @@ function getNewMessageCountInAreaForUser(userId, areaTag, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
// New message count -- for all areas available to the user
|
||||
// that are addressed to that user (ie: matching username)
|
||||
// Does NOT Include private messages.
|
||||
function getNewMessageCountAddressedToUser(client, cb) {
|
||||
const areaTags = getAllAvailableMessageAreaTags(client).filter(areaTag => areaTag !== Message.WellKnownAreaTags.Private);
|
||||
|
||||
let newMessageCount = 0;
|
||||
async.forEach(areaTags, (areaTag, nextAreaTag) => {
|
||||
getMessageAreaLastReadId(client.user.userId, areaTag, (_, lastMessageId) => {
|
||||
lastMessageId = lastMessageId || 0;
|
||||
getNewMessageCountInAreaForUser(client.user.userId, areaTag, (err, count) => {
|
||||
newMessageCount += count;
|
||||
return nextAreaTag(err);
|
||||
});
|
||||
});
|
||||
}, () => {
|
||||
return cb(null, newMessageCount);
|
||||
});
|
||||
}
|
||||
|
||||
function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
||||
getMessageAreaLastReadId(userId, areaTag, (err, lastMessageId) => {
|
||||
lastMessageId = lastMessageId || 0;
|
||||
|
@ -509,6 +530,7 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
function getMessageListForArea(client, areaTag, filter, cb)
|
||||
{
|
||||
if(!cb && _.isFunction(filter)) {
|
||||
|
|
|
@ -6,6 +6,7 @@ const { MenuModule } = require('./menu_module.js');
|
|||
const {
|
||||
getActiveConnectionList,
|
||||
getConnectionByNodeId,
|
||||
UserMessageableConnections,
|
||||
} = require('./client_connections.js');
|
||||
const UserInterruptQueue = require('./user_interrupt_queue.js');
|
||||
const { getThemeArt } = require('./theme.js');
|
||||
|
@ -204,7 +205,7 @@ exports.getModule = class NodeMessageModule extends MenuModule {
|
|||
location : 'N/A',
|
||||
affils : 'N/A',
|
||||
timeOn : 'N/A',
|
||||
}].concat(getActiveConnectionList()
|
||||
}].concat(getActiveConnectionList(UserMessageableConnections)
|
||||
.map(node => Object.assign(node, { text : -1 == node.node ? '-ALL-' : node.node.toString() } ))
|
||||
).filter(node => node.node !== this.client.node); // remove our client's node
|
||||
this.nodeList.sort( (a, b) => a.node - b.node ); // sort by node
|
||||
|
|
|
@ -186,6 +186,12 @@ const PREDEFINED_MCI_GENERATORS = {
|
|||
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);
|
||||
},
|
||||
|
||||
//
|
||||
// Date/Time
|
||||
|
@ -243,7 +249,7 @@ const PREDEFINED_MCI_GENERATORS = {
|
|||
return moment.duration(process.uptime(), 'seconds').humanize();
|
||||
},
|
||||
NV : function nodeVersion() { return process.version; },
|
||||
AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); },
|
||||
AN : function activeNodes() { return clientConnections.getActiveConnections(clientConnections.UserVisibleConnections).length.toString(); },
|
||||
|
||||
TC : function totalCalls() { return StatLog.getSystemStat(SysProps.LoginCount).toLocaleString(); },
|
||||
TT : function totalCallsToday() {
|
||||
|
|
|
@ -7,6 +7,8 @@ const {
|
|||
} = require('./database.js');
|
||||
const Errors = require('./enig_error.js');
|
||||
const SysProps = require('./system_property.js');
|
||||
const UserProps = require('./user_property');
|
||||
const Message = require('./message');
|
||||
|
||||
// deps
|
||||
const _ = require('lodash');
|
||||
|
@ -152,13 +154,25 @@ class StatLog {
|
|||
}
|
||||
|
||||
getUserStat(user, statName) {
|
||||
return user.properties[statName];
|
||||
return user.getProperty(statName);
|
||||
}
|
||||
|
||||
getUserStatByClient(client, statName) {
|
||||
const stat = this.getUserStat(client.user, statName);
|
||||
this._refreshUserStat(client, statName);
|
||||
return stat;
|
||||
}
|
||||
|
||||
getUserStatNum(user, statName) {
|
||||
return parseInt(this.getUserStat(user, statName)) || 0;
|
||||
}
|
||||
|
||||
getUserStatNumByClient(client, statName, ttlSeconds=10) {
|
||||
const stat = this.getUserStatNum(client.user, statName);
|
||||
this._refreshUserStat(client, statName, ttlSeconds);
|
||||
return stat;
|
||||
}
|
||||
|
||||
incrementUserStat(user, statName, incrementBy, cb) {
|
||||
incrementBy = incrementBy || 1;
|
||||
|
||||
|
@ -391,6 +405,49 @@ class StatLog {
|
|||
});
|
||||
}
|
||||
|
||||
_refreshUserStat(client, statName, ttlSeconds) {
|
||||
switch(statName) {
|
||||
case UserProps.NewPrivateMailCount:
|
||||
this._wrapUserRefreshWithCachedTTL(client, statName, this._refreshUserPrivateMailCount, ttlSeconds);
|
||||
break;
|
||||
|
||||
case UserProps.NewAddressedToMessageCount:
|
||||
this._wrapUserRefreshWithCachedTTL(client, statName, this._refreshUserNewAddressedToMessageCount, ttlSeconds);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_wrapUserRefreshWithCachedTTL(client, statName, updateMethod, ttlSeconds) {
|
||||
client.statLogRefreshCache = client.statLogRefreshCache || new Map();
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const old = client.statLogRefreshCache.get(statName) || 0;
|
||||
if (now < old + ttlSeconds) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMethod(client);
|
||||
client.statLogRefreshCache.set(statName, now);
|
||||
}
|
||||
|
||||
_refreshUserPrivateMailCount(client) {
|
||||
const MsgArea = require('./message_area');
|
||||
MsgArea.getNewMessageCountInAreaForUser(client.user.userId, Message.WellKnownAreaTags.Private, (err, count) => {
|
||||
if (!err) {
|
||||
client.user.setProperty(UserProps.NewPrivateMailCount, count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_refreshUserNewAddressedToMessageCount(client) {
|
||||
const MsgArea = require('./message_area');
|
||||
MsgArea.getNewMessageCountAddressedToUser(client, (err, count) => {
|
||||
if(!err) {
|
||||
client.user.setProperty(UserProps.NewAddressedToMessageCount, count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_findLogEntries(logTable, filter, cb) {
|
||||
filter = filter || {};
|
||||
if(!_.isString(filter.logName)) {
|
||||
|
|
|
@ -29,6 +29,11 @@ module.exports = class UserInterruptQueue
|
|||
omitNodes = [ opts.omit ];
|
||||
}
|
||||
omitNodes = omitNodes.map(n => _.isNumber(n) ? n : n.node);
|
||||
const connOpts = {
|
||||
authUsersOnly: true,
|
||||
visibleOnly: true,
|
||||
availOnly: true,
|
||||
};
|
||||
opts.clients = getActiveConnections(true).filter(ac => !omitNodes.includes(ac.node));
|
||||
}
|
||||
if(!Array.isArray(opts.clients)) {
|
||||
|
|
|
@ -65,5 +65,7 @@ module.exports = {
|
|||
AuthFactor2OTPSecret : 'auth_factor2_otp_secret', // Secret used in conjunction with OTP 2FA
|
||||
AuthFactor2OTPBackupCodes : 'auth_factor2_otp_backup', // JSON array of backup codes
|
||||
|
||||
NewPrivateMailCount : 'new_private_mail_count', // non-persistent
|
||||
NewAddressedToMessageCount : 'new_addr_to_msg_count', // non-persistent
|
||||
};
|
||||
|
||||
|
|
11
core/wfc.js
11
core/wfc.js
|
@ -1,7 +1,10 @@
|
|||
// ENiGMA½
|
||||
const { MenuModule } = require('./menu_module');
|
||||
|
||||
const { getActiveConnectionList } = require('./client_connections');
|
||||
const {
|
||||
getActiveConnectionList,
|
||||
AllConnections
|
||||
} = require('./client_connections');
|
||||
const StatLog = require('./stat_log');
|
||||
const SysProps = require('./system_property');
|
||||
const UserProps = require('./user_property');
|
||||
|
@ -36,6 +39,7 @@ const MciViewIds = {
|
|||
// Secure + 2FA + root user + 'wfc' group.
|
||||
const DefaultACS = 'SCAF2ID1GM[wfc]';
|
||||
const MainStatRefreshTimeMs = 5000; // 5s
|
||||
const MailCountTTLSeconds = 10;
|
||||
|
||||
exports.getModule = class WaitingForCallerModule extends MenuModule {
|
||||
constructor(options) {
|
||||
|
@ -217,11 +221,12 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
|
|||
lastLoginDate : moment(lastLoginStats.timestamp).format(this.getDateFormat()),
|
||||
lastLoginTime : moment(lastLoginStats.timestamp).format(this.getTimeFormat()),
|
||||
lastLogin : moment(lastLoginStats.timestamp).format(this._dateTimeFormat('lastLogin')),
|
||||
|
||||
totalMemoryBytes : sysMemStats.totalBytes || 0,
|
||||
freeMemoryBytes : sysMemStats.freeBytes || 0,
|
||||
systemAvgLoad : sysLoadStats.average || 0,
|
||||
systemCurrentLoad : sysLoadStats.current || 0,
|
||||
newPrivateMail : StatLog.getUserStatNumByClient(this.client, UserProps.NewPrivateMailCount, MailCountTTLSeconds),
|
||||
newMessagesAddrTo : StatLog.getUserStatNumByClient(this.client, UserProps.NewAddressedToMessageCount, MailCountTTLSeconds),
|
||||
};
|
||||
|
||||
return cb(null);
|
||||
|
@ -233,7 +238,7 @@ exports.getModule = class WaitingForCallerModule extends MenuModule {
|
|||
return cb(null);
|
||||
}
|
||||
|
||||
const nodeStatusItems = getActiveConnectionList({authUsersOnly: false, visibleOnly: false})
|
||||
const nodeStatusItems = getActiveConnectionList(AllConnections)
|
||||
.slice(0, nodeStatusView.dimens.height)
|
||||
.map(ac => {
|
||||
// Handle pre-authenticated
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
// ENiGMA½
|
||||
const { MenuModule } = require('./menu_module.js');
|
||||
const { getActiveConnectionList } = require('./client_connections.js');
|
||||
const {
|
||||
getActiveConnectionList,
|
||||
UserVisibleConnections } = require('./client_connections.js');
|
||||
const { Errors } = require('./enig_error.js');
|
||||
|
||||
// deps
|
||||
|
@ -43,7 +45,7 @@ exports.getModule = class WhosOnlineModule extends MenuModule {
|
|||
return cb(Errors.MissingMci(`Missing online list MCI ${MciViewIds.onlineList}`));
|
||||
}
|
||||
|
||||
const onlineList = getActiveConnectionList().slice(0, onlineListView.height).map(
|
||||
const onlineList = getActiveConnectionList(UserVisibleConnections).slice(0, onlineListView.height).map(
|
||||
oe => Object.assign(oe, { text : oe.userName, timeOn : _.upperFirst(oe.timeOn.humanize()) })
|
||||
);
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ There are many predefined MCI codes that can be used anywhere on the system (pla
|
|||
| `LD` | Date of last caller |
|
||||
| `TU` | Total number of users on the system |
|
||||
| `NT` | Total *new* users *today* |
|
||||
| `NM` | Count of new messages **address to the current user** across all message areas in which they have access |
|
||||
| `NP` | Count of new private mail to the current user |
|
||||
|
||||
Some additional special case codes also exist:
|
||||
|
||||
|
|
|
@ -79,3 +79,5 @@ The following MCI codes are available:
|
|||
* `freeMemoryBytes`: Free system memory in bytes.
|
||||
* `systemAvgLoad`: System average load.
|
||||
* `systemCurrentLoad`: System current load.
|
||||
* `newPrivateMail`: Number of new **privae** mail for current user.
|
||||
* `newMessagesAddrTo`: Number of new messages **addressed to the current user**.
|
Loading…
Reference in New Issue