Skip to content
Snippets Groups Projects
Commit 028f4dac authored by echicken's avatar echicken
Browse files

Sufficiently revised to be called version 3.

Now you can include your door game score files at the top of the
list, or whatever thrilling .ans or .txt files you wish to showcase.

Improved scrolling. Added horizontal scrolling for score files that
may be slightly wider than the 'window'.

There's also some web junk that I added earlier for, uh ... Drakahn99,
who requested it.

If you're already using a previous version, the config file has been
changed and you'll need to change with it.
parent 43edb7e8
Branches
Tags
No related merge requests found
; 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]
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
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.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment