Skip to content
Snippets Groups Projects
Commit 5e63f317 authored by echicken's avatar echicken :chicken:
Browse files

various

parent 0d574b4e
No related branches found
No related tags found
No related merge requests found
...@@ -1985,7 +1985,7 @@ ...@@ -1985,7 +1985,7 @@
}, },
"node_modules/@swag/ts4s": { "node_modules/@swag/ts4s": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+ssh://git@gitlab.synchro.net:swag/ts4s.git#74b34ae871f6be2b69627a821e1779eb930090c6", "resolved": "git+ssh://git@gitlab.synchro.net:swag/ts4s.git#d3f8edb99956374272e3fd68dc8ed22d09f1e5f5",
"dependencies": { "dependencies": {
"@babel/cli": "^7.20.7", "@babel/cli": "^7.20.7",
"@babel/core": "^7.20.12", "@babel/core": "^7.20.12",
...@@ -3454,7 +3454,7 @@ ...@@ -3454,7 +3454,7 @@
}, },
"node_modules/swindows": { "node_modules/swindows": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+ssh://git@gitlab.synchro.net:echicken/swindows.git#38822fa9ca3b43ce1a59f07332f03f13925f437a" "resolved": "git+ssh://git@gitlab.synchro.net:echicken/swindows.git#e716822d9d3d34deb8ea1612cbbf00e232394829"
}, },
"node_modules/to-fast-properties": { "node_modules/to-fast-properties": {
"version": "2.0.0", "version": "2.0.0",
......
import type { INodeDefs, ISbbsDefs, ISmbDefs } from '@swag/ts4s'; import type { INodeDefs, ISbbsDefs, ISmbDefs, IIRCLib } from '@swag/ts4s';
import { load } from '@swag/ts4s'; import { load } from '@swag/ts4s';
const sbbsdefs: ISbbsDefs = load('sbbsdefs.js'); const sbbsdefs: ISbbsDefs = load('sbbsdefs.js');
const smbdefs: ISmbDefs = load('smbdefs.js'); const smbdefs: ISmbDefs = load('smbdefs.js');
const nodedefs: INodeDefs = load('nodedefs.js'); const nodedefs: INodeDefs = load('nodedefs.js');
const irclib: IIRCLib = load('irclib.js');
const { argc, argv, format, skipsp, truncsp, mswait, time, parent_queue, msg_area, system, File, MsgBase, Queue } = js.global; const { argc, argv, format, skipsp, truncsp, time, parent_queue, msg_area, system, user, File, MsgBase, Queue } = js.global;
if (argc < 2) throw new Error(`'background.js <user#> <node #>' expected two arguments but got ${argc}`); if (argc < 2) throw new Error(`'background.js <user#> <node #>' expected two arguments but got ${argc}`);
const IRC_CHANNEL = '#ecbbs';
const IRC_SERVER = 'bbs.electronicchicken.com';
const IRC_PORT = 6667;
const userNumber = parseInt(argv[0], 10); const userNumber = parseInt(argv[0], 10);
const nodeNumber = parseInt(argv[1], 10); const nodeNumber = parseInt(argv[1], 10);
const queue = parent_queue ?? new Queue('never'); const queue = parent_queue ?? new Queue('never');
const msgPointers: Record<string, number> = {}; const msgPointers: Record<string, number> = {};
user.number = userNumber;
let ircNick: string = user.handle ?? user.alias;
let ircSock = irclib.IRC_client_connect(IRC_SERVER, ircNick, user.alias, user.alias, IRC_PORT);
let ircReady: boolean = false;
let ircJoined: boolean = false;
function scanSubs(): void { function scanSubs(): void {
const sf = new File(`${system.data_dir}user/${format('%04d', userNumber)}.subs`); const sf = new File(`${system.data_dir}user/${format('%04d', userNumber)}.subs`);
if (!sf.open('r')) return; if (!sf.open('r')) return;
...@@ -58,11 +70,59 @@ function scanInstantMessages(): void { ...@@ -58,11 +70,59 @@ function scanInstantMessages(): void {
if (msg.length > 0) queue.write(msg.join('\r\n')); if (msg.length > 0) queue.write(msg.join('\r\n'));
} }
function cycleIRC(): void {
if (typeof ircSock === 'number') return;
if (!ircSock.is_connected) return;
while (!js.terminated && !queue.orphan && ircSock.is_connected && ircSock.data_waiting) {
const msg = ircSock.recvline();
if (msg === '') continue;
const cmd = irclib.IRC_parsecommand(msg);
if (cmd === 0) continue;
switch (cmd[0]) {
case '376':
ircReady = true;
ircNick = cmd[1];
if (!ircJoined) ircSock.sendline(`JOIN ${IRC_CHANNEL}`);
break;
case '331':
ircJoined = true;
queue.write(`\x01h\x01wNow chatting in \x01c${IRC_CHANNEL}`);
break;
case '353':
const nicks = cmd.slice(4).join('\x01w,\x01c ').substring(1);
queue.write(`\x01h\x01wUsers in \x01c${IRC_CHANNEL}\x01w:\r\n\x01c${nicks}`);
break;
case '366': // end of names
break;
case '433':
ircSock.sendline(`NICK ${cmd[2]}_`);
break;
case 'PING':
ircSock.sendline(`PONG ${cmd[1]}`);
break;
case 'PRIVMSG':
const source = irclib.IRC_parse_source(msg);
const text = irclib.IRC_string(msg, 3);
queue.write(`\x01h\x01c<\x01w${source.name}\x01c>\x01n\x01w ${text}`);
break;
case 'JOIN': // :echicken___!~echicken@bras-base-toroon0359w-grc-21-74-12-233-177.dsl.bell.ca JOIN :#ecbbs
break; // ["JOIN",":#ecbbs"] ^ parse nick from above and write join message to queue
case 'PART': // As above
break;
}
}
if (queue.data_waiting && ircJoined) {
const msg = queue.read();
ircSock.sendline(`PRIVMSG ${IRC_CHANNEL} :${msg}`);
queue.write(`\x01h\x01c<\x01w${ircNick}\x01c>\x01n\x01w ${msg}`);
}
}
js.time_limit = 0; js.time_limit = 0;
let now = time(); let now = time();
let sscan = now - 30; let sscan = now - 30;
while (!js.terminated && !queue.orphan) { while (!js.terminated && !queue.orphan && queue.peek(100) !== '\x04') {
if (queue.write_level < 10) { // Command shell isn't reading from queue right now, so don't keep filling it if (queue.write_level < 10) { // Command shell isn't reading from queue right now, so don't keep filling it
now = time(); now = time();
if (now - sscan >= 30) { if (now - sscan >= 30) {
...@@ -70,6 +130,9 @@ while (!js.terminated && !queue.orphan) { ...@@ -70,6 +130,9 @@ while (!js.terminated && !queue.orphan) {
scanSubs(); scanSubs();
} }
scanInstantMessages(); scanInstantMessages();
cycleIRC();
} }
mswait(1000);
} }
if (typeof ircSock !== 'number' && ircSock.is_connected) irclib.IRC_quit(ircSock, 'Logged off of the bulletin B.B.S. system.');
if (!queue.orphan) queue.write('\x04');
import type { IKeyDefs } from '@swag/ts4s'; import type { ICgaDefs, IKeyDefs } from '@swag/ts4s';
import { load } from '@swag/ts4s'; import { load } from '@swag/ts4s';
import { WindowManager, defs } from 'swindows'; import { ControlledWindow, Input, WindowManager, defs } from 'swindows';
import { IBorderText, IPosition, ISize } from 'swindows/src/types'; import { IBorderText, IPosition, ISize, attr } from 'swindows/src/types';
import ShellWindow from './ShellWindow'; import ShellWindow from './ShellWindow';
import { getPresence } from './presence'; import { getPresence } from './presence';
const cgadefs: ICgaDefs = load('cga_defs.js');
const keydefs: IKeyDefs = load('key_defs.js'); const keydefs: IKeyDefs = load('key_defs.js');
const { user, bbs, system, time } = js.global; const { user, bbs, system, time } = js.global;
const queue = js.global.load(true, `${js.exec_dir}background.js`, `${user.number}`, `${bbs.node_num}`); const queue = js.global.load(true, `${js.exec_dir}background.js`, `${user.number}`, `${bbs.node_num}`);
...@@ -13,17 +14,58 @@ const HELP = ...@@ -13,17 +14,58 @@ const HELP =
'\r\n\x01h\x01wHelp:\r\n' '\r\n\x01h\x01wHelp:\r\n'
+ '\x01nUse your \x01h\x01ctab \x01n\x01wor \x01h\x01cleft\x01n\x01w/\x01h\x01cright \x01n\x01warrow keys to navigate between windows.\r\n' + '\x01nUse your \x01h\x01ctab \x01n\x01wor \x01h\x01cleft\x01n\x01w/\x01h\x01cright \x01n\x01warrow keys to navigate between windows.\r\n'
+ 'Use your \x01h\x01cup\x01n\x01w,\x01h\x01c down\x01n\x01w,\x01h\x01c page up\x01n\x01w,\x01h\x01c page down\x01n\x01w,\x01h\x01c home\x01n\x01w, and \x01h\x01cend \x01n\x01wkeys to scroll.\r\n' + 'Use your \x01h\x01cup\x01n\x01w,\x01h\x01c down\x01n\x01w,\x01h\x01c page up\x01n\x01w,\x01h\x01c page down\x01n\x01w,\x01h\x01c home\x01n\x01w, and \x01h\x01cend \x01n\x01wkeys to scroll.\r\n'
+ '\x01h\x01cCTRL-W\x01n\x01who\'s online \x01h\x01cCTRL-L\x01n\x01wog off \x01h\x01cCTRL-D\x01n\x01wisconnect' + '\x01h\x01cCTRL-W\x01n\x01who\'s online; \x01h\x01cCTRL-S\x01n\x01wend message; \x01h\x01cCTRL-L\x01n\x01wog off; \x01h\x01cCTRL-D\x01n\x01wisconnect'
export default class ActivityWindow extends ShellWindow { export default class ActivityWindow extends ShellWindow {
input: Input;
inputWindow: ControlledWindow;
constructor(name: string, wm: WindowManager, position: IPosition, size: ISize, footer: IBorderText) { constructor(name: string, wm: WindowManager, position: IPosition, size: ISize, footer: IBorderText) {
super(name, wm, position, size, footer); super(name, wm, position, size, footer);
this.window.wrap = defs.WRAP.NONE; this.window.wrap = defs.WRAP.WORD;
this.window.write(HELP.split('\r\n').slice(2).join('\r\n')); this.window.write(HELP.split('\r\n').slice(2).join('\r\n'));
this.inputWindow = new ControlledWindow({
windowManager: wm,
parent: this.window.id,
attr: this.window.attr,
border: {
style: defs.BORDER_STYLE.SINGLE,
pattern: defs.BORDER_PATTERN.DIAGONAL,
attr: [
((cgadefs.BG_BLACK|cgadefs.WHITE) as attr),
((cgadefs.BG_BLACK|cgadefs.LIGHTCYAN) as attr),
((cgadefs.BG_BLACK|cgadefs.CYAN) as attr),
((cgadefs.BG_BLACK|cgadefs.LIGHTBLUE) as attr),
],
},
position: {
x: 1,
y: position.y + size.height - 2,
},
size: {
width: size.width - 2,
height: 3,
},
title: {
text: 'Enter to send, ESC to abort',
},
});
this.input = new Input({ window: this.inputWindow, attr: (cgadefs.BG_BLUE|cgadefs.WHITE) as attr });
this.inputWindow.close();
} }
getCmd(cmd: string): void { getCmd(cmd: string): void {
if (this.forceFocus) {
const str = this.input.getCmd(cmd);
if (str !== undefined) {
if (str !== '') queue.write(str); // Send message to background worker to send to IRC
this.inputWindow.close();
this.forceFocus = false;
}
} else {
switch (cmd) { switch (cmd) {
case keydefs.KEY_UP: case keydefs.KEY_UP:
this.window.scroll(0, -1); this.window.scroll(0, -1);
...@@ -51,19 +93,33 @@ export default class ActivityWindow extends ShellWindow { ...@@ -51,19 +93,33 @@ export default class ActivityWindow extends ShellWindow {
const presence = getPresence(); const presence = getPresence();
if (presence !== undefined) this.window.write(presence); if (presence !== undefined) this.window.write(presence);
break; break;
case keydefs.CTRL_S: // Send
if (!this.forceFocus) {
this.forceFocus = true;
this.inputWindow.open();
this.window.raise(true);
}
break;
default: default:
break; break;
} }
} }
}
cycle(): void { cycle(): void {
const msg: string[] = []; const msg: string[] = [];
while (!js.terminated && queue.data_waiting) { while (!js.terminated && queue.data_waiting) {
msg.push(queue.read()); const str = queue.read();
if (str !== '\x04') msg.push(str);
} }
if (msg.length < 1) return; if (msg.length < 1) return;
this.window.write(`\r\n\r\n\x01n\x01m${system.timestr(time())}\x01n\x01w\r\n`); this.window.write(`\r\n\x01n\x01m${system.timestr(time())}\x01n\x01w\r\n`);
this.window.write(msg.join('\r\n')); this.window.write(msg.join('\r\n'));
} }
close(): void {
queue.write('\x04');
queue.read(250);
}
} }
\ No newline at end of file
...@@ -8,6 +8,7 @@ export default abstract class ShellWindow { ...@@ -8,6 +8,7 @@ export default abstract class ShellWindow {
name: string; name: string;
window: swindows.types.IControlledWindow; window: swindows.types.IControlledWindow;
windowManager: swindows.WindowManager; windowManager: swindows.WindowManager;
forceFocus: boolean = false;
constructor(name: string, windowManager: swindows.WindowManager, position: swindows.types.IPosition, size: swindows.types.ISize, footer?: swindows.types.IBorderText) { constructor(name: string, windowManager: swindows.WindowManager, position: swindows.types.IPosition, size: swindows.types.ISize, footer?: swindows.types.IBorderText) {
this.name = name; this.name = name;
......
...@@ -106,7 +106,13 @@ export default class UI { ...@@ -106,7 +106,13 @@ export default class UI {
getInput(): void { getInput(): void {
let input = console.inkey(sbbsdefs.K_NONE); let input = console.inkey(sbbsdefs.K_NONE);
if (input === '') return; if (input === '') return;
if (input === 'q') js.global.exit(0); if (input === 'q') {
this.windows[0].close?.();
js.global.exit(0);
}
if (this.windows[0].forceFocus) {
this.windows[0].getCmd(input);
} else {
switch (input) { switch (input) {
// Window navigation commands // Window navigation commands
case '\t': case '\t':
...@@ -119,13 +125,16 @@ export default class UI { ...@@ -119,13 +125,16 @@ export default class UI {
// Global ActivityWindow commands regardless of which window has focus // Global ActivityWindow commands regardless of which window has focus
case keydefs.CTRL_H: // Help case keydefs.CTRL_H: // Help
case keydefs.CTRL_W: // Who's Online case keydefs.CTRL_W: // Who's Online
case keydefs.CTRL_S: // Send message
this.windows[0].getCmd(input); this.windows[0].getCmd(input);
break; break;
// Global commands not handled by any window // Global commands not handled by any window
case keydefs.CTRL_D: // Disconnect immediately case keydefs.CTRL_D: // Disconnect immediately
this.windows[0].close?.();
bbs.hangup(); bbs.hangup();
break; break;
case keydefs.CTRL_L: // Log off case keydefs.CTRL_L: // Log off
this.windows[0].close?.();
bbs.logoff(true); bbs.logoff(true);
break; break;
// Pass input to the focused window // Pass input to the focused window
...@@ -134,5 +143,6 @@ export default class UI { ...@@ -134,5 +143,6 @@ export default class UI {
break; break;
} }
} }
}
} }
\ No newline at end of file
...@@ -4,4 +4,6 @@ export interface IShellWindow { ...@@ -4,4 +4,6 @@ export interface IShellWindow {
window: IControlledWindow, window: IControlledWindow,
getCmd: (str: string) => void, getCmd: (str: string) => void,
cycle: () => void, cycle: () => void,
forceFocus: boolean,
close?: () => void,
}; };
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment