timebot

Convert live time across time-zones on IRC
git clone http://git.hanabi.in/repos/timebot.git
Log | Files | Refs | LICENSE

commit 18d8b9186a8f000c709783cc005144efc38a7e78
Author: Agastya Chandrakant <acagastya@outlook.com>
Date:   Sun, 16 Aug 2020 02:38:19 +0530

initial commit

Diffstat:
A.gitignore | 2++
Aconfig.js | 6++++++
Aconfig.json | 10++++++++++
Aindex.js | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackage.json | 9+++++++++
Ayarn.lock | 49+++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 216 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +node_modules +config.gitignore.json diff --git a/config.js b/config.js @@ -0,0 +1,6 @@ +module.exports = { + server: "irc.freenode.net", + nick: "timebot", + channels: ["#nie", "##lipsum"], + maintainers: ["acagastya"] +}; diff --git a/config.json b/config.json @@ -0,0 +1,10 @@ +{ + "channels": [ + "#foo", + "##bar" + ], + "alias": { + "john": "America/New_York", + "doe": "Australia/Sydney", + } +} diff --git a/index.js b/index.js @@ -0,0 +1,140 @@ +const fs = require("fs"); +const moment = require("moment-timezone"); +const irc = require("irc"); + +const { server, nick, maintainers } = require("./config"); +const FILE = "./config.json"; +let { alias, channels } = JSON.parse(fs.readFileSync(FILE)); + +const ircClient = new irc.Client(server, nick, { channels }); + +ircClient.addListener("error", errorHandler); + +ircClient.addListener("message", normalMsg); + +ircClient.addListener("pm", inviteHandler); + +function inviteHandler(sender, msg) { + if (!msg.startsWith("#")) return; + channels.push(msg); + fs.writeFileSync( + FILE, + JSON.stringify({ channels, alias }, null, 2) + "\n", + err => { + if (err) { + ircClient.say(sender, `Error occurred: ${err}.`); + return; + } + } + ); + ircClient.join(msg); + ircClient.say(sender, "Attempted to join."); + channels = JSON.parse(fs.readFileSync(FILE)); +} + +function normalMsg(sender, channel, msg) { + if (msg.includes(`${nick} help`)) showHelp(channel, msg); + else if (msg.includes(`${nick} ls `)) showList(channel, msg); + else if (msg.includes(`${nick} add `)) addAlias(sender, channel, msg); + else if (msg.includes(`${nick} rm `)) deleteAlias(sender, channel, msg); + else if (msg.includes(`${nick} `)) sayTime(channel, msg); + else if (msg.includes(nick)) showHelp(channel, msg); +} + +function errorHandler(msg) { + for (const maintainer of maintainers) + ircClient.say(maintainer, `${nick} error: ${msg}`); +} + +function showHelp(channel, msg) { + ircClient.say(channel, `Use "${nick} help" to show help.`); + ircClient.say( + channel, + `Use "${nick} ls <timezone>" to show a list of valid timezones.` + ); + ircClient.say( + channel, + `Use "${nick} <timezone>" to display current time of <timezone>.` + ); + ircClient.say( + channel, + `Use "${nick} add <alias>:<timezone>" for adding an alias for a timezone.` + ); + ircClient.say( + channel, + `Use "${nick} rm <alias>" from removing an alias for a timezone.` + ); +} + +function showList(channel, msg) { + const reg = new RegExp(`${nick}:? ls (.*)`); + const zone = msg.match(reg)[1]; + const allZones = moment.tz.names().map(el => el.replace(/_/g, " ")); + const res = allZones + .filter(el => el.toLowerCase().includes(zone.toLowerCase())) + .join(", "); + ircClient.say(channel, res); +} + +function sayTime(channel, msg) { + const reg = new RegExp(`${nick}:? (.*)`); + const zone = msg.match(reg)[1].replace(/ /g, "_"); + const machineReadableZone = alias[zone] || zone; + const TZ = moment.tz.names().includes(machineReadableZone) + ? machineReadableZone + : "UTC"; + let time = moment.tz(TZ).format("HH:mm MMM DD z"); + ircClient.say(channel, time); +} + +function addAlias(sender, channel, msg) { + if (!maintainers.includes(sender)) { + ircClient.say(channel, "Only maintainers allowed to add aliases."); + return; + } + if (!msg.includes(":")) { + ircClient.say(channel, "Wrong syntax."); + showHelp(channel); + return; + } + const reg = new RegExp(`${nick}:? add (.*)`); + const [key, value] = msg.match(reg)[1].split(":"); + const machineReadableValue = value.replace(/ /g, "_"); + if (moment.tz.names().includes(machineReadableValue)) { + alias[key] = machineReadableValue; + fs.writeFileSync( + FILE, + JSON.stringify({ channels, alias }, null, 2) + "\n", + err => { + if (err) { + ircClient.say(channel, `Error occurred: ${err}`); + return; + } + alias = JSON.parse(fs.readFileSync(FILE)).alias; + ircClient.say(channel, `Alias for ${key} has been added.`); + } + ); + } +} + +function deleteAlias(sender, channel, msg) { + if (!maintainers.includes(sender)) { + ircClient.say(channel, "Only maintainers allowed to delete aliases."); + return; + } + const reg = new RegExp(`${nick}:? rm (.*)`); + const key = msg.match(reg)[1]; + delete alias[key]; + fs.writeFileSync( + FILE, + JSON.stringify({ channels, alias }, null, 2) + "\n", + err => { + if (err) { + ircClient.say(channel, `Error occurred: ${err}`); + return; + } + alias = JSON.parse(fs.readFileSync(FILE)).alias; + ircClient.say(channel, `Alias for ${key} now does not exist.`); + } + ); +} diff --git a/package.json b/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "irc": "^0.5.2", + "moment-timezone": "^0.5.31" + }, + "scripts": { + "start": "node index.js" + } +} diff --git a/yarn.lock b/yarn.lock @@ -0,0 +1,49 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +iconv@~2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/iconv/-/iconv-2.2.3.tgz#e084d60eeb7d73da7f0a9c096e4c8abe090bfaed" + integrity sha1-4ITWDut9c9p/CpwJbkyKvgkL+u0= + dependencies: + nan "^2.3.5" + +irc-colors@^1.1.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/irc-colors/-/irc-colors-1.5.0.tgz#08834c01ead88b0fd88386a5f2af8f2b0bb963fb" + integrity sha512-HtszKchBQTcqw1DC09uD7i7vvMayHGM1OCo6AHt5pkgZEyo99ClhHTMJdf+Ezc9ovuNNxcH89QfyclGthjZJOw== + +irc@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/irc/-/irc-0.5.2.tgz#3714f4768365a96d0b2f776bc91166beb2464bbc" + integrity sha1-NxT0doNlqW0LL3dryRFmvrJGS7w= + dependencies: + irc-colors "^1.1.0" + optionalDependencies: + iconv "~2.2.1" + node-icu-charset-detector "~0.2.0" + +moment-timezone@^0.5.31: + version "0.5.31" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05" + integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA== + dependencies: + moment ">= 2.9.0" + +"moment@>= 2.9.0": + version "2.27.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + +nan@^2.3.3, nan@^2.3.5: + version "2.14.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" + integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== + +node-icu-charset-detector@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/node-icu-charset-detector/-/node-icu-charset-detector-0.2.0.tgz#c2320da374ddcb671fc54cb4a0e041e156ffd639" + integrity sha1-wjINo3Tdy2cfxUy0oOBB4Vb/1jk= + dependencies: + nan "^2.3.3"