Browse Source

Initial commit

tags/v0.1.1
Stephen 10 months ago
commit
c3ab631a19
9 changed files with 2318 additions and 0 deletions
  1. +1
    -0
      .gitignore
  2. +2052
    -0
      Cargo.lock
  3. +34
    -0
      Cargo.toml
  4. +9
    -0
      README.md
  5. +5
    -0
      config.example
  6. +13
    -0
      debian/cat-disruptor.service
  7. +28
    -0
      src/config.rs
  8. +45
    -0
      src/create_relation_event.rs
  9. +131
    -0
      src/main.rs

+ 1
- 0
.gitignore View File

@ -0,0 +1 @@
/target

+ 2052
- 0
Cargo.lock
File diff suppressed because it is too large
View File


+ 34
- 0
Cargo.toml View File

@ -0,0 +1,34 @@
[package]
name = "cat_disruptor_7000"
version = "0.1.0"
authors = ["Stephen D <@stephen:m.scd31.com>"]
edition = "2018"
[dependencies]
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk", features = ["encryption", "sqlite-cryptostore"] }
matrix-sdk-common-macros = { git = "https://github.com/matrix-org/matrix-rust-sdk" }
tokio = { version = "0.2", features = ["full"] }
url = "2.1"
async-trait = "0.1.36"
ruma-identifiers = "0.16"
ruma-api = "0.16"
ruma-events = "0.16"
ruma-client-api = "0.9.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tracing = "0.1"
tracing-subscriber = "0.2"
uuid = { version = "0.8.1", features = ["v4"] }
[package.metadata.deb]
maintainer = "Stephen D <@stephen:m.scd31.com>"
copyright = "2020, Stephen D <@stephen:m.scd31.com>"
extended-description = """\
Reacts to matrix messages contaning 'cat' with a cat emoji."""
depends = "systemd"
section = "utility"
priority = "optional"
assets = [
["target/release/cat_disruptor_7000", "usr/bin/", "755"],
["debian/cat-disruptor.service", "/lib/systemd/system/cat-disruptor.service", "644"]
]

+ 9
- 0
README.md View File

@ -0,0 +1,9 @@
# Cat Disruptor 7000
This is a matrix bot which will react to messages containing "cat" with a cat emoji. It is written in Rust and supports both encrypted and unencrypted rooms.
## Usage
You can test the bot out by inviting `@catdisruptorbot:m.scd31.com` to your room. It will automatically join and do its thing.
You can also install manually, using a deb package from the releases section. Make sure to configure the user settings in `/etc/cat_disruptor/config.json` .

+ 5
- 0
config.example View File

@ -0,0 +1,5 @@
{
"homeserver_url": "changeme",
"username": "changeme",
"password": "changeme"
}

+ 13
- 0
debian/cat-disruptor.service View File

@ -0,0 +1,13 @@
[Unit]
Description=Cat Disruptor 7000
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
PIDFile=/run/cat-disruptor.pid
ExecStart=/usr/bin/cat_disruptor_7000
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target

+ 28
- 0
src/config.rs View File

@ -0,0 +1,28 @@
use serde::{Serialize, Deserialize};
use std::fs::File;
#[derive(Default, Serialize, Deserialize)]
pub struct Config {
pub homeserver_url: String,
pub username: String,
pub password: String,
}
pub fn get_config() -> Config {
let file = get_conf_file();
let json: serde_json::Value = serde_json::from_reader(file).unwrap();
serde_json::from_value(json).unwrap()
}
fn get_conf_file() -> File {
let filenames = ["config.json", "/etc/cat_disruptor_7000/config.json"];
for filename in filenames.iter() {
match File::open(filename) {
Ok(x) => return x,
Err(_) => continue
}
}
panic!("Could not open config file");
}

+ 45
- 0
src/create_relation_event.rs View File

