const PAGE_LENGTH = 4; // The size of our page tag as stored in the msgbase for echomail/netmail const PAGE_LAST_KEY = 'last_page'; // Last page which has the latest message const MAX_PAGE_NUM = 9999; // Maximum page number. @todo Can this be changed to '9'.repeat(PAGE_LENGTH)? // Our message bases function MsgAreas() { 'use strict'; this.areas = []; this.areas_excluded = []; var zone_id; var zone_name; var ma; for(var g in msg_area.grp_list) { if (msg_area.grp_list[g].name.indexOf(':') !== -1) { zone_id = msg_area.grp_list[g].name.split(':')[0]; zone_name = msg_area.grp_list[g].name.split(':')[1]; for (var a in msg_area.grp_list[g].sub_list) { if (msg_area.grp_list[g].sub_list[a].name.indexOf(':') !== -1) { ma = new MsgArea(); ma.zone_id = zone_id; ma.zone_name = zone_name; ma.area_id = msg_area.grp_list[g].sub_list[a].name.split(':')[0]; ma.area_name = msg_area.grp_list[g].sub_list[a].name.split(':')[1]; ma.code = msg_area.grp_list[g].sub_list[a].code; this.areas.push(ma); } else { this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name); } } } else { zone_name = msg_area.grp_list[g].name; for (var a in msg_area.grp_list[g].sub_list) { this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name); } } } Object.defineProperty(this,'list',{ get: function() { writeln('Areas that we are NOT managing mail:'+this.areas_excluded.length); writeln('Areas that we ARE managing mail:'+this.areas.length); for(var x in this.areas_excluded) { writeln(x+':'+((this.areas_excluded[x].area_name === undefined) ? this.areas_excluded[x] : JSON.stringify(this.areas_excluded[x]))); } } }); } function MsgArea() { this.zone_id = undefined; this.zone_name = undefined; this.area_id = undefined; this.area_name = undefined; this.msgbase = undefined; this.headers = undefined; this.tagged_list = undefined; this.untagged_list = undefined; this.grp_number = undefined; this.subnum = undefined; /** * Build a MsgArea once we are given the code */ Object.defineProperty(this,'code',{ set: function(code) { this.msgbase = new MsgBase(code); try { if (this.msgbase.open()) { headers = this.msgbase.get_all_msg_headers(false,false) || []; // Just take the last MAX_MESSAGES this.headers = Object.keys(headers).slice(-(MAX_PAGE_NUM+1)).map(function(key) { return headers[key]; }); headers = undefined; this.msgbase.close(); } else { log(LOG_ERROR,code+' cannot be opened:'+this.msgbase.error); this.headers = []; } } catch (e) { log(LOG_ERROR,code+' cannot be opened:'+e.message); this.headers = []; } } }); // Get Area's full name Object.defineProperty(this,'full_name',{ get: function() { return this.zone_name+':'+this.area_name; } }); // Total tagged messages Object.defineProperty(this,'list_tagged',{ get: function() { if (this.tagged_list === undefined) { this.tagged_list = []; if (! this.headers) return this.tagged_list; for(var x in this.headers) { if (this.headers[x].tags && (this.headers[x].tags.length === PAGE_LENGTH)) { this.tagged_list.push(this.headers[x]); write(); // @todo This is needed for this to work? } } } return this.tagged_list; } }); // List untagged messages Object.defineProperty(this,'list_untagged',{ get: function() { if (this.untagged_list === undefined) { this.untagged_list = []; if (! this.headers) return this.untagged_list; for(var x in this.headers) { if ((! this.headers[x].tags) || (this.headers[x].tags.length !== PAGE_LENGTH)) { this.untagged_list.push(this.headers[x]); write(); // @todo This is needed for this to work? } } } return this.untagged_list; } }); // Get Next page number Object.defineProperty(this,'page_next',{ get: function() { var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini')); if (! f.open('r')) { writeln('Unable to open ini file'); exit(2); } var page = f.iniGetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY) f.close(); return page ? page : '0'.repeat(PAGE_LENGTH); }, set: function(page) { var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini')); if (! f.open('r+')) { writeln('Unable to open ini file'); exit(2); } f.iniSetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY,(''+page).padStart(4,'0')); f.close(); } }); // Our page prefix for this msg area Object.defineProperty(this,'page_prefix',{ get: function() { return ''+this.zone_id+this.area_id; }, }); } /** * Unread messages [1..] * Array key 0 returns the last read message * * @returns {*[]} */ MsgArea.prototype.newMsgs = function() { var msgs = []; var stats = this.getUserStats(); //log(LOG_DEBUG,'Users last_read pointer: '+JSON.stringify(stats.last_read)); for(var x in this.list_tagged) { // Advance past our last scan_ptr if (this.list_tagged[x].number <= stats.last_read) continue; msgs.push(this.list_tagged[x]); write(); // @todo This is needed for this to work? } return msgs; } /** * New Messages for the logged in user */ MsgArea.prototype.newMsgsToMe = function() { var msgs = []; var stats = this.getUserStats(); var last = null; //log(LOG_DEBUG,'Users scan_ptr pointer: '+JSON.stringify(stats.scan_ptr)); for(var x in this.list_tagged) { // Advance past our last scan_ptr if (this.list_tagged[x].number <= stats.scan_ptr) { if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) last = x; continue; } // Add our previous to me message if (msgs.length === 0) msgs.push(last !== null ? this.list_tagged[last] : []); if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) msgs.push(this.list_tagged[x]); write(); // @todo This is needed for this to work? } return msgs; } /** * Get a specific message with a tag */ MsgArea.prototype.getMessage = function(page) { var msg = undefined; for(var x in this.list_tagged) { if (this.list_tagged[x].tags === page) { msg = this.list_tagged[x]; break; } write(); // @todo This is needed for this to work? } if (! msg) return undefined; if (! this.msgbase.open()) { writeln(code+' cannot be opened:'+this.msgbase.error); return undefined; } msg.grp_number = this.msgbase.cfg.grp_number; var cfg = this.msgbase.cfg; msg.subnum = msg_area.grp_list[cfg.grp_number].sub_list.filter(function(x) { return x.number === cfg.number; }).pop().index; msg.content = this.msgbase.get_msg_body(false,msg.number,false,false,true,true); this.msgbase.close(); return msg; } /** * Get a message page by pointer * * @param number * @returns {string} */ MsgArea.prototype.getMessagePage = function(number) { log(LOG_DEBUG,'Get Message Page with number ['+number+']'); var r; for (var x in this.list_tagged) { if (this.list_tagged[x].number === number) { r = this.list_tagged[x]; break; } } if (! r || r.tags === undefined) return null; return '1'+this.zone_id+this.area_id+r.tags; } MsgArea.prototype.getUserStats = function() { return this.msgbase.cfg ? msg_area.grp_list[this.msgbase.cfg.grp_number].sub_list[msg_area.sub[this.msgbase.cfg.code].index] : []; } MsgArea.prototype.MessageNext = function(page) { var x = null; if (! page) return undefined; var msgid = page.substr(7,4); for(x in this.list_tagged) { if (this.list_tagged[x].tags === msgid) { break; } write(); // @todo This is needed for this to work? } //log(LOG_DEBUG,'- Next Message is:'+JSON.stringify(this.list_tagged[(parseInt(x)+1)])+', msgid:'+msgid+', page:'+page+', x:'+x); /* = Our next message is either + x+1 if x < this.list_tagged.length + x=0 if x == this.list_tagged.length (-1) + null if this.list_tagged.length == null; (thus no messages) */ return x === null ? null : this.list_tagged[(parseInt(x) === this.list_tagged.length-1) ? 0 : (parseInt(x)+1)]; } MsgArea.prototype.MessagePrev = function(page) { var prev = null; var x = null; if (! page) return undefined; var msgid = page.substr(7,4); for(x in this.list_tagged) { if (this.list_tagged[x].tags === msgid) { break; } else { prev = x; } write(); // @todo This is needed for this to work? } /* = Our previous message is either + prev if a tag was found, unless + prev is null, in which case it is this.list_tagged.length -1 + null if x is still null (thus no messages) */ // If prev is still null, then our last message must be the last one, unless x is null then there are no messages return x === null ? null : this.list_tagged[(prev === null) ? this.list_tagged.length-1 : parseInt(prev)]; } /** * Tag messages with a frame number * @note: May need to run jsexec with -m 32MB to overcome memory issues * * @returns {boolean} */ MsgArea.prototype.tag_msgs = function() { var msgs = this.list_untagged; writeln("We have "+msgs.length+" messages to tag."); // See if we need to tag something if (! msgs.length) return; if (! this.msgbase.open()) { writeln(code+' cannot be opened:'+this.msgbase.error); return false; } var page_next = this.page_next; for(var x in msgs) { msgs[x].tags = (''+(page_next)).padStart(4,'0'); if(! this.msgbase.put_msg_header(msgs[x].number,msgs[x])) { writeln('ERROR:'+this.msgbase.error); } else { page_next++; if (page_next > MAX_PAGE_NUM) page_next = 0; } } this.msgbase.close(); this.page_next = page_next; return true; } MsgArea.prototype.page = function(msgid) { return '1'+this.page_prefix+msgid; } MsgAreas.prototype.getArea = function(area) { log(LOG_DEBUG,'- AREA:'+JSON.stringify(area)); if (area === undefined) return undefined; var zone = (''+area).substr(1,4); var echo = (''+area).substr(5,2); log(LOG_DEBUG,' - zone:'+zone); log(LOG_DEBUG,' - echo:'+echo); return this.areas.filter(function(x) { return x.zone_id === zone && x.area_id === echo; })[0] } MsgAreas.prototype.getMessage = function(page) { var area = this.getArea(page); log(LOG_DEBUG,' - msg:'+JSON.stringify(page.substr(7,4))); return area ? area.getMessage(page.substr(7,4)) : undefined; } MsgAreas.prototype.getUserStats = function(page) { var area = this.getArea(page); return area ? msg_area.grp_list[area.msgbase.cfg.grp_number].sub_list[msg_area.sub[area.msgbase.cfg.code].index] : undefined; }