#[macro_use] extern crate log; #[macro_use] extern crate failure; #[macro_use] extern crate smart_default; #[macro_use] extern crate serde; use websocket::{OwnedMessage}; use std::thread; mod bootstrap; mod store; mod ele; use crate::bootstrap::handle_cli_args_and_load_config; use crate::store::Store; use crate::ele::{EleSession, EleStreamSocket, parse_stream_socket_msg, ParsedSocketMsg}; use std::time::Duration; use elefren::entities::prelude::*; use elefren::entities::notification::NotificationType; use elefren::{MastodonClient, StatusBuilder}; use elefren::status_builder::Visibility; #[derive(SmartDefault,Serialize,Deserialize,Debug)] #[serde(default)] pub struct Config { /// Logging level to use (can be increased using -v flags) #[default="info"] pub logging: String, /// Instance domain name, e.g. example.com pub instance: String, /// File to use for session storage #[default="manabu_store.json"] pub store: String, } fn main() { let config : Config = handle_cli_args_and_load_config().expect("error init"); debug!("Loaded config: {:#?}", config); let mut store = Store::from_file(&config.store); store.set_autosave(true); store.set_pretty_print(true); let registered = ele::register(&mut store, &config); let session = ele::open_session(&mut store, registered); debug!("Listening to events"); let m_session = session.clone(); let handle = thread::spawn(move || job_listen_to_stream(m_session, "user")); let _ = handle.join(); } /// Listen to a streaming API channel, reconnecting on failure. fn job_listen_to_stream(session : EleSession, kind : &str) -> ! { loop { debug!("Trying to open websocket"); match ele::open_stream_websocket(&session, kind) { Ok(socket) => process_incoming_events(&session, socket), Err(e) => error!("Error opening socket: {}", e) } debug!("Delay before reconnecting..."); thread::sleep(Duration::from_secs(5)); } } /// Process events received from a streaming API websocket fn process_incoming_events(session : &EleSession, mut socket : EleStreamSocket) { 'listen: loop { let recv_result = socket.recv_message(); if let Ok(Some(msg)) = parse_stream_socket_msg(recv_result) { match msg { ParsedSocketMsg::WsClose => { debug!("Close"); break 'listen; }, ParsedSocketMsg::WsPing(data) => { debug!("Sending Pong"); let _ = socket.send_message(&OwnedMessage::Pong(data)); }, ParsedSocketMsg::Event(event) => { handle_received_event(session, event); } } } } } /// Build the special syntax for a @ mention fn make_mention(user : &Account) -> String { format!(r#"@{handle}"#, url=user.url, handle=user.acct ) } /// Do something with an event we received from the API fn handle_received_event(session : &EleSession, event: Event) { debug!("*** Event received:\n{:?}", event); match event { Event::Notification(Notification { notification_type: NotificationType::Mention, account, status: Some(status), .. }) => { debug!("Sending a reply."); let poste = StatusBuilder::new() .status(format!("Hello {} :blobpats:", &make_mention(&account))) .content_type("text/html") .in_reply_to(status.id) .visibility(Visibility::Unlisted) .build().expect("error build status"); session.new_status(poste).expect("error send."); } _ => { // } } }