Skip to main content

tulpje_framework/
lib.rs

1use twilight_gateway::Event;
2use twilight_model::gateway::payload::incoming::InteractionCreate;
3
4pub use context::{Context, EventContext, InteractionContext};
5pub use framework::Framework;
6pub use metadata::Metadata;
7pub use module::{Module, builder::ModuleBuilder, registry::Registry};
8
9pub mod color;
10pub mod context;
11pub mod framework;
12pub mod handler;
13pub mod interaction;
14pub mod macros;
15pub mod metadata;
16pub mod module;
17pub mod scheduler;
18
19pub type Error = Box<dyn std::error::Error + Send + Sync>;
20
21pub async fn handle_interaction<T: Clone + Send + Sync + 'static>(
22    event: InteractionCreate,
23    context: Context<T>,
24    meta: &Metadata,
25    registry: &Registry<T>,
26    processed: twilight_standby::ProcessResults,
27) -> Result<(), Error> {
28    match interaction::parse(&event, meta.clone(), context) {
29        Ok(InteractionContext::Command(ctx)) => {
30            let Some(command) = registry.find_command(&ctx.name) else {
31                return Err(format!("unknown command /{}", ctx.name).into());
32            };
33
34            if let Err(err) = command.run(ctx.clone()).await {
35                return Err(format!("error running command /{}: {}", ctx.name, err).into());
36            }
37        }
38        Ok(InteractionContext::ComponentInteraction(ctx)) => {
39            let Some(component_interaction) = registry.components.get(&ctx.interaction.custom_id)
40            else {
41                // twilight-standby already handled this interaction as well, so
42                // we don't wanna error for it
43                if processed.matched() > 0 {
44                    return Ok(());
45                }
46
47                // otherwise do error so the missing handler gets logged
48                return Err(format!(
49                    "no handler for component interaction {}",
50                    ctx.interaction.custom_id
51                )
52                .into());
53            };
54
55            if let Err(err) = component_interaction.run(ctx.clone()).await {
56                return Err(format!(
57                    "error handling component interaction {}: {}",
58                    ctx.interaction.custom_id, err
59                )
60                .into());
61            }
62        }
63        Ok(InteractionContext::Modal(_modal_context)) => {
64            todo!()
65        }
66        Err(err) => return Err(format!("error handling interaction: {}", err).into()),
67    };
68
69    Ok(())
70}
71
72pub async fn handle<T: Clone + Send + Sync + 'static>(
73    meta: Metadata,
74    ctx: Context<T>,
75    registry: &Registry<T>,
76    event: Event,
77) {
78    let processed = ctx.standby.process(&event);
79
80    if let twilight_gateway::Event::InteractionCreate(event) = event.clone()
81        && let Err(err) = handle_interaction(*event, ctx.clone(), &meta, registry, processed).await
82    {
83        tracing::warn!(err);
84    }
85
86    if let Some(handlers) = registry.events.get(&event.kind()) {
87        tracing::debug!("running event handlers for {:?}", event.kind());
88
89        for handler in handlers {
90            let event_ctx = EventContext::from_context(ctx.clone(), meta.clone(), event.clone());
91
92            if let Err(err) = handler.run(event_ctx).await {
93                tracing::warn!(
94                    "error running event handler {} for event {:?}: {}",
95                    handler.uuid,
96                    event.kind(),
97                    err
98                );
99            }
100        }
101    }
102}