1
0
Fork
You've already forked weatherbot
0
An IRC bot written without any IRC library, to read temperatures and fetch the text of Mastodon toots. Primarily a learning experience.
  • TypeScript 95.6%
  • Dockerfile 2.2%
  • Shell 2.2%
Find a file
2025年01月28日 20:43:48 +11:00
bin Re-added --detach flag in bin/run.sh. 2019年12月16日 22:54:12 +11:00
src Updated fact listener to match the mqtt, restart, and weather listener behaviour. 2025年01月28日 20:43:48 +11:00
.dockerignore Added facts listener. 2024年05月09日 20:20:29 +10:00
.eslintrc.json Initial commit. 2019年01月28日 21:27:15 +11:00
.gitignore Added facts listener. 2024年05月09日 20:20:29 +10:00
config.example.json Changed toot listener to be a generic URL listener. 2024年11月30日 10:58:58 +11:00
docker-compose.yml Removed PWD from docker-compose.yml. 2024年05月09日 21:48:21 +10:00
Dockerfile Bumped dependencies. 2025年01月28日 18:50:01 +11:00
LICENSE Added license. 2021年10月24日 16:28:30 +11:00
nodemon.json Converted to TypeScript, moved weather functionality to a separate listener module in preparation for the addition of others. 2019年12月15日 21:40:54 +11:00
package-lock.json Bumped dependencies. 2025年01月28日 18:50:01 +11:00
package.json Bumped dependencies. 2025年01月28日 18:50:01 +11:00
README.md Updated URL listener so it only previews links when the bot's nickname is mentioned. 2025年01月28日 20:25:44 +11:00
tsconfig.json Added ability to connect to MQTT broker and use EJS templates to render data. Bumped dependencies. 2024年04月25日 23:03:16 +10:00

weatherbot

An IRC bot written without any IRC library, to read weather data from one or more HTTP endpoints, or to read arbitrary data from one or more MQTT topics, and fetch the text of Mastodon toots. Primarily a learning experience to play with the IRC protocol.

10:01 <@virtualwolf> weatherbot: weather
10:01 < weatherbot> The current outdoor temperature is 20.3°C, humidity is 51%
10:03 <@virtualwolf> https://aus.social/@virtualwolf/107142233345319503
10:03 < weatherbot> > Beanie is very pooped out after his walk.
10:03 < weatherbot> > https://mediacdn.aus.social/media_attachments/files/107/142/233/323/259/793/original/2156d87b7f910b82.jpeg

Configuration

Requires a file called config.json at the root of the repository with the following format to specify an IRC server and a list of channels to join, and optionally a list of HTTP endpoints to fetch weather data from plus an EJS template string to render and optionally format the data:

{
 "connections": [
 {
 "host": "irc.example.com",
 "channels": [
 {
 "name": "#public-channel"
 }
 ]
 }
 ],
 "weather": [
 {
 "url": "https://example.com/rest/weather/locations/outdoor",
 "template": "The current outdoor temperature is <%= temperature %>°C, humidity is <%= humidity %>%"
 }
 ]
}

By default, TLS is not enabled and the bot will try to connect on port 6667.

The bot expects the weather URL(s) to return their data in the following format:

{
 "temperature": 18.8,
 "humidity": 60
}

Additional options can be specified to use a server password or join a private or secret channel, as well as enabling TLS, not verifying self-signed certificates, changing the port number, and disabling the weather, URL, fact, or restart listeners:

{
 "connections": [
 {
 "host": "irc.example.com",
 "tlsEnabled": true,
 "rejectAuthorized": false,
 "port": 6697,
 "serverPassword": "my-very-secure-password",
 "nick": "my-cool-bot",
 "channels": [
 {
 "name": "#public-channel"
 },
 {
 "name": "#secret-channel",
 "key": "hunter2",
 "disableListeners": ["url", "weather", "restart"]
 }
 ]
 }
 ]
}

URL previews

The bot can read the OpenGraph tags from a URL to show a preview:

