zynko 0.1.0

Biblioteca oficial para bots do Zynko: API, gateway e voz.
Documentation
//! Events: an [`EventHandler`] trait, and the [`Event`] enum for `subscribe` and `wait_for`.

use std::sync::Arc;
use std::time::Duration;

use async_trait::async_trait;
use serde_json::Value;

use crate::cache::CachedGuild;
use crate::client::Client;
use crate::error::{BoxError, Error};
use crate::interaction::Interaction;
use crate::message::Message;
use crate::types::*;

/// What a handler returns: any error becomes an `error` event, without stopping the bot.
pub type HandlerResult = Result<(), BoxError>;

/// The link dropped; the client is connecting again after `wait`.
#[derive(Debug, Clone)]
pub struct ReconnectInfo {
    pub code: u16,
    pub reason: String,
    pub wait: Duration,
}

/// Every event the client hands out, for [`Client::subscribe`] and [`Client::wait_for`].
#[derive(Debug, Clone)]
#[non_exhaustive]
#[allow(clippy::large_enum_variant)]
pub enum Event {
    /// Connected (again): the caches were rebuilt from READY.
    Ready(User),
    MessageCreate(Message),
    /// Edited, or link previews are ready. Partial: see [`Message`].
    MessageUpdate(Message),
    MessageDelete(MessageDeleteEvent),
    ReactionAdd(ReactionEvent),
    ReactionRemove(ReactionEvent),
    TypingStart(TypingEvent),
    /// Added to a server, or connected with it in READY's list.
    GuildCreate(CachedGuild),
    GuildUpdate(CachedGuild),
    /// Removed from a server, or the server was deleted.
    GuildDelete(Id),
    ChannelCreate(Channel),
    ChannelUpdate {
        channel: Channel,
        previous: Option<Channel>,
    },
    ChannelDelete(Channel),
    MemberAdd(Member),
    MemberUpdate {
        member: Member,
        previous: Option<Member>,
    },
    MemberRemove(MemberRemoveEvent),
    PresenceUpdate(PresenceEvent),
    VoiceStateUpdate {
        state: VoiceState,
        previous: Option<VoiceState>,
    },
    /// Someone used one of the bot's "/" commands, or clicked a button on its message.
    Interaction(Interaction),
    /// Every gateway event, as it came.
    Raw {
        kind: String,
        data: Value,
    },
    /// The link dropped; the client is connecting again.
    Reconnecting(ReconnectInfo),
    /// An error in a handler, or the token was refused.
    Error(Arc<Error>),
}

