rustybook 0.2.0

An ergonomic Facebook client in Rust
Documentation
use std::error::Error;
use std::sync::Arc;

use rustybook::{
    Context,
    EventHandler,
    Message,
    Rustybook,
    User,
};
use rustybook_logging::{
    Logger,
    TracingHookConfig,
};
use tracing::level_filters::LevelFilter;

struct Handler;

impl EventHandler for Handler {
    async fn ready(&self, _ctx: Context, user: User) {
        logger().verb(format!("ready: id={} name={:?}", user.id, user.name));
    }

    async fn message(&self, ctx: Context, msg: Message) {
        logger().verb(format!(
            "message: thread={} sender={} text={:?}",
            msg.thread_id, msg.sender_id, msg.text
        ));

        match msg.text.as_deref() {
            Some("hi") => {
                if let Err(error) = ctx.client().send_text(&msg.thread_id, "hello").await {
                    logger().warn(format!("failed to auto-reply: {error}"));
                }
            }
            #[cfg(all(feature = "messenger", feature = "cache"))]
            Some(message) => {
                if let Some(user) = ctx.client().user().await
                    && msg.sender_id != user.id
                    && let Err(error) = ctx.client().send_text(&msg.thread_id, message).await
                {
                    logger().warn(format!("failed to echo: {error}"));
                }
            }
            #[cfg(not(all(feature = "messenger", feature = "cache")))]
            Some(_) => {}
            None => {}
        }
    }

    async fn typing(&self, _ctx: Context, typing: rustybook::Typing) {
        logger().verb(format!(
            "typing: user_id={} thread_id={:?} is_typing={}",
            typing.user_id, typing.thread_id, typing.is_typing
        ));
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    init_logging();

    let mut args = std::env::args();
    let _bin = args.next();

    let Some(cookies_path) = args.next() else {
        eprintln!("usage: cargo run --example messenger --features messenger -- <cookies.json>");
        return Ok(());
    };

    let client = Rustybook::builder()
        .cookies_file_path(cookies_path)
        .online(true)
        .build()?;

    client.start(Arc::new(Handler)).await?;
    logger().verb("listening for events (Ctrl+C to stop)");

    tokio::signal::ctrl_c().await?;
    client.stop().await?;

    Ok(())
}

fn init_logging() {
    let logger = logger();
    if let Err(error) = logger.install_tracing_hook_from_config() {
        eprintln!("failed to install tracing hook subscriber: {error}");
    }
}

fn logger() -> Logger {
    let tracing_config = TracingHookConfig::builder()
        .level_filter(LevelFilter::DEBUG)
        .filter("rustybook=debug,rustybook_messenger=debug")
        .include_level(true)
        .include_target(true)
        .include_fields(true)
        .build();

    Logger::builder().tracing_config(tracing_config).build()
}