20:23 <@virtualwolf> weatherbot: https://aus.social/@virtualwolf/113904270125475469
20:23 < weatherbot> > Well, the southerly has certainly arrived with a vengeance. #SydneyWeatherTooting
20:23 < weatherbot> > https://mediacdn.aus.social/media_attachments/files/113/904/270/104/532/889/original/490bfbdd2a0dffa2.jpeg

It includes a link to images so IRC clients like The Lounge can display them inline.

Fact listener

This is a very basic setup that uses a JSON file called facts.json in the root of the repository as a persistent datastore to store "facts" in the form of " is ":

22:08 <@virtualwolf> weatherbot: virtualwolf is a drummer
22:08 < weatherbot> Thanks, I have made a note of this.
22:09 <@virtualwolf> weatherbot: define virtualwolf
22:09 < weatherbot> virtualwolf is a drummer.
22:10 <@virtualwolf> weatherbot: virtualwolf is a drummer
22:10 < weatherbot> This has already been noted.
22:11 <@virtualwolf> weatherbot: virtualwolf is also a guitar player
22:11 < weatherbot> Thanks, I have made a note of this.

Multiple facts can be saved against a given subject, and if more than one exists, a random one will be chosen when the define command is used.

To forget a specific fact, use "forget that is ":

22:11 <@virtualwolf> weatherbot: forget that virtualwolf is a drummer
22:11 < weatherbot> I've forgotten that virtualwolf is a drummer.

MQTT

You can also have the bot connect to an MQTT broker to read JSON data from an arbitrary topic or set of topics, and render the data appropriately. EJS is used instead of Mustache so the MQTT data can be reformatted as necessary.

Update config.json to include an mqtt block (the port defaults to 1883 if not specified):

{
 "connections": [...],
 "mqtt": {
 "brokerAddress": "mqtt.example.com",
 "brokerPort": 1883,
 "dataTypes": {
 "weather": [
 {
 "topic": "home/outdoor/weather",
 "template": "Outdoor: <%= temperature.toFixed(1) %>°C & <%= Math.round(humidity) %>% humidity. Dew point is <%= dew_point.toFixed(1) %>°C. Atmospheric pressure is <%= Math.round(pressure) %> hPa."
 },
 {
 "topic": "home/indoor/weather",
 "template": "Indoor: <%= temperature.toFixed(1) %>°C & <%= Math.round(humidity) %>% humidity."
 }
 ],
 "power": [
 {
 "topic": "home/power",
 "template": "Consumption is <%= (home_usage/1000).toFixed(2) %>kW, solar generation is <%= solar_generation === 0 ? solar_generation : (solar_generation/1000).toFixed(2) %>kW. Current battery charge is <%= battery_charge_percentage === 100 ? battery_charge_percentage : battery_charge_percentage.toFixed(1) %>%."
 }
 ],
 "airquality": [
 {
 "topic": "home/outdoor/airquality",
 "template": "Outdoor: PM1.0 is <%= pm_1_0 %>, PM2.5 is <%= pm_2_5 %>, PM10 is <%= pm_10 %>."
 }
 ]
 }
 },
}

The bot will respond to the name of the dataType key, so in the example above, weather, power, and airquality:

10:04 <@virtualwolf> weatherbot: weather
10:04 < weatherbot> Outdoor: 20.6°C & 47% humidity. Dew point is 9.3°C. Atmospheric pressure is 1016 hPa.
10:04 < weatherbot> Indoor: 21.7°C & 42% humidity.
10:04 <@virtualwolf> weatherbot: airquality
10:04 < weatherbot> Outdoor: PM1.0 is 1, PM2.5 is 3, PM10 is 4.
10:04 <@virtualwolf> weatherbot: power
10:04 < weatherbot> Consumption is 1.19kW, solar generation is 4.45kW. Current battery charge is 100%.

If the weather key exists as a top-level configuration key and in the mqtt block, the MQTT block will take precedence.

Running

Build the container and start it with docker compose up --build -d