diff --git a/xtrn/chat_pager/example-settings.ini b/xtrn/chat_pager/example-settings.ini
new file mode 100644
index 0000000000000000000000000000000000000000..617e31f54f6e0690f7191f3b492690056beacf36
--- /dev/null
+++ b/xtrn/chat_pager/example-settings.ini
@@ -0,0 +1,20 @@
+; Make a copy of this file in the same directory, and name it 'settings.ini'
+; Edit the values to your liking.
+
+[queue]
+queue_name = chat_pager
+
+[scanner]
+semaphore_scan_interval = 2500
+
+[ircbot]
+notification_format = %s is paging the sysop from node %s.
+
+[terminal]
+wait_time = 30000
+wait_message = Please wait here. %s will respond if they are available.
+cancel_message = [ Hit any key to cancel ]
+irc_pull = true
+irc_server = irc.synchro.net
+irc_port = 6667
+irc_channel = #my_bbs_channel
diff --git a/xtrn/chat_pager/ircbot/for-ircbot.ini b/xtrn/chat_pager/ircbot/for-ircbot.ini
new file mode 100644
index 0000000000000000000000000000000000000000..02cc0c1d873d3afdafcceea52a73818b9c2c2425
--- /dev/null
+++ b/xtrn/chat_pager/ircbot/for-ircbot.ini
@@ -0,0 +1,6 @@
+; Paste this into your 'ctrl/ircbot.ini' file
+; Edit the 'channels' list to suit your needs
+[module_pager]
+channels = #my_bbs_channel
+lib = event-timer.js,../xtrn/chat_pager/lib.js
+dir = ../xtrn/chat_pager/ircbot
diff --git a/xtrn/chat_pager/ircbot/pager.js b/xtrn/chat_pager/ircbot/pager.js
new file mode 100644
index 0000000000000000000000000000000000000000..43a7c2b4c5413052c0a748cf56825778ce2670f1
--- /dev/null
+++ b/xtrn/chat_pager/ircbot/pager.js
@@ -0,0 +1,72 @@
+function get_config(module) {
+	var cfg = null;
+	var fn = system.ctrl_dir + 'ircbot.ini';
+	if (file_exists(fn)) {
+		var f = new File(fn);
+		if (f.open('r')) {
+			var cfg = f.iniGetObject(module);
+			f.close();
+			if (typeof cfg.channels !== 'undefined') {
+				cfg.channels = cfg.channels.split(',').map(
+					function (e) { return e.replace(/\s.*/g, ''); }
+				);
+			} else {
+				cfg.channels = [];
+			}
+		}
+	}
+	return cfg;
+}
+
+var bot_cfg = get_config('module_pager');
+var settings = load_settings(fullpath(this.dir + '../settings.ini'));
+
+if (bot_cfg !== null && settings !== null) {
+
+	var queue = new Queue(settings.queue.queue_name);
+	var timer = new Timer();
+	var scanner = new Scanner(settings.scanner);
+	var messages = [];
+
+	function scan_nodes() {
+		var nodes_paging = scanner.scan();
+		nodes_paging.forEach(
+			function (e, i) {
+				var un = user_online(i);
+				if (e == PAGE_NEW && un > 0) {
+					var u = new User(un);
+					messages.push(
+						format(
+							settings.ircbot.notification_format, u.alias, i + 1
+						)
+					);
+				}
+			}
+		);
+	}
+
+	timer.addEvent(settings.scanner.semaphore_scan_interval, true, scan_nodes);
+
+	function main(srv, target) {
+		timer.cycle();
+		while (messages.length > 0) {
+			bot_cfg.channels.forEach(
+				function (e) {
+					srv.o(e, messages.shift());
+				}
+			);
+		}
+	}
+
+	Bot_Commands["CHAT"] = new Bot_Command(0, false, false);
+	Bot_Commands["CHAT"].usage = get_cmd_prefix() + "!chat [node]";
+	Bot_Commands["CHAT"].command = function (target, onick, ouh, srv, lbl, cmd) {
+		var valname = "chat_" + cmd[1];
+		queue.write(system.timer, valname);
+	}
+
+} else {
+
+	log(LOG_ERR, 'Unable to load pager settings.');
+
+}
diff --git a/xtrn/chat_pager/lib.js b/xtrn/chat_pager/lib.js
new file mode 100644
index 0000000000000000000000000000000000000000..c5c324d7efeecb17b04999baf74193c00c53dc19
--- /dev/null
+++ b/xtrn/chat_pager/lib.js
@@ -0,0 +1,74 @@
+const PAGE_NEW = 0;
+const PAGE_REPEAT = 1;
+const PAGE_NONE = 2;
+
+function load_settings(fp) {
+    var ret = {};
+    if (file_exists(fp)) {
+        var f = new File(fp);
+        if (f.open('r')) {
+            var ini = f.iniGetAllObjects();
+            f.close();
+            ini.forEach(
+                function (e) {
+                    ret[e.name] = e;
+                    delete ret[e.name].name;
+                }
+            )
+        }
+    }
+    return ret;
+}
+
+function user_online(n) {
+	return (
+		system.node_list[n].status == 3 || system.node_list[n].status == 4
+		? system.node_list[n].useron : 0
+	);
+}
+
+// Returns array of 1-based node numbers that are currently paging the sysop
+function nodes_paging() {
+    return directory(
+        system.ctrl_dir + 'syspage.*'
+    ).map(
+        function (e) {
+            return parseInt(e.split('.').pop());
+        }
+    ).filter(
+        function (e) {
+            return (!isNaN(e) && e > 0 && e <= system.node_list.length);
+        }
+    );
+}
+
+var Scanner = function (settings) {
+
+    var node_stat;
+
+    this.reset = function () {
+        node_stat = system.node_list.map(function () { return PAGE_NONE });
+    }
+
+    // Returns an array (index is zero-based node number) of PAGE_* status
+    this.scan = function () {
+        var np = nodes_paging();
+        node_stat = node_stat.map(
+            function (e, i) {
+                if (e == PAGE_NONE && np.indexOf(i+1) >= 0) {
+                    return PAGE_NEW;
+                } else if (
+                    (e == PAGE_NEW || e == PAGE_REPEAT) && np.indexOf(i+1) >= 0
+                ) {
+                    return PAGE_REPEAT;
+                } else {
+                    return PAGE_NONE;
+                }
+            }
+        );
+        return node_stat;
+    }
+
+    this.reset();
+
+}
diff --git a/xtrn/chat_pager/page_sysop.js b/xtrn/chat_pager/page_sysop.js
new file mode 100644
index 0000000000000000000000000000000000000000..0184b7c609bc8920882c151799be4f6c45a8d3c3
--- /dev/null
+++ b/xtrn/chat_pager/page_sysop.js
@@ -0,0 +1,71 @@
+load('sbbsdefs.js');
+load(js.exec_dir + 'lib.js');
+load('progress-bar.js');
+
+function get_last_queued_value(queue, valname) {
+    var val, temp_val;
+    while (typeof (temp_val = queue.read(valname)) !== 'undefined') {
+        val = temp_val;
+    }
+    return val;
+}
+
+function await_page_response(settings) {
+    var queue = new Queue(settings.queue.queue_name);
+    var valname = "chat_" + bbs.node_num;
+    var answered = false;
+    var stime = system.timer;
+    var utime = system.timer;
+    var progress_bar = new ProgressBar(1, 2);
+    progress_bar.init();
+    while (
+        (system.timer - stime) * 1000 < settings.terminal.wait_time &&
+        console.inkey(K_NONE) == '' &&
+        !answered
+    ) {
+        var now = system.timer;
+        if (now - utime >= 1) {
+            progress_bar.set_progress(
+                (((now - stime) * 1000) / settings.terminal.wait_time) * 100
+            );
+            progress_bar.cycle();
+            utime = now;
+        }
+        var val = get_last_queued_value(queue, valname);
+        if (typeof val == 'number' && val > stime) answered = true;
+        yield();
+    }
+    progress_bar.set_progress(100);
+    progress_bar.cycle();
+    progress_bar.close();
+    return answered;
+}
+
+function main() {
+    var settings = load_settings(js.exec_dir + 'settings.ini');
+    console.clear(WHITE);
+    console.home(0, 0);
+    console.center(format(settings.terminal.wait_message, system.operator));
+    console.crlf();
+    console.center(settings.terminal.cancel_message);
+    var xy = console.getxy();
+    if (settings !== null && await_page_response(settings)) {
+        if (settings.terminal.irc_pull) {
+            bbs.exec(
+                format(
+                    '?irc.js -a %s %s %s',
+                    settings.terminal.irc_server,
+                    settings.terminal.irc_port,
+                    settings.terminal.irc_channel
+                )
+            );
+        }
+    } else {
+        console.gotoxy(xy);
+        console.clearline();
+        console.center(format(bbs.text(522).trim(), system.operator));
+        console.pause();
+    }
+}
+
+main();
diff --git a/xtrn/chat_pager/readme.txt b/xtrn/chat_pager/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1f5e5903401d6c2ef2c7716d1172d031997a1924
--- /dev/null
+++ b/xtrn/chat_pager/readme.txt
@@ -0,0 +1,55 @@
+Some kinda page-sysop thingie
+by echicken, November 2017
+echicken at bbs.electronicchicken.com
+
+1) Setup
+
+- Run scfg
+- Select 'Chat Features'
+- Select 'External Sysop Chat Pagers'
+- Hit enter on the blank line at the bottom to create a new record
+- Set the 'Command Line' to: ?/sbbs/xtrn/chat_pager/page_sysop.js
+
+2) Configuration
+
+- Browse to the 'xtrn/chat_pager/' directory
+- Copy 'example-settings.ini' to a new file, name it 'settings.ini'
+- Edit 'settings.ini'
+- In the [terminal] section, edit irc_server, irc_port, and irc_channel as needed
+
+3) IRC Bot setup
+
+- Browse to the 'xtrn/chat_pager/ircbot/' directory
+- Open 'for-ircbot.ini' with a text editor
+- Copy its contents to your clipboard
+- Browse to your 'ctrl/' directory
+- Open 'ircbot.ini' with a text editor
+- Paste the previously copied file contents into the bottom of this file
+
+4) Finishing Up
+
+- Synchronet will need to recycle the terminal server thread to pick up changes
+- It may be necessary to restart your BBS
+- You will need to issue the 'restart' command to your IRC bot
+
+5) Using
+
+Users who page the sysop will be shown a progress/time bar while they wait for
+you to respond.
+
+Your IRC bot will notify you in the channel you've configured that somebody is
+paging you.  Respond with the command '!chat n', where 'n' is the node number,
+to pull the user into IRC chat.
+
+6) Notes
+
+I haven't tested this much.  Use at your own risk.
+
+If you run your IRC bot via jsexec, the 'IRC Pull' feature will not work; I will
+add a workaround for that soon.  It will work if you run your IRC bot as a
+service.
+
+Additional notification methods (other than IRC) may be added in the future.
+
+You don't have to use the 'IRC Pull' feature; you can always go to your host
+system and break into chat with the built in tools (from sbbsctrl or umonitor).