1use crate::cache::invalidate_channel;
7use crate::config::BabyriteConfig;
8use crate::expand::{EXPANDERS, ExpandContext, ExpandedContent};
9use serenity::all::{
10 ActivityData, Context, EventHandler, GuildChannel, Message, PartialGuildChannel, Ready,
11};
12use serenity::futures::future::join_all;
13use tracing::Instrument;
14
15pub struct BabyriteEventHandler;
17
18#[serenity::async_trait]
19impl EventHandler for BabyriteEventHandler {
20 async fn ready(&self, ctx: Context, bot: Ready) {
21 let version = format!("v{}", env!("CARGO_PKG_VERSION"));
22 ctx.set_activity(ActivityData::custom(format!("Running {}", version)).into());
23 tracing::info!("Running {}, {} is connected!", version, bot.user.name);
24 }
25
26 async fn channel_create(&self, _ctx: Context, channel: GuildChannel) {
35 invalidate_channel(channel.guild_id, channel.id).await;
36 }
37
38 async fn channel_update(&self, _ctx: Context, _old: Option<GuildChannel>, new: GuildChannel) {
39 invalidate_channel(new.guild_id, new.id).await;
40 }
41
42 async fn channel_delete(
43 &self,
44 _ctx: Context,
45 channel: GuildChannel,
46 _messages: Option<Vec<Message>>,
47 ) {
48 invalidate_channel(channel.guild_id, channel.id).await;
49 }
50
51 async fn thread_create(&self, _ctx: Context, thread: GuildChannel) {
52 invalidate_channel(thread.guild_id, thread.id).await;
53 }
54
55 async fn thread_update(&self, _ctx: Context, _old: Option<GuildChannel>, new: GuildChannel) {
56 invalidate_channel(new.guild_id, new.id).await;
57 }
58
59 async fn thread_delete(
60 &self,
61 _ctx: Context,
62 thread: PartialGuildChannel,
63 _full_thread_data: Option<GuildChannel>,
64 ) {
65 invalidate_channel(thread.guild_id, thread.id).await;
66 }
67
68 async fn message(&self, ctx: Context, request: Message) {
69 if request.author.bot {
70 return;
71 }
72
73 let Some(request_guild_id) = request.guild_id else {
74 return;
75 };
76
77 let span = tracing::info_span!(
82 "message",
83 message_id = %request.id,
84 guild_id = %request_guild_id,
85 channel_id = %request.channel_id,
86 author = %request.author.name,
87 );
88
89 async {
90 let config = BabyriteConfig::get();
91
92 let cx = ExpandContext {
95 ctx: &ctx,
96 message: &request,
97 guild_id: request_guild_id,
98 };
99 let results: Vec<ExpandedContent> = join_all(
100 EXPANDERS
101 .iter()
102 .filter(|expander| expander.enabled(config))
103 .map(|expander| expander.expand_all(&cx)),
104 )
105 .await
106 .into_iter()
107 .flatten()
108 .collect();
109
110 if results.is_empty() {
111 tracing::debug!("no expandable content found");
112 return;
113 }
114
115 send_expanded_contents(&ctx, &request, results).await;
116 }
117 .instrument(span)
118 .await;
119 }
120}
121
122async fn send_expanded_contents(ctx: &Context, request: &Message, results: Vec<ExpandedContent>) {
128 let embeds = results
129 .iter()
130 .filter(|result| matches!(result, ExpandedContent::Embed(_)))
131 .count();
132 let code_blocks = results.len() - embeds;
133
134 let messages = crate::reply::build_messages(request, results);
135 let total = messages.len();
136 let mut sent = 0;
137 for message in messages {
138 match request.channel_id.send_message(&ctx.http, message).await {
139 Ok(_) => sent += 1,
140 Err(e) => tracing::error!(error = ?e, "failed to send expanded content"),
141 }
142 }
143
144 if sent == total {
146 tracing::info!(embeds, code_blocks, "preview sent");
147 } else {
148 tracing::warn!(embeds, code_blocks, sent, total, "preview partially sent");
149 }
150}