Browse Source

Add gitea-issue command

master
NGnius (Graham) 3 years ago
parent
commit
724deb6847
5 changed files with 198 additions and 8 deletions
  1. +30
    -0
      src/command_definitions.rs
  2. +19
    -1
      src/discord.rs
  3. +57
    -1
      src/gitea.rs
  4. +88
    -4
      src/gitea_command.rs
  5. +4
    -2
      src/main.rs

+ 30
- 0
src/command_definitions.rs View File

@@ -51,3 +51,33 @@ pub fn def_gitea_release() -> (discord::ApplicationCommand, Option<String>) {
}, Some("616329232389505055".to_string()))
}

// gitea-issue
pub fn def_gitea_issue() -> (discord::ApplicationCommand, Option<String>) {
(discord::ApplicationCommand {
id: None,
application_id: None,
name: "gitea-issue".to_string(),
description: "Display an issue".to_string(),
options: Some(vec![
discord::ApplicationCommandOption::String {
name: "username".to_string(),
description: "Gitea username".to_string(),
required: true,
choices: None
},
discord::ApplicationCommandOption::String {
name: "repo".to_string(),
description: "Gitea repository".to_string(),
required: true,
choices: None
},
discord::ApplicationCommandOption::Integer {
name: "issue".to_string(),
description: "Gitea issue number".to_string(),
required: true,
choices: None
},
]),
}, Some("616329232389505055".to_string()))
}


+ 19
- 1
src/discord.rs View File

@@ -139,10 +139,28 @@ pub struct ApplicationCommandInteractionData {
#[derive(Serialize, Deserialize, Clone)]
pub struct ApplicationCommandInteractionDataOption {
pub name: String,
pub value: Option<String>, // FIXME this could be bool, integer, or sub-command as well
pub value: Option<CommandValue>, // FIXME this could be bool, integer, or sub-command as well
pub options: Option<Vec<ApplicationCommandInteractionDataOption>>,
}

#[derive(Serialize, Deserialize, Clone)]
#[serde(untagged)]
pub enum CommandValue {
BoolVal(bool),
IntVal(usize),
StrVal(String),
}

impl ToString for CommandValue {
fn to_string(&self) -> String {
match self {
Self::StrVal(v) => v.to_string(),
Self::BoolVal(v) => v.to_string(),
Self::IntVal(v) => v.to_string(),
}
}
}

pub enum InteractionResponse {
//#[serde(rename = "1")]
Pong {},


+ 57
- 1
src/gitea.rs View File

@@ -19,6 +19,21 @@ pub fn get_releases(owner: &str, repo: &str) -> Result<Vec<Release>, String> {
Err("Invalid Result".to_string())
}

pub fn get_issue_by_index(owner: &str, repo: &str, index: usize) -> Result<Issue, String> {
let client = blocking::Client::new();
let url = format!("{}/repos/{}/{}/issues/{}", GITEA_API_URL, owner, repo, index);
let result = client.get(&url).send();
if let Ok(resp) = result {
let result = resp.json::<Issue>();
if let Ok(data) = result {
return Ok(data);
} else {
return Err(format!("Invalid JSON payload {}", result.err().unwrap()));
}
}
Err("Invalid Result".to_string())
}

// API structures

#[derive(Serialize, Deserialize, Clone)]
@@ -62,4 +77,45 @@ pub struct Asset {
pub created_at: String,
pub uuid: String,
pub browser_download_url: String,
}
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Issue {
pub id: usize,
pub url: String,
pub html_url: String,
pub number: usize,
pub user: Author,
pub original_author: String,
pub original_author_id: usize,
pub title: String,
pub body: String,
pub labels: Vec<IssueTag>,
//pub milestone: Option<???>,
//pub assignee: Option<???>,
//pub assignees: Option<???>,
pub state: String,
pub comments: usize,
pub created_at: String,
pub updated_at: String,
pub closed_at: Option<String>,
pub due_date: Option<String>,
//pub pull_request: Option<???>,
pub repository: IssueRespository,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct IssueTag {
pub id: usize,
pub name: String,
pub color: String,
pub description: String,
pub url: Option<String>,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct IssueRespository {
pub id: usize,
pub name: String,
pub full_name: String,
}

+ 88
- 4
src/gitea_command.rs View File

@@ -1,5 +1,5 @@
use crate::gitea::get_releases;
use crate::discord::{Interaction, InteractionResponse, InteractionApplicationCommandCallbackData, Embed, EmbedFooter, EmbedAuthor, EmbedField};
use crate::gitea::{get_releases, get_issue_by_index};
use crate::discord::{Interaction, InteractionResponse, InteractionApplicationCommandCallbackData, Embed, EmbedFooter, EmbedAuthor, EmbedField, CommandValue};

pub fn gitea_release(interaction: &Interaction) -> InteractionResponse {
let cmd = interaction.cmd().unwrap();
@@ -7,8 +7,8 @@ pub fn gitea_release(interaction: &Interaction) -> InteractionResponse {
let mut repo_name = String::new();
for opt in &cmd.data.options.unwrap() {
match &opt.name as &str {
"username" => username = opt.value.clone().unwrap(),
"repo" => repo_name = opt.value.clone().unwrap(),
"username" => username = opt.value.clone().unwrap().to_string(),
"repo" => repo_name = opt.value.clone().unwrap().to_string(),
_ => {}
}
}
@@ -89,3 +89,87 @@ pub fn gitea_release(interaction: &Interaction) -> InteractionResponse {
}
}
}

pub fn gitea_issue(interaction: &Interaction) -> InteractionResponse {
let cmd = interaction.cmd().unwrap();
// these should always be populated, but Rust doesn't know that
let mut username = String::new();
let mut repo_name = String::new();
let mut index = 0;
for opt in &cmd.data.options.unwrap() {
match &opt.name as &str {
"username" => {
if let CommandValue::StrVal(v) = opt.value.clone().unwrap() {
username = v;
}
},
"repo" => {
if let CommandValue::StrVal(v) = opt.value.clone().unwrap() {
repo_name = v;
}
},
"issue" => {
if let CommandValue::IntVal(v) = opt.value.clone().unwrap() {
index = v;
}
}
_ => {}
}
}
let res = get_issue_by_index(&username, &repo_name, index);
if let Ok(resp) = res {
// limit description to 2000 characters
let mut desc = resp.body.clone();
if desc.len() > 2000 {
desc = desc[..2000].to_string() + "...";
}
let embed = Embed {
title: Some(format!("{} #{}", &repo_name, index)),
type_: None,
description: Some(desc),
url: None,
timestamp: None,
color: Some(0x00C800), // Colour::from_rgb(0, 200, 0)
footer: Some(EmbedFooter {
text: resp.user.login.clone(),
icon_url: Some(resp.user.avatar_url.clone()),
proxy_icon_url: None
}),
image: None,
thumbnail: None,
video: None,
provider: None,
author: Some(EmbedAuthor {
name: Some(resp.title.clone()),
url: Some(format!("https://git.exmods.org/{}/{}/issues/{}", &username, &repo_name, index)),
icon_url: None,
proxy_icon_url: None
}),
fields: None,
/*fields: Some(vec![
EmbedField {
name: "Download".to_string(),
value: asset_str,
inline: Some(true)
}
])*/
};
return InteractionResponse::ChannelMessageWithSource {
data: Some(InteractionApplicationCommandCallbackData {
tts: false,
content: "".to_string(),
embeds: Some(vec![embed]),
allowed_mentions: None
})
}
} else {
return InteractionResponse::ChannelMessageWithSource {
data: Some(InteractionApplicationCommandCallbackData {
tts: false,
content: format!("Gitea API error: `{}`", res.err().unwrap()),
embeds: None,
allowed_mentions: None
})
}
}
}

+ 4
- 2
src/main.rs View File

@@ -13,8 +13,8 @@ use rocket_contrib::json::Json;
use std::sync::RwLock;
use crate::auth_tools::{AuthenticatedInteraction};
use crate::discord::{Interaction, InteractionResponse, InteractionResponseRaw, InteractionApplicationCommandCallbackData};
use crate::command_definitions::{hello_world, def_hello_world, def_gitea_release};
use crate::gitea_command::gitea_release;
use crate::command_definitions::{hello_world, def_hello_world, def_gitea_release, def_gitea_issue};
use crate::gitea_command::{gitea_release, gitea_issue};

static GLOBAL_COMMAND_KEY: &str = "GLOBAL command KEY";

@@ -33,6 +33,7 @@ fn root_post(interaction: AuthenticatedInteraction) -> Json<InteractionResponseR
match &data.name as &str {
"hello-world" => hello_world(&interaction.interaction),
"gitea-release" => gitea_release(&interaction.interaction),
"gitea-issue" => gitea_issue(&interaction.interaction),
_ => InteractionResponse::ChannelMessageWithSource {
data: Some(InteractionApplicationCommandCallbackData {
tts: false,
@@ -77,6 +78,7 @@ fn main() {
// TODO add more commands
register_command(&def_hello_world, &req_client);
register_command(&def_gitea_release, &req_client);
register_command(&def_gitea_issue, &req_client);
// start web server
rocket::ignite().mount("/", routes![root_post, hello_get]).launch();
}


Loading…
Cancel
Save