Skip to content
Snippets Groups Projects
Commit c1ef853a authored by Craig Hendricks's avatar Craig Hendricks
Browse files

MCR Change List on 3/3/2025:

 Enhancements:
 
 - Enhanced mention handling: user can use the /mentions command or
   press the UP hotkey to reset the mention counter to zero (0) and 
   also review all previous mentions for the current session.

 - User can press the PGUP hotkey as a shortcut key for /scroll
   
 - Seconds are included in the timestamp in widescreen (132 column) mode.

 - Passwords get masked when using trust commands (identify, register, 
   roompass, update password, & roomconfig password).

 
 Fixes:
 
 - Added an IAMHERE immediately after connecting before joining a room. 
   This fixes auto-joining the lobby (or the configured room) on startup.
   In addition, mrc-session now also passes user's terminal size, security 
   level, IP address, and sysop's name to the MRC server on startup. 

 - The to_site property in messages from the server no longer needs to be 
   considered when allowing an incoming message from mrc-connector to 
   mrc-client. This resolves the issue of messages from ddial users not 
   appearing in mrc-client, as well as other types of messagea 
   (e.g.: !weather <city name> $).
     
 - Fixed user list (nick list) refresh when someone joins a room or their
   client session times out.
   
 - Stats on the status line no longer wait for user input to get updated.
   
 - Commands not strictly defined in mrc-session are no longer stopped from 
   being sent out to the server. This means server commands are no longer 
   required to be issued with /quote (but it will still work for things like 
   /quote help for the server command list).
   
 - The HOME, END, INSERT, and PAGEDN keys no longer send an empty message
   if accidentally pressed.
 
 - mrc-connector.js now announces a user's departure immediately before
   sending the LOGOFF command.
   
 - Fixed an issue in mrc-client.js where the new_alias function saved
   the new default alias string, but didn't actually use the new alias
   string for the current session.
      
 
 New Features:
 
 - Basic twit filter implemented. Hides messages from problematic chatters.
   Usage:
   -  /twit add nameoftwit    Filters out nameoftwit's messages.
   -  /twit del nameoftwit    nameoftwit's messages are re-allowed.
   -  /twit list              Lists twits the user has added.
   -  /twit clear             Empty's the user's twit list.
   Twit names are saved per-user to mrc-client.ini (separated by ~ character)
   and remembered in future sessions.

 - User can press the LEFT and RIGHT arrow hotkeys to change their outgoing 
   message color. The selected color is indicated by the > symbol to the left 
   of the inputline, and gets saved and reused in future sessions.

 - Added "commands" option under [startup] in mrc-client.ini. This is a 
   comma-separated list of commands defined by the sysop to execute when a 
   user enters MRC. For example, including "chatters" will automatically
   display chatters in the current room on startup after displaying the motd 
   screen and joining the room (requested by Amessyroom of Too Lazy BBS).

 - Local system messages for the user get displayed in the chat window (e.g.:
   email notifications, new echomail posts & replies, etc.). This way the user
   doesn't need to wait until leaving MRC to be notified of such things.

 - Added /r as a shortcut to reply to the last private message.

 - User can use the CTRL+D shortcut to clear the inputline, rather than 
   having to repeatedly press backspace.
 
 - Optional "MRC for Synchronet" splash on startup. Sysop can customize it 
   by editing the mrc-splash.msg file, or just disable it entirely by setting 
   splash = false in mrc-client.ini.

   
 Housekeeping:

 - Massive reorganization of the /help information. All help info on local 
   commands is now contained in external msg files. Redundant and obsolete 
   commands have been removed, and text has been added to bring awareness
   to additional server command availability by typing /quote help
   
 - "banners" is not a command that the server expects to receive from clients
   in the USER context, so the banners definition in mrc-session.js, the 
   associated function call in mrc-client.js, and the associated setting 
   in mrc-session.ini have been removed. 
   
 - Updated readme.txt file, including info on the toggle_nicks setting that
   was missed in the previous update.


Known issue:

 - Strings starting with semicolons do not go out, because they are recognized
   by InputLine.js as sysop command prefixes. Maybe the "submit" command can
   be overridden to bypass this.
         

 Acknowledgements:
 
  Big thanks to StingRay, cr1mson, paulie420, and MeaTLoTioN for testing
  and providing helpful feedback, and as always to StackFault for insight.
