@@ -22,6 +22,7 @@ pub struct Request {
22
22
23
23
#[ derive( Debug , serde:: Deserialize ) ]
24
24
struct Message {
25
+ id : u64 ,
25
26
sender_id : u64 ,
26
27
#[ allow( unused) ]
27
28
recipient_id : u64 ,
@@ -45,7 +46,8 @@ struct ResponseOwned {
45
46
content : String ,
46
47
}
47
48
48
- pub const BOT_EMAIL : & str =
"[email protected] " ;
49
+ const BOT_EMAIL : & str =
"[email protected] " ;
50
+ const ZULIP_HOST : & str = "https://rust-lang.zulipchat.com" ;
49
51
50
52
pub async fn to_github_id ( client : & GithubClient , zulip_id : usize ) -> anyhow:: Result < Option < i64 > > {
51
53
let map = crate :: team_data:: zulip_map ( client) . await ?;
@@ -188,6 +190,15 @@ fn handle_command<'a>(
188
190
} )
189
191
. unwrap ( ) ,
190
192
} ,
193
+ // @triagebot prio #12345 P-high
194
+ Some ( "prio" ) => return match add_comment_to_issue ( & ctx, message_data, words, CommentType :: AssignIssuePriority ) . await {
195
+ Ok ( r) => r,
196
+ Err ( e) => serde_json:: to_string ( & Response {
197
+ content : & format ! ( "Failed to await at this time: {:?}" , e) ,
198
+ } )
199
+ . unwrap ( ) ,
200
+ } ,
201
+
191
202
_ => { }
192
203
}
193
204
}
@@ -203,6 +214,130 @@ fn handle_command<'a>(
203
214
} )
204
215
}
205
216
217
+ #[ derive( PartialEq ) ]
218
+ enum CommentType {
219
+ AssignIssuePriority ,
220
+ }
221
+
222
+ // https://docs.zulip.com/api/outgoing-webhooks#outgoing-webhook-format
223
+ #[ derive( serde:: Deserialize , Debug ) ]
224
+ struct ZulipReply {
225
+ messages : Vec < ZulipMessage > ,
226
+ }
227
+
228
+ #[ derive( serde:: Deserialize , Debug ) ]
229
+ struct ZulipMessage {
230
+ subject : String , // ex.: "[weekly] 2023-04-13"
231
+ stream_id : u32 ,
232
+ display_recipient : String , // ex. "t-compiler/major changes"
233
+ }
234
+
235
+ async fn get_zulip_msg ( ctx : & Context , msg_id : Option < u64 > ) -> anyhow:: Result < ZulipReply > {
236
+ let bot_api_token = env:: var ( "ZULIP_API_TOKEN" ) . expect ( "ZULIP_API_TOKEN" ) ;
237
+ let zulip_user = env:: var ( "ZULIP_USER" ) . expect ( "ZULIP_USER" ) ;
238
+
239
+ let mut url = format ! ( "{}/api/v1/messages?apply_markdown=false" , ZULIP_HOST ) ;
240
+
241
+ // TODO: Either pick a specific message of a Zulip topic or the first one
242
+ if msg_id. is_some ( ) {
243
+ url = format ! (
244
+ "{}&num_before=0&num_after=0&anchor={}" ,
245
+ url,
246
+ msg_id. unwrap( )
247
+ )
248
+ } else {
249
+ url = format ! ( "{}&num_before=1&num_after=1&anchor=oldest" , url)
250
+ }
251
+
252
+ let zulip_resp = ctx
253
+ . github
254
+ . raw ( )
255
+ . get ( url)
256
+ . basic_auth ( zulip_user, Some ( & bot_api_token) )
257
+ . send ( )
258
+ . await ?;
259
+
260
+ let zulip_msg_data = zulip_resp. json :: < ZulipReply > ( ) . await ?;
261
+ log:: debug!( "Zulip reply {:?}" , zulip_msg_data) ;
262
+ Ok ( zulip_msg_data)
263
+ }
264
+
265
+ // Add a comment to a Github issue/pr and issue a @rustbot command
266
+ async fn add_comment_to_issue (
267
+ ctx : & Context ,
268
+ message : & Message ,
269
+ mut words : impl Iterator < Item = & str > + std:: fmt:: Debug ,
270
+ ty : CommentType ,
271
+ ) -> anyhow:: Result < String > {
272
+ // retrieve the original Zulip topic and rebuild the complete URL to it
273
+ let zulip_msg = get_zulip_msg ( ctx, None ) . await ?;
274
+
275
+ if zulip_msg. messages . is_empty ( ) {
276
+ return Ok ( serde_json:: to_string ( & Response {
277
+ content : & format ! ( "Failed creating comment on Github: could not retrieve Zulip topic" ) ,
278
+ } )
279
+ . unwrap ( ) ) ;
280
+ }
281
+
282
+ // comment example:
283
+ // WG-prioritization assigning priority ([Zulip discussion](#)).
284
+ // @rustbot label -I-prioritize +P-XXX
285
+ let mut issue_id = 0 ;
286
+ let mut comment = String :: new ( ) ;
287
+ if ty == CommentType :: AssignIssuePriority {
288
+ // ex. "245100-t-compiler/wg-prioritization/alerts";
289
+ let zulip_stream = format ! (
290
+ "{}-{}" ,
291
+ zulip_msg. messages[ 0 ] . stream_id, zulip_msg. messages[ 0 ] . display_recipient
292
+ ) ;
293
+ let zulip_msg_link = format ! (
294
+ "narrow/stream/{}/topic/{}/near/{}" ,
295
+ zulip_stream, zulip_msg. messages[ 0 ] . subject, message. id
296
+ ) ;
297
+ // Don't urlencode, just replace spaces (Zulip custom URL encoding)
298
+ let zulip_msg_link = zulip_msg_link. replace ( " " , ".20" ) ;
299
+ let zulip_msg_link = format ! ( "{}/#{}" , ZULIP_HOST , zulip_msg_link) ;
300
+ log:: debug!( "Zulip link: {}" , zulip_msg_link) ;
301
+
302
+ issue_id = words
303
+ . next ( )
304
+ . unwrap ( )
305
+ . replace ( "#" , "" )
306
+ . parse :: < u64 > ( )
307
+ . unwrap ( ) ;
308
+ let p_label = words. next ( ) . unwrap ( ) ;
309
+
310
+ comment = format ! (
311
+ "WG-prioritization assigning priority ([Zulip discussion]({}))
312
+ \n \n @rustbot label -I-prioritize +{}" ,
313
+ zulip_msg_link, p_label
314
+ ) ;
315
+ }
316
+ // else ... handle other comment type
317
+
318
+ let github_resp = ctx
319
+ . octocrab
320
+ . issues ( "rust-lang" , "rust" )
321
+ . create_comment ( issue_id. clone ( ) , comment. clone ( ) )
322
+ . await ;
323
+
324
+ let _reply = match github_resp {
325
+ Ok ( data) => data,
326
+ Err ( e) => {
327
+ return Ok ( serde_json:: to_string ( & Response {
328
+ content : & format ! ( "Failed creating comment on Github: {:?}." , e) ,
329
+ } )
330
+ . unwrap ( ) ) ;
331
+ }
332
+ } ;
333
+ log:: debug!( "Created comment on issue #{}: {:?}" , issue_id, comment) ;
334
+
335
+ Ok ( serde_json:: to_string ( & ResponseNotRequired {
336
+ response_not_required : true ,
337
+ } )
338
+ . unwrap ( ) )
339
+ }
340
+
206
341
// This does two things:
207
342
// * execute the command for the other user
208
343
// * tell the user executed for that a command was run as them by the user
@@ -249,7 +384,7 @@ async fn execute_for_other_user(
249
384
let members = ctx
250
385
. github
251
386
. raw ( )
252
- . get ( "https://rust-lang.zulipchat.com/ api/v1/users")
387
+ . get ( format ! ( "{}/ api/v1/users", ZULIP_HOST ) )
253
388
. basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
254
389
. send ( )
255
390
. await ;
@@ -402,7 +537,7 @@ impl Recipient<'_> {
402
537
}
403
538
404
539
pub fn url ( & self ) -> String {
405
- format ! ( "https://rust-lang.zulipchat.com/ #narrow/{}" , self . narrow( ) )
540
+ format ! ( "{}/ #narrow/{}" , ZULIP_HOST , self . narrow( ) )
406
541
}
407
542
}
408
543
@@ -458,7 +593,7 @@ impl<'a> MessageApiRequest<'a> {
458
593
}
459
594
460
595
Ok ( client
461
- . post ( "https://rust-lang.zulipchat.com/ api/v1/messages")
596
+ . post ( format ! ( "{}/ api/v1/messages", ZULIP_HOST ) )
462
597
. basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
463
598
. form ( & SerializedApi {
464
599
type_ : match self . recipient {
@@ -510,8 +645,8 @@ impl<'a> UpdateMessageApiRequest<'a> {
510
645
511
646
Ok ( client
512
647
. patch ( & format ! (
513
- "https://rust-lang.zulipchat.com /api/v1/messages/{}" ,
514
- self . message_id
648
+ "{} /api/v1/messages/{}" ,
649
+ ZULIP_HOST , self . message_id
515
650
) )
516
651
. basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
517
652
. form ( & SerializedApi {
@@ -723,8 +858,8 @@ impl<'a> AddReaction<'a> {
723
858
724
859
Ok ( client
725
860
. post ( & format ! (
726
- "https://rust-lang.zulipchat.com /api/v1/messages/{}/reactions" ,
727
- self . message_id
861
+ "{} /api/v1/messages/{}/reactions" ,
862
+ ZULIP_HOST , self . message_id
728
863
) )
729
864
. basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
730
865
. form ( & self )
0 commit comments