@@ -22,6 +22,7 @@ use anyhow::{Context as _, format_err};
22
22
use rust_team_data:: v1:: { TeamKind , TeamMember } ;
23
23
use std:: cmp:: Reverse ;
24
24
use std:: fmt:: Write as _;
25
+ use std:: sync:: Arc ;
25
26
use subtle:: ConstantTimeEq ;
26
27
use tracing as log;
27
28
@@ -88,7 +89,7 @@ struct Response {
88
89
/// Top-level handler for Zulip webhooks.
89
90
///
90
91
/// Returns a JSON response.
91
- pub async fn respond ( ctx : & Context , req : Request ) -> String {
92
+ pub async fn respond ( ctx : Arc < Context > , req : Request ) -> String {
92
93
let content = match process_zulip_request ( ctx, req) . await {
93
94
Ok ( None ) => {
94
95
return serde_json:: to_string ( & ResponseNotRequired {
@@ -123,7 +124,7 @@ pub fn get_token_from_env() -> Result<String, anyhow::Error> {
123
124
/// Processes a Zulip webhook.
124
125
///
125
126
/// Returns a string of the response, or None if no response is needed.
126
- async fn process_zulip_request ( ctx : & Context , req : Request ) -> anyhow:: Result < Option < String > > {
127
+ async fn process_zulip_request ( ctx : Arc < Context > , req : Request ) -> anyhow:: Result < Option < String > > {
127
128
let expected_token = get_token_from_env ( ) ?;
128
129
if !bool:: from ( req. token . as_bytes ( ) . ct_eq ( expected_token. as_bytes ( ) ) ) {
129
130
anyhow:: bail!( "Invalid authorization." ) ;
@@ -148,7 +149,7 @@ async fn process_zulip_request(ctx: &Context, req: Request) -> anyhow::Result<Op
148
149
}
149
150
150
151
async fn handle_command < ' a > (
151
- ctx : & ' a Context ,
152
+ ctx : Arc < Context > ,
152
153
mut gh_id : u64 ,
153
154
command : & ' a str ,
154
155
message_data : & ' a Message ,
@@ -188,6 +189,8 @@ async fn handle_command<'a>(
188
189
}
189
190
190
191
let cmd = parse_cli :: < ChatCommand , _ > ( words. into_iter ( ) ) ?;
192
+ tracing:: info!( "command parsed to {cmd:?} (impersonated: {impersonated})" ) ;
193
+
191
194
let output = match & cmd {
192
195
ChatCommand :: Acknowledge { identifier } => {
193
196
acknowledge ( & ctx, gh_id, identifier. into ( ) ) . await
@@ -201,10 +204,12 @@ async fn handle_command<'a>(
201
204
}
202
205
ChatCommand :: Whoami => whoami_cmd ( & ctx, gh_id) . await ,
203
206
ChatCommand :: Lookup ( cmd) => lookup_cmd ( & ctx, cmd) . await ,
204
- ChatCommand :: Work ( cmd) => workqueue_commands ( ctx, gh_id, cmd) . await ,
205
- ChatCommand :: PingGoals ( args) => ping_goals_cmd ( ctx, gh_id, & args) . await ,
207
+ ChatCommand :: Work ( cmd) => workqueue_commands ( & ctx, gh_id, cmd) . await ,
208
+ ChatCommand :: PingGoals ( args) => {
209
+ ping_goals_cmd ( ctx. clone ( ) , gh_id, message_data, & args) . await
210
+ }
206
211
ChatCommand :: DocsUpdate => trigger_docs_update ( message_data, & ctx. zulip ) ,
207
- ChatCommand :: TeamStats { name } => team_status_cmd ( ctx, & name) . await ,
212
+ ChatCommand :: TeamStats { name } => team_status_cmd ( & ctx, & name) . await ,
208
213
} ;
209
214
210
215
let output = output?;
@@ -249,7 +254,10 @@ async fn handle_command<'a>(
249
254
if cmd_index >= words. len ( ) {
250
255
return Ok ( Some ( "Unknown command" . to_string ( ) ) ) ;
251
256
}
257
+
252
258
let cmd = parse_cli :: < StreamCommand , _ > ( words[ cmd_index..] . into_iter ( ) . copied ( ) ) ?;
259
+ tracing:: info!( "command parsed to {cmd:?}" ) ;
260
+
253
261
match cmd {
254
262
StreamCommand :: EndTopic => post_waiter ( & ctx, message_data, WaitingMessage :: end_topic ( ) )
255
263
. await
@@ -262,29 +270,55 @@ async fn handle_command<'a>(
262
270
StreamCommand :: Read => post_waiter ( & ctx, message_data, WaitingMessage :: start_reading ( ) )
263
271
. await
264
272
. map_err ( |e| format_err ! ( "Failed to await at this time: {e:?}" ) ) ,
265
- StreamCommand :: PingGoals ( args) => ping_goals_cmd ( ctx, gh_id, & args) . await ,
273
+ StreamCommand :: PingGoals ( args) => ping_goals_cmd ( ctx, gh_id, message_data , & args) . await ,
266
274
StreamCommand :: DocsUpdate => trigger_docs_update ( message_data, & ctx. zulip ) ,
267
275
}
268
276
}
269
277
}
270
278
271
279
async fn ping_goals_cmd (
272
- ctx : & Context ,
280
+ ctx : Arc < Context > ,
273
281
gh_id : u64 ,
282
+ message : & Message ,
274
283
args : & PingGoalsArgs ,
275
284
) -> anyhow:: Result < Option < String > > {
276
285
if project_goals:: check_project_goal_acl ( & ctx. team , gh_id) . await ? {
277
- ping_project_goals_owners (
278
- & ctx. github ,
279
- & ctx. zulip ,
280
- & ctx. team ,
281
- false ,
282
- args. threshold as i64 ,
283
- & format ! ( "on {}" , args. next_update) ,
284
- )
285
- . await
286
- . map_err ( |e| format_err ! ( "Failed to await at this time: {e:?}" ) ) ?;
287
- Ok ( None )
286
+ let args = args. clone ( ) ;
287
+ let message = message. clone ( ) ;
288
+ tokio:: spawn ( async move {
289
+ let res = ping_project_goals_owners (
290
+ & ctx. github ,
291
+ & ctx. zulip ,
292
+ & ctx. team ,
293
+ false ,
294
+ args. threshold as i64 ,
295
+ & format ! ( "on {}" , args. next_update) ,
296
+ )
297
+ . await ;
298
+
299
+ let status = match res {
300
+ Ok ( _res) => "OK" . to_string ( ) ,
301
+ Err ( err) => {
302
+ tracing:: error!( "ping_project_goals_owners: {err:?}" ) ;
303
+ format ! ( "ERROR\n \n ```\n {err:#?}\n ```\n " )
304
+ }
305
+ } ;
306
+
307
+ let res = MessageApiRequest {
308
+ recipient : message. sender_to_recipient ( ) ,
309
+ content : & format ! ( "End pinging project groups owners: {status}" ) ,
310
+ }
311
+ . send ( & ctx. zulip )
312
+ . await ;
313
+
314
+ if let Err ( err) = res {
315
+ tracing:: error!(
316
+ "error sending project goals ping reply: {err:?} for status: {status}"
317
+ ) ;
318
+ }
319
+ } ) ;
320
+
321
+ Ok ( Some ( "Started pinging project groups owners..." . to_string ( ) ) )
288
322
} else {
289
323
Err ( format_err ! (
290
324
"That command is only permitted for those running the project-goal program." ,
0 commit comments