parent 340dea55
No related branches found
No related tags found
No related merge requests found
Pipeline #8634 failed
...@@ -5,10 +5,15 @@ ping_interval = 60 ...@@ -5,10 +5,15 @@ ping_interval = 60
[startup] [startup]
room = lobby room = lobby
motd = true motd = true
banners = true commands =
splash = true
[aliases] [aliases]
[client] [theme]
;change this to true if you want show the nick list on connect.
show_nicks=false [msg_color]
[show_nicks]
[twit_list]
...@@ -5,6 +5,36 @@ ...@@ -5,6 +5,36 @@
* echicken -at- bbs.electronicchicken.com * echicken -at- bbs.electronicchicken.com
* *
* I started out with good intentions. At least it works. * I started out with good intentions. At least it works.
*
* -------------------------------------------------------------
* Codefenix's comments:
* codefenix -at- conchaos.synchro.net
*
* Sincere thanks and kudos to echicken for laying down the
* foundation with a great initial working build. I've used it
* for a long time to join weekly MRC meetups, and it has always
* worked great.
*
* In 2023 I took it upon myself to start studying this code,
* and attempted make some basic cosmetic changes I felt were
* needed, like timestamped messages and a togglable nick list.
*
* In 2024, nelgin implemented SSL (BIG thanks for that!). Later
* that same year I decided to get back into it add basic support
* for mentions, indented line-wrapping, and basic customizable
* themes.
*
* 2025 brings the most changes yet: twit filter, browseable
* mentions, and numerous fixes previously missed.
*
* I'm a big believer in MRC, and I've always tried to be
* respectful of the original design of this code and not make
* too many jarring changes. Please excuse some of the coding
* decisions I made that may appear less than elegant. I do hope
* to optimize and improve the features implemented. Thanks to
* everyone who has provided feedback and words of encouragement.
*
*
*/ */
load('sbbsdefs.js'); load('sbbsdefs.js');
...@@ -21,8 +51,11 @@ var input_state = 'chat'; ...@@ -21,8 +51,11 @@ var input_state = 'chat';
var show_nicks = false; var show_nicks = false;
var stat_mode = 0; // 0 = Local Session stats (Chatters, Latency, & Mentions); 1 = Global MRC Stats var stat_mode = 0; // 0 = Local Session stats (Chatters, Latency, & Mentions); 1 = Global MRC Stats
var mentions = [];
var mention_index = 0;
const MENTION_MARKER = ascii(15); const MENTION_MARKER = ascii(15);
const INDENT_MARKER = ascii(28); const INDENT_MARKER = ascii(28);
const NOTIF_FILE = system.data_dir + "msgs/" + format("%04d.msg", user.number);
// default theme // default theme
var theme_bg_color = BG_BLUE; var theme_bg_color = BG_BLUE;
...@@ -38,13 +71,14 @@ const settings = { ...@@ -38,13 +71,14 @@ const settings = {
aliases: f.iniGetObject('aliases') || {}, aliases: f.iniGetObject('aliases') || {},
client: f.iniGetObject('client') || {}, client: f.iniGetObject('client') || {},
show_nicks: f.iniGetObject('show_nicks') || {}, show_nicks: f.iniGetObject('show_nicks') || {},
theme: f.iniGetObject('theme') || {} theme: f.iniGetObject('theme') || {},
msg_color: f.iniGetObject('msg_color') || {},
twit_list: f.iniGetObject('twit_list') || {}
}; };
f.close(); f.close();
f = undefined; f = undefined;
const NICK_COLOURS = [ const NICK_COLOURS = [
'\x01h\x01r', '\x01h\x01r',
'\x01h\x01g', '\x01h\x01g',
...@@ -71,34 +105,6 @@ const ACT_LVL = [ ...@@ -71,34 +105,6 @@ const ACT_LVL = [
/* 3: HI */ "\x01H\x01RHI" /* 3: HI */ "\x01H\x01RHI"
]; ];
function pipe_to_ctrl_a(str) { // could use the pipeToCtrlA function in funclib.js instead
str = str.replace(/\|00/g, "\x01N\x01K");
str = str.replace(/\|01/g, "\x01N\x01B");
str = str.replace(/\|02/g, "\x01N\x01G");
str = str.replace(/\|03/g, "\x01N\x01C");
str = str.replace(/\|04/g, "\x01N\x01R");
str = str.replace(/\|05/g, "\x01N\x01M");
str = str.replace(/\|06/g, "\x01N\x01Y");
str = str.replace(/\|07/g, "\x01N\x01W");
str = str.replace(/\|08/g, "\x01H\x01K");
str = str.replace(/\|09/g, "\x01H\x01B");
str = str.replace(/\|10/g, "\x01H\x01G");
str = str.replace(/\|11/g, "\x01H\x01C");
str = str.replace(/\|12/g, "\x01H\x01R");
str = str.replace(/\|13/g, "\x01H\x01M");
str = str.replace(/\|14/g, "\x01H\x01Y");
str = str.replace(/\|15/g, "\x01H\x01W");
str = str.replace(/\|16/g, "\x01" + 0);
str = str.replace(/\|17/g, "\x01" + 4);
str = str.replace(/\|18/g, "\x01" + 2);
str = str.replace(/\|19/g, "\x01" + 6);
str = str.replace(/\|20/g, "\x01" + 1);
str = str.replace(/\|21/g, "\x01" + 5);
str = str.replace(/\|22/g, "\x01" + 3);
str = str.replace(/\|23/g, "\x01" + 7);
return str;
}
function resize_nicklist(frames, nicks) { function resize_nicklist(frames, nicks) {
const maxlen = show_nicks ? Math.max(1, nicks.reduce(function (a, c) { const maxlen = show_nicks ? Math.max(1, nicks.reduce(function (a, c) {
return c.length > a ? c.length : a; return c.length > a ? c.length : a;
...@@ -121,7 +127,7 @@ function redraw_nicklist(frames, nicks, colours) { ...@@ -121,7 +127,7 @@ function redraw_nicklist(frames, nicks, colours) {
} }
} }
function init_display() { function init_display(msg_color) {
const w = console.screen_columns; const w = console.screen_columns;
const h = console.screen_rows; const h = console.screen_rows;
const f = { top: new Frame(1, 1, w, h, BG_BLACK|LIGHTGRAY) }; const f = { top: new Frame(1, 1, w, h, BG_BLACK|LIGHTGRAY) };
...@@ -130,10 +136,12 @@ function init_display() { ...@@ -130,10 +136,12 @@ function init_display() {
f.divider = new Frame(1, h - 1, w, 1, theme_bg_color|LIGHTGRAY, f.top); f.divider = new Frame(1, h - 1, w, 1, theme_bg_color|LIGHTGRAY, f.top);
f.nicklist = new Frame(w - 2, 2, 2, h - 3, BG_BLACK|LIGHTGRAY, f.top); f.nicklist = new Frame(w - 2, 2, 2, h - 3, BG_BLACK|LIGHTGRAY, f.top);
f.nicks = new Frame(w - 1, 2, 1, h - 3, BG_BLACK|LIGHTGRAY, f.nicklist); f.nicks = new Frame(w - 1, 2, 1, h - 3, BG_BLACK|LIGHTGRAY, f.nicklist);
f.input = new Frame(1, h, w, 1, BG_BLACK|WHITE, f.top); f.input_color = new Frame(1, h, 1, 1, BG_BLACK|LIGHTGRAY, f.top);
f.input = new Frame(2, h, w-2, 1, BG_BLACK|WHITE, f.top); // TODO: Test
f.output_scroll = new ScrollBar(f.output, { autohide: true }); f.output_scroll = new ScrollBar(f.output, { autohide: true });
f.nick_scroll = new ScrollBar(f.nicks, { autohide: true }); f.nick_scroll = new ScrollBar(f.nicks, { autohide: true });
f.output.word_wrap = true; f.output.word_wrap = true;
f.input_color.putmsg( pipeToCtrlA( format( "|%02d>", msg_color) ) );
f.divider.gotoxy(f.divider.width - 5, 1); f.divider.gotoxy(f.divider.width - 5, 1);
f.divider.putmsg(theme_fg_color + '/help'); f.divider.putmsg(theme_fg_color + '/help');
f.top.open(); f.top.open();
...@@ -179,8 +187,8 @@ function append_message(frames, msg, mention) { ...@@ -179,8 +187,8 @@ function append_message(frames, msg, mention) {
} }
// programmatically insert indent(s), if the message length exceeds the frame width. // programmatically insert indent(s), if the message length exceeds the frame width.
if (strlen(msg) >= frames.output.width - 8) { if (strlen(msg) >= frames.output.width - (console.screen_columns < 132 ? 8 : 11)) {
var indent = "\r\n" + (new Array(7).join( " " )) + INDENT_MARKER + " "; var indent = "\r\n" + (new Array( console.screen_columns < 132 ? 7 : 10 ).join( " " )) + INDENT_MARKER + " ";
msg = lfexpand(word_wrap(msg, frames.output.width - 9)).replace(/\r\n$/g, "").replace(/\r\n/g, indent).trim(); msg = lfexpand(word_wrap(msg, frames.output.width - 9)).replace(/\r\n$/g, "").replace(/\r\n/g, indent).trim();
} }
...@@ -190,20 +198,29 @@ function append_message(frames, msg, mention) { ...@@ -190,20 +198,29 @@ function append_message(frames, msg, mention) {
"\x01n" + msg + '\r\n' // message itself "\x01n" + msg + '\r\n' // message itself
); );
frames.output.scroll(0, -1); frames.output.scroll(0, -1);
if (input_state == 'scroll') frames.output.scrollTo(0, top);
if (mention) {
mentions.push(top+1);
}
if (input_state == 'scroll' || input_state == 'mentions') frames.output.scrollTo(0, top);
} }
function getShortTime(d) { function getShortTime(d) {
if (console.screen_columns < 132 ) {
return format( "%02d:%02d", d.getHours(), d.getMinutes() ); return format( "%02d:%02d", d.getHours(), d.getMinutes() );
} else {
return format( "%02d:%02d:%02d", d.getHours(), d.getMinutes(), d.getSeconds() );
}
} }
function display_message(frames, msg, mention) { //, colour param was listed but not used.. function display_message(frames, msg, mention) {
const body = pipe_to_ctrl_a(truncsp(msg.body) || '').split(' '); const body = pipeToCtrlA(truncsp(msg.body) || '').split(' ');
append_message(frames, body[0] + '\x01n\x01w ' + body.slice(1).join(' ') + '\x01n\x01w', mention); append_message(frames, body[0] + '\x01n\x01w ' + body.slice(1).join(' ') + '\x01n\x01w', mention);
} }
function display_server_message(frames, msg) { function display_server_message(frames, msg) {
append_message(frames, '\x01h\x01w' + pipe_to_ctrl_a(truncsp(msg) || '')); append_message(frames, '\x01h\x01w' + pipeToCtrlA(truncsp(msg) || ''));
} }
function display_title(frames, room, title) { function display_title(frames, room, title) {
...@@ -225,6 +242,7 @@ function new_alias() { ...@@ -225,6 +242,7 @@ function new_alias() {
) + suffix; ) + suffix;
set_alias(alias); set_alias(alias);
settings.aliases[user.alias] = alias; settings.aliases[user.alias] = alias;
return alias;
} }
function list_themes() { function list_themes() {
...@@ -257,6 +275,92 @@ function save_setting (alias, setting_name, setting_value) { ...@@ -257,6 +275,92 @@ function save_setting (alias, setting_name, setting_value) {
f.close(); f.close();
} }
function manage_twits(cmd, twit, twitlist, frames) {
switch (cmd) {
case "add":
if (twitlist.indexOf(twit) < 0) {
twitlist.push(twit);
if (twitlist.indexOf(twit) >= 0) {
save_setting(user.alias, "twit_list", twitlist.join(ascii(126)));
display_server_message(frames, "\x01n\x01c\x01h" + twit + "\x01w successfully added to Twit List\x01n.\x01n\x01w");
}
}
break;
case "del":
var indexRmv = twitlist.indexOf(twit);
if (indexRmv >= 0) {
twitlist.splice(indexRmv, 1);
if (twitlist.indexOf(twit.toLowerCase()) < 0) {
save_setting(user.alias, "twit_list", twitlist.join(ascii(126)));
display_server_message(frames, "\x01n\x01c\x01h" + twit + "\x01w successfully REMOVED from Twit List\x01n.\x01n\x01w");
}
}
break;
case "list":
if (twitlist.length > 0) {
display_server_message(frames, "\x01n\x01c\x01hTwit List (\x01w" + twitlist.length + "\x01c)\x01n:\x01n\x01w");
for (var twit in twitlist ) {
display_server_message(frames, "\x01n\x01w - \x01h" + twitlist[twit] + "\x01n\x01w");
}
} else {
display_server_message(frames, "\x01n\x01c\x01hYour Twit List is \x01wempty\x01n.\x01n\x01w");
}
break;
case "clear":
twitlist.splice(0,twitlist.length);
save_setting(user.alias, "twit_list", "");
display_server_message(frames, "\x01n\x01c\x01hYour Twit List is cleared.\x01n\x01w");
break;
default:
display_server_message(frames, "\x01k\x01h*.:\x01n\x01w (*) Invalid twit command.\x01n\x01w");
display_server_message(frames, "\x01k\x01h*.:\x01n\x01w See\x01H\x01K:\x01N \x01H/\x01Chelp\x01N \x01Htwit\x01n\x01w");
display_server_message(frames, "\x01k\x01h*.: __\x01n\x01w");
}
}
function display_system_messages(frames) { // display local system messages (e.g.: user new email alerts, etc.)
if (file_size(NOTIF_FILE) > 0) {
var fnotif = new File(NOTIF_FILE);
if ( fnotif.open('r') ) {
const notif = fnotif.readAll();
fnotif.close();
fnotif = undefined;
display_server_message(frames, "\x01k\x01h*.:\x01y\x01h :: System Notification ::\x01n\x01w");
for (var notif_line in notif) {
display_server_message(frames, "\x01k\x01h*.: " + notif[notif_line]);
}
display_server_message(frames, "\x01k\x01h*.:\x01k\x01h__\x01n\x01w");
}
console.beep();
file_removecase(NOTIF_FILE);
}
}
function browse_mentions(frames) {
if (mentions.length > 0) {
mention_index = mentions.length-1;
input_state = 'mentions';
frames.divider.clear();
frames.divider.putmsg(theme_fg_color + format('UP/DOWN to browse mentions (%d of %d), ENTER to return', mention_index+1, mentions.length ));
frames.output.scrollTo(1, mentions[mention_index]);
frames.output_scroll.cycle();
}
}
function display_external_text(frames, which) {
var text = [];
var fhelp = new File(js.startup_dir + "mrc-" + which + ".msg");
if ( fhelp.open('r') ) {
text = fhelp.readAll();
fhelp.close();
for (var line in text) {
display_server_message(frames, text[line]);
}
} else {
display_server_message(frames, "\x01w\x01h (*) Topic not found: \x01c" + which + "\x01n" );
}
}
function main() { function main() {
var msg; var msg;
...@@ -274,15 +378,21 @@ function main() { ...@@ -274,15 +378,21 @@ function main() {
if (settings.show_nicks[user.alias]) { if (settings.show_nicks[user.alias]) {
show_nicks = settings.show_nicks[user.alias]; show_nicks = settings.show_nicks[user.alias];
} }
if (settings.theme[user.alias]) { if (settings.theme[user.alias]) {
set_theme(settings.theme[user.alias]); set_theme(settings.theme[user.alias]);
} }
if (settings.msg_color[user.alias]) {
session.msg_color = settings.msg_color[user.alias];
}
if (settings.twit_list[user.alias]) {
session.twit_list = settings.twit_list[user.alias].split(ascii(126));
}
const frames = init_display(); const frames = init_display(session.msg_color);
const inputline = new InputLine(frames.input); const inputline = new InputLine(frames.input);
inputline.show_cursor = true; inputline.show_cursor = true;
inputline.max_buffer = 140; inputline.max_buffer = 140;
inputline.cursor_attr = BG_BLACK|session.msg_color;
resize_nicklist(frames, []); resize_nicklist(frames, []);
redraw_nicklist(frames, []); redraw_nicklist(frames, []);
...@@ -297,34 +407,19 @@ function main() { ...@@ -297,34 +407,19 @@ function main() {
session.on('error', function (err) { session.on('error', function (err) {
display_message(frames, { from_user: 'ERROR', body: err }); display_message(frames, { from_user: 'ERROR', body: err });
}); });
session.on('help', function (cmd, help, ars) {
if (!ars || user.compare_ars(ars)) { session.on('local_help', function (help_topic) {
display_server_message(frames, format('\x01h\x01w/\x01h\x01c%s \x01h\x01w- \x01n\x01w%s', cmd, help)); display_external_text(frames, "help-" + (help_topic ? help_topic : "main"));
if (help_topic == "theme") {
display_server_message(frames, "\x01n\x01w Available theme options: " + list_themes());
} }
}); display_server_message(frames, "\x01k\x01h*.:\x01k\x01h__\x01n\x01w");
session.on('local_help', function (msg) {
display_server_message(frames, '\x01h\x01w/\x01h\x01cscroll \x01h\x01w- \x01n\x01wScroll the output area');
display_server_message(frames, '\x01h\x01w/\x01h\x01cscroll_nicks \x01h\x01w- \x01n\x01wScroll the nicklist');
display_server_message(frames, '\x01h\x01w/\x01h\x01cnick_prefix \x01h\x01w- \x01n\x01wSet a single-character prefix for your handle, eg. /nick_prefix @');
display_server_message(
frames,
'\x01h\x01w/\x01h\x01cnick_color \x01h\x01w- \x01n\x01wSet your nick color to one of '
+ PIPE_COLOURS.reduce(function (a, c) {
a += format('|%02d%s ', c, c);
return a;
}, '')
+ ', eg. /nick_color 11'
);
display_server_message(frames, '\x01h\x01w/\x01h\x01cnick_suffix \x01h\x01w- \x01n\x01wSet an eight-character suffix for your handle, eg. /nick_suffix <poop>');
display_server_message(frames, '\x01h\x01w/\x01h\x01ctoggle_nicks \x01h\x01w- \x01n\x01wShow/hide the nicklist');
display_server_message(frames, '\x01h\x01w/\x01h\x01ctoggle_stats \x01h\x01w- \x01n\x01wSwitch between global MRC stats and client/session stats.');
display_server_message(frames, "\x01h\x01w/\x01h\x01ctheme \x01n\x01w- \x01nSelect a theme: " + list_themes() );
display_server_message(frames, '\x01h\x01w/\x01h\x01cquit \x01n\x01w- \x01h\x01wExit the program');
}); });
session.on('message', function (msg) { session.on('message', function (msg) {
if (msg.from_user == 'SERVER') { if (msg.from_user == 'SERVER') {
display_server_message(frames, msg.body); display_server_message(frames, msg.body);
} else { } else {
if (session.twit_list.indexOf(msg.from_user.toLowerCase()) < 0) {
var mention = false; var mention = false;
if (msg.body.toUpperCase().indexOf(user.alias.toUpperCase()) >= 0 && msg.from_user !== user.alias) { if (msg.body.toUpperCase().indexOf(user.alias.toUpperCase()) >= 0 && msg.from_user !== user.alias) {
console.beep(); console.beep();
...@@ -332,7 +427,10 @@ function main() { ...@@ -332,7 +427,10 @@ function main() {
session.mention_count = session.mention_count + 1; session.mention_count = session.mention_count + 1;
refresh_stats (frames, session); refresh_stats (frames, session);
} }
display_message(frames, msg, mention); //, nick_colours[msg.from_user] || ''); display_message(frames, msg, mention);
} /*else {
log ( "filtered message from " + msg.from_user + ".");
}*/
} }
}); });
session.on('nicks', function (nicks) { session.on('nicks', function (nicks) {
...@@ -356,19 +454,64 @@ function main() { ...@@ -356,19 +454,64 @@ function main() {
refresh_stats(frames, session); refresh_stats(frames, session);
}); });
if (settings.startup.splash) display_external_text(frames, "splash");
if (settings.startup.motd) session.motd(); if (settings.startup.motd) session.motd();
if (settings.startup.banners) session.banners(); session.send_notme("|07- |11" + user.alias + " |03has arrived.");
mswait(20);
session.send_command('TERMSIZE:' + console.screen_columns + 'x' + console.screen_rows);
mswait(20);
session.send_command('BBSMETA: SecLevel(' + user.security.level + ') SysOp(' + system.operator + ')');
mswait(20);
session.send_command('USERIP:' + (bbs.atcode("IP") == "127.0.0.1" ? client.ip_address : bbs.atcode("IP")) );
if (settings.startup.room) session.join(settings.startup.room); if (settings.startup.room) session.join(settings.startup.room);
if (settings.startup.commands) {
var startup_cmds = settings.startup.commands.split(',');
for (var startup_cmd in startup_cmds) {
session.send_command(startup_cmds[startup_cmd].toUpperCase());
}
}
var cmd, line, user_input; var cmd, line, user_input;
while (!js.terminated && !break_loop) { while (!js.terminated && !break_loop) {
session.cycle(); session.cycle();
if (input_state == 'chat') {
frames.divider.gotoxy(frames.divider.width - 16, 1); frames.divider.gotoxy(frames.divider.width - 16, 1);
frames.divider.putmsg(theme_2nd_color + "[" + theme_fg_color + ("000" + inputline.buffer.length).slice(-3) + theme_2nd_color + '/' + theme_fg_color + inputline.max_buffer + theme_2nd_color + "]"); frames.divider.putmsg(theme_2nd_color + "[" + theme_fg_color + ("000" + inputline.buffer.length).slice(-3) + theme_2nd_color + '/' + theme_fg_color + inputline.max_buffer + theme_2nd_color + "]");
if (inputline.buffer.search(/^\/(identify|register|roompass|update password|roomconfig password) ./i) == 0) {
inputline.frame.left();
inputline.frame.write("*"); // mask text after commands involving passwords
}
}
user_input = inputline.getkey(); user_input = inputline.getkey();
if (typeof user_input == 'string') { if (typeof user_input == 'string') {
if (input_state == 'chat') { if (input_state == 'chat') {
if (user_input.substring(0, 1) == '/') { // It's a command // Define some hotkeys
if (user_input == KEY_LEFT || user_input == KEY_RIGHT) { // Change user's text color
session.msg_color = session.msg_color + (user_input == KEY_LEFT ? -1 : 1);
if (session.msg_color < 1) {
session.msg_color = 15;
} else if (session.msg_color > 15) {
session.msg_color = 1;
}
inputline.cursor_attr = BG_BLACK|session.msg_color;
frames.input_color.clear();
frames.input_color.putmsg( pipeToCtrlA( format( "|%02d>", session.msg_color) ) );
save_setting(user.alias, "msg_color", session.msg_color);
} else if (user_input == KEY_PAGEUP) { // Shortcut for scroll
input_state = 'scroll';
if (frames.output.offset.y > 0) {
frames.output.scroll(0, -1 * frames.output.height);
frames.output_scroll.cycle();
}
frames.divider.clear();
frames.divider.putmsg(theme_fg_color + 'UP/DOWN, PGUP/PGDN, HOME/END to scroll, ENTER to return');
} else if (user_input == KEY_UP) {
browse_mentions(frames);
} else if (user_input == CTRL_D) {
inputline.clear();
} else if (user_input.substring(0, 1) == '/') { // It's a command
cmd = user_input.split(' '); cmd = user_input.split(' ');
cmd[0] = cmd[0].substr(1).toLowerCase(); cmd[0] = cmd[0].substr(1).toLowerCase();
switch (cmd[0]) { switch (cmd[0]) {
...@@ -440,15 +583,33 @@ function main() { ...@@ -440,15 +583,33 @@ function main() {
refresh_stats(frames, session); refresh_stats(frames, session);
break; break;
case "theme": case "theme":
if (cmd.length == 2) {
set_theme( cmd[1] ); set_theme( cmd[1] );
frames.title.attr = theme_bg_color|theme_fg_color; frames.title.attr = theme_bg_color|theme_fg_color;
frames.divider.attr = theme_bg_color|theme_fg_color; frames.divider.attr = theme_bg_color|theme_fg_color;
display_title(frames, session.room, session.room_topic); display_title(frames, session.room, session.room_topic);
refresh_stats(frames, session); refresh_stats(frames, session);
}
break;
case "mentions":
browse_mentions(frames);
break;
case "twit":
if (cmd.length >= 2) {
manage_twits( cmd[1].toLowerCase(), cmd.slice(2).join(' ').toLowerCase(), session.twit_list, frames );
}
break; break;
default: default:
if (typeof session[cmd[0]] == 'function') { if (typeof session[cmd[0]] == 'function') {
session[cmd[0]](cmd.slice(1).join(' ')); session[cmd[0]](cmd.slice(1).join(' '));
// This check doesn't let commands out unless they're
// strictly defined in mrc-session.js, meaning we'd have
// to constantly chase down new server commands each time
// they're added/removed.
} else {
// Just send the command to the server if it's not defined as
// a local command. The server will tell the user if it's not valid.
session.send_command(user_input.substr(1));
} }
break; break;
} }
...@@ -475,10 +636,32 @@ function main() { ...@@ -475,10 +636,32 @@ function main() {
} else if ( // Regular message } else if ( // Regular message
typeof user_input == 'string' // Could be bool at this point typeof user_input == 'string' // Could be bool at this point
&& user_input != '' && user_input != ''
&& [KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT].indexOf(user_input) < 0 && [KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT,
KEY_HOME, KEY_END, KEY_INSERT, KEY_PAGEDN].indexOf(user_input) < 0
) { ) {
session.send_room_message(user_input); session.send_room_message(user_input);
} }
} else if (input_state == 'mentions') {
if (user_input == KEY_UP || user_input == KEY_DOWN) {
mention_index = mention_index + (user_input == KEY_UP ? -1 : 1);
if (mention_index < 0) {
mention_index = 0;
} else if (mention_index > mentions.length-1) {
mention_index = mentions.length-1;
}
if (frames.output.offset.y !== mentions[mention_index] ) {
frames.output.scrollTo(1, mentions[mention_index]);
frames.output_scroll.cycle();
frames.divider.clear();
frames.divider.putmsg(theme_fg_color + format('UP/DOWN to browse mentions (%d of %d), ENTER to return', mention_index+1, mentions.length ));
}
} else if (user_input == '' || user_input == 'q') {
frames.output.scrollTo(1, frames.output.data_height - frames.output.height-1);
frames.output_scroll.cycle();
input_state = 'chat';
session.mention_count = 0;
refresh_stats(frames, session);
}
} else if (input_state == 'scroll' || input_state == 'scroll_nicks') { } else if (input_state == 'scroll' || input_state == 'scroll_nicks') {
var sframe = input_state == 'scroll' ? frames.output : frames.nicks; var sframe = input_state == 'scroll' ? frames.output : frames.nicks;
if (user_input == KEY_UP && sframe.offset.y > 0) { if (user_input == KEY_UP && sframe.offset.y > 0) {
...@@ -510,8 +693,12 @@ function main() { ...@@ -510,8 +693,12 @@ function main() {
frames.nick_scroll.cycle(); frames.nick_scroll.cycle();
console.gotoxy(inputline.frame.__relations__.child[0].x, inputline.frame.y); console.gotoxy(inputline.frame.__relations__.child[0].x, inputline.frame.y);
} }
display_system_messages(frames);
yield(); yield();
} }
console.clear(autopause=false); // prevent an unintended auto-pause when quitting
} }
main(); main();
...@@ -28,8 +28,8 @@ f = undefined; ...@@ -28,8 +28,8 @@ f = undefined;
if (!settings.ssl) if (!settings.ssl)
settings.ssl=false; settings.ssl=false;
const PROTOCOL_VERSION = '1.3.1'; const PROTOCOL_VERSION = '1.3.2';
const MAX_LINE = 256; const MAX_LINE = 512;
const FROM_SITE = system.name.replace(/ /g, "_"); const FROM_SITE = system.name.replace(/ /g, "_");
const SYSTEM_NAME = system_info.system_name || system.name; const SYSTEM_NAME = system_info.system_name || system.name;
...@@ -221,8 +221,10 @@ function mrc_receive(sock) { ...@@ -221,8 +221,10 @@ function mrc_receive(sock) {
line = sock.recvline(MAX_LINE, settings.timeout); line = sock.recvline(MAX_LINE, settings.timeout);
if (!line || line == '') break; if (!line || line == '') break;
latency_tracker.forEach(function(m) { latency_tracker.forEach(function(m) {
if (m.line===line.trim()) { if (m.line===line.trim() ||
(m.line.indexOf("~STATS") >= 0 && line.indexOf("~STATS") >= 0 ) ) {
client_send({ from_user: "SERVER", to_user: 'CLIENT', body: 'LATENCY:' + (Date.now() - m.time) }); client_send({ from_user: "SERVER", to_user: 'CLIENT', body: 'LATENCY:' + (Date.now() - m.time) });
log(LOG_DEBUG, 'Latency: ' + (Date.now() - m.time) );
latency_tracker = []; latency_tracker = [];
} }
}); });
...@@ -239,7 +241,6 @@ function mrc_receive(sock) { ...@@ -239,7 +241,6 @@ function mrc_receive(sock) {
fMa.write(message.body.substr(message.body.indexOf(':')+1).trim()); fMa.write(message.body.substr(message.body.indexOf(':')+1).trim());
fMa.close(); fMa.close();
} }
if (['', 'ALL', FROM_SITE].indexOf(message.to_site) > -1) {
if (['', 'CLIENT', 'ALL', 'NOTME'].indexOf(message.to_user) > -1) { if (['', 'CLIENT', 'ALL', 'NOTME'].indexOf(message.to_user) > -1) {
// Forward to all clients // Forward to all clients
client_send(message); client_send(message);
...@@ -247,7 +248,6 @@ function mrc_receive(sock) { ...@@ -247,7 +248,6 @@ function mrc_receive(sock) {
// Send to this user // Send to this user
client_send(message, message.to_user); client_send(message, message.to_user);
} }
}
yield(); yield();
} }
} }
...@@ -274,6 +274,7 @@ function main() { ...@@ -274,6 +274,7 @@ function main() {
Object.keys(clients).forEach(function (e, i) { Object.keys(clients).forEach(function (e, i) {
if (!clients[e].socket.is_connected) { if (!clients[e].socket.is_connected) {
mrc_send(mrc_sock, clients[e].username, "", "NOTME", "", "", "|07- |12" + clients[e].username + " |04has left chat|08.");
mrc_send(mrc_sock, clients[e].username, '', 'SERVER', '', '', 'LOGOFF'); mrc_send(mrc_sock, clients[e].username, '', 'SERVER', '', '', 'LOGOFF');
client_send({ from_user: clients[e].username, to_user: 'SERVER', body: 'LOGOFF' }); // Notify local clients client_send({ from_user: clients[e].username, to_user: 'SERVER', body: 'LOGOFF' }); // Notify local clients
delete clients[e]; delete clients[e];
......
NB__[ HWList of available commandsN: B]_____________________________
WH/CinfoNHK:N View information about a BBSHK:N H/CinfoN H#
WH/Ct Kor W/CmsgK:N Send a direct messageHK:NH/CtN HnickN HBmessage
WH/CrNHK:N Reply to last direct messageHK:N H/CrB message
WH/CjoinNHK:N Move to a new roomHK:W/CjoinN Hroom_name
WH/CtopicNHK:N Change room topic:H/CtopicW topic
WH/CroomsNHK:N List available rooms
WH/CusersNHK:N List users
WH/CwhoonNHK:N List users and BBSes
WH/CmotdNHK:N Display the Message of the Day
WH/Cscroll Kor CPGUPK:N Scroll the chat window
WH/Cmentions Kor CUPK:N Reset mention counter and review mentions
WH/Cscroll_nicksN HK:N Scroll the nicklist
WH/Ctoggle_nicksN HK:N Show/hide the nicklist
WH/Ctoggle_statsN HK:N Toggle MRC stats view
WH/CthemeNHK:N Select a theme HK..........N seeHK:N H/ChelpN Htheme
WH/CtwitNHK:N Twit List management HK....N seeHK:N H/ChelpN Htwit
WH/CquitNHK:N Exit the program
WHCLEFTK/CRIGHTWK:N Change your text color HK..N seeHK:N H/ChelpN Hnick
WHCCTRLK+CDNHK:N Clear the input line
NFor a list of additional HSERVERN commands, see H/CquoteN Hhelp
\ No newline at end of file
NB__[ HWNick customization optionsN: B]_____________________________
HW/Cnick_prefix K:W NSet a single-character prefix for your handle
Neg.H /Cnick_prefixN H@
WH/Cnick_colorN HK:W NSet your nick color
HNG02HW NC03HW NR04HW NM05HW NY06HW B09W G10W C11W R12M 13W Y14W 15
WH/Cnick_suffix K:W NSet an eight-character suffix for your handle
Neg.H N/HCnick_suffix W<afilz>
N You can include pipe color codes in your nick_prefix and
N nick_suffix. Get as creative as you want!
HB NExample: HB@CUserNameB<GaFiLZB>
NB__[ HWHow to set a custom themeN: B]______________________________
N Usage:
WH/Ctheme NHnameoftheme
N
\ No newline at end of file
NB__[ HWHow to use the twit listN: B]_______________________________
N Basic twit list implemented! Hides messages from problematic
N chatters.
N Usage:
WH/Ctwit NHadd HBnameoftwit K:W NFilters out nameoftwit's messages
WH/Ctwit NHdel HBnameoftwit K:W Nnameoftwit's messages are re-allowed
WH/Ctwit NHlist K:W NLists twits the user has added
WH/Ctwit NHclear K:W NEmpty's the user's twit list
...@@ -16,7 +16,10 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -16,7 +16,10 @@ function MRC_Session(host, port, user, pass, alias) {
alias: alias || user, alias: alias || user,
stats: ['-','-','-','0'], stats: ['-','-','-','0'],
mention_count: 0, mention_count: 0,
latency: '-' latency: '-',
msg_color: 7,
twit_list: [],
last_private_msg_from: ""
}; };
const callbacks = { const callbacks = {
...@@ -41,10 +44,10 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -41,10 +44,10 @@ function MRC_Session(host, port, user, pass, alias) {
function send_message(to_user, to_room, body) { function send_message(to_user, to_room, body) {
if (body.length + state.alias.length + 1 > 250) { if (body.length + state.alias.length + 1 > 250) {
word_wrap(body, 250 - 1 - state.alias.length).split(/\n/).forEach(function (e) { word_wrap(body, 250 - 1 - state.alias.length).split(/\n/).forEach(function (e) {
send(to_user, '', to_room, state.alias + ' ' + e); send(to_user, '', to_room, state.alias + ' ' + format("|%02d", state.msg_color) + e);
}); });
} else { } else {
send(to_user, '', to_room, state.alias + ' ' + body); send(to_user, '', to_room, state.alias + ' ' + format("|%02d", state.msg_color) + body);
} }
} }
...@@ -80,7 +83,7 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -80,7 +83,7 @@ function MRC_Session(host, port, user, pass, alias) {
break; break;
case 'STATS': case 'STATS':
state.stats = params.split(' '); state.stats = params.split(' ');
emit('stats'); //, state.stats); emit('stats');
break; break;
case 'LATENCY': case 'LATENCY':
state.latency = params; state.latency = params;
...@@ -90,6 +93,10 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -90,6 +93,10 @@ function MRC_Session(host, port, user, pass, alias) {
emit('message', msg); emit('message', msg);
break; break;
} }
if (msg.body.search(/just joined room/) > -1 ||
msg.body.search(/session has timed-out/) > -1) {
send_command('USERLIST', 'ALL');
}
} else if (msg.to_user == 'SERVER') { } else if (msg.to_user == 'SERVER') {
if (msg.body == 'LOGOFF') { if (msg.body == 'LOGOFF') {
uidx = state.nicks.indexOf(msg.from_user); uidx = state.nicks.indexOf(msg.from_user);
...@@ -99,6 +106,11 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -99,6 +106,11 @@ function MRC_Session(host, port, user, pass, alias) {
} }
} }
} else if (msg.to_user == '' || user.toLowerCase() == msg.to_user.toLowerCase()) { } else if (msg.to_user == '' || user.toLowerCase() == msg.to_user.toLowerCase()) {
if (user.toLowerCase() == msg.to_user.toLowerCase()) {
state.last_private_msg_from = msg.from_user;
}
if (msg.to_room == '' || msg.to_room == state.room) { if (msg.to_room == '' || msg.to_room == state.room) {
emit('message', msg); emit('message', msg);
} }
...@@ -112,9 +124,9 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -112,9 +124,9 @@ function MRC_Session(host, port, user, pass, alias) {
state.nicks.splice(uidx, 1); state.nicks.splice(uidx, 1);
emit('nicks', state.nicks); emit('nicks', state.nicks);
} }
} else if (msg.body.search(/just joined room/) > -1) { } /*else if (msg.body.search(/just joined room/) > -1) {
send_command('USERLIST', 'ALL'); send_command('USERLIST', 'ALL'); // moved above to msg.from_user == 'SERVER'
} }*/
emit('message', msg); emit('message', msg);
} }
} }
...@@ -123,6 +135,10 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -123,6 +135,10 @@ function MRC_Session(host, port, user, pass, alias) {
send_message('', state.room, msg); send_message('', state.room, msg);
} }
this.send_notme = function (msg) {
send("NOTME", "", "", msg);
}
this.send_private_messsage = function (user, msg) { this.send_private_messsage = function (user, msg) {
msg = '|11(|03Private|11)|07 ' + msg; msg = '|11(|03Private|11)|07 ' + msg;
send_message(user, state.room, msg); send_message(user, state.room, msg);
...@@ -140,7 +156,11 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -140,7 +156,11 @@ function MRC_Session(host, port, user, pass, alias) {
password: pass, password: pass,
alias: state.alias alias: state.alias
}) + '\r\n'); }) + '\r\n');
this.send_command('IAMHERE');
mswait(20);
this.send_command('USERLIST', 'ALL'); this.send_command('USERLIST', 'ALL');
mswait(20);
this.send_command('STATS', 'ALL'); this.send_command('STATS', 'ALL');
} }
...@@ -181,34 +201,40 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -181,34 +201,40 @@ function MRC_Session(host, port, user, pass, alias) {
} }
const commands = { const commands = {
banners: {
help: 'List of banners from server' // Doesn't do anything? // This section has been cleaned up significantly.
}, //
chatters: { // Server commands no longer need to be strictly defined
help: 'List current users' // here.
}, //
bbses: { // Some commands are still defined if they have specific
help: 'List connected BBSs', // function calls attached to them in mrc-client.js
command: 'CONNECTED' // (e.g. /motd) and or are local command shortcuts
}, // (e.g. /t and /r).
//
// Any command not defined here (or in mrc-client.js) will
// be assumed to be a server command. The server will
// inform the user if a command is invalid.
//
// The "help" properties are no longer needed, since
// help is contained in external .msg file. They have been
// left commended out to provide reference.
//
help: { help: {
help: 'Display this help message', //help: 'Display this help message',
callback: function (str) { callback: function (str) {
emit('help', 'List of available commands:', ''); emit('local_help', str);
for (var c in commands) {
emit('help', c, commands[c].help, commands[c].ars);
}
emit('local_help');
} }
}, },
info: { info: {
help: 'View information about a BBS (/info #)', //help: 'View information about a BBS (/info #)',
callback: function (str) { callback: function (str) {
this.send_command('INFO ' + str); this.send_command('INFO ' + str);
} }
}, },
join: { join: {
help: 'Move to a new room: /join room_name', //help: 'Move to a new room: /join room_name',
callback: function (str) { // validate valid room name? callback: function (str) { // validate valid room name?
str = str.replace(/^#/, ''); str = str.replace(/^#/, '');
this.send_command(format('NEWROOM:%s:%s', state.room, str)); this.send_command(format('NEWROOM:%s:%s', state.room, str));
...@@ -217,14 +243,11 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -217,14 +243,11 @@ function MRC_Session(host, port, user, pass, alias) {
this.send_command('USERLIST', 'ALL'); this.send_command('USERLIST', 'ALL');
} }
}, },
meetups: {
help: 'Display information about upcoming meetups'
},
motd: { motd: {
help: 'Display the Message of the Day' //help: 'Display the Message of the Day'
}, },
msg: { msg: { // This is largely overtaken by /t, but we'll still handle it.
help: 'Send a private message: /msg nick message goes here', //help: 'Send a private message: /msg nick message goes here',
callback: function (str) { callback: function (str) {
const cmd = str.split(' '); const cmd = str.split(' ');
if (cmd.length > 1 && state.nicks.indexOf(cmd[0]) > -1) { if (cmd.length > 1 && state.nicks.indexOf(cmd[0]) > -1) {
...@@ -232,8 +255,8 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -232,8 +255,8 @@ function MRC_Session(host, port, user, pass, alias) {
} }
} }
}, },
t: { // added as shorthand for /msg t: {
help: 'Shorthand for /msg: /t nick message goes here', //help: 'Send a private message: /t nick message goes here',
callback: function (str) { callback: function (str) {
const cmd = str.split(' '); const cmd = str.split(' ');
if (cmd.length > 1 && state.nicks.indexOf(cmd[0]) > -1) { if (cmd.length > 1 && state.nicks.indexOf(cmd[0]) > -1) {
...@@ -241,70 +264,41 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -241,70 +264,41 @@ function MRC_Session(host, port, user, pass, alias) {
} }
} }
}, },
r: {
//help: 'Reply to last private message: /r message goes here',
callback: function (str) {
if (state.last_private_msg_from) {
this.send_private_messsage(state.last_private_msg_from, str);
}
}
},
quote: { quote: {
help: 'Send a raw command to the server', //help: 'Send a raw command to the server',
callback: function (str) { callback: function (str) {
this.send_command(str); this.send_command(str);
} }
}, },
quit: { quit: {
help: 'Quit the program', //help: 'Quit the program',
callback: function () { callback: function () {
handle.close();
emit('disconnect'); emit('disconnect');
handle.close();
} }
}, },
rooms: { rooms: {
help: 'List available rooms', //help: 'List available rooms',
command: 'LIST' command: 'LIST'
}, },
stats: { stats: {
help: 'Return anonymous server stats', //help: 'Return anonymous server stats',
command: 'statistics' command: 'statistics'
}, },
topic: { topic: {
help: 'Change the topic of the current room', //help: 'Change the topic of the current room',
callback: function (str) { callback: function (str) {
this.send_command(format('NEWTOPIC:%s:%s', state.room, str)); this.send_command(format('NEWTOPIC:%s:%s', state.room, str));
} }
},
users: {
help: 'Display list of users'
},
whoon: {
help: 'Display list of users and BBSs'
},
afk: {
help: "Set yourself AFK (Shortcut for STATUS AFK)",
callback: function (str) {
this.send_command('AFK ' + str);
} }
},
register: {
help: "Register handle on server (MRC Trust)",
callback: function (str) {
this.send_command('REGISTER ' + str);
}
},
identify: {
help: "Identify as a registered user (MRC Trust)",
callback: function (str) {
this.send_command('IDENTIFY ' + str);
}
},
update: {
help: "Update user registration (MRC Trust)",
callback: function (str) {
this.send_command('UPDATE ' + str);
}
},
trust: {
help: "MRC Trust Info (MRC Trust)",
callback: function (str) {
this.send_command('TRUST ' + str);
}
}
}; };
Object.keys(commands).forEach(function (e) { Object.keys(commands).forEach(function (e) {
...@@ -312,11 +306,11 @@ function MRC_Session(host, port, user, pass, alias) { ...@@ -312,11 +306,11 @@ function MRC_Session(host, port, user, pass, alias) {
this[e] = commands[e].callback; this[e] = commands[e].callback;
} else if (commands[e].command) { } else if (commands[e].command) {
this[e] = function () { this[e] = function () {
this.send_command(commands[e].command.toUpperCase()); this.send_command(commands[e].command);
} }
} else { } else {
this[e] = function () { this[e] = function () {
this.send_command(e.toUpperCase()); this.send_command(e);
} }
} }
}, this); }, this);
......
NHK.N HK.N . HK~N HK~N HK~N HK.N HK~N HK~N HK.N HK~N HK~N HK~N HK~N HK.N HK~N HK~N HK~N . HK.N HK.
N HK.N HK.N . H.N HB88888b.d88b.N HB888d888 .d8888b W.N .HK .N HK.
.N .HK W.K NB.W B8H88N HB"888N HB"88b NB8H88P"NB dH88P"NB.HK W.N . HK.
N.HK W.K NB.W B.W B8H88N B8H88 NB8H88 NB8H88NB8H88NB.W B.HK W.N .
HK . N.HK W.K NB.W B8H88N B8H88 NB8H88 NB8H88NBY8H8b.NB.HK W.N . HK.
. .N . H.N B8H88N B8H88 NB8H88 NB8H88NB"Y8H888 W.N . HK.N HK.
` .N HK.N HK=N==H=========================N==HK=N HK.N HK. '
NHK-=[N HCMNCulti HRNCelay HCNChat forW HCSynchronetN HK]=-
...@@ -60,7 +60,6 @@ colour codes are permitted: ...@@ -60,7 +60,6 @@ colour codes are permitted:
http://wiki.mysticbbs.com/doku.php?id=displaycodes#color_codes_pipe_colors http://wiki.mysticbbs.com/doku.php?id=displaycodes#color_codes_pipe_colors
I may write a CTRL-A to pipe converter at some later date.
mrc-connector.ini: mrc-connector.ini:
...@@ -77,11 +76,16 @@ mrc-client.ini: ...@@ -77,11 +76,16 @@ mrc-client.ini:
above instructions while editing '/sbbs/ctrl/services.ini'. above instructions while editing '/sbbs/ctrl/services.ini'.
- The 'ping_interval' setting should be left at the default value unless you - The 'ping_interval' setting should be left at the default value unless you
have a good reason for changing it. have a good reason for changing it.
- The values in the [startup] section determine which room the client joins - The values in the [startup] section control the following:
on startup, and whether the Message of the Day and banners are displayed. room: Which room the client joins on startup.
- Change show_nicks in the [client] section to always display the nick list motd: Whether the Message of the Day is displayed.
when connecting to the MRC server. commands: Any additional commands to execute, eg: "chatters" to display
the list of active users in the room on startup. Use commas
to separate multiple commands.
splash: Whether the "Multi Relay Chat for Synchronet" splash is shown
on startup.
- The [aliases], [theme], [msg_color], [show_nicks], and [twit_list] sections
are for user settings which get added or updated any time a user uses them.
4) MRC Stats 4) MRC Stats
...@@ -123,19 +127,35 @@ see "Yes" next to your BBS in the SSL column. ...@@ -123,19 +127,35 @@ see "Yes" next to your BBS in the SSL column.
6) Themes 6) Themes
MRC comes with several customizable theme files. These can be added/edited as MRC comes with several customizable theme files. These can be added/edited as
the system wishes. The client automatically detects any available theme files the sysop wishes. The client automatically detects any available theme files
matching the pattern: matching the pattern:
mrc-theme-<theme_name>.ini mrc-theme-<theme_name>.ini
Available themes are listed in /help. User selects a theme by typing Available themes are listed in /help theme. User selects a theme by typing
/theme <name>, and the selected theme gets saved to settings for future /theme <name>, and the selected theme gets saved to settings for future
sessions. sessions.
If a theme file is not available, the classic blue theme gets used by default. If a theme file is not available, the classic blue theme gets used by default.
To customize the splash that gets shown on startup, edit the mrc-splash.msg
file. Or just disable it by setting splash = false in mrc-client.ini
7) Support
7) Known issues
- Outgoing messages beginning with a semicolon do not get sent out to the MRC
server. For example, a winking face emoticon: ;) or ;-)
This is because the ; character is used by the inputline.js library to
indicate the start of a sysop command. In the case of the MRC client, such
text is simply ignored and does not get sent over.
As a workround, you may simply insert a space in front of the message you
want to send that starts with a semicolon.
8) Support
- Post a message to 'echicken' in the Synchronet Sysops area on DOVE-Net - Post a message to 'echicken' in the Synchronet Sysops area on DOVE-Net
- Find me on irc.synchro.net in #synchronet - Find me on irc.synchro.net in #synchronet
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment