Skip to content

Commit

Permalink
Triagebot learns how to comment on GitHub
Browse files Browse the repository at this point in the history
In this first version triagebot learns how to post a comment on GitHub
to assign priority to an issue marked as regression.

The code should allow for any kind of comment to be created.
  • Loading branch information
apiraino committed Apr 18, 2023
1 parent add83c3 commit e50aef6
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ rand = "0.8.5"
ignore = "0.4.18"
postgres-types = { version = "0.2.4", features = ["derive"] }
cron = { version = "0.12.0" }
urlencoding = "2.1.2"

[dependencies.serde]
version = "1"
Expand Down
109 changes: 109 additions & 0 deletions src/zulip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct Request {

#[derive(Debug, serde::Deserialize)]
struct Message {
id: u64,
sender_id: u64,
#[allow(unused)]
recipient_id: u64,
Expand Down Expand Up @@ -188,6 +189,15 @@ fn handle_command<'a>(
})
.unwrap(),
},
// @triagebot prio #12345 P-high
Some("prio") => return match add_comment_to_issue(&ctx, message_data, words, CommentType::AssignIssuePriority).await {
Ok(r) => r,
Err(e) => serde_json::to_string(&Response {
content: &format!("Failed to await at this time: {:?}", e),
})
.unwrap(),
},

_ => {}
}
}
Expand All @@ -203,6 +213,105 @@ fn handle_command<'a>(
})
}

#[derive(PartialEq)]
enum CommentType {
AssignIssuePriority,
}

// https://docs.zulip.com/api/outgoing-webhooks#outgoing-webhook-format
#[derive(serde::Deserialize, Debug)]
struct ZulipReply {
message: ZulipMessage,
}

#[derive(serde::Deserialize, Debug)]
struct ZulipMessage {
subject: String, // ex.: "[weekly] 2023-04-13"
}

async fn get_zulip_msg(ctx: &Context, id: u64) -> anyhow::Result<ZulipReply> {
let bot_api_token = env::var("ZULIP_API_TOKEN").expect("ZULIP_API_TOKEN");
let zulip_resp = ctx
.github
.raw()
.get(format!(
"https://rust-lang.zulipchat.com/api/v1/messages/{}",
id
))
.basic_auth(BOT_EMAIL, Some(&bot_api_token))
.send()
.await?;

let zulip_msg_data = zulip_resp.json::<ZulipReply>().await.expect("TODO");
log::debug!("Zulip reply {:?}", zulip_msg_data);
Ok(zulip_msg_data)
}

// Add a comment to a Github issue/pr issuing a @rustbot command
async fn add_comment_to_issue(
ctx: &Context,
message: &Message,
mut words: impl Iterator<Item = &str> + std::fmt::Debug,
ty: CommentType,
) -> anyhow::Result<String> {
// retrieve the original Zulip message to build the complete URL
let zulip_msg = get_zulip_msg(ctx, message.id).await?;

// comment example:
// WG-prioritization assigning priority ([Zulip discussion](#)).
// @rustbot label -I-prioritize +P-XXX
let mut issue_id = 0;
let mut comment = String::new();
if ty == CommentType::AssignIssuePriority {
let zulip_stream = "245100-t-compiler/wg-prioritization/alerts";
let mut zulip_msg_link = format!(
"https://rust-lang.zulipchat.com/#narrow/stream/{}/topic/{}/near/{}",
zulip_stream, zulip_msg.message.subject, message.id
);
// Encode url and replace "%" with "."
// (apparently Zulip does that to public URLs)
urlencoding::encode(&zulip_msg_link);
zulip_msg_link = zulip_msg_link.replace("%", ".");

issue_id = words
.next()
.unwrap()
.replace("#", "")
.parse::<u64>()
.unwrap();
let p_label = words.next().unwrap();

comment = format!(
"WG-prioritization assigning priority ([Zulip discussion]({}))
\n\n@rustbot label -I-prioritize +{}",
zulip_msg_link, p_label
);
}
// else ... handle other comment type

let github_resp = ctx
.octocrab
.issues("owner", "repo")
.create_comment(issue_id.clone(), comment.clone())
.await;

let _reply = match github_resp {
Ok(data) => data,
Err(e) => {
return Ok(serde_json::to_string(&Response {
content: &format!("Failed to post comment on Github: {:?}.", e),
})
.unwrap());
}
};
log::debug!("Created comment on issue #{}: {:?}", issue_id, comment);

Ok(serde_json::to_string(&ResponseNotRequired {
response_not_required: true,
})
.unwrap())
}

// This does two things:
// * execute the command for the other user
// * tell the user executed for that a command was run as them by the user
Expand Down

0 comments on commit e50aef6

Please sign in to comment.