Initial commit
This commit is contained in:
commit
9be5f1c1b6
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
.env
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "matrix-ril100"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Agatha V. Lovelace <agatha@technogothic.net>"]
|
||||
description = "A matrix bot that looks up RIL100 codes and station names"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dotenv = "0.15.0"
|
||||
matrix-sdk = "0.6.2"
|
||||
miette = { version = "5.9.0", features = ["fancy"] }
|
||||
reqwest = { version = "0.11.18", features = ["json"] }
|
||||
serde_json = "1.0.99"
|
||||
tokio = { version = "1.29.0", features = ["full"] }
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"nodes": {
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1687852486,
|
||||
"narHash": "sha256-2rXkhKUVQxbVaC+TITPpILiy/dSbordOLs87eoWHYxA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "df10963b956962913b693a638746a95d6c506404",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"ref": "master",
|
||||
"repo": "naersk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1687946342,
|
||||
"narHash": "sha256-vRxti8pOuXS0rJmqjbD8ueEEFXWSK22ISHoCWkhgzzg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1c851e8c92b76a00ce84167984a7ec7ba2b1f29c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1687946342,
|
||||
"narHash": "sha256-vRxti8pOuXS0rJmqjbD8ueEEFXWSK22ISHoCWkhgzzg=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "1c851e8c92b76a00ce84167984a7ec7ba2b1f29c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"utils": "utils"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1687709756,
|
||||
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
inputs = {
|
||||
naersk.url = "github:nix-community/naersk/master";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, utils, naersk }:
|
||||
utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
naersk-lib = pkgs.callPackage naersk { };
|
||||
in {
|
||||
packages.default = naersk-lib.buildPackage {
|
||||
src = ./.;
|
||||
nativeBuildInputs = with pkgs; [ pkg-config ];
|
||||
buildInputs = with pkgs; [ openssl ];
|
||||
};
|
||||
devShells.default = with pkgs;
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
cargo
|
||||
rustc
|
||||
rustfmt
|
||||
pre-commit
|
||||
rustPackages.clippy
|
||||
rust-analyzer
|
||||
];
|
||||
RUST_SRC_PATH = rustPlatform.rustLibSrc;
|
||||
};
|
||||
});
|
||||
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
use matrix_sdk::{
|
||||
config::SyncSettings,
|
||||
room::Room,
|
||||
ruma::{
|
||||
events::room::message::{
|
||||
MessageType, OriginalSyncRoomMessageEvent, RoomMessageEventContent,
|
||||
},
|
||||
OwnedUserId,
|
||||
},
|
||||
Client,
|
||||
};
|
||||
use miette::{miette, Context, IntoDiagnostic, Result};
|
||||
|
||||
use dotenv::dotenv;
|
||||
use reqwest::Url;
|
||||
use serde_json::Value;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
dotenv().ok();
|
||||
|
||||
let hs = env::var("MATRIX_HOMESERVER")
|
||||
.into_diagnostic()
|
||||
.wrap_err("Variable: MATRIX_HOMESERVER")?;
|
||||
let username = env::var("MATRIX_USERNAME")
|
||||
.into_diagnostic()
|
||||
.wrap_err("Variable: MATRIX_USERNAME")?;
|
||||
let password = env::var("MATRIX_PASSWORD")
|
||||
.into_diagnostic()
|
||||
.wrap_err("Variable: MATRIX_PASSWORD")?;
|
||||
|
||||
let hs_url = Url::parse(&hs).into_diagnostic()?;
|
||||
let client = Client::new(hs_url.clone()).await.into_diagnostic()?;
|
||||
|
||||
client
|
||||
.login_username(&username, &password)
|
||||
.initial_device_display_name("RIL100 Bot")
|
||||
.send()
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
let response = client
|
||||
.sync_once(SyncSettings::default())
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
println!("Logged in as RIL100 bot");
|
||||
|
||||
let homeserver = hs_url
|
||||
.host_str()
|
||||
.ok_or_else(|| return miette!("Homeserver URL does not contain a host 🤨"))?
|
||||
.to_string();
|
||||
|
||||
let user_id = client
|
||||
.user_id()
|
||||
.ok_or_else(|| return miette!("Client does not have a User ID"))?
|
||||
.to_owned();
|
||||
|
||||
client.add_event_handler(move |ev, room| {
|
||||
on_room_message(
|
||||
ev,
|
||||
room,
|
||||
username.clone(),
|
||||
homeserver.clone(),
|
||||
user_id.clone(),
|
||||
)
|
||||
});
|
||||
|
||||
let settings = SyncSettings::default().token(response.next_batch);
|
||||
client.sync(settings).await.into_diagnostic()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn on_room_message(
|
||||
event: OriginalSyncRoomMessageEvent,
|
||||
room: Room,
|
||||
username: String,
|
||||
homeserver: String,
|
||||
current_user: OwnedUserId,
|
||||
) -> Result<()> {
|
||||
// Make sure room is joined
|
||||
let Room::Joined(room) = room else { return Ok(()) };
|
||||
// Check if message has text content
|
||||
let MessageType::Text(text) = event.clone().content.msgtype else { return Ok(()) };
|
||||
// Do not reply to own messages
|
||||
if event.sender == current_user {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Only reply to mentions
|
||||
if text.formatted.map_or(false, |v| {
|
||||
v.body.contains(&format!(
|
||||
"<a href=\"https://matrix.to/#/@{username}:{homeserver}\">"
|
||||
))
|
||||
}) {
|
||||
// Drop the mention
|
||||
let query = text
|
||||
.body
|
||||
.split_once(": ")
|
||||
.ok_or_else(|| {
|
||||
return miette!("Message both contains and doesn't contain a mention 🤨");
|
||||
})?
|
||||
.1
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let response = if query.chars().all(char::is_uppercase) {
|
||||
let body = reqwest::get(format!("https://v6.db.transport.rest/stations/{query}"))
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.json::<Value>()
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
let name = body
|
||||
.get("name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("unknown");
|
||||
|
||||
format!("<b>{query}</b> is <b>{name}</b>")
|
||||
} else {
|
||||
let body = reqwest::get(format!(
|
||||
"https://v6.db.transport.rest/stations?query={query}&results=1"
|
||||
))
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.json::<Value>()
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
// Get first object field
|
||||
let body = body.as_object().and_then(|v| v.values().next());
|
||||
|
||||
let code = body
|
||||
.and_then(|v| v.get("ril100"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("unknown");
|
||||
|
||||
let station = body
|
||||
.and_then(|v| v.get("name"))
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(&query);
|
||||
|
||||
format!("<b>{station}</b> is <b>{code}</b>")
|
||||
};
|
||||
|
||||
room.send(
|
||||
RoomMessageEventContent::text_html(
|
||||
response.replace("<b>", "").replace("</b>", ""),
|
||||
response,
|
||||
)
|
||||
.make_reply_to(&event.into_full_event(room.room_id().into())),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue