diff --git a/xtrn/bullshit/bullshit.ini b/xtrn/bullshit/bullshit.ini index e020d38ef2cbe928e5e857391ad990534613121a..050ed417092e08afae89245ad1f2942a0e6c2cbb 100644 --- a/xtrn/bullshit/bullshit.ini +++ b/xtrn/bullshit/bullshit.ini @@ -1,13 +1,14 @@ -; Internal code of the message area containing your bulletins -messageBase = BULLSHIT - -; See sbbsdefs.js for valid colors to specify here -titleColor = WHITE -headingColor = DARKGRAY -textColor = LIGHTGRAY -footerColor = WHITE -lightbarForeground = LIGHTCYAN -lightbarBackground = BG_CYAN -listForeground = LIGHTGRAY -; borderColor may be a comma-separated list if you want a gradient -borderColor = LIGHTBLUE,CYAN,LIGHTCYAN,LIGHTGRAY,WHITE +messageBase = BULLSHIT +maxMessages = 0 + +[colors] +title = WHITE +text = LIGHTGRAY +heading = DARKGRAY +lightbarForeground = LIGHTCYAN +lightbarBackground = BG_CYAN +listForeground = LIGHTGRAY +footer = WHITE +border = LIGHTBLUE,CYAN,LIGHTCYAN,LIGHTGRAY,WHITE + +[files] diff --git a/xtrn/bullshit/bullshit.js b/xtrn/bullshit/bullshit.js index 122297710047fe15ba0c9cfcf61142ee326db920..14ae4b72afa7a3c96c8ae61a7aa37e6da95b29b6 100644 --- a/xtrn/bullshit/bullshit.js +++ b/xtrn/bullshit/bullshit.js @@ -1,186 +1,388 @@ -load("sbbsdefs.js"); -load("frame.js"); -load("tree.js"); -load("scrollbar.js"); -load("funclib.js"); - -js.branch_limit = 0; - -var frame, - titleFrame, - listFrame, - footerFrame, - tree, - ini, - msgBase, - treeScroll; - -/* drawFrameBorder(frame, color, gradient) - Draws a border of color 'color' around Frame object 'frame' - If 'gradient' is true, 'color' must be an array of colors to draw in order - See sbbsdefs.js for valid colors */ -var drawFrameBorder = function(frame, color) { - var theColor = color; - if(color instanceof Array) - var sectionLength = Math.round(frame.width / color.length); - for(var y = 1; y <= frame.height; y++) { - for(var x = 1; x <= frame.width; x++) { - if(x > 1 && x < frame.width && y > 1 && y < frame.height) - continue; - var msg = false; - frame.gotoxy(x, y); - if(y == 1 && x == 1) - msg = ascii(218); - else if(y == 1 && x == frame.width) - msg = ascii(191); - else if(y == frame.height && x == 1) - msg = ascii(192); - else if(y == frame.height && x == frame.width) - msg = ascii(217); - else if(x == 1 || x == frame.width) - msg = ascii(179); - else - msg = ascii(196); - if(color instanceof Array) { - if(x == 1) - theColor = color[0]; - else if(x % sectionLength == 0 && x < frame.width) - theColor = color[x / sectionLength]; - else if(x == frame.width) - theColor = color[color.length - 1]; - } - frame.putmsg(msg, theColor); - } - } -} - -var showMsg = function(msg) { - try { - var readerFrame = new Frame(2, 5, console.screen_columns - 2, console.screen_rows - 8, BG_BLACK|WHITE, frame); - readerFrame.open(); - msgBase.open(); - var h = msgBase.get_msg_header(msg); - var b = msgBase.get_msg_body(msg); - msgBase.close(); - readerFrame.putmsg( - format( - "%-52s%s\r\n\r\n", - h.subject.substr(0, console.screen_columns - 30), - system.timestr(h.when_written_time) - ), - getColor(ini.titleColor) - ); - readerFrame.putmsg(word_wrap(b, console.screen_columns - 2), getColor(ini.textColor)); - readerFrame.scrollTo(0, 0); - var scroller = new ScrollBar(readerFrame, { autohide : true }); - scroller.open(); - scroller.cycle(); - } catch(err) { - log(LOG_ERR, err); - return false; - } - while(!js.terminated) { - if(frame.cycle()) { - scroller.cycle(); - console.gotoxy(console.screen_columns, console.screen_rows); - } - var userInput = console.inkey(K_NONE, 5); - if(userInput.toUpperCase() == "Q" || ascii(userInput) == 27) - break; - else if(userInput == KEY_UP && readerFrame.data_height > readerFrame.height) - readerFrame.scroll(0, -1); - else if(userInput == KEY_DOWN && readerFrame.data_height > readerFrame.height) - readerFrame.scroll(0, 1); - } - readerFrame.close(); - readerFrame.delete(); - return true; -} - -var init = function() { - try { - var f = new File(js.exec_dir + "bullshit.ini"); - f.open("r"); - ini = f.iniGetObject(); - f.close(); - ini.borderColor = ini.borderColor.toUpperCase().split(","); - if(ini.borderColor.length > 1) { - for(var b = 0; b < ini.borderColor.length; b++) - ini.borderColor[b] = getColor(ini.borderColor[b]); - } else { - ini.borderColor = getColor(ini.borderColor[0]); - } - ini.lightbarForeground = getColor(ini.lightbarForeground); - ini.lightbarBackground = getColor(ini.lightbarBackground); - - frame = new Frame(1, 1, console.screen_columns, console.screen_rows, BG_BLACK|WHITE); - titleFrame = new Frame(1, 1, console.screen_columns, 3, BG_BLACK|WHITE, frame); - listFrame = new Frame(1, 4, console.screen_columns, console.screen_rows - 6, BG_BLACK|WHITE, frame); - listTreeFrame = new Frame(2, 6, console.screen_columns - 2, console.screen_rows - 9, BG_BLACK|WHITE, listFrame); - footerFrame = new Frame(1, console.screen_rows - 2, console.screen_columns, 3, BG_BLACK|WHITE, frame); - drawFrameBorder(titleFrame, ini.borderColor); - drawFrameBorder(listFrame, ini.borderColor); - drawFrameBorder(footerFrame, ini.borderColor); - tree = new Tree(listTreeFrame); - tree.colors.lfg = ini.lightbarForeground; - tree.colors.lbg = ini.lightbarBackground; - - var titleLen = console.screen_columns - 29; - msgBase = new MsgBase(ini.messageBase); - msgBase.open(); - for(var m = msgBase.last_msg; m >= msgBase.first_msg; m = m - 1) { - try { - var h = msgBase.get_msg_header(m); - if(h === null) - throw "Header is null"; - tree.addItem( - format( - "%-" + titleLen + "s%s", - h.subject.substr(0, console.screen_columns - 29), - system.timestr(h.when_written_time) - ), - showMsg, - m - ); - } catch(err) { - continue; - } - } - msgBase.close(); - - titleFrame.gotoxy(3, 2); - titleFrame.putmsg("Bulletins", getColor(ini.titleColor)); - titleFrame.gotoxy(console.screen_columns - 25, 2); - titleFrame.putmsg("bullshit v2 by echicken", DARKGRAY); - listFrame.gotoxy(3, 2); - listFrame.putmsg(format("%-" + titleLen + "s%s", "Title", "Date"), getColor(ini.headingColor)); - footerFrame.gotoxy(3, 2); - footerFrame.putmsg("[ESC] or Q to quit, Up/Down arrows to scroll", getColor(ini.footerColor)); - treeScroll = new ScrollBar(tree); - frame.open(); - tree.open(); - } catch(err) { - log(LOG_ERR, err); - return false; - } - return true; -} - -var main = function() { - while(!js.terminated) { - var userInput = console.inkey(K_NONE, 5); - if(userInput.toUpperCase() == "Q" || ascii(userInput) == 27) - break; - tree.getcmd(userInput); - if(frame.cycle()) { - treeScroll.cycle(); - console.gotoxy(console.screen_columns, console.screen_rows); - } - } -} - -if(init()) { - main(); - frame.close(); -} -exit(); +load('sbbsdefs.js'); +load('frame.js'); +load('tree.js'); +load('scrollbar.js'); +load('funclib.js'); + +js.branch_limit = 0; +js.time_limit = 0; + +var settings, frame, tree, treeScroll, viewer; + +Frame.prototype.drawBorder = function(color, title) { + this.pushxy(); + var theColor = color; + if (Array.isArray(color)) { + var sectionLength = Math.round(this.width / color.length); + } + for (var y = 1; y <= this.height; y++) { + for (var x = 1; x <= this.width; x++) { + if (x > 1 && x < this.width && y > 1 && y < this.height) continue; + var msg; + this.gotoxy(x, y); + if (y === 1 && x === 1) { + msg = ascii(218); + } else if (y === 1 && x === this.width) { + msg = ascii(191); + } else if (y === this.height && x === 1) { + msg = ascii(192); + } else if (y === this.height && x === this.width) { + msg = ascii(217); + } else if (x === 1 || x === this.width) { + msg = ascii(179); + } else { + msg = ascii(196); + } + if (Array.isArray(color)) { + if (x === 1) { + theColor = color[0]; + } else if (x % sectionLength === 0 && x < this.width) { + theColor = color[x / sectionLength]; + } else if (x === this.width) { + theColor = color[color.length - 1]; + } + } + this.putmsg(msg, theColor); + } + } + if (typeof title === 'object') { + this.gotoxy(title.x, title.y); + this.attr = title.attr; + this.putmsg(ascii(180) + title.text + ascii(195)); + } + this.popxy(); +} + +var Viewer = function (item) { + + var frames = { + top : null, + title : null, + content : null + }; + + frames.top = new Frame( + frame.x, + frame.y + 3, + frame.width, + frame.height - 6, + WHITE, + frame + ); + frames.top.drawBorder(settings.colors.border); + + frames.title = new Frame( + frames.top.x + 1, + frames.top.y + 1, + frames.top.width - 2, + 1, + settings.colors.title, + frames.top + ); + + frames.content = new Frame( + frames.top.x + 1, + frames.title.y + frames.title.height + 1, + frames.top.width - 2, + frames.top.height - frames.title.height - 3, + settings.colors.text, + frames.top + ); + + if (typeof item === 'number') { + + var msgBase = new MsgBase(settings.messageBase); + msgBase.open(); + var header = msgBase.get_msg_header(item); + var body = msgBase.get_msg_body(item); + msgBase.close(); + + frames.title.putmsg( + format( + '%-' + (frame.width - 29) + 's%s', + header.subject.substr(0, frame.width - 30), + system.timestr(header.when_written_time) + ), + settings.colors.title + ); + frames.content.putmsg(word_wrap(body, frames.content.width)); + + } else { + + frames.title.putmsg( + format( + '%-' + (frame.width - 29) + 's%s', + item.substr(0, frame.width - 30), + system.timestr(file_date(settings.files[item])) + ), + settings.colors.title + ); + + frames.content.x = frames.top.x; + frames.content.width = frames.top.width; + + try { + frames.content.load(settings.files[item]); + } catch (err) { + log(LOG_ERR, err); + } + + frames.content.width = frames.top.width - 2; + frames.content.x = frames.top.x + 1; + + } + + frames.content.scrollTo(0, 0); + frames.content.h_scroll = true; + + var scrollbar = new ScrollBar(frames.content); + frames.top.open(); + + this.getcmd = function (cmd) { + + var ret = true; + switch (cmd.toUpperCase()) { + case '\x1B': + case 'Q': + ret = false; + frames.top.close(); + break; + case KEY_UP: + if (frames.content.data_height > frames.content.height && + frames.content.offset.y >= 1 + ) { + frames.content.scroll(0, -1); + } + break; + case KEY_DOWN: + if (frames.content.data_height > frames.content.height && + frames.content.data_height - frames.content.offset.y > + frames.content.height + ) { + frames.content.scroll(0, 1); + } + break; + case KEY_LEFT: + if (frames.content.data_width > frames.content.width && + frames.content.offset.x >= 1 + ) { + frames.content.scroll(-1, 0); + } + break; + case KEY_RIGHT: + if (frames.content.data_width > frames.content.width && + frames.content.data_width - frames.content.offset.x > + frames.content.width + ) { + frames.content.scroll(1, 0); + } + break; + default: + break; + } + + return ret; + + } + + this.cycle = function () { + scrollbar.cycle(); + } + +} + +function loadSettings() { + + var f = new File(js.exec_dir + 'bullshit.ini'); + if (!f.open('r')) throw 'Failed to open bullshit.ini.'; + var settings = f.iniGetObject(); + settings.colors = f.iniGetObject('colors'); + settings.files = f.iniGetObject('files'); + f.close(); + + Object.keys(settings.colors).forEach( + function (k) { + settings.colors[k] = settings.colors[k].toUpperCase().split(','); + if (settings.colors[k].length > 1) { + settings.colors[k].forEach( + function (e, i, a) { a[i] = getColor(e); } + ); + } else { + settings.colors[k] = getColor(settings.colors[k][0]); + } + } + ); + + return settings; + +} + +function loadList() { + + Object.keys(settings.files).forEach( + function (key) { + if (!file_exists(settings.files[key])) return; + tree.addItem( + format ( + '%-' + (frame.width - 29) + 's%s', + key, system.timestr(file_date(settings.files[key])) + ), + key + ); + } + ); + + var msgBase = new MsgBase(settings.messageBase); + msgBase.open(); + var shown = 0; + for (var m = msgBase.last_msg; m >= msgBase.first_msg; m = m - 1) { + try { + var h = msgBase.get_msg_header(m); + } catch (err) { + continue; + } + if (h === null) continue; + tree.addItem( + format( + '%-' + (frame.width - 29) + 's%s', + h.subject.substr(0, frame.width - 30), + system.timestr(h.when_written_time) + ), + m + ); + shown++; + if (settings.maxMessages > 0 && shown >= settings.maxMessages) break; + } + msgBase.close(); + + tree.open(); + +} + +function initDisplay() { + + frame = new Frame( + 1, + 1, + console.screen_columns, + console.screen_rows, + WHITE + ); + + var titleFrame = new Frame( + frame.x, + frame.y, + frame.width, + 3, + WHITE, + frame + ); + + var footerFrame = new Frame( + frame.x, + frame.y + frame.height - 3, + frame.width, + 3, + WHITE, + frame + ); + + var treeFrame = new Frame( + frame.x, + titleFrame.y + titleFrame.height, + frame.width, + frame.height - titleFrame.height - footerFrame.height, + WHITE, + frame + ); + + var treeSubFrame = new Frame( + treeFrame.x + 1, + treeFrame.y + 2, + treeFrame.width - 2, + treeFrame.height - 3, + WHITE, + treeFrame + ); + + titleFrame.drawBorder(settings.colors.border); + treeFrame.drawBorder(settings.colors.border); + footerFrame.drawBorder(settings.colors.border); + + titleFrame.gotoxy(3, 2); + titleFrame.putmsg('Bulletins', settings.colors.title); + titleFrame.gotoxy(frame.width - 25, 2); + titleFrame.putmsg('bullshit v3 by echicken', settings.colors.heading); + + treeFrame.gotoxy(3, 2); + treeFrame.putmsg('Title', settings.colors.heading); + treeFrame.gotoxy(treeFrame.x + treeFrame.width - 27, 2); + treeFrame.putmsg('Date', settings.colors.heading); + + footerFrame.gotoxy(3, 2); + footerFrame.putmsg( + 'Q to quit, Up/Down arrows to scroll', settings.colors.footer + ); + + tree = new Tree(treeSubFrame); + tree.colors.lfg = settings.colors.lightbarForeground; + tree.colors.lbg = settings.colors.lightbarBackground; + tree.colors.fg = settings.colors.listForeground; + + treeScroll = new ScrollBar(tree); + + frame.open(); + +} + +function init() { + settings = loadSettings(); + initDisplay(); + loadList(); +} + +function main() { + + while (!js.terminated) { + + var userInput = console.inkey(K_NONE, 5); + + if (typeof viewer !== 'undefined') { + + var ret = viewer.getcmd(userInput); + viewer.cycle(); + if (!ret) viewer = undefined; + + } else { + + if (userInput.toUpperCase() === 'Q' || userInput == '\x1B') { + break; + } else { + var ret = tree.getcmd(userInput); + treeScroll.cycle(); + if (typeof ret === 'number' || typeof ret === 'string') { + viewer = new Viewer(ret); + } + } + + } + + if (frame.cycle()) { + console.gotoxy(console.screen_columns, console.screen_rows); + } + + } + +} + +function cleanUp() { + frame.close(); +} + +try { + init(); + main(); + cleanUp(); +} catch (err) { + log(LOG_ERR, err); +} \ No newline at end of file diff --git a/xtrn/bullshit/readme.txt b/xtrn/bullshit/readme.txt index 1ccc060653b0140039a33a9966b30dc584f8382e..82755507f8bb1af95b94d1fc5830751218ef560c 100644 --- a/xtrn/bullshit/readme.txt +++ b/xtrn/bullshit/readme.txt @@ -1,53 +1,152 @@ -Bullshit 2.0 --=-=-=-=-=-= +Bullshit 3.0 by echicken -at- bbs.electronicchicken.com -Bullshit is a bulletin listing/reading module for Synchronet BBS 3.16+. It -uses a sysops-only message base on your BBS as a storage back-end. Adding -new bulletins to your system becomes as easy as posting a message. +Contents + 1) About + 2) Setup + 3) Customization + 4) Bullshit for the web + 5) Support -Installation: --=-=-=-=-=-=- -Launch SCFG (BBS->Configure in the Synchronet Control Panel on Windows.) +1) About -In 'Message Areas', select your local message group, and create a new sub with -the following details: + Bullshit is a lightbar bulletin lister/reader for Synchronet BBS 3.16+. + A message sub-board is used as a storage back-end, so that you can add a + new bulletin to the list just by posting a message. You can also include + .ANS or .TXT files - such as door game score files - as pinned items at the + top of the list. -Long Name Bulletins -Short Name Bulletins -QWK Name BULLSHIT -Internal Code BULLSHIT -Access Requirements LEVEL 90 -Reading Requirements LEVEL 90 -Posting Requirements LEVEL 90 -In the "Toggle Options" for the sub, ensure that "Default on for new scan", -"Forced on for new scan", and "Default on for your scan" are set to 'No'. +2) Setup -(You can set the Access, Reading, and Posting requirements to any ARS value -that you want. Ideally, only the Sysop should be able to see or post to this -sub, and Bullshit will be the interface by which users view messages here.) + 2.1) Create a message area -Return to the main menu of SCFG, go to 'External Programs', then to -'Online Programs (Doors)'. Select whatever externals section you want to -place Bullshit in ('Main' might be a good choice) and then add a new item -with the following details: + Launch SCFG (BBS->Configure in the Synchronet Control Panel on Windows.) -Name Bullshit -Internal Code BULLSHIT -Start-up Directory /sbbs/xtrn/bullshit -Command Line ?bullshit.js -Multiple Concurrent Users Yes + In 'Message Areas', select your local message group, and create a new + sub with the following details: -If you want Bullshit to run during your logon process, set the following: + Long Name Bulletins + Short Name Bulletins + QWK Name BULLSHIT + Internal Code BULLSHIT + Access Requirements LEVEL 90 + Reading Requirements LEVEL 90 + Posting Requirements LEVEL 90 -Execute on Event logon + Toggle Options... -Next you can edit 'bullshit.ini' to your liking. It contains some options for -colors and such. + Default on for new scan No + Forced on for new scan No + Default on for your scan No -Now go ahead and start posting messages in the Bulletins sub-board that you -created. The subject line of each message will appear as the bulletin title, -and the message body will be the text of the bulletin. \ No newline at end of file + Note: You can name this message area whatever you want, and you can use + an existing message area if you wish. Ideally only the sysop will be + able to read or post to this area, and it won't be included in the new + message scan. + + + 2.2) Create an External Program entry + + Still in SCFG, return to the main menu, select 'External Programs', + then 'Online Programs (Doors)', choose the section you wish to add + Bullshit to, then create a new entry with the following details: + + Name Bullshit + Internal Code BULLSHIT + Start-up Directory /sbbs/xtrn/bullshit + Command Line ?bullshit.js + Multiple Concurrent Users Yes + + If you want Bullshit to run during your logon process, set the + following: + + Execute on Event logon + + All other options can be left at their default settings. + + +3) Customization + + Some customization settings are available in 'bullshit.ini': + + In the root section: + + - The 'messageBase' setting specifies the internal code of the + message sub-board that Bullshit should load. + - The 'maxMessages' setting specifies how many of the most recent + messages in this area should be listed. (Use 0 for no limit.) + + + In the 'colors' section: + + - 'title' and 'text' are the colors used when viewing an item + - 'heading' controls color or the 'Title' and 'Date' column headings + - 'lightbarForeground' and 'lightbarBackground' control the color of + a highlighted item in the list + - 'listForeground' controls the color of a non-highlighted item + - 'footer' controls the color of the 'Q to quit ...' text + - 'border' can be a single color, or a comma-separated list + + The 'files' section is empty by default. Here you can add any number of + entries for files that you wish to include in the list. The key will be + used as the item's title in the list, while the value must be the path to + the file, for example: + + LORD Scores = /sbbs/xtrn/lord/score.ans + + At the moment, only files with .ANS and .TXT extensions can be loaded. If + that's a huge pain for you, let me know and I'll maybe do something about + it at some point. + + +4) Bullshit for the web + + The included '999-bullshit.xjs' file is compatible with my "new" web UI for + Synchronet (https://github.com/echicken/synchronet-web-v4). You can add it + to your site by copying it to the 'pages' subdirectory, renaming it as + needed. You could also just copy the contents of this file into another of + your pages if you wish for bulletins to show up there (000-home.xjs, for + example.) + + This will probably work with ecweb v3 as well, but I haven't bothered to + test it. + + For cosmetic and snobbish design reasons, your pinned 'files' list will not + be displayed in the web UI. You're better off creating a 'Scores' page of + some kind for this purpose. + + +5) Support + + You can contact me for support via any of the following means, in the + following order of preference: + + DOVE-Net: + + Post a message to 'echicken' in the 'Synchronet Sysops' sub-board. + Unless I'm dead or on vacation, I'll probably get back to you within a + day or so. + + + electronic chicken bbs: + + Post a message in the 'Support' sub-board of the 'Local' message group + on my BBS, bbs.electronicchicken.com + + + IRC : #synchronet on irc.synchro.net: + + I'm not always in front of a computer, so you won't always receive an + immediate response if you contact me on IRC. That said, if you stay + online and idle, I'll probably see your message and respond eventually. + + + Email: + + You can email me at echicken -at- bbs.electronicchicken.com, however I + prefer to discuss problems & provide support in a public forum in case + any information that comes up can be of benefit to somebody else in the + future.