@ -0,0 +1,45 @@
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use ruma_identifiers::EventId;
use ruma_api::ruma_api;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct ContentStruct {
pub rel_type: String,
pub event_id: EventId,
pub key: String
}
ruma_api! {
metadata {
description: "Send a relation event to a room",
method: PUT,
name: "create_relation_event",
path: "/_matrix/client/r0/rooms/:room_id/send/:event_type/:txn_id",
rate_limited: false,
requires_authentication: true
}
request {
/// The room to send the relation to.
#[ruma_api(path)]
pub room_id: crate::RoomId,
/// The type of event to send.
#[ruma_api(path)]
pub event_type: String,
/// The transaction ID for this event
#[ruma_api(path)]
pub txn_id: String,
#[serde(flatten)]
pub content: HashMap<String, ContentStruct>
}
response {
pub event_id: String
}
error: ruma_client_api::Error
}

+ 131
- 0
src/main.rs View File

@ -0,0 +1,131 @@
use std::collections::HashMap;
use url::Url;
use ruma_identifiers::user_id::UserId;
use ruma_identifiers::RoomId;
use matrix_sdk::{
self,
events::{
room::{
message::{MessageEvent, MessageEventContent, TextMessageEventContent},
member::MemberEventContent
},
stripped::{
StrippedRoomMember,
},
},
Client, ClientConfig, EventEmitter, SyncRoom, SyncSettings, RoomState
};
use async_trait::async_trait;
use uuid::Uuid;
mod create_relation_event;
mod config;
struct EventCallback {
client: Client,
user_id: UserId
}
impl EventCallback {
pub fn new(client: Client, user_id: UserId) -> Self {
Self { client, user_id }
}
}
#[async_trait]
impl EventEmitter for EventCallback {
//Handles room messages
async fn on_room_message(&self, room: SyncRoom, event: &MessageEvent) {
if let SyncRoom::Joined(room) = room {
if let MessageEvent {
content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
event_id,
..
} = event {
if msg_body.to_lowercase().contains("cat") {
// Cat in message. React with emoji
let mut content_hm: HashMap<String, create_relation_event::ContentStruct>
= HashMap::new();
content_hm.insert("m.relates_to".to_string(),
create_relation_event::ContentStruct {
rel_type: "m.annotation".to_string(),
event_id: event_id.clone(),
key: "🐈".to_string() // cat emoji
});
let request = create_relation_event::Request {
event_type: "m.reaction".to_string(),
room_id: room.read().await.room_id.clone(),
txn_id: Uuid::new_v4().to_string(),
content: content_hm
};
match self.client.send(request).await {
Ok(_) => {},
Err(x) => println!("Error while reacting: {}", x)
}
}
}
}
}
//Handles incoming invitations
async fn on_stripped_state_member(&self,
sync_room: SyncRoom,
room_member: &StrippedRoomMember,
_: Option<MemberEventContent>) {
if room_member.state_key == self.user_id {
match sync_room {
RoomState::Invited(x) => {
let room = x.read().await;
println!("Invitation received from {}", room.display_name());
match self.client.join_room_by_id(&room.room_id).await {
Ok(_) => println!("Joined {}", room.display_name()),
Err(_) => println!("There was an error while joining {}", room.display_name())
};
},
_ => {}
}
}
}
}
async fn login(
homeserver_url: String,
username: String,
password: String
) -> Result<(), matrix_sdk::Error> {
let config = ClientConfig::new();
let homeserver_url = Url::parse(&homeserver_url).expect("Couldn't parse the homeserver URL");
let mut client = Client::new_with_config(homeserver_url, config).unwrap();
let login_response = client.login(username, password, None, Some("Cat Disruptor".to_string())).await?;
println!("Logged in as {}", login_response.user_id);
let event_callback = EventCallback::new(client.clone(), login_response.user_id);
client.add_event_emitter(Box::new(event_callback)).await;
client.sync_forever(SyncSettings::new(), |_| async {}).await;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), matrix_sdk::Error> {
tracing_subscriber::fmt().with_env_filter(tracing_subscriber::EnvFilter::from_default_env()).init();
let config = config::get_config();
if config.homeserver_url == "changeme" &&
config.username == "changeme" &&
config.password == "changeme" {
panic!("Please update the values in the config file!");
}
login(config.homeserver_url, config.username, config.password).await
}

Loading…
Cancel
Save