#[cfg(feature = "crypto")]
pub mod crypto;
#[cfg(feature = "ffi")]
pub mod ffi;
#[cfg(feature = "websocket")]
pub mod ws;
#[cfg(feature = "xftp")]
pub mod xftp;
pub mod bot;
pub mod dispatcher;
pub mod ext;
pub mod id;
pub mod messages;
pub mod prelude;
pub mod preview;
mod util;
pub use simploxide_api_types::{
self as types,
client_api::{self, BadResponseError, ClientApi, ClientApiError},
commands, events,
events::{Event, EventKind},
responses,
utils::CommandSyntax,
};
#[cfg(feature = "cancellation")]
pub use tokio_util::{self, sync::CancellationToken};
pub use dispatcher::DispatchChain;
use futures::{Stream, TryStreamExt as _};
use std::{
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
pub struct EventStream<P> {
filter: [bool; EventKind::COUNT],
receiver: tokio::sync::mpsc::UnboundedReceiver<P>,
hooks: Vec<Box<dyn Hook>>,
}
impl<P> From<tokio::sync::mpsc::UnboundedReceiver<P>> for EventStream<P> {
fn from(receiver: tokio::sync::mpsc::UnboundedReceiver<P>) -> Self {
Self {
filter: [true; EventKind::COUNT],
receiver,
hooks: Vec::new(),
}
}
}
impl<P> EventStream<P> {
pub fn add_hook(&mut self, hook: Box<dyn Hook>) {
self.hooks.push(hook);
}
#[cfg(feature = "xftp")]
pub fn hook_xftp<C: 'static + Clone + ClientApi>(&mut self, client: C) -> xftp::XftpClient<C> {
let xftp_client = xftp::XftpClient::from(client);
let hook = xftp_client.clone();
self.add_hook(Box::new(hook));
xftp_client
}
pub fn set_filter<I: IntoIterator<Item = EventKind>>(&mut self, f: Filter<I>) -> &mut Self {
match f {
Filter::Accept(kinds) => {
self.reject_all();
for kind in kinds {
self.filter[kind.as_usize()] = true;
}
}
Filter::AcceptAllExcept(kinds) => {
self.accept_all();
for kind in kinds {
self.filter[kind.as_usize()] = false;
}
}
Filter::AcceptAll => self.accept_all(),
}
self
}
pub fn accept(&mut self, kind: EventKind) {
self.filter[kind.as_usize()] = true;
}
pub fn reject(&mut self, kind: EventKind) {
self.filter[kind.as_usize()] = false;
}
pub fn accept_all(&mut self) {
self.set_all(true);
}
pub fn reject_all(&mut self) {
self.set_all(false)
}
fn set_all(&mut self, new: bool) {
for old in &mut self.filter {
*old = new;
}
}
}
impl<P: EventParser> EventStream<P> {
pub fn into_dispatcher<C>(self, ctx: C) -> DispatchChain<P, C> {
DispatchChain::with_ctx(self, ctx)
}
pub async fn wait_for<Ev: events::EventData>(&mut self) -> Result<Option<Arc<Ev>>, P::Error> {
self.reject_all();
self.accept(Ev::KIND);
let result = self.try_next().await;
self.accept_all();
let ev = result?;
Ok(ev.map(|ev| Ev::from_event(ev).unwrap()))
}
pub async fn wait_for_any(
&mut self,
kinds: impl IntoIterator<Item = EventKind>,
) -> Result<Option<Event>, P::Error> {
self.set_filter(Filter::Accept(kinds));
let result = self.try_next().await;
self.accept_all();
result
}
pub async fn stream_events<E, F>(mut self, mut f: F) -> Result<Self, E>
where
F: AsyncFnMut(Event) -> Result<StreamEvents, E>,
E: From<P::Error>,
{
while let Some(event) = self.try_next().await? {
if let StreamEvents::Break = f(event).await? {
break;
}
}
Ok(self)
}
pub async fn stream_events_with_ctx_mut<E, Ctx, F>(
mut self,
mut f: F,
mut ctx: Ctx,
) -> Result<(Self, Ctx), E>
where
F: AsyncFnMut(Event, &mut Ctx) -> Result<StreamEvents, E>,
E: From<P::Error>,
{
while let Some(event) = self.try_next().await? {
if let StreamEvents::Break = f(event, &mut ctx).await? {
break;
}
}
Ok((self, ctx))
}
pub async fn stream_events_with_ctx_cloned<E, Ctx, F>(
mut self,
f: F,
ctx: Ctx,
) -> Result<(Self, Ctx), E>
where
Ctx: Clone,
F: AsyncFn(Event, Ctx) -> Result<StreamEvents, E>,
E: From<P::Error>,
{
while let Some(event) = self.try_next().await? {
if let StreamEvents::Break = f(event, ctx.clone()).await? {
break;
}
}
Ok((self, ctx))
}
}
pub enum Filter<I: IntoIterator<Item = EventKind>> {
Accept(I),
AcceptAll,
AcceptAllExcept(I),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StreamEvents {
Break,
Continue,
}
impl<P: EventParser> Stream for EventStream<P> {
type Item = Result<Event, P::Error>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
loop {
match self.receiver.poll_recv(cx) {
Poll::Ready(Some(raw_event)) => {
let kind = match raw_event.parse_kind() {
Ok(kind) => kind,
Err(e) => break Poll::Ready(Some(Err(e))),
};
if !self.hooks.iter().any(|h| h.should_intercept(kind))
&& !self.filter[kind.as_usize()]
{
continue;
}
match raw_event.parse_event() {
Ok(event) => {
for hook in self.hooks.iter_mut() {
if hook.should_intercept(kind) {
hook.intercept_event(event.clone());
}
}
if self.filter[kind.as_usize()] {
break Poll::Ready(Some(Ok(event)));
}
}
Err(e) => break Poll::Ready(Some(Err(e))),
}
}
Poll::Ready(None) => break Poll::Ready(None),
Poll::Pending => break Poll::Pending,
}
}
}
}
pub trait EventParser {
type Error;
fn parse_kind(&self) -> Result<EventKind, Self::Error>;
fn parse_event(&self) -> Result<Event, Self::Error>;
}
impl EventParser for Event {
type Error = std::convert::Infallible;
fn parse_kind(&self) -> Result<EventKind, Self::Error> {
Ok(self.kind())
}
fn parse_event(&self) -> Result<Event, Self::Error> {
Ok(self.clone())
}
}
pub trait Hook {
fn should_intercept(&self, kind: EventKind) -> bool;
fn intercept_event(&mut self, event: Event);
}
pub mod preferences {
use simploxide_api_types::{FeatureAllowed, SimplePreference};
pub mod timed_messages {
use super::*;
use simploxide_api_types::TimedMessagesPreference;
pub const TTL_MAX: std::time::Duration = std::time::Duration::from_hours(8784);
pub fn ttl_to_secs(ttl: std::time::Duration) -> i32 {
let clamped = std::cmp::min(ttl, TTL_MAX);
clamped.as_secs() as i32
}
pub fn always(ttl: std::time::Duration) -> Option<TimedMessagesPreference> {
Some(TimedMessagesPreference {
allow: FeatureAllowed::Always,
ttl: Some(ttl_to_secs(ttl)),
undocumented: serde_json::Value::Null,
})
}
pub fn yes(ttl: std::time::Duration) -> Option<TimedMessagesPreference> {
Some(TimedMessagesPreference {
allow: FeatureAllowed::Yes,
ttl: Some(ttl_to_secs(ttl)),
undocumented: serde_json::Value::Null,
})
}
pub const NO: Option<TimedMessagesPreference> = Some(TimedMessagesPreference {
allow: FeatureAllowed::No,
ttl: None,
undocumented: serde_json::Value::Null,
});
}
pub const ALWAYS: Option<SimplePreference> = Some(SimplePreference {
allow: FeatureAllowed::Always,
undocumented: serde_json::Value::Null,
});
pub const YES: Option<SimplePreference> = Some(SimplePreference {
allow: FeatureAllowed::Yes,
undocumented: serde_json::Value::Null,
});
pub const NO: Option<SimplePreference> = Some(SimplePreference {
allow: FeatureAllowed::No,
undocumented: serde_json::Value::Null,
});
}