@@ -22,6 +22,7 @@ use anyhow::{Context as _, format_err};
2222use rust_team_data:: v1:: { TeamKind , TeamMember } ;
2323use std:: cmp:: Reverse ;
2424use std:: fmt:: Write as _;
25+ use std:: sync:: Arc ;
2526use subtle:: ConstantTimeEq ;
2627use tracing as log;
2728
@@ -88,7 +89,7 @@ struct Response {
8889/// Top-level handler for Zulip webhooks.
8990///
9091/// 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 {
9293 let content = match process_zulip_request ( ctx, req) . await {
9394 Ok ( None ) => {
9495 return serde_json:: to_string ( & ResponseNotRequired {
@@ -123,7 +124,7 @@ pub fn get_token_from_env() -> Result<String, anyhow::Error> {
123124/// Processes a Zulip webhook.
124125///
125126/// 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 > > {
127128 let expected_token = get_token_from_env ( ) ?;
128129 if !bool:: from ( req. token . as_bytes ( ) . ct_eq ( expected_token. as_bytes ( ) ) ) {
129130 anyhow:: bail!( "Invalid authorization." ) ;
@@ -148,7 +149,7 @@ async fn process_zulip_request(ctx: &Context, req: Request) -> anyhow::Result<Op
148149}
149150
150151async fn handle_command < ' a > (
151- ctx : & ' a Context ,
152+ ctx : Arc < Context > ,
152153 mut gh_id : u64 ,
153154 command : & ' a str ,
154155 message_data : & ' a Message ,
@@ -188,6 +189,8 @@ async fn handle_command<'a>(
188189 }
189190
190191 let cmd = parse_cli :: < ChatCommand , _ > ( words. into_iter ( ) ) ?;
192+ tracing:: info!( "command parsed to {cmd:?} (impersonated: {impersonated})" ) ;
193+
191194 let output = match & cmd {
192195 ChatCommand :: Acknowledge { identifier } => {
193196 acknowledge ( & ctx, gh_id, identifier. into ( ) ) . await
@@ -201,10 +204,12 @@ async fn handle_command<'a>(
201204 }
202205 ChatCommand :: Whoami => whoami_cmd ( & ctx, gh_id) . await ,
203206 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+ }
206211 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 ,
208213 } ;
209214
210215 let output = output?;
@@ -249,7 +254,10 @@ async fn handle_command<'a>(
249254 if cmd_index >= words. len ( ) {
250255 return Ok ( Some ( "Unknown command" . to_string ( ) ) ) ;
251256 }
257+
252258 let cmd = parse_cli :: < StreamCommand , _ > ( words[ cmd_index..] . into_iter ( ) . copied ( ) ) ?;
259+ tracing:: info!( "command parsed to {cmd:?}" ) ;
260+
253261 match cmd {
254262 StreamCommand :: EndTopic => post_waiter ( & ctx, message_data, WaitingMessage :: end_topic ( ) )
255263 . await
@@ -262,29 +270,55 @@ async fn handle_command<'a>(
262270 StreamCommand :: Read => post_waiter ( & ctx, message_data, WaitingMessage :: start_reading ( ) )
263271 . await
264272 . 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 ,
266274 StreamCommand :: DocsUpdate => trigger_docs_update ( message_data, & ctx. zulip ) ,
267275 }
268276 }
269277}
270278
271279async fn ping_goals_cmd (
272- ctx : & Context ,
280+ ctx : Arc < Context > ,
273281 gh_id : u64 ,
282+ message : & Message ,
274283 args : & PingGoalsArgs ,
275284) -> anyhow:: Result < Option < String > > {
276285 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 ( ) ) )
288322 } else {
289323 Err ( format_err ! (
290324 "That command is only permitted for those running the project-goal program." ,
0 commit comments