-
-
Save jgaskins/cc6b66c4624d65577f91800b14573dc5 to your computer and use it in GitHub Desktop.
Mastodon weather bot written in Crystal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Replace the domain with your Mastodon server | |
API_URL="https://your.server/api/v1/statuses" | |
# On your Mastodon server, go to `/settings/applications` and create an | |
# application, then get the access token from it and place it here. | |
API_KEY= | |
# Comma-separated list of URLs. Use RSS links from the NWS website, found here: | |
# https://forecast.weather.gov/xml/current_obs/seek.php?state=KS | |
WEATHER_FEEDS= |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: weather_bot | |
version: 0.1.0 | |
authors: | |
- Jamie Gaskins <[email protected]> | |
targets: | |
weather_bot: | |
main: weather_bot.cr | |
dependencies: | |
rss: | |
github: ruivieira/rss | |
sqlite3: | |
github: crystal-lang/crystal-sqlite3 | |
dotenv: | |
github: gdotdesign/cr-dotenv | |
crystal: '>= 1.16.3' | |
license: MIT |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "rss" | |
require "sqlite3" | |
require "dotenv" | |
Dotenv.load? | |
api_url = ENV["API_URL"] | |
api_key = ENV["API_KEY"] | |
urls = ENV["WEATHER_FEEDS"].split(',') | |
log = Log.for("weather-bot") | |
running = true | |
Signal::TERM.trap { running = false } | |
db = DB.open("sqlite3://#{ENV.fetch("DB_PATH", "db.sqlite3")}") | |
provision db | |
while running | |
urls.each do |url| | |
feed = RSS.parse url | |
feed.items.each do |e| | |
result = db.exec <<-SQL, url.to_s, e.guid, e.title, e.description | |
INSERT INTO feed_items (source, guid, title, description) | |
VALUES (?, ?, ?, ?) | |
ON CONFLICT (guid) DO NOTHING | |
SQL | |
begin | |
if result.rows_affected > 0 | |
response = HTTP::Client.post api_url, | |
headers: HTTP::Headers{ | |
"Authorization" => "Bearer #{api_key}", | |
"Idempotency-Key" => "#{url} #{e.guid}", | |
}, | |
form: {"status" => e.description.strip.lines[1..].join("\n")} | |
log.info &.emit "posted", | |
status: response.status.to_s, | |
response: response.body | |
end | |
rescue ex | |
log.error &.emit "Error occurred in talking to the Mastodon API", error: ex.message || "[no message provided]" | |
end | |
rescue ex | |
log.error &.emit "Error occurred in saving the feed item", error: ex.message || "[no message provided]" | |
end | |
rescue ex | |
log.error &.emit "Error occurred in fetching or parsing the feed", error: ex.message || "[no message provided]" | |
end | |
sleep 1.minute | |
end | |
def provision(db) | |
db.exec <<-SQL | |
CREATE TABLE IF NOT EXISTS feed_items( | |
source TEXT, | |
guid TEXT UNIQUE, | |
title TEXT, | |
description TEXT | |
) | |
SQL | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment