Skip to content
Snippets Groups Projects
ecReader.js 11.44 KiB
// ecReader
// A lightbar, threaded message reader for Synchronet 3.16+
// echicken -at- bbs.electronicchicken.com, 2012

js.branch_limit = 0;
console.clear();
console.putmsg("\1h\1wecReader by echicken, loading message threads...");

load("sbbsdefs.js");
load("msgutils.js");
load(js.exec_dir + "msglib.js");
load("frame.js");
load("tree.js");

var showMail = true;	// Allow access to the private 'mail' sub-board
var threaded = true;	// False to default to flat view
var lbg = BG_CYAN;		// Lightbar background
var lfg = WHITE;		// Foreground colour of highlighted text
var nfg = LIGHTGRAY;	// Foreground colour of non-highlighted text
var xfg = LIGHTCYAN;	// Subtree expansion indicator colour
var tfg = LIGHTCYAN;	// Uh, that line beside subtree items
var fbg = BG_BLUE;		// Title, Header, Help frame background colour
var hfg = "\1h\1w"; 	// Heading text (CTRL-A format, for now)
var sffg = "\1h\1c";	// Heading sub-field text (CTRL-A format, for now)
var mfg = "\1n\1w";		// Message text colour (CTRL-A format, for now)

var messages;
var tree;
var currentMessage = 0;
var mail = false;

if(argc > 0 && argv[0])
	var msgBase = new MsgBase(argv[0]);
else
	var msgBase = new MsgBase(msg_area.grp_list[bbs.curgrp].sub_list[bbs.cursub].code);

var frame = new Frame(1, 1, 80, 24, BG_BLACK|WHITE);
var titleFrame = new Frame(1, 1, 80, 2, fbg|WHITE, frame);
var columnFrame = new Frame(1, 3, 80, 1, fbg|WHITE, frame);
var treeFrame = new Frame(1, 4, 80, 20, BG_BLACK|WHITE, frame);
var helpFrame = new Frame(1, 24, 80, 1, fbg|WHITE, frame);
var messageFrame = new Frame(1, 3, 80, 21, BG_BLACK|WHITE, frame);
var headerFrame = new Frame(1, 3, 80, 4, fbg|WHITE, messageFrame);
var bodyFrame = new Frame(1, 7, 80, 17, BG_BLACK|WHITE, messageFrame);
var messageBar = new Frame(1, 24, 80, 1, fbg|WHITE, messageFrame);
var promptFrame = new Frame(20, 8, 40, 6, fbg|WHITE, frame);
var promptSubFrame = new Frame(22, 9, 36, 4, BG_BLACK|WHITE, promptFrame);

frame.open();
messageFrame.bottom();
headerFrame.bottom();
promptFrame.bottom();

columnFrame.putmsg(
	format("%-9s", "Msg #")
	+ format("%-13s", "From")
	+ format("%-13s", "To")
	+ format("%-28s", "Subject")
	+ "Date"
);
helpFrame.putmsg(
	hfg + "HOME" + sffg + "/" + hfg + "END "
	+ hfg + "[" + sffg + "PgUp/PgDn" + hfg + "] "
	+ hfg + "N" + sffg + "ew Scan "
	+ hfg + "T" + sffg + "hreaded "
	+ hfg + "F" + sffg + "lat "
	+ hfg + "C" + sffg + "hange Area "
	+ hfg + "P" + sffg + "ost "
	+ hfg + "E" + sffg + "mail "
	+ hfg + "DEL" + sffg + "ete "
	+ hfg + "Q" + sffg + "uit"
);
messageBar.putmsg(
	hfg + "HOME" + sffg + "/" + hfg + "END "
	+ hfg + "[" + sffg + "PgUp/PgDn" + hfg + "] "
	+ hfg + "R" + sffg + "eply "
	+ hfg + "P" + sffg + "revious "
	+ hfg + "N" + sffg + "ext "
	+ hfg + "DEL" + sffg + "ete "
	+ hfg + "Q" + sffg + "uit"
);

function formatItem(messageNumber, from, to, subject, date) {
	var retval = 
			format("%-8s", messageNumber)
			+ format("%-13s", from.substr(0, 12))
			+ format("%-13s", to.substr(0, 12))
			+ format("%-28s", subject.substr(0, 27))
			+ strftime("%m-%d-%Y %H:%I", date);
	return retval;
}

function getFlatList(oldestFirst) {
	var header = null;
	var item;
	if(!mail)
		var mb = msgBase;
	else
		var mb = new MsgBase('mail');
	mb.open();
	for(var m = mb.first_msg; m <= mb.last_msg; m++) {
		header = mb.get_msg_header(m);
		if(
			header === null
			||
			header.attr&MSG_DELETE
			||
			(mail
				&&
				header.to != user.alias
				&&
				header.to != user.name
				&&
				header.to_ext != user.number
			)
		)
			continue;
		messages.push(header);
	}
	mb.close();
	if(oldestFirst === undefined || !oldestFirst)
		messages.reverse();
	for(var m = 0; m < messages.length; m++) {
		item = formatItem(messages[m].number, messages[m].from, messages[m].to, messages[m].subject, messages[m].when_written_time);;
		tree.addItem(item, showMessage, messages[m], mail);
	}
	tree.open();
	tree.cycle();
}

