@@ -15,13 +15,15 @@ mod update_copy;
1515
1616use crate :: config;
1717use crate :: discord_bot:: guild_storage:: GuildStorage ;
18- use crate :: pterodactyl:: { send_command_safe , PterodactylServer } ;
18+ use crate :: pterodactyl:: { tellraw , PterodactylChatBridge , PterodactylServer } ;
1919use async_trait:: async_trait;
20+ use dashmap:: { DashMap , Entry } ;
21+ use futures:: future:: try_join_all;
2022use log:: { error, info, warn} ;
21- use serde :: Serialize ;
23+ use serenity :: all :: Webhook ;
2224use serenity:: builder:: {
23- CreateCommand , CreateInteractionResponse , CreateInteractionResponseMessage , CreateMessage ,
24- EditInteractionResponse ,
25+ CreateAttachment , CreateCommand , CreateInteractionResponse , CreateInteractionResponseMessage ,
26+ CreateMessage , EditInteractionResponse , ExecuteWebhook ,
2527} ;
2628use serenity:: client:: { Context , EventHandler } ;
2729use serenity:: http:: Http ;
@@ -39,6 +41,7 @@ use std::sync::Arc;
3941pub ( crate ) type Handle = Arc < Http > ;
4042
4143struct Handler {
44+ webhook_cache : Arc < DashMap < String , Webhook > > ,
4245 pterodactyl : Arc < pterodactyl_api:: client:: Client > ,
4346}
4447
@@ -119,45 +122,116 @@ async fn process_command(
119122
120123async fn process_chatbridge (
121124 ctx : Context ,
125+ webhook_cache : & DashMap < String , Webhook > ,
122126 pterodactyl : & pterodactyl_api:: client:: Client ,
123- chatbridge_server : & PterodactylServer ,
127+ chat_bridge : & PterodactylChatBridge ,
124128 new_message : & Message ,
125129) -> Result < ( ) , crate :: Error > {
126- let ptero_server = pterodactyl. get_server ( & chatbridge_server. id ) ;
130+ let config = config:: get ( ) ;
131+ try_join_all (
132+ chat_bridge
133+ . ptero_servers
134+ . iter ( )
135+ . filter_map ( |server_name| {
136+ config
137+ . pterodactyl_servers
138+ . iter ( )
139+ . find ( |server| & server. name == server_name)
140+ } )
141+ . map ( |server| send_chatbridge_message ( & ctx, pterodactyl, server, new_message) ) ,
142+ )
143+ . await ?;
144+ try_join_all (
145+ chat_bridge
146+ . discord_channels
147+ . iter ( )
148+ . filter ( |channel| channel. id != new_message. channel_id )
149+ . map ( |channel| {
150+ send_chatbridge_message_to_discord (
151+ & ctx,
152+ webhook_cache,
153+ & channel. webhook ,
154+ new_message,
155+ )
156+ } ) ,
157+ )
158+ . await ?;
159+ Ok ( ( ) )
160+ }
127161
128- #[ derive( Serialize ) ]
129- struct TextComponent {
130- text : String ,
131- }
162+ async fn send_chatbridge_message (
163+ ctx : & Context ,
164+ pterodactyl : & pterodactyl_api:: client:: Client ,
165+ server : & PterodactylServer ,
166+ message : & Message ,
167+ ) -> Result < ( ) , crate :: Error > {
168+ let ptero_server = pterodactyl. get_server ( & server. id ) ;
132169
133- let sanitized_message = new_message . content_safe ( & ctx) ;
170+ let sanitized_message = message . content_safe ( ctx) ;
134171 if !sanitized_message. is_empty ( ) {
135- let text_component = TextComponent {
136- text : format ! ( "[{}] {}" , new_message . author . name , sanitized_message ) ,
137- } ;
138- let text_component = serde_json :: to_string ( & text_component ) ? ;
139- send_command_safe ( & ptero_server , format ! ( "tellraw @a {}" , text_component ) ) . await ?;
172+ tellraw (
173+ & ptero_server ,
174+ format ! ( "[Discord] [{}] {}" , message . author . name , sanitized_message ) ,
175+ )
176+ . await ?;
140177 }
141178
142- if !new_message . attachments . is_empty ( ) {
143- let attachment_message = if new_message . attachments . len ( ) == 1 {
179+ if !message . attachments . is_empty ( ) {
180+ let attachment_message = if message . attachments . len ( ) == 1 {
144181 "an attachment"
145182 } else {
146183 "multiple attachments"
147184 } ;
148- let text_component = TextComponent {
149- text : format ! (
185+ tellraw (
186+ & ptero_server,
187+ format ! (
150188 "{} posted {} in Discord." ,
151- new_message . author. name, attachment_message
189+ message . author. name, attachment_message
152190 ) ,
153- } ;
154- let text_component = serde_json:: to_string ( & text_component) ?;
155- send_command_safe ( & ptero_server, format ! ( "tellraw @a {}" , text_component) ) . await ?;
191+ )
192+ . await ?;
156193 }
157194
158195 Ok ( ( ) )
159196}
160197
198+ async fn send_chatbridge_message_to_discord (
199+ ctx : & Context ,
200+ webhook_cache : & DashMap < String , Webhook > ,
201+ webhook : & str ,
202+ message : & Message ,
203+ ) -> Result < ( ) , crate :: Error > {
204+ let webhook = match webhook_cache. entry ( webhook. to_owned ( ) ) {
205+ Entry :: Occupied ( entry) => entry. get ( ) . clone ( ) ,
206+ Entry :: Vacant ( entry) => entry. insert ( Webhook :: from_url ( ctx, webhook) . await ?) . clone ( ) ,
207+ } ;
208+ webhook
209+ . execute (
210+ ctx,
211+ false ,
212+ ExecuteWebhook :: new ( )
213+ . content ( & message. content )
214+ . files (
215+ try_join_all (
216+ message
217+ . attachments
218+ . iter ( )
219+ . map ( |attachment| CreateAttachment :: url ( ctx, & attachment. url ) ) ,
220+ )
221+ . await ?,
222+ )
223+ . username ( & message. author . name )
224+ . avatar_url (
225+ message
226+ . author
227+ . avatar_url ( )
228+ . unwrap_or_else ( || message. author . default_avatar_url ( ) ) ,
229+ ) ,
230+ )
231+ . await ?;
232+ Ok ( ( ) )
233+ }
234+
161235#[ async_trait]
162236impl EventHandler for Handler {
163237 async fn guild_member_addition ( & self , ctx : Context , new_member : Member ) {
@@ -211,10 +285,11 @@ impl EventHandler for Handler {
211285 None => return ,
212286 } ;
213287 let pterodactyl = self . pterodactyl . clone ( ) ;
288+ let webhook_cache = self . webhook_cache . clone ( ) ;
214289
215290 tokio:: runtime:: Handle :: current ( ) . spawn ( async move {
216291 enum MessageHandling < ' a > {
217- ChatBridge ( & ' a PterodactylServer ) ,
292+ ChatBridge ( & ' a PterodactylChatBridge ) ,
218293 Command ( & ' a str ) ,
219294 IncCounter ( & ' a str ) ,
220295 PermanentLatest ,
@@ -228,15 +303,10 @@ impl EventHandler for Handler {
228303 MessageHandling :: SimpleWords
229304 } else if new_message. author . bot {
230305 return ;
231- } else if let Some ( chatbridge_server) =
232- config. pterodactyl_servers . iter ( ) . find ( |server| {
233- server
234- . bridge
235- . as_ref ( )
236- . is_some_and ( |bridge| bridge. discord_channel == new_message. channel_id )
237- } )
306+ } else if let Some ( chat_bridge) =
307+ config. chat_bridge_by_discord_channel ( new_message. channel_id )
238308 {
239- MessageHandling :: ChatBridge ( chatbridge_server )
309+ MessageHandling :: ChatBridge ( chat_bridge )
240310 } else {
241311 let storage = GuildStorage :: get ( guild_id) . await ;
242312 if storage
@@ -264,7 +334,14 @@ impl EventHandler for Handler {
264334
265335 if let Err ( err) = match message_handling {
266336 MessageHandling :: ChatBridge ( chatbridge_server) => {
267- process_chatbridge ( ctx, & pterodactyl, chatbridge_server, & new_message) . await
337+ process_chatbridge (
338+ ctx,
339+ & webhook_cache,
340+ & pterodactyl,
341+ chatbridge_server,
342+ & new_message,
343+ )
344+ . await
268345 }
269346 MessageHandling :: Command ( command) => {
270347 commands:: run ( command, guild_id, ctx, & new_message) . await
@@ -380,7 +457,10 @@ pub(crate) async fn create_client(
380457 | GatewayIntents :: MESSAGE_CONTENT
381458 | GatewayIntents :: GUILD_MESSAGE_REACTIONS ;
382459 Ok ( Client :: builder ( & config:: get ( ) . discord_token , intents)
383- . event_handler ( Handler { pterodactyl } )
460+ . event_handler ( Handler {
461+ webhook_cache : Arc :: new ( DashMap :: new ( ) ) ,
462+ pterodactyl,
463+ } )
384464 . await ?)
385465}
386466
0 commit comments