mirror of
https://github.com/revanced/gh-discord-transformer.git
synced 2025-04-29 13:54:25 +02:00
feat: init
This commit is contained in:
commit
d460078801
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/**/target
|
||||
.vercel
|
||||
Cargo.lock
|
1
.vercelignore
Normal file
1
.vercelignore
Normal file
@ -0,0 +1 @@
|
||||
/**/target
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# gh-discord-transformer
|
||||
|
||||
A small serverless function to transform GitHub sponsorship webhook events into Discord webhook events.
|
||||
**Requires Vercel.**
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `GITHUB_SECRET`: github webhook secret
|
||||
- `DISCORD_WEBHOOK_URL`: discord webhook url
|
||||
- `DISCORD_WEBHOOK_TID`: optional discord webhook thread id
|
21
api/Cargo.toml
Normal file
21
api/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "sponsor-shields"
|
||||
version = "0.1.0"
|
||||
authors = ["Sculas <contact@sculas.xyz>"]
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
http = "0.2"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
serde = "1"
|
||||
serde_json = "1.0"
|
||||
hmac-sha256 = "1.1.6"
|
||||
hex = "0.4.3"
|
||||
|
||||
[dependencies.vercel_lambda]
|
||||
git = "https://github.com/hanabi1224/rust"
|
||||
branch = "update_lambda_runtime_deps"
|
||||
|
||||
[[bin]]
|
||||
name = "github"
|
||||
path = "./github.rs"
|
56
api/github.rs
Normal file
56
api/github.rs
Normal file
@ -0,0 +1,56 @@
|
||||
use hmac_sha256::HMAC;
|
||||
use http::StatusCode;
|
||||
use serde_json::from_slice;
|
||||
use std::env::var;
|
||||
use std::result::Result as StdResult;
|
||||
use utils::{Error, Result};
|
||||
use vercel_lambda::{lambda_async, IntoResponse, Request, Response};
|
||||
|
||||
mod types;
|
||||
mod utils;
|
||||
|
||||
async fn handler(req: Request) -> Result<impl IntoResponse> {
|
||||
verify_sig(&req)?;
|
||||
if !is_ping_event(&req) {
|
||||
let event: types::GithubEvent = from_slice(req.body()).map_err(|_| Error("bad-json"))?;
|
||||
if event.action == "created" {
|
||||
utils::notify_discord(
|
||||
&event.sponsorship.sponsor.login,
|
||||
&event.sponsorship.sponsor.html_url,
|
||||
event.sponsorship.tier.monthly_price_in_dollars,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
} else {
|
||||
println!("ping event");
|
||||
}
|
||||
Ok(Response::builder().status(StatusCode::OK).body(()).unwrap())
|
||||
}
|
||||
|
||||
fn is_ping_event(req: &Request) -> bool {
|
||||
req.headers().get("X-GitHub-Event").map(|s| s.as_bytes()) == Some(b"ping")
|
||||
}
|
||||
|
||||
fn verify_sig(req: &Request) -> Result<()> {
|
||||
let req_sig = match req
|
||||
.headers()
|
||||
.get("X-Hub-Signature-256")
|
||||
.map(|s| s.as_bytes())
|
||||
{
|
||||
Some(sig) => sig,
|
||||
None => return Err(Error("no-sig")),
|
||||
};
|
||||
let secret = var("GITHUB_SECRET").map_err(|_| Error("no-secret"))?;
|
||||
let sig = format!("sha256={}", hex::encode(HMAC::mac(req.body(), &secret)));
|
||||
// I know this is unsafe, but I don't really care for this project
|
||||
if sig.as_bytes() != req_sig {
|
||||
return Err(Error("bad-sig"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Start the runtime with the handler
|
||||
fn main() -> StdResult<(), Box<dyn std::error::Error>> {
|
||||
wrap_handler!(handler => wrapped_handler);
|
||||
Ok(lambda_async!(wrapped_handler))
|
||||
}
|
24
api/types.rs
Normal file
24
api/types.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct GithubEvent {
|
||||
pub action: String,
|
||||
pub sponsorship: Sponsorship,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Sponsorship {
|
||||
pub sponsor: Sponsor,
|
||||
pub tier: Tier,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Sponsor {
|
||||
pub login: String,
|
||||
pub html_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Tier {
|
||||
pub monthly_price_in_dollars: f64,
|
||||
}
|
58
api/utils.rs
Normal file
58
api/utils.rs
Normal file
@ -0,0 +1,58 @@
|
||||
pub struct Error(pub &'static str);
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub async fn notify_discord(username: &str, profile_url: &str, usd_donated: f64) -> Result<()> {
|
||||
let webhook_url = std::env::var("DISCORD_WEBHOOK_URL").map_err(|_| Error("no-webhook-url"))?;
|
||||
let thread_id = std::env::var("DISCORD_WEBHOOK_TID").ok();
|
||||
let client = reqwest::Client::builder()
|
||||
.build()
|
||||
.map_err(|_| Error("bad-client"))?;
|
||||
let mut req = client.post(&webhook_url);
|
||||
if let Some(thread_id) = thread_id {
|
||||
req = req.query(&[("thread_id", thread_id)]);
|
||||
}
|
||||
let res = req
|
||||
.json(&serde_json::json!({
|
||||
"embeds": [
|
||||
{
|
||||
"title": "New Sponsor!",
|
||||
"description": format!("[{}]({}) just donated ${}!", username, profile_url, usd_donated),
|
||||
"color": 0x00ff00,
|
||||
"footer": {
|
||||
"text": "Sponsorship Notifications"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowed_mentions": {
|
||||
"parse": []
|
||||
}
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|_| Error("discord-bad-request"))?;
|
||||
if res.status() != http::StatusCode::NO_CONTENT {
|
||||
return Err(Error("bad-discord-status"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! wrap_handler {
|
||||
($handler:ident => $wrapped_handler:ident) => {
|
||||
async fn $wrapped_handler(
|
||||
req: vercel_lambda::Request,
|
||||
) -> std::result::Result<
|
||||
vercel_lambda::Response<vercel_lambda::Body>,
|
||||
vercel_lambda::error::VercelError,
|
||||
> {
|
||||
match $handler(req).await {
|
||||
Ok(res) => Ok(res.into_response()),
|
||||
Err(err) => Ok(vercel_lambda::Response::builder()
|
||||
.status(http::StatusCode::BAD_REQUEST)
|
||||
.body(err.0.into())
|
||||
.unwrap()),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
7
vercel.json
Normal file
7
vercel.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"functions": {
|
||||
"api/github.rs": {
|
||||
"runtime": "vercel-rust@3.1.2"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user