function getThreadedList() {
	if(!mail)
		var threads = getMessageThreads(msgBase.cfg.code);
	else
		var threads = getMessageThreads('mail');
	var item;
	for(var t in threads.order) {
		if(threads.thread[threads.order[t]].messages.length < 2) {
			messages.push(threads.thread[threads.order[t]].messages[0]);
			item = formatItem(
				threads.thread[threads.order[t]].messages[0].number,
				threads.thread[threads.order[t]].messages[0].from,
				threads.thread[threads.order[t]].messages[0].to,
				threads.thread[threads.order[t]].messages[0].subject,
				threads.thread[threads.order[t]].messages[0].when_written_time
			);
			tree.addItem(item, showMessage, messages[messages.length - 1], mail);
			continue;
		}
		item = formatItem(
			threads.thread[threads.order[t]].messages[0].number,
			threads.thread[threads.order[t]].messages[0].from,
			threads.thread[threads.order[t]].messages[0].to,
			threads.thread[threads.order[t]].messages[0].subject,
			threads.thread[threads.order[t]].newest
		);
		st = tree.addTree(item);
		for(var m = 0; m < threads.thread[threads.order[t]].messages.length; m++) {
			messages.push(threads.thread[threads.order[t]].messages[m]);
			item = formatItem(
				threads.thread[threads.order[t]].messages[m].number,
				threads.thread[threads.order[t]].messages[m].from,
				threads.thread[threads.order[t]].messages[m].to,
				threads.thread[threads.order[t]].messages[m].subject,
				threads.thread[threads.order[t]].messages[m].when_written_time
			);
			st.addItem(item, showMessage, messages[messages.length - 1], mail);
		}
	}
	tree.open();
	tree.cycle();
}

function getList() {
	tree = new Tree(treeFrame);
	tree.colors.lbg = lbg;
	tree.colors.lfg = lfg;
	tree.colors.fg = nfg;
	tree.colors.xfg = xfg;
	tree.colors.tfg = tfg;
	messages = [];
	if(!mail) {
		var mg = msgBase.cfg.grp_name;
		var ms = msgBase.cfg.name;
	} else {	
		var mg = "Private email";
		var ms = "Private email";
	}
	titleFrame.clear();
	titleFrame.putmsg(hfg + "Message Group: " + sffg + mg);
	titleFrame.crlf();
	titleFrame.putmsg(hfg + "    Sub Board: " + sffg + ms);
	if(threaded)
		getThreadedList();
	else
		getFlatList(false);
}

function showMessage(header) {
	currentMessage = messages.indexOf(header);
	messageFrame.top();
	if(!mail)
		mb = msgBase;
	else
		mb = new MsgBase('mail');
	mb.open();
	var body = mb.get_msg_body(header.number);
	mb.close();
	headerFrame.putmsg(
		hfg + format("%15s", "Subject: ") + sffg + header.subject.substr(0, 65)
		+ "\r\n" +
		hfg + format("%15s", "From: ") + sffg + header.from.substr(0, 65)
		+ "\r\n" +
		hfg + format("%15s", "To: ") + sffg + header.to.substr(0, 65)
		+ "\r\n" +
		hfg + format("%15s", "Date: ") + sffg + system.timestr(header.when_written_time)
	);
	bodyFrame.scrollTo(0, 0);
	bodyFrame.putmsg(mfg + word_wrap(body));
	bodyFrame.scrollTo(0, 0);
	var retval = true;
	var userInput = "";
	var h = null;
	var n = header.number;
	while(userInput != "Q") {
		if(frame.cycle())
			console.gotoxy(80, 24);
		userInput = console.getkey().toUpperCase();
		switch(userInput) {
			case "R":
				var f = new File(system.node_dir + "QUOTES.TXT");
				f.open("w");
				f.write(body);
				f.close();
				frame.invalidate();
				console.clear();
				if(!mail)
					bbs.post_msg(mb.cfg.code, WM_QUOTE, header);
				else if(mail && header.from_net_type == NET_NONE)
					bbs.email(parseInt(header.from_ext), WM_EMAIL|WM_QUOTE, "", header.subject);
				else if(mail)
					bbs.netmail(header.from_net_addr, WM_NETMAIL|WM_QUOTE, header.subject);
				frame.draw();
				retval = header;
				userInput = "Q";
				getList();
				break;
			case "P":
				if(!threaded && currentMessage < (messages.length - 1)) {
					currentMessage++;
					h = messages[currentMessage];
					retval = h;
					userInput = "Q";
				} else if(threaded && currentMessage > 0) {
					currentMessage = currentMessage - 1;
					h = messages[currentMessage];
					retval = h;
					userInput = "Q";
				}
				break;
			case "N":
				if(!threaded && currentMessage > 0) {
					currentMessage = currentMessage - 1;
					h = messages[currentMessage];
					retval = h;
					userInput = "Q";
				} else if(threaded && currentMessage < (messages.length - 1)) {
					currentMessage++;
					h = messages[currentMessage];
					retval = h;
					userInput = "Q";
				}
				break;
			case KEY_UP:
				bodyFrame.scroll(0, -1);
				break;
			case KEY_DOWN:
				bodyFrame.scroll(0, 1);
				break;
			case KEY_HOME:
				bodyFrame.scrollTo(0, 0);
				break;
			case KEY_END:
				bodyFrame.end();
				bodyFrame.scrollTo(0, bodyFrame.data_height - bodyFrame.height);
				break;
			case "[":
				if(bodyFrame.offset.y - bodyFrame.height < 0)
					bodyFrame.scrollTo(0, 0);
				else
					bodyFrame.scroll(0, -bodyFrame.height);
				break;
			case "]":
				if(bodyFrame.offset.y + bodyFrame.height > bodyFrame.data_height)
					bodyFrame.scrollTo(bodyFrame.data_height - bodyFrame.height);
				else
					bodyFrame.scroll(0, bodyFrame.height);
				break;
			case KEY_DEL:
				if(deleteMessage(header)) {
					userInput = "Q";
					retval = "REFRESH";
				} else {
					frame.cycle();
				}
				break;
			default:
				break;
		}
	}
	messageFrame.clear();
	headerFrame.clear();
	bodyFrame.clear();
	if(!retval.hasOwnProperty("number"))
		messageFrame.bottom();
	else if(mail)
		retval.mail = true;
	return retval;
}

function deleteMessage(header) {
	var ret = false;
	if(!mail && user.number == 1) {
		if(prompt("Delete message #" + header.number)) {
			msgBase.open();
			msgBase.remove_msg(header.number);
			msgBase.close();
			ret = true;
		}
	} else if(mail && prompt("Delete this message")) {
		/*	Could verify again that mail is addressed to this user, but
			they shouldn't have been able to select it otherwise. */
		var mailBase = new MsgBase("mail");
		mailBase.open();
		mailBase.remove_msg(header.number);
		mailBase.close();
		ret = true;
	}
	return ret;
}

function sendEmail() {
	var ret = true;
	console.putmsg(hfg + "Send email to: ");
	var to = console.getstr('', 64, K_LINE);
	if(to == "")
		ret = false;
	else if(system.matchuser(to) > 0)
		bbs.email(system.matchuser(to, WM_EMAIL));
	else if(netaddr_type(to) != NET_NONE)
		bbs.netmail(to, WM_NETMAIL)
	else
		ret = false;
	if(!ret) {
		console.putmsg(hfg + "Invalid user or netmail/email address.");
		console.crlf();
		console.pause();
	}
}

function prompt(str) {
	promptFrame.top();
	promptSubFrame.clear();
	frame.cycle();
	console.gotoxy(promptSubFrame.x, promptSubFrame.y + 1);
	var ret = console.yesno(str);
	promptFrame.bottom();
	frame.invalidate();
	return ret;
}

getList();
var userInput = "";
var r = "";
while(userInput != "Q") {
	switch(userInput) {
		case "T":
			tree.close();
			treeFrame.clear();
			threaded = true;
			getList();
			break;
		case "F":
			tree.close();
			treeFrame.clear();
			threaded = false;
			getList();
			break;
		case "C":
			messageAreaSelector(4, 5, 70, 16, frame);
			msgBase = new MsgBase(msg_area.grp_list[bbs.curgrp].sub_list[bbs.cursub].code);
			mail = false;
			getList();
			break;
		case "P":
			frame.invalidate();
			console.clear();
			if(!mail)
				bbs.post_msg(msgBase.cfg.code);
			else
				sendEmail();
			getList();
			frame.draw();
			break;
		case "E":
			mail = true;
			getList();
			break;
		case KEY_DEL:
			if(!(tree.currentItem instanceof Tree)) {
				if(deleteMessage(tree.currentItem.args[0])) {
					tree.currentTree.deleteItem();
					getList();
				}
			}
			break;
		default:
			if(tree.current === undefined)
				break;
			r = tree.getcmd(userInput);
			if(r == "REFRESH")
				getList();
			while(r.hasOwnProperty("number")) {
				frame.cycle();
				if(r.hasOwnProperty('mail') && r.mail)
					r = showMessage(r, true);
				else
					r = showMessage(r);
			}
			break;
	}
	userInput = console.inkey(5).toUpperCase();
	if(frame.cycle())
		console.gotoxy(80, 24);
}