pub extern crate async_std;
pub extern crate typemap;
pub extern crate robespierre_http;
pub extern crate robespierre_models;
#[cfg(feature = "cache")]
pub extern crate robespierre_cache;
#[cfg(feature = "events")]
pub extern crate robespierre_events;
use std::sync::Arc;
#[cfg(feature = "framework")]
use framework::Framework;
use model::ServerIdExt;
#[cfg(feature = "cache")]
use robespierre_cache::{Cache, CacheConfig, CommitToCache, HasCache};
#[cfg(feature = "events")]
use robespierre_events::{
typing::TypingSession, ConnectionMessage, ConnectionMessanger, EventsError, RawEventHandler,
};
use robespierre_http::{Http, HttpAuthentication, HttpError};
use robespierre_models::{
channels::{Channel, ChannelField, Message, PartialChannel, PartialMessage},
events::{ReadyEvent, ServerToClientEvent},
id::{ChannelId, MemberId, MessageId, RoleId, ServerId, UserId},
servers::{MemberField, PartialMember, PartialRole, PartialServer, RoleField, ServerField},
users::{RelationshipStatus, UserField, UserPatch},
};
pub use async_trait::async_trait;
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use typemap::ShareMap;
#[cfg(feature = "framework")]
pub mod framework;
pub mod model;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("http error")]
Http(#[from] HttpError),
#[cfg(feature = "events")]
#[error("events error")]
Events(#[from] EventsError),
}
pub type Result<T = ()> = std::result::Result<T, Error>;
#[cfg(feature = "events")]
#[async_trait::async_trait]
#[allow(unused_variables)]
pub trait EventHandler: Send + Sync {
async fn on_ready(&self, ctx: Context, ready: ReadyEvent) {}
async fn on_message(&self, ctx: Context, message: Message) {}
async fn on_message_update(
&self,
ctx: Context,
channel: ChannelId,
message: MessageId,
modifications: PartialMessage,
) {
}
async fn on_message_delete(&self, ctx: Context, channel_id: ChannelId, message_id: MessageId) {}
async fn on_channel_create(&self, ctx: Context, channel: Channel) {}
async fn on_channel_update(
&self,
ctx: Context,
channel_id: ChannelId,
modifications: PartialChannel,
remove: Option<ChannelField>,
) {
}
async fn on_channel_delete(&self, ctx: Context, channel_id: ChannelId) {}
async fn on_group_join(&self, ctx: Context, id: ChannelId, user: UserId) {}
async fn on_group_leave(&self, ctx: Context, id: ChannelId, user: UserId) {}
async fn on_start_typing(&self, ctx: Context, channel: ChannelId, user: UserId) {}
async fn on_stop_typing(&self, ctx: Context, channel: ChannelId, user: UserId) {}
async fn on_server_update(
&self,
ctx: Context,
server: ServerId,
modifications: PartialServer,
remove: Option<ServerField>,
) {
}
async fn on_server_delete(&self, ctx: Context, server: ServerId) {}
async fn on_server_member_join(&self, ctx: Context, server: ServerId, user: UserId) {}
async fn on_server_member_update(
&self,
ctx: Context,
member: MemberId,
modifications: PartialMember,
remove: Option<MemberField>,
) {
}
async fn on_server_member_leave(&self, ctx: Context, server: ServerId, user: UserId) {}
async fn on_server_role_update(
&self,
ctx: Context,
server: ServerId,
role: RoleId,
modifications: PartialRole,
remove: Option<RoleField>,
) {
}
async fn on_server_role_delete(&self, ctx: Context, server: ServerId, role: RoleId) {}
async fn on_user_update(
&self,
ctx: Context,
id: UserId,
modifications: UserPatch,
remove: Option<UserField>,
) {
}
async fn on_user_relationship_update(
&self,
ctx: Context,
self_id: UserId,
other_id: UserId,
status: RelationshipStatus,
) {
}
}
#[cfg(all(feature = "events", feature = "cache"))]
#[derive(Clone)]
pub struct CacheWrap<Inner>(Inner)
where
Inner: RawEventHandler,
Inner::Context: HasCache + Clone + 'static;
#[cfg(all(feature = "events", feature = "cache"))]
impl<Inner> CacheWrap<Inner>
where
Inner: RawEventHandler,
Inner::Context: HasCache + Clone + 'static,
{
pub fn new(inner: Inner) -> Self {
Self(inner)
}
}
#[cfg(all(feature = "events", feature = "cache"))]
#[async_trait::async_trait]
impl<Inner> RawEventHandler for CacheWrap<Inner>
where
Inner: RawEventHandler,
Inner::Context: HasCache + Clone + 'static,
{
type Context = Inner::Context;
async fn handle(self, ctx: Self::Context, event: ServerToClientEvent) {
event.commit_to_cache_ref(&ctx).await;
self.0.handle(ctx, event).await
}
}
#[cfg(all(feature = "events", feature = "framework"))]
#[derive(Clone)]
pub struct FrameworkWrap<
FrameworkContext: From<Context> + Send + Sync + 'static,
Inner: EventHandler + Clone + 'static,
> {
fw: Arc<RwLock<Box<dyn Framework<Context = FrameworkContext> + 'static>>>,
inner: Inner,
}
#[cfg(all(feature = "events", feature = "framework"))]
impl<
FrameworkContext: From<Context> + Send + Sync + 'static,
Inner: EventHandler + Clone + 'static,
> FrameworkWrap<FrameworkContext, Inner>
{
pub fn new<Fw: Framework<Context = FrameworkContext> + 'static>(fw: Fw, inner: Inner) -> Self {
Self {
fw: Arc::new(RwLock::new(Box::new(fw))),
inner,
}
}
}
#[cfg(all(feature = "events", feature = "framework"))]
#[async_trait::async_trait]
impl<
FrameworkContext: From<Context> + Send + Sync + 'static,
Inner: EventHandler + Clone + 'static,
> EventHandler for FrameworkWrap<FrameworkContext, Inner>
{
async fn on_ready(&self, ctx: Context, ready: ReadyEvent) {
self.inner.on_ready(ctx, ready).await
}
async fn on_message(&self, ctx: Context, message: Message) {
let message = Arc::new(message);
self.fw
.read()
.await
.handle(FrameworkContext::from(ctx.clone()), &message)
.await;
let message = Arc::try_unwrap(message).expect("Do not store `Arc<Message>`s while handling them in the framework. Instead, clone the inner `Message`s.");
self.inner.on_message(ctx, message).await
}
async fn on_message_update(
&self,
ctx: Context,
channel: ChannelId,
message: MessageId,
modifications: PartialMessage,
) {
self.inner
.on_message_update(ctx, channel, message, modifications)
.await
}
async fn on_message_delete(&self, ctx: Context, channel_id: ChannelId, message_id: MessageId) {
self.inner
.on_message_delete(ctx, channel_id, message_id)
.await
}
async fn on_channel_create(&self, ctx: Context, channel: Channel) {
self.inner.on_channel_create(ctx, channel).await
}
async fn on_channel_update(
&self,
ctx: Context,
channel_id: ChannelId,
modifications: PartialChannel,
remove: Option<ChannelField>,
) {
self.inner
.on_channel_update(ctx, channel_id, modifications, remove)
.await
}
async fn on_channel_delete(&self, ctx: Context, channel_id: ChannelId) {
self.inner.on_channel_delete(ctx, channel_id).await
}
async fn on_group_join(&self, ctx: Context, id: ChannelId, user: UserId) {
self.inner.on_group_join(ctx, id, user).await
}
async fn on_group_leave(&self, ctx: Context, id: ChannelId, user: UserId) {
self.inner.on_group_leave(ctx, id, user).await
}
async fn on_start_typing(&self, ctx: Context, channel: ChannelId, user: UserId) {
self.inner.on_start_typing(ctx, channel, user).await
}
async fn on_stop_typing(&self, ctx: Context, channel: ChannelId, user: UserId) {
self.inner.on_stop_typing(ctx, channel, user).await
}
async fn on_server_update(
&self,
ctx: Context,
server: ServerId,
modifications: PartialServer,
remove: Option<ServerField>,
) {
self.inner
.on_server_update(ctx, server, modifications, remove)
.await
}
async fn on_server_delete(&self, ctx: Context, server: ServerId) {
self.inner.on_server_delete(ctx, server).await
}
async fn on_server_member_join(&self, ctx: Context, server: ServerId, user: UserId) {
self.inner.on_server_member_join(ctx, server, user).await
}
async fn on_server_member_update(
&self,
ctx: Context,
member: MemberId,
modifications: PartialMember,
remove: Option<MemberField>,
) {
self.inner
.on_server_member_update(ctx, member, modifications, remove)
.await
}
async fn on_server_member_leave(&self, ctx: Context, server: ServerId, user: UserId) {
self.inner.on_server_member_leave(ctx, server, user).await
}
async fn on_server_role_update(
&self,
ctx: Context,
server: ServerId,
role: RoleId,
modifications: PartialRole,
remove: Option<RoleField>,
) {
self.inner
.on_server_role_update(ctx, server, role, modifications, remove)
.await
}
async fn on_server_role_delete(&self, ctx: Context, server: ServerId, role: RoleId) {
self.inner.on_server_role_delete(ctx, server, role).await
}
async fn on_user_update(
&self,
ctx: Context,
id: UserId,
modifications: UserPatch,
remove: Option<UserField>,
) {
self.inner
.on_user_update(ctx, id, modifications, remove)
.await
}
async fn on_user_relationship_update(
&self,
ctx: Context,
self_id: UserId,
other_id: UserId,
status: RelationshipStatus,
) {
self.inner
.on_user_relationship_update(ctx, self_id, other_id, status)
.await
}
}
#[cfg(all(feature = "events", feature = "cache"))]
#[derive(Clone)]
pub struct CacheServersMaintainer<Inner>
where
Inner: RawEventHandler + Clone,
Inner::Context: CacheHttp,
{
user_id: UserId,
inner: Inner,
}
#[cfg(all(feature = "events", feature = "cache"))]
impl<Inner> CacheServersMaintainer<Inner>
where
Inner: RawEventHandler + Clone,
Inner::Context: CacheHttp,
{
pub fn new(user_id: UserId, inner: Inner) -> Self {
Self { user_id, inner }
}
}
#[cfg(all(feature = "events", feature = "cache"))]
#[async_trait::async_trait]
impl<Inner> RawEventHandler for CacheServersMaintainer<Inner>
where
Inner: RawEventHandler + Clone,
Inner::Context: CacheHttp,
{
type Context = Inner::Context;
async fn handle(self, ctx: Self::Context, event: ServerToClientEvent) {
if let Some(cache) = ctx.cache() {
match &event {
ServerToClientEvent::ServerMemberJoin { id, user } => {
if *user == self.user_id {
let _ = id.server(&ctx).await; }
}
ServerToClientEvent::ServerMemberLeave { id, user } => {
if *user == self.user_id {
cache.delete_server(*id).await;
}
}
_ => {}
}
}
self.inner.handle(ctx, event).await
}
}
#[cfg(feature = "events")]
#[derive(Clone)]
pub struct EventHandlerWrap<Inner: EventHandler + Clone + 'static>(Inner);
#[cfg(feature = "events")]
impl<Inner: EventHandler + Clone + 'static> EventHandlerWrap<Inner> {
pub fn new(inner: Inner) -> Self {
Self(inner)
}
}
#[cfg(feature = "events")]
#[async_trait::async_trait]
impl<T> RawEventHandler for EventHandlerWrap<T>
where
T: EventHandler + Clone + 'static,
{
type Context = Context;
async fn handle(self, ctx: Self::Context, event: ServerToClientEvent) {
match event {
ServerToClientEvent::Error { error } => tracing::error!("Error: {}", error),
ServerToClientEvent::Authenticated => {}
ServerToClientEvent::Pong { data } => {
tracing::debug!("Got a pong from the server, time: {}", data)
}
ServerToClientEvent::Ready { event } => self.0.on_ready(ctx, event).await,
ServerToClientEvent::Message { message } => self.0.on_message(ctx, message).await,
ServerToClientEvent::MessageUpdate { id, channel, data } => {
self.0.on_message_update(ctx, channel, id, data).await
}
ServerToClientEvent::MessageDelete { id, channel } => {
self.0.on_message_delete(ctx, channel, id).await
}
ServerToClientEvent::ChannelCreate { channel } => {
self.0.on_channel_create(ctx, channel).await
}
ServerToClientEvent::ChannelUpdate { id, data, clear } => {
self.0.on_channel_update(ctx, id, data, clear).await
}
ServerToClientEvent::ChannelDelete { id } => self.0.on_channel_delete(ctx, id).await,
ServerToClientEvent::ChannelGroupJoin { id, user } => {
self.0.on_group_join(ctx, id, user).await
}
ServerToClientEvent::ChannelGroupLeave { id, user } => {
self.0.on_group_leave(ctx, id, user).await
}
ServerToClientEvent::ChannelStartTyping { id, user } => {
self.0.on_start_typing(ctx, id, user).await
}
ServerToClientEvent::ChannelStopTyping { id, user } => {
self.0.on_stop_typing(ctx, id, user).await
}
ServerToClientEvent::ChannelAck {
id,
user,
message_id,
} => tracing::debug!("Got ack ch={}, user={}, message={}", id, user, message_id),
ServerToClientEvent::ServerUpdate { id, data, clear } => {
self.0.on_server_update(ctx, id, data, clear).await
}
ServerToClientEvent::ServerDelete { id } => self.0.on_server_delete(ctx, id).await,
ServerToClientEvent::ServerMemberUpdate { id, data, clear } => {
self.0.on_server_member_update(ctx, id, data, clear).await
}
ServerToClientEvent::ServerMemberJoin { id, user } => {
self.0.on_server_member_join(ctx, id, user).await
}
ServerToClientEvent::ServerMemberLeave { id, user } => {
self.0.on_server_member_leave(ctx, id, user).await
}
ServerToClientEvent::ServerRoleUpdate {
id,
role_id,
data,
clear,
} => {
self.0
.on_server_role_update(ctx, id, role_id, data, clear)
.await
}
ServerToClientEvent::ServerRoleDelete { id, role_id } => {
self.0.on_server_role_delete(ctx, id, role_id).await
}
ServerToClientEvent::UserUpdate { id, data, clear } => {
self.0.on_user_update(ctx, id, data, clear).await
}
ServerToClientEvent::UserRelationship { id, user, status } => {
self.0
.on_user_relationship_update(ctx, id, user, status)
.await
}
}
}
}
pub enum Authentication {
Bot { token: String },
User { session_token: String },
}
impl Authentication {
pub fn bot(token: impl Into<String>) -> Self {
Self::Bot {
token: token.into(),
}
}
pub fn user(session_token: impl Into<String>) -> Self {
Self::User {
session_token: session_token.into(),
}
}
}
#[cfg(feature = "events")]
impl<'a> From<&'a Authentication> for robespierre_events::Authentication<'a> {
fn from(auth: &'a Authentication) -> Self {
match auth {
Authentication::Bot { token } => Self::Bot {
token: token.as_str(),
},
Authentication::User { session_token } => Self::User {
session_token: session_token.as_str(),
},
}
}
}
impl<'a> From<&'a Authentication> for HttpAuthentication<'a> {
fn from(auth: &'a Authentication) -> Self {
match auth {
Authentication::Bot { token } => Self::BotToken {
token: token.as_str(),
},
Authentication::User { session_token } => Self::UserSession {
session_token: session_token.as_str(),
},
}
}
}
#[derive(Clone)]
pub struct Context {
pub http: Arc<Http>,
#[cfg(feature = "cache")]
pub cache: Option<Arc<Cache>>,
pub data: Arc<RwLock<ShareMap>>,
#[cfg(feature = "events")]
messanger: Option<ConnectionMessanger>,
}
impl AsRef<Context> for Context {
fn as_ref(&self) -> &Context {
self
}
}
#[async_trait::async_trait]
pub trait UserData {
async fn data_lock_read(&self) -> RwLockReadGuard<ShareMap>;
async fn data_lock_write(&self) -> RwLockWriteGuard<ShareMap>;
}
#[async_trait::async_trait]
impl UserData for Context {
async fn data_lock_read(&self) -> RwLockReadGuard<ShareMap> {
self.data.read().await
}
async fn data_lock_write(&self) -> RwLockWriteGuard<ShareMap> {
self.data.write().await
}
}
impl Context {
pub fn new(http: Http, typemap: impl Into<ShareMap>) -> Self {
Self {
http: Arc::new(http),
#[cfg(feature = "cache")]
cache: None,
data: Arc::new(RwLock::new(typemap.into())),
#[cfg(feature = "events")]
messanger: None,
}
}
#[cfg(feature = "cache")]
pub fn with_cache(self, cache_config: CacheConfig) -> Self {
Self {
cache: Some(Cache::new(cache_config)),
..self
}
}
#[cfg(feature = "events")]
pub(crate) fn start_typing(&self, channel: ChannelId) -> TypingSession {
let messanger = self.messanger.as_ref().expect(
"need messager; did you forget to call .set_messager(...) on robespierre::Context?",
);
messanger.send(ConnectionMessage::StartTyping { channel });
TypingSession::new(channel, messanger.clone())
}
}
#[cfg(feature = "events")]
impl robespierre_events::Context for Context {
fn set_messanger(self, messanger: ConnectionMessanger) -> Self {
Self {
messanger: Some(messanger),
..self
}
}
}
#[cfg(feature = "cache")]
impl HasCache for Context {
fn get_cache(&self) -> Option<&Cache> {
self.cache.as_deref()
}
}
pub trait HasHttp: Send + Sync {
fn get_http(&self) -> &Http;
}
impl HasHttp for Context {
fn get_http(&self) -> &Http {
&*self.http
}
}
impl HasHttp for Http {
fn get_http(&self) -> &Http {
self
}
}
#[cfg(feature = "cache")]
pub trait CacheHttp: HasCache {
fn http(&self) -> &Http;
fn cache(&self) -> Option<&Cache>;
}
#[cfg(feature = "cache")]
impl<T: HasCache + HasHttp> CacheHttp for T {
fn http(&self) -> &Http {
self.get_http()
}
fn cache(&self) -> Option<&Cache> {
self.get_cache()
}
}
#[cfg(not(feature = "cache"))]
pub trait CacheHttp {
fn http(&self) -> &Http;
}
#[cfg(not(feature = "cache"))]
impl<T: HasHttp> CacheHttp for T {
fn http(&self) -> &Http {
self.get_http()
}
}
#[cfg(doctest)]
mod booktests;