/// The bot's reactions to events. Every method has an empty default: implement the ones you
/// need. Each call runs in its own task; an `Err` (or a panic) becomes an [`error`](Self::error)
/// call instead of a crash.
///
/// ```no_run
/// use zynko::{async_trait, Client, EventHandler, HandlerResult, Message};
///
/// struct Bot;
///
/// #[async_trait]
/// impl EventHandler for Bot {
///     async fn message_create(&self, _ctx: Client, msg: Message) -> HandlerResult {
///         if !msg.mine() && msg.content == "!ping" {
///             msg.reply("pong").await?;
///         }
///         Ok(())
///     }
/// }
/// ```
#[async_trait]
#[allow(unused_variables)]
pub trait EventHandler: Send + Sync + 'static {
    /// Connected (again): the caches were rebuilt from READY.
    async fn ready(&self, ctx: Client, user: User) -> HandlerResult {
        Ok(())
    }
    async fn message_create(&self, ctx: Client, message: Message) -> HandlerResult {
        Ok(())
    }
    async fn message_update(&self, ctx: Client, message: Message) -> HandlerResult {
        Ok(())
    }
    async fn message_delete(&self, ctx: Client, event: MessageDeleteEvent) -> HandlerResult {
        Ok(())
    }
    async fn reaction_add(&self, ctx: Client, reaction: ReactionEvent) -> HandlerResult {
        Ok(())
    }
    async fn reaction_remove(&self, ctx: Client, reaction: ReactionEvent) -> HandlerResult {
        Ok(())
    }
    async fn typing_start(&self, ctx: Client, typing: TypingEvent) -> HandlerResult {
        Ok(())
    }
    async fn guild_create(&self, ctx: Client, guild: CachedGuild) -> HandlerResult {
        Ok(())
    }
    async fn guild_update(&self, ctx: Client, guild: CachedGuild) -> HandlerResult {
        Ok(())
    }
    async fn guild_delete(&self, ctx: Client, guild_id: Id) -> HandlerResult {
        Ok(())
    }
    async fn channel_create(&self, ctx: Client, channel: Channel) -> HandlerResult {
        Ok(())
    }
    async fn channel_update(
        &self,
        ctx: Client,
        channel: Channel,
        previous: Option<Channel>,
    ) -> HandlerResult {
        Ok(())
    }
    async fn channel_delete(&self, ctx: Client, channel: Channel) -> HandlerResult {
        Ok(())
    }
    async fn member_add(&self, ctx: Client, member: Member) -> HandlerResult {
        Ok(())
    }
    async fn member_update(
        &self,
        ctx: Client,
        member: Member,
        previous: Option<Member>,
    ) -> HandlerResult {
        Ok(())
    }
    async fn member_remove(&self, ctx: Client, event: MemberRemoveEvent) -> HandlerResult {
        Ok(())
    }
    async fn presence_update(&self, ctx: Client, presence: PresenceEvent) -> HandlerResult {
        Ok(())
    }
    async fn voice_state_update(
        &self,
        ctx: Client,
        state: VoiceState,
        previous: Option<VoiceState>,
    ) -> HandlerResult {
        Ok(())
    }
    /// Someone used one of the bot's "/" commands, or clicked a button on its message.
    async fn interaction(&self, ctx: Client, interaction: Interaction) -> HandlerResult {
        Ok(())
    }
    /// Every gateway event, as it came.
    async fn raw(&self, ctx: Client, kind: String, data: Value) -> HandlerResult {
        Ok(())
    }
    /// The link dropped; the client is connecting again.
    async fn reconnecting(&self, ctx: Client, info: ReconnectInfo) -> HandlerResult {
        Ok(())
    }
    /// An error in another handler, or the token was refused. By default it is printed to
    /// stderr.
    async fn error(&self, ctx: Client, error: Arc<Error>) {
        eprintln!("zynko: {error}");
    }
}

/// Calls the handler's method for an event.
pub(crate) async fn call(handler: &dyn EventHandler, ctx: Client, event: Event) -> HandlerResult {
    match event {
        Event::Ready(u) => handler.ready(ctx, u).await,
        Event::MessageCreate(m) => handler.message_create(ctx, m).await,
        Event::MessageUpdate(m) => handler.message_update(ctx, m).await,
        Event::MessageDelete(d) => handler.message_delete(ctx, d).await,
        Event::ReactionAdd(r) => handler.reaction_add(ctx, r).await,
        Event::ReactionRemove(r) => handler.reaction_remove(ctx, r).await,
        Event::TypingStart(t) => handler.typing_start(ctx, t).await,
        Event::GuildCreate(g) => handler.guild_create(ctx, g).await,
        Event::GuildUpdate(g) => handler.guild_update(ctx, g).await,
        Event::GuildDelete(id) => handler.guild_delete(ctx, id).await,
        Event::ChannelCreate(c) => handler.channel_create(ctx, c).await,
        Event::ChannelUpdate { channel, previous } => {
            handler.channel_update(ctx, channel, previous).await
        }
        Event::ChannelDelete(c) => handler.channel_delete(ctx, c).await,
        Event::MemberAdd(m) => handler.member_add(ctx, m).await,
        Event::MemberUpdate { member, previous } => {
            handler.member_update(ctx, member, previous).await
        }
        Event::MemberRemove(d) => handler.member_remove(ctx, d).await,
        Event::PresenceUpdate(p) => handler.presence_update(ctx, p).await,
        Event::VoiceStateUpdate { state, previous } => {
            handler.voice_state_update(ctx, state, previous).await
        }
        Event::Interaction(i) => handler.interaction(ctx, i).await,
        Event::Raw { kind, data } => handler.raw(ctx, kind, data).await,
        Event::Reconnecting(info) => handler.reconnecting(ctx, info).await,
        Event::Error(e) => {
            handler.error(ctx, e).await;
            Ok(())
        }
    }
}