1- use std::
2- sync:: Arc
1+ use std:: { collections:: { hash_map:: Entry , HashMap } , sync:: { Arc , LazyLock } , time:: Instant }
32;
43
54use axum:: {
65 extract:: State , http:: StatusCode , response:: { IntoResponse , Response } , routing:: { delete, get, post} , Json , Router
76} ;
87use discord_webhook2:: message:: Message ;
8+ use parking_lot:: Mutex ;
99use sea_orm:: {
1010 prelude:: Decimal , sea_query:: Table , sqlx:: types:: chrono:: Local , ActiveModelTrait , ActiveValue , ConnectionTrait , DatabaseConnection , EntityTrait , Schema
1111} ;
@@ -15,6 +15,14 @@ use tracing::error;
1515use crate :: { scheduler, UsrState } ;
1616
1717mod order;
18+ struct BatchedTask {
19+ queue : HashMap < u32 , String > ,
20+ deadline : Option < Instant > ,
21+ }
22+ static BATCHED : LazyLock < Mutex < BatchedTask > > = LazyLock :: new ( || Mutex :: new ( BatchedTask {
23+ queue : HashMap :: new ( ) ,
24+ deadline : None ,
25+ } ) ) ;
1826
1927#[ derive( Deserialize ) ]
2028pub struct PendingOrder {
@@ -57,20 +65,67 @@ async fn new_order(
5765 vendor : ActiveValue :: Set ( pending_order. vendor ) ,
5866 link : ActiveValue :: Set ( pending_order. link ) ,
5967 } ;
60- if let Err ( e) = active_model. insert ( & state. db ) . await {
61- error ! ( "Failed to create new order: {e}" ) ;
62- ( StatusCode :: INTERNAL_SERVER_ERROR , "" )
63- } else {
64- tokio:: spawn ( async move {
65- if let Err ( e) = state
66- . new_orders_webhook
67- . send ( & Message :: new ( |message| message. content ( webhook_msg) ) )
68- . await
69- {
70- error ! ( "Failed to trigger new-order webhook: {e}" ) ;
68+ match active_model. insert ( & state. db ) . await {
69+ Ok ( m) => {
70+ let mut guard = BATCHED . lock ( ) ;
71+ guard. queue . insert ( m. id , webhook_msg) ;
72+ let was_none = guard. deadline . is_none ( ) ;
73+ guard. deadline = Some ( Instant :: now ( ) + std:: time:: Duration :: from_secs ( 60 * 5 ) ) ;
74+
75+ if was_none {
76+ drop ( guard) ;
77+
78+ tokio:: spawn ( async move {
79+ loop {
80+ let deadline = BATCHED . lock ( ) . deadline . unwrap ( ) ;
81+ tokio:: time:: sleep_until ( deadline. into ( ) ) . await ;
82+ let queue;
83+ {
84+ let mut guard = BATCHED . lock ( ) ;
85+ if guard. deadline . unwrap ( ) != deadline {
86+ continue ;
87+ }
88+ let replacement = HashMap :: with_capacity ( guard. queue . capacity ( ) ) ;
89+ queue = std:: mem:: replace ( & mut guard. queue , replacement) ;
90+ }
91+ let mut running = String :: new ( ) ;
92+ for ( _, msg) in queue {
93+ if running. len ( ) + msg. len ( ) + 1 < 2000 {
94+ running. push_str ( & msg) ;
95+ running. push_str ( "\n " ) ;
96+ } else {
97+ if let Err ( e) = state
98+ . new_orders_webhook
99+ . send ( & Message :: new ( |message| message. content ( running) ) )
100+ . await
101+ {
102+ error ! ( "Failed to trigger new-order webhook: {e}" ) ;
103+ }
104+ running = msg;
105+ }
106+ }
107+ if let Err ( e) = state
108+ . new_orders_webhook
109+ . send ( & Message :: new ( |message| message. content ( running) ) )
110+ . await
111+ {
112+ error ! ( "Failed to trigger new-order webhook: {e}" ) ;
113+ }
114+ let mut guard = BATCHED . lock ( ) ;
115+ if guard. queue . is_empty ( ) {
116+ guard. deadline = None ;
117+ break ;
118+ }
119+ }
120+ } ) ;
71121 }
72- } ) ;
73- ( StatusCode :: OK , "" )
122+
123+ ( StatusCode :: OK , "" )
124+ }
125+ Err ( e) => {
126+ error ! ( "Failed to create new order: {e}" ) ;
127+ ( StatusCode :: INTERNAL_SERVER_ERROR , "" )
128+ }
74129 }
75130}
76131
@@ -134,15 +189,24 @@ async fn change_order(
134189 error ! ( "Failed to change order: {e}" ) ;
135190 ( StatusCode :: INTERNAL_SERVER_ERROR , "" )
136191 } else {
137- tokio:: spawn ( async move {
138- if let Err ( e) = state
139- . new_orders_webhook
140- . send ( & Message :: new ( |message| message. content ( webhook_msg) ) )
141- . await
142- {
143- error ! ( "Failed to trigger new-order webhook: {e}" ) ;
192+ let mut guard = BATCHED . lock ( ) ;
193+ match guard. queue . entry ( change_order. id ) {
194+ Entry :: Occupied ( mut entry) => {
195+ entry. insert ( webhook_msg) ;
144196 }
145- } ) ;
197+ Entry :: Vacant ( _) => {
198+ tokio:: spawn ( async move {
199+ if let Err ( e) = state
200+ . new_orders_webhook
201+ . send ( & Message :: new ( |message| message. content ( webhook_msg) ) )
202+ . await
203+ {
204+ error ! ( "Failed to trigger new-order webhook: {e}" ) ;
205+ }
206+ } ) ;
207+ }
208+ }
209+
146210 ( StatusCode :: OK , "" )
147211 }
148212}
0 commit comments