@@ -22,6 +22,7 @@ pub struct Request {
2222
2323#[ derive( Debug , serde:: Deserialize ) ]
2424struct Message {
25+ id : u64 ,
2526 sender_id : u64 ,
2627 #[ allow( unused) ]
2728 recipient_id : u64 ,
@@ -45,7 +46,8 @@ struct ResponseOwned {
4546 content : String ,
4647}
4748
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" ;
4951
5052pub async fn to_github_id ( client : & GithubClient , zulip_id : usize ) -> anyhow:: Result < Option < i64 > > {
5153 let map = crate :: team_data:: zulip_map ( client) . await ?;
@@ -188,6 +190,15 @@ fn handle_command<'a>(
188190 } )
189191 . unwrap ( ) ,
190192 } ,
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+
191202 _ => { }
192203 }
193204 }
@@ -203,6 +214,130 @@ fn handle_command<'a>(
203214 } )
204215}
205216
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+
206341// This does two things:
207342// * execute the command for the other user
208343// * 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(
249384 let members = ctx
250385 . github
251386 . raw ( )
252- . get ( "https://rust-lang.zulipchat.com/ api/v1/users")
387+ . get ( format ! ( "{}/ api/v1/users", ZULIP_HOST ) )
253388 . basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
254389 . send ( )
255390 . await ;
@@ -402,7 +537,7 @@ impl Recipient<'_> {
402537 }
403538
404539 pub fn url ( & self ) -> String {
405- format ! ( "https://rust-lang.zulipchat.com/ #narrow/{}" , self . narrow( ) )
540+ format ! ( "{}/ #narrow/{}" , ZULIP_HOST , self . narrow( ) )
406541 }
407542}
408543
@@ -458,7 +593,7 @@ impl<'a> MessageApiRequest<'a> {
458593 }
459594
460595 Ok ( client
461- . post ( "https://rust-lang.zulipchat.com/ api/v1/messages")
596+ . post ( format ! ( "{}/ api/v1/messages", ZULIP_HOST ) )
462597 . basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
463598 . form ( & SerializedApi {
464599 type_ : match self . recipient {
@@ -510,8 +645,8 @@ impl<'a> UpdateMessageApiRequest<'a> {
510645
511646 Ok ( client
512647 . 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
515650 ) )
516651 . basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
517652 . form ( & SerializedApi {
@@ -723,8 +858,8 @@ impl<'a> AddReaction<'a> {
723858
724859 Ok ( client
725860 . 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
728863 ) )
729864 . basic_auth ( BOT_EMAIL , Some ( & bot_api_token) )
730865 . form ( & self )
0 commit comments