1use crate::config::BabyriteConfig;
7use crate::expand::ExpandedContent;
8use crate::expand::discord::MessageLinkIDs;
9use crate::expand::github::GitHubPermalink;
10use serenity::all::{ActivityData, Context, EventHandler, Message, Ready};
11use serenity::prelude::TypeMapKey;
12use serenity_builder::model::message::{SerenityMessage, SerenityMessageMentionType};
13
14pub struct HttpClient;
16
17impl TypeMapKey for HttpClient {
18 type Value = reqwest::Client;
19}
20
21pub struct BabyriteEventHandler;
23
24#[serenity::async_trait]
25impl EventHandler for BabyriteEventHandler {
26 async fn ready(&self, ctx: Context, bot: Ready) {
27 let version = format!("v{}", env!("CARGO_PKG_VERSION"));
28 ctx.set_activity(ActivityData::custom(format!("Running {}", version)).into());
29 tracing::info!("Running {}, {} is connected!", version, bot.user.name);
30 }
31
32 async fn message(&self, ctx: Context, request: Message) {
33 if request.author.bot {
34 return;
35 }
36
37 let request_guild_id = match request.guild_id {
38 Some(id) => id,
39 None => return,
40 };
41
42 let text = &request.content;
43 let config = BabyriteConfig::get();
44 let mut results = Vec::new();
45
46 for ids in MessageLinkIDs::parse_all(text) {
48 if ids.guild_id != request_guild_id {
49 continue;
50 }
51
52 tracing::info!(
53 "Begin generating the preview. (Requester: {})",
54 &request.author.name
55 );
56
57 match ids.fetch(&ctx).await {
58 Ok(content) => results.push(content),
59 Err(e) => tracing::error!("{}", e),
60 }
61 }
62
63 if config.features.github_permalink {
65 let permalinks = GitHubPermalink::parse_all(text);
66 if !permalinks.is_empty() {
67 let data = ctx.data.read().await;
68 if let Some(http_client) = data.get::<HttpClient>() {
69 for permalink in permalinks {
70 tracing::info!(
71 "Begin expanding GitHub permalink. (Requester: {})",
72 &request.author.name
73 );
74
75 match permalink.fetch(http_client).await {
76 Ok(content) => results.push(content),
77 Err(e) => tracing::error!("{}", e),
78 }
79 }
80 } else {
81 tracing::error!("HTTP client not found in TypeMap");
82 }
83 }
84 }
85
86 if results.is_empty() {
87 return;
88 }
89
90 send_expanded_contents(&ctx, &request, results).await;
91 }
92}
93
94async fn send_expanded_contents(ctx: &Context, request: &Message, results: Vec<ExpandedContent>) {
96 let mut embeds = Vec::new();
97 let mut code_blocks = Vec::new();
98
99 for result in results {
100 match result {
101 ExpandedContent::Embed(embed) => embeds.push(*embed),
102 ExpandedContent::CodeBlock {
103 language,
104 code,
105 metadata,
106 } => {
107 code_blocks.push(format!("{metadata}\n```{language}\n{code}\n```"));
108 }
109 }
110 }
111
112 if !embeds.is_empty() {
114 let message_builder = SerenityMessage::builder()
115 .embeds(embeds)
116 .mention_type(SerenityMessageMentionType::Reply(Box::new(request.clone())))
117 .build();
118
119 let converted_message = match message_builder.convert() {
120 Ok(m) => m,
121 Err(e) => {
122 tracing::error!(?e);
123 return;
124 }
125 };
126
127 if let Err(e) = request
128 .channel_id
129 .send_message(&ctx.http, converted_message)
130 .await
131 {
132 tracing::error!("Failed to send preview: {:?}", e);
133 return;
134 }
135 }
136
137 for block in code_blocks {
139 if let Err(e) = request.channel_id.say(&ctx.http, &block).await {
140 tracing::error!("Failed to send code block: {:?}", e);
141 }
142 }
143
144 tracing::info!("Preview sent successfully.");
145}