started gitea api impl #2

Merged
qpismont merged 9 commits from gitea_api into main 2026-06-07 10:23:31 +02:00
9 changed files with 541 additions and 50 deletions
Showing only changes of commit de81232201 - Show all commits
+1 -11
View File
@@ -12,18 +12,8 @@
"containerEnv": {
"SHELL": "/bin/bash"
},
"customizations": {
"vscode": {
"extensions": ["rust-lang.rust-analyzer", "tamasfe.even-better-toml", "fill-labs.dependi"],
"settings": {
"[rust]": {
"editor.defaultFormatter": "rust-lang.rust-analyzer",
"editor.formatOnSave": true
}
}
}
},
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/herald,type=bind",
"workspaceFolder": "/workspaces/herald",
"runArgs": ["--userns=keep-id", "--security-opt", "label=disable"],
"appPort": [3000]
}
Generated
+314 -18
View File
@@ -229,6 +229,72 @@ dependencies = [
"cmov",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 2.0.117",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn 2.0.117",
]
[[package]]
name = "derive_builder"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "derive_builder_macro"
version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.117",
]
[[package]]
name = "digest"
version = "0.11.3"
@@ -249,7 +315,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -258,12 +324,30 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "dotenvy_macro"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0235d912a8c749f4e0c9f18ca253b4c28cfefc1d2518096016d6e3230b6424"
dependencies = [
"dotenvy",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
[[package]]
name = "dyn-clone"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "encoding_rs"
version = "0.8.35"
@@ -331,6 +415,23 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-io"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-macro"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "futures-sink"
version = "0.3.32"
@@ -350,7 +451,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"slab",
]
@@ -417,12 +522,13 @@ dependencies = [
"dotenvy",
"hex",
"hmac",
"reqwest",
"openrouter-rs",
"reqwest 0.13.3",
"serde",
"serde_json",
"sha2",
"subtle",
"thiserror",
"thiserror 2.0.18",
"tokio",
]
@@ -530,6 +636,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots",
]
[[package]]
@@ -639,6 +746,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.1.0"
@@ -694,7 +807,7 @@ dependencies = [
"jni-sys",
"log",
"simd_cesu8",
"thiserror",
"thiserror 2.0.18",
"walkdir",
"windows-link",
]
@@ -709,7 +822,7 @@ dependencies = [
"quote",
"rustc_version",
"simd_cesu8",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -728,7 +841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
dependencies = [
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -821,6 +934,26 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "openrouter-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7101578df2f54d9013594e94367adbe656521ef6002f03e4577f1e00e57cb4"
dependencies = [
"derive_builder",
"dotenvy_macro",
"futures-util",
"http",
"reqwest 0.12.28",
"schemars",
"serde",
"serde_json",
"thiserror 1.0.69",
"tokio",
"tokio-util",
"urlencoding",
]
[[package]]
name = "openssl-probe"
version = "0.2.1"
@@ -903,7 +1036,7 @@ dependencies = [
"rustc-hash",
"rustls",
"socket2",
"thiserror",
"thiserror 2.0.18",
"tokio",
"tracing",
"web-time",
@@ -925,7 +1058,7 @@ dependencies = [
"rustls",
"rustls-pki-types",
"slab",
"thiserror",
"thiserror 2.0.18",
"tinyvec",
"tracing",
"web-time",
@@ -998,6 +1131,67 @@ dependencies = [
"bitflags",
]
[[package]]
name = "ref-cast"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots",
]
[[package]]
name = "reqwest"
version = "0.13.3"
@@ -1075,6 +1269,7 @@ checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
dependencies = [
"aws-lc-rs",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -1172,6 +1367,31 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "schemars"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc"
dependencies = [
"dyn-clone",
"ref-cast",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d115b50f4aaeea07e79c1912f645c7513d81715d0420f8bc77a18c6260b307f"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.117",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -1234,7 +1454,18 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
@@ -1344,12 +1575,29 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.117"
@@ -1378,7 +1626,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1402,13 +1650,33 @@ dependencies = [
"libc",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
"thiserror-impl 2.0.18",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
@@ -1419,7 +1687,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1472,7 +1740,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1600,6 +1868,12 @@ dependencies = [
"serde",
]
[[package]]
name = "urlencoding"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -1682,7 +1956,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
"wasm-bindgen-shared",
]
@@ -1695,6 +1969,19 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "web-sys"
version = "0.3.98"
@@ -1724,6 +2011,15 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webpki-roots"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "winapi-util"
version = "0.1.11"
@@ -1955,7 +2251,7 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
"synstructure",
]
@@ -1976,7 +2272,7 @@ checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
@@ -1996,7 +2292,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
"synstructure",
]
@@ -2036,7 +2332,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.117",
]
[[package]]
+2 -1
View File
@@ -8,6 +8,7 @@ reqwest = { version = "0.13", features = ["json"] }
tokio = { version = "1.52", features = ["full"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
openrouter-rs = "0.10"
dotenvy = "0.15"
axum = "0.8"
anyhow = "1.0"
@@ -16,4 +17,4 @@ hmac = "0.13"
sha2 = "0.11"
hex = "0.4"
subtle = "2.6"
bytes = "1.11"
bytes = "1.11"
+135 -6
View File
@@ -1,22 +1,81 @@
use std::time::Duration;
use serde::Deserialize;
use crate::{
consts::{BOT_PROCESS_MSG, REVIEW_PROMPT},
env::EnvConfig,
errors::AppError,
gitea::{GiteaAPI, ReviewPayload, WebhookType},
open_router::OpenRouterClient,
};
#[derive(Deserialize, Debug)]
struct ReviewResult {
reviews: Vec<ReviewItem>,
comment: String,
}
#[derive(Deserialize, Debug)]
struct ReviewItem {
filename: String,
line: Option<u64>,
code: String,
message: String,
}
/// Map a filename to a markdown language identifier for syntax highlighting.
fn lang_from_filename(filename: &str) -> &str {
match std::path::Path::new(filename)
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
{
"rs" => "rust",
"py" => "python",
"js" | "mjs" => "javascript",
"ts" => "typescript",
"jsx" => "jsx",
"tsx" => "tsx",
"go" => "go",
"java" => "java",
"kt" | "kts" => "kotlin",
"scala" => "scala",
"c" | "h" => "c",
"cpp" | "cc" | "cxx" | "hpp" | "hxx" => "cpp",
"rb" => "ruby",
"php" => "php",
"swift" => "swift",
"sh" | "bash" | "zsh" => "bash",
"sql" => "sql",
"html" | "htm" => "html",
"css" => "css",
"scss" | "sass" => "scss",
"json" => "json",
"yaml" | "yml" => "yaml",
"xml" => "xml",
"toml" => "toml",
"md" | "mdx" => "markdown",
"dockerfile" | "Dockerfile" => "dockerfile",
"Makefile" => "makefile",
_ => "",
}
}
pub struct Bot {
config: EnvConfig,
gitea_api: GiteaAPI,
open_router_client: OpenRouterClient,
}
impl Bot {
pub fn new(config: EnvConfig) -> Self {
Self {
pub fn new(config: EnvConfig) -> anyhow::Result<Self> {
Ok(Self {
gitea_api: GiteaAPI::new(&config.gitea_url, &config.gitea_token),
open_router_client: OpenRouterClient::new(
&config.open_router_api_key,
&config.open_router_model,
)?,
config,
}
})
}
pub async fn start(
@@ -46,17 +105,87 @@ impl Bot {
let new_comment = self
.gitea_api
.comment(
&BOT_PROCESS_MSG.replace("{model}", &self.config.open_router_model),
&review_payload.repository.full_name,
review_payload.pull_request.number,
)
.await?;
tokio::time::sleep(Duration::from_secs(10)).await;
let bot_result: Result<String, anyhow::Error> = async {
let git_diff = self
.download_git_diff(&review_payload.pull_request.diff_url)
.await?;
let bot_request = &&REVIEW_PROMPT
.replace("{subject}", &review_payload.pull_request.title)
.replace("{comment}", &review_payload.comment.body)
.replace("{diff}", &git_diff);
self.open_router_client.chat(&bot_request).await
}
.await;
let edit_msg = match bot_result {
Ok(bot_result) => self.review_result_to_markdown(&bot_result),
Err(e) => format!("Error while reviewing: {}", e),
};
self.gitea_api
.edit_comment(&review_payload.repository.full_name, new_comment.id)
.edit_comment(
&edit_msg,
&review_payload.repository.full_name,
new_comment.id,
)
.await?;
Ok(())
}
fn review_result_to_markdown(&self, result: &str) -> String {
let review_result: ReviewResult = match serde_json::from_str(result) {
Ok(review_result) => review_result,
Err(_) => {
return format!(
"Failed to parse review result. Raw output:\n\n```json\n{}\n```",
result
);
}
};
if review_result.reviews.is_empty() {
return String::from("No issues found. ✅");
}
let mut md = String::from("## Review Feedback\n\n");
for (i, item) in review_result.reviews.iter().enumerate() {
if i > 0 {
md.push_str("\n---\n\n");
}
md.push_str(&format!("### `{}`\n\n", item.filename));
if let Some(line) = item.line {
md.push_str(&format!("> **Line {}**\n\n", line));
}
if !item.code.is_empty() {
let lang = lang_from_filename(&item.filename);
md.push_str(&format!("```{}\n{}\n```\n\n", lang, item.code));
}
md.push_str(&item.message);
md.push('\n');
}
if !review_result.comment.is_empty() {
md.push_str("\n---\n\n");
md.push_str("### Summary\n\n");
md.push_str(&review_result.comment);
md.push('\n');
}
md
}
async fn download_git_diff(&self, url: &str) -> anyhow::Result<String> {
let response = reqwest::get(url).await?;
let body = response.text().await?;
Ok(body)
}
}
+33
View File
@@ -1,3 +1,36 @@
pub const GITEA_SIG_HEADER_NAME: &str = "x-gitea-signature";
pub const GITEA_EVENT_TYPE_HEADER_NAME: &str = "x-gitea-event-type";
pub const MAX_WEBHOOK_BODY_SIZE: usize = 1024 * 1024; // 1 MiB
pub const BOT_PROCESS_MSG: &str = "
Review in progress with the model \"{model}\"...
";
pub const REVIEW_PROMPT: &str = "
You are a senior software engineer reviewing code changes.
Check good practices and code quality.
This is the pull request subject: \"{subject}\"
This is the user comment: \"{comment}\"
This is the git diff of the code changes:
\"{diff}\"
Please review the code changes and provide feedback.
Return your feedback with only this json format, reviews must contain each review (filename field must contain the full path with extension) and comment msut contain a final summary:
{
\"reviews\": [
{
\"filename\": \"\",
\"line\": ,
\"code\": \"\",
\"message\": \"\"
}
],
\"comment\": \"\"
}
";
+3
View File
@@ -6,6 +6,7 @@ pub struct EnvConfig {
pub http_port: u16,
pub webhook_secret: String,
pub open_router_api_key: String,
pub open_router_model: String,
pub bot_name: String,
pub gitea_url: String,
pub gitea_token: String,
@@ -18,6 +19,7 @@ pub fn load_config() -> anyhow::Result<EnvConfig> {
let bot_name = try_get_env("BOT_NAME")?;
let webhook_secret = try_get_env("WEBHOOK_SIG_HEADER_SECRET")?;
let open_router_api_key = try_get_env("OPEN_ROUTER_API_KEY")?;
let open_router_model = try_get_env("OPEN_ROUTER_MODEL")?;
let gitea_url = try_get_env("GITEA_URL")?;
let gitea_token = try_get_env("GITEA_TOKEN")?;
@@ -26,6 +28,7 @@ pub fn load_config() -> anyhow::Result<EnvConfig> {
webhook_secret,
bot_name,
open_router_api_key,
open_router_model,
gitea_url,
gitea_token,
})
+15 -8
View File
@@ -16,7 +16,12 @@ impl GiteaAPI {
}
}
pub async fn comment(&self, full_name: &str, index: u64) -> anyhow::Result<Comment> {
pub async fn comment(
&self,
body: &str,
full_name: &str,
index: u64,
) -> anyhow::Result<Comment> {
let url = format!(
"{}/api/v1/repos/{}/issues/{}/comments?access_token={}",
self.base_url, full_name, index, self.token
@@ -26,17 +31,20 @@ impl GiteaAPI {
let res = client
.post(url)
.json(&json!({
"body": "Hello world :)"
"body": body
}))
.send()
.await?;
println!("{}", res.status());
res.json::<Comment>().await.map_err(anyhow::Error::from)
}
pub async fn edit_comment(&self, full_name: &str, comment_id: u64) -> anyhow::Result<()> {
pub async fn edit_comment(
&self,
body: &str,
full_name: &str,
comment_id: u64,
) -> anyhow::Result<()> {
let url = format!(
"{}/api/v1/repos/{}/issues/comments/{}?access_token={}",
self.base_url, full_name, comment_id, self.token
@@ -46,13 +54,11 @@ impl GiteaAPI {
let res = client
.patch(url)
.json(&json!({
"body": "Updated Hello world :)"
"body": body
}))
.send()
.await?;
println!("{}", res.status());
Ok(())
}
}
@@ -75,6 +81,7 @@ pub struct PullRequest {
pub id: u64,
pub diff_url: String,
pub number: u64,
pub title: String,
}
#[derive(Deserialize, Debug)]
+4 -6
View File
@@ -6,21 +6,19 @@ mod consts;
mod env;
mod errors;
mod gitea;
mod open_router;
mod state;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = env::load_config()?;
let bot = Bot::new(config.clone());
let bot = Bot::new(config.clone())?;
let (tx, rx) = tokio::sync::mpsc::channel::<WebhookType>(1);
let app_state = AppState {
bot_tx: tx,
config,
};
let app_state = AppState { bot_tx: tx, config };
tokio::try_join!(bot.start(rx), api::start(app_state))?;
Ok(())
}
}
+34
View File
@@ -0,0 +1,34 @@
use openrouter_rs::{Message, api::chat::ChatCompletionRequest};
pub struct OpenRouterClient {
client: openrouter_rs::OpenRouterClient,
model: String,
}
impl OpenRouterClient {
pub fn new(token: &str, model: &str) -> anyhow::Result<Self> {
Ok(Self {
client: openrouter_rs::OpenRouterClient::builder()
.api_key(token)
.build()?,
model: String::from(model),
})
}
pub async fn chat(&self, msg: &str) -> anyhow::Result<String> {
let request = ChatCompletionRequest::builder()
.model(&self.model)
.messages(vec![Message::new(
openrouter_rs::types::Role::Developer,
msg,
)])
.build()?;
let response = self.client.chat().create(&request).await?;
response.choices[0]
.content()
.map(|msg| String::from(msg))
.ok_or(anyhow::anyhow!("No content"))
}
}