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

Merge branch 'wttr.in' into 'master'

wttr.in weather forecast viewer

See merge request !258
parents 79ef3bc9 3f7d18c3
No related branches found
No related tags found
2 merge requests!463MRC mods by Codefenix (2024-10-20),!258wttr.in weather forecast viewer
/*
* Just IP address lookup for now
* wttr.in does the geoip for us (albeit inaccurately)
* To do: do something with user.location / zipcode as an alternative?
*
* IP address lookup stuff adapted from syncWXremix
* (Contributed by echicken)
* https://github.com/KenDB3/syncWXremix
* Copyright (c) 2015, Kenny DiBattista <kendb3@bbs.kd3.us>
*
* ISC License
*
* Copyright (c) 2022 Zaidhaan Hussain
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
// To do: I think this is only good for IPv4
function wstsGetIPAddress() {
const ip = [];
const data = [];
try {
console.lock_input(true);
console.telnet_cmd(253, 28); // DO TTYLOC
const stime = system.timer;
while (system.timer - stime < 1) {
if (!client.socket.data_waiting) continue;
data.push(client.socket.recvBin(1));
if (data.length >= 14 && data[data.length - 3] !== 255 && data[data.length - 2] === 255 && data[data.length - 1] === 240) break;
}
// Check for a valid reply
if (data.length < 14 || // Minimum response length
// Should start like this
data[0] !== 255 || // IAC
data[1] !== 250 || // SB
data[2] !== 28 || // TTYLOC
data[3] !== 0 || // FORMAT
// Should end like this
data[data.length - 2] !== 255 || // IAC
data[data.length - 1] !== 240 // SE
) {
throw 'Invalid reply to TTYLOC command.';
}
for (var d = 4; d < data.length - 2; d++) {
ip.push(data[d]);
if (data[d] === 255) d++;
}
if (ip.length !== 8) throw 'Invalid reply to TTYLOC command.';
} catch (err) {
log(LOG_DEBUG, err);
} finally {
console.lock_input(false);
if (ip.length !== 8) return;
return ip.slice(0, 4).join('.');
}
}
// for webv4 ... I think
function wsrsGetIPAddress() {
var fn = format('%suser/%04d.web', system.data_dir, user.number);
if (!file_exists(fn)) return;
var f = new File(fn);
if (!f.open('r')) return;
var session = f.iniGetObject();
f.close();
if (typeof session.ip_address === 'undefined') return;
return session.ip_address;
}
function getAddress() {
const addrRe = /^(127\.)|(192\.168\.)|(10\.)|(172\.1[6-9]\.)|(172\.2[0-9]\.)|(172\.3[0-1]\.)|(169\.254\.)|(::1$)|([fF][cCdD])/;
if (user.ip_address.search(addrRe) > -1) {
var addr;
if (client.protocol === 'Telnet') {
addr = wstsGetIPAddress();
} else if (bbs.sys_status&SS_RLOGIN) {
addr = wsrsGetIPAddress();
}
if (addr === undefined || addr.search(addrRe) > -1) return;
return addr;
}
return user.ip_address;
}
this;
\ No newline at end of file
wttr.in viewer for Synchronet BBS
by echicken -at- bbs.electronicchicken.com
Contents
1) About
2) Installation
3) Customization
1) About
This script pulls a weather report from https://wttr.in and displays it in
the terminal. wttr.in is a console-oriented weather forecast service
created and hosted by Igor Chubin (https://github.com/chubin/wttr.in).
2) Installation
In SCFG, go to 'External Programs', then 'Online Programs (Doors)', and
choose the section to which you'd like to add this mod. Fill out the new
entry with the following details:
Name wttr.in Weather Forecast
Internal Code WTTR
Start-up Directory /sbbs/xtrn/wttr.in
Command Line ?wttr.js
Multiple Concurrent Users Yes
If you want this mod 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
Please see https://wttr.in/:help for a list of options. You may pass any
of the 'Units' and 'View options' values to this script on the command
line to customize the output, for example:
Command Line ?wttr.js m0AFn
The default is 'AFn' for ANSI, no 'Follow' line, and narrow output.
Note that your command line argument will completely replace the default
parameters. You will probably want to specify 'A' and 'n' in addition to
your chosen values.
require('sbbsdefs.js', 'P_UTF8');
require('http.js', 'HTTPRequest');
const xterm = load({}, js.exec_dir + 'xterm-colors.js');
const locator = load({}, js.exec_dir + 'locator.js');
function uReplace(str) {
return str.replace(/\xE2\x9A\xA1/g, '/ '); // U+26A1 Lightning bolt
}
function fetchWeather(addr) {
const qs = argc > 0 ? argv.join('') : 'AFn';
const http = new HTTPRequest();
if (addr !== undefined) http.extra_headers = { 'X-Forwarded-For': addr };
const body = http.Get('https://wttr.in/?' + qs);
if (http.response_code !== 200) throw new Error('wttr.in response had status ' + http.response_code);
return body;
}
function main() {
const addr = locator.getAddress();
const weather = fetchWeather(addr);
const text = uReplace(weather);
const ansi = xterm.convertColors(text);
const attr = console.attributes;
console.clear(BG_BLACK|LIGHTGRAY);
console.putmsg(ansi, P_UTF8);
console.pause();
console.attributes = attr;
}
try {
main();
} catch (err) {
log(LOG_ERROR, err);
}
\ No newline at end of file
/*
* Adapted from
* https://github.com/zaidhaan/xterm2ansi
* Copyright (c) 2022 Zaidhaan Hussain
*
* ISC License
*
* Copyright (c) 2022 Zaidhaan Hussain
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
const colors = [
{ r: 0x00, g: 0x00, b: 0x00, code: 30, bold: 0 }, // black
{ r: 0xCD, g: 0x00, b: 0x00, code: 31, bold: 0 }, // red
{ r: 0x00, g: 0xCD, b: 0x00, code: 32, bold: 0 }, // green
{ r: 0xCD, g: 0xCD, b: 0x00, code: 33, bold: 0 }, // yellow
{ r: 0x00, g: 0x00, b: 0xEE, code: 34, bold: 0 }, // blue
{ r: 0xCD, g: 0x00, b: 0xCD, code: 35, bold: 0 }, // magenta
{ r: 0x00, g: 0xCD, b: 0xCD, code: 36, bold: 0 }, // cyan
{ r: 0xE5, g: 0xE5, b: 0xE5, code: 37, bold: 0 }, // white
{ r: 0x7F, g: 0x7F, b: 0x7F, code: 30, bold: 1 }, // bright black
{ r: 0xFF, g: 0x00, b: 0x00, code: 31, bold: 1 }, // bright red
{ r: 0x00, g: 0xFF, b: 0x00, code: 32, bold: 1 }, // bright green
{ r: 0xFF, g: 0xFF, b: 0x00, code: 33, bold: 1 }, // bright yellow
{ r: 0x5C, g: 0x5C, b: 0xFF, code: 34, bold: 1 }, // bright blue
{ r: 0xFF, g: 0x00, b: 0xFF, code: 35, bold: 1 }, // bright magenta
{ r: 0x00, g: 0xFF, b: 0xFF, code: 36, bold: 1 }, // bright cyan
{ r: 0xFF, g: 0xFF, b: 0xFF, code: 37, bold: 1 }, // bright white
];
function colorDistance(r1, g1, b1, r2, g2, b2) {
return Math.sqrt(Math.pow(r2 - r1, 2) + Math.pow(b2 - b1, 2) + Math.pow(g2 - g1, 2));
}
function rgbToANSI(rgb) {
var nearest = 0;
var minDistance = 256;
for (var i = 0; i < 16; i++) {
var currentColor = colors[i];
var distance = colorDistance(rgb.r, rgb.g, rgb.b, currentColor.r, currentColor.g, currentColor.b);
if (distance < minDistance) {
minDistance = distance;
nearest = currentColor;
}
}
return nearest;
}
function xterm256ToRGB(color) {
var r = 0;
var g = 0;
var b = 0;
if (color < 16) {
r = ansi_colors[color].r;
g = ansi_colors[color].g;
b = ansi_colors[color].b;
} else if (color < 232) {
color -= 16;
const _r = (color / 36);
const _g = (color % 36) / 6;
const _b = (color % 6);
r = _r ? _r * 40 + 55 : 0;
g = _g ? _g * 40 + 55 : 0;
b = _b ? _b * 40 + 55 : 0;
} else {
color -= 232;
r = g = b = (color * 10) + 8;
}
return { r: r, g: g, b: b };
}
function xterm256ToANSI(color) {
const rgb = xterm256ToRGB(color);
return rgbToANSI(rgb);
}
function replace256(match, p1, p2, p3) {
const color = parseInt(p2, 10);
if (isNaN(color)) return '';
const ansiColor = xterm256ToANSI(color);
const code = (p1 === '48') ? (ansiColor.code + 10) : ansiColor.code;
return '\x1b[' + ansiColor.bold + ';' + code + p3 + 'm';
}
function replaceRGB(match, p1, p2, p3, p4, p5) {
const r = parseInt(p2, 10);
if (isNaN(r)) return '';
const g = parseInt(p3, 10);
if (isNaN(g)) return '';
const b = parseInt(p4, 10);
if (isNaN(b)) return '';
const ansiColor = rgbToANSI({ r: r, g: g, b:b });
const code = (p1 === '48') ? (ansiColor.code + 10): ansiColor.code;
return '\x1b[' + ansiColor.bold + ';' + code + p5 + 'm';
}
function convertColors(str) {
return str.replace(
/\x1b\[([34]8);5;(\d+)(;\d+)?m/g, replace256
).replace(
/\x1b\[([34]8);2;(\d+);(\d+);(\d+)(;\d+)?m/g, replaceRGB
);
}
this;
\ 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