use http client from context
This commit is contained in:
parent
921540e249
commit
84a7cfe151
26 changed files with 148 additions and 149 deletions
|
@ -1,18 +1,11 @@
|
||||||
|
use super::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::debug;
|
|
||||||
|
|
||||||
const DADJOKE: &str = "https://icanhazdadjoke.com";
|
const DADJOKE: &str = "https://icanhazdadjoke.com";
|
||||||
|
|
||||||
pub async fn get_joke() -> Result<String> {
|
pub async fn get_joke(http: &HttpClient) -> Result<String> {
|
||||||
debug!("Making request to {DADJOKE}");
|
let joke = http.get_request(DADJOKE).await?.text().await?;
|
||||||
|
|
||||||
let resp = super::client()
|
|
||||||
.get(DADJOKE)
|
|
||||||
.header("Accept", "text/plain")
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
resp.error_for_status_ref()?;
|
|
||||||
|
|
||||||
let joke = resp.text().await?;
|
|
||||||
Ok(joke)
|
Ok(joke)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
use std::sync::OnceLock;
|
use eyre::{OptionExt, Result, WrapErr};
|
||||||
|
|
||||||
use eyre::{Context, OptionExt, Result};
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use octocrab::Octocrab;
|
use octocrab::Octocrab;
|
||||||
|
|
||||||
fn octocrab() -> &'static Octocrab {
|
pub async fn get_latest_prism_version(octocrab: &Octocrab) -> Result<String> {
|
||||||
static OCTOCRAB: OnceLock<Octocrab> = OnceLock::new();
|
|
||||||
OCTOCRAB.get_or_init(Octocrab::default)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_latest_prism_version() -> Result<String> {
|
|
||||||
debug!("Fetching the latest version of Prism Launcher");
|
debug!("Fetching the latest version of Prism Launcher");
|
||||||
|
|
||||||
let version = octocrab()
|
let version = octocrab
|
||||||
.repos("PrismLauncher", "PrismLauncher")
|
.repos("PrismLauncher", "PrismLauncher")
|
||||||
.releases()
|
.releases()
|
||||||
.get_latest()
|
.get_latest()
|
||||||
|
@ -22,10 +15,10 @@ pub async fn get_latest_prism_version() -> Result<String> {
|
||||||
Ok(version)
|
Ok(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_prism_stargazers_count() -> Result<u32> {
|
pub async fn get_prism_stargazers_count(octocrab: &Octocrab) -> Result<u32> {
|
||||||
debug!("Fetching Prism Launcher's stargazer count");
|
debug!("Fetching Prism Launcher's stargazer count");
|
||||||
|
|
||||||
let stargazers_count = octocrab()
|
let stargazers_count = octocrab
|
||||||
.repos("PrismLauncher", "PrismLauncher")
|
.repos("PrismLauncher", "PrismLauncher")
|
||||||
.get()
|
.get()
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
use std::sync::OnceLock;
|
use log::trace;
|
||||||
|
use reqwest::Response;
|
||||||
use eyre::Result;
|
|
||||||
use log::debug;
|
|
||||||
use reqwest::{Client, Response};
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
|
|
||||||
pub mod dadjoke;
|
pub mod dadjoke;
|
||||||
pub mod github;
|
pub mod github;
|
||||||
|
@ -12,43 +8,29 @@ pub mod pluralkit;
|
||||||
pub mod prism_meta;
|
pub mod prism_meta;
|
||||||
pub mod rory;
|
pub mod rory;
|
||||||
|
|
||||||
pub fn client() -> &'static reqwest::Client {
|
pub type HttpClient = reqwest::Client;
|
||||||
static CLIENT: OnceLock<Client> = OnceLock::new();
|
|
||||||
CLIENT.get_or_init(|| {
|
pub trait HttpClientExt {
|
||||||
|
// sadly i can't implement the actual Default trait :/
|
||||||
|
fn default() -> Self;
|
||||||
|
async fn get_request(&self, url: &str) -> Result<Response, reqwest::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpClientExt for HttpClient {
|
||||||
|
fn default() -> Self {
|
||||||
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development");
|
let version = option_env!("CARGO_PKG_VERSION").unwrap_or("development");
|
||||||
let user_agent = format!("refraction/{version}");
|
let user_agent = format!("refraction/{version}");
|
||||||
Client::builder()
|
reqwest::ClientBuilder::new()
|
||||||
.user_agent(user_agent)
|
.user_agent(user_agent)
|
||||||
.build()
|
.build()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
async fn get_request(&self, url: &str) -> Result<Response, reqwest::Error> {
|
||||||
pub async fn get_url(url: &str) -> Result<Response> {
|
trace!("Making request to {url}");
|
||||||
debug!("Making request to {url}");
|
let resp = self.get(url).send().await?;
|
||||||
let resp = client().get(url).send().await?;
|
resp.error_for_status_ref()?;
|
||||||
resp.error_for_status_ref()?;
|
|
||||||
|
Ok(resp)
|
||||||
Ok(resp)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn text_from_url(url: &str) -> Result<String> {
|
|
||||||
let resp = get_url(url).await?;
|
|
||||||
|
|
||||||
let text = resp.text().await?;
|
|
||||||
Ok(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn bytes_from_url(url: &str) -> Result<Vec<u8>> {
|
|
||||||
let resp = get_url(url).await?;
|
|
||||||
|
|
||||||
let bytes = resp.bytes().await?;
|
|
||||||
Ok(bytes.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn json_from_url<T: DeserializeOwned>(url: &str) -> Result<T> {
|
|
||||||
let resp = get_url(url).await?;
|
|
||||||
|
|
||||||
let json = resp.json().await?;
|
|
||||||
Ok(json)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use super::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::{eyre, OptionExt, Result};
|
use eyre::{eyre, OptionExt, Result};
|
||||||
use log::debug;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const PASTE_GG: &str = "https://api.paste.gg/v1";
|
const PASTE_GG: &str = "https://api.paste.gg/v1";
|
||||||
|
@ -27,11 +28,9 @@ pub struct Files {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn files_from(id: &str) -> Result<Response<Files>> {
|
pub async fn files_from(http: &HttpClient, id: &str) -> Result<Response<Files>> {
|
||||||
let url = format!("{PASTE_GG}{PASTES}/{id}/files");
|
let url = format!("{PASTE_GG}{PASTES}/{id}/files");
|
||||||
debug!("Making request to {url}");
|
let resp: Response<Files> = http.get_request(&url).await?.json().await?;
|
||||||
|
|
||||||
let resp: Response<Files> = super::json_from_url(&url).await?;
|
|
||||||
|
|
||||||
if resp.status == Status::Error {
|
if resp.status == Status::Error {
|
||||||
let message = resp
|
let message = resp
|
||||||
|
@ -44,9 +43,13 @@ pub async fn files_from(id: &str) -> Result<Response<Files>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_raw_file(paste_id: &str, file_id: &str) -> eyre::Result<String> {
|
pub async fn get_raw_file(
|
||||||
|
http: &HttpClient,
|
||||||
|
paste_id: &str,
|
||||||
|
file_id: &str,
|
||||||
|
) -> eyre::Result<String> {
|
||||||
let url = format!("{PASTE_GG}{PASTES}/{paste_id}/files/{file_id}/raw");
|
let url = format!("{PASTE_GG}{PASTES}/{paste_id}/files/{file_id}/raw");
|
||||||
let text = super::text_from_url(&url).await?;
|
let text = http.get_request(&url).await?.text().await?;
|
||||||
|
|
||||||
Ok(text)
|
Ok(text)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use super::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::{Context, Result};
|
use eyre::{Context, Result};
|
||||||
use log::debug;
|
|
||||||
use poise::serenity_prelude::{MessageId, UserId};
|
use poise::serenity_prelude::{MessageId, UserId};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -11,11 +12,9 @@ pub struct Message {
|
||||||
const PLURAL_KIT: &str = "https://api.pluralkit.me/v2";
|
const PLURAL_KIT: &str = "https://api.pluralkit.me/v2";
|
||||||
const MESSAGES: &str = "/messages";
|
const MESSAGES: &str = "/messages";
|
||||||
|
|
||||||
pub async fn sender_from(message_id: MessageId) -> Result<UserId> {
|
pub async fn sender_from(http: &HttpClient, message_id: MessageId) -> Result<UserId> {
|
||||||
let url = format!("{PLURAL_KIT}{MESSAGES}/{message_id}");
|
let url = format!("{PLURAL_KIT}{MESSAGES}/{message_id}");
|
||||||
debug!("Making request to {url}");
|
let resp: Message = http.get_request(&url).await?.json().await?;
|
||||||
|
|
||||||
let resp: Message = super::json_from_url(&url).await?;
|
|
||||||
|
|
||||||
let id: u64 =
|
let id: u64 =
|
||||||
resp.sender.parse().wrap_err_with(|| {
|
resp.sender.parse().wrap_err_with(|| {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use super::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::{OptionExt, Result};
|
use eyre::{OptionExt, Result};
|
||||||
use log::debug;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -14,14 +15,9 @@ pub struct MinecraftPackageJson {
|
||||||
const META: &str = "https://meta.prismlauncher.org/v1";
|
const META: &str = "https://meta.prismlauncher.org/v1";
|
||||||
const MINECRAFT_PACKAGEJSON: &str = "/net.minecraft/package.json";
|
const MINECRAFT_PACKAGEJSON: &str = "/net.minecraft/package.json";
|
||||||
|
|
||||||
pub async fn latest_minecraft_version() -> Result<String> {
|
pub async fn latest_minecraft_version(http: &HttpClient) -> Result<String> {
|
||||||
let url = format!("{META}{MINECRAFT_PACKAGEJSON}");
|
let url = format!("{META}{MINECRAFT_PACKAGEJSON}");
|
||||||
|
let data: MinecraftPackageJson = http.get_request(&url).await?.json().await?;
|
||||||
debug!("Making request to {url}");
|
|
||||||
let resp = super::client().get(url).send().await?;
|
|
||||||
resp.error_for_status_ref()?;
|
|
||||||
|
|
||||||
let data: MinecraftPackageJson = resp.json().await?;
|
|
||||||
|
|
||||||
let version = data
|
let version = data
|
||||||
.recommended
|
.recommended
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use super::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::{Context, Result};
|
use eyre::{Context, Result};
|
||||||
use log::debug;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -12,16 +13,13 @@ pub struct Response {
|
||||||
const RORY: &str = "https://rory.cat";
|
const RORY: &str = "https://rory.cat";
|
||||||
const PURR: &str = "/purr";
|
const PURR: &str = "/purr";
|
||||||
|
|
||||||
pub async fn get(id: Option<u64>) -> Result<Response> {
|
pub async fn get(http: &HttpClient, id: Option<u64>) -> Result<Response> {
|
||||||
let target = id.map(|id| id.to_string()).unwrap_or_default();
|
let target = id.map(|id| id.to_string()).unwrap_or_default();
|
||||||
let url = format!("{RORY}{PURR}/{target}");
|
let url = format!("{RORY}{PURR}/{target}");
|
||||||
|
|
||||||
debug!("Making request to {url}");
|
let data: Response = http
|
||||||
|
.get_request(&url)
|
||||||
let resp = super::client().get(url).send().await?;
|
.await?
|
||||||
resp.error_for_status_ref()?;
|
|
||||||
|
|
||||||
let data: Response = resp
|
|
||||||
.json()
|
.json()
|
||||||
.await
|
.await
|
||||||
.wrap_err("Couldn't parse the rory response!")?;
|
.wrap_err("Couldn't parse the rory response!")?;
|
||||||
|
|
|
@ -9,7 +9,7 @@ pub async fn joke(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
trace!("Running joke command");
|
trace!("Running joke command");
|
||||||
|
|
||||||
ctx.defer().await?;
|
ctx.defer().await?;
|
||||||
let joke = dadjoke::get_joke().await?;
|
let joke = dadjoke::get_joke(&ctx.data().http_client).await?;
|
||||||
ctx.say(joke).await?;
|
ctx.say(joke).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub async fn rory(
|
||||||
|
|
||||||
ctx.defer().await?;
|
ctx.defer().await?;
|
||||||
|
|
||||||
let rory = rory::get(id).await?;
|
let rory = rory::get(&ctx.data().http_client, id).await?;
|
||||||
|
|
||||||
let embed = {
|
let embed = {
|
||||||
let embed = CreateEmbed::new();
|
let embed = CreateEmbed::new();
|
||||||
|
|
|
@ -8,6 +8,7 @@ use poise::CreateReply;
|
||||||
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
#[poise::command(slash_command, prefix_command, track_edits = true)]
|
||||||
pub async fn stars(ctx: Context<'_>) -> Result<(), Error> {
|
pub async fn stars(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
trace!("Running stars command");
|
trace!("Running stars command");
|
||||||
|
let octocrab = &ctx.data().octocrab;
|
||||||
|
|
||||||
ctx.defer().await?;
|
ctx.defer().await?;
|
||||||
|
|
||||||
|
@ -15,13 +16,13 @@ pub async fn stars(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
if let Ok(count) = storage.launcher_stargazer_count().await {
|
if let Ok(count) = storage.launcher_stargazer_count().await {
|
||||||
count
|
count
|
||||||
} else {
|
} else {
|
||||||
let count = api::github::get_prism_stargazers_count().await?;
|
let count = api::github::get_prism_stargazers_count(octocrab).await?;
|
||||||
storage.cache_launcher_stargazer_count(count).await?;
|
storage.cache_launcher_stargazer_count(count).await?;
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace!("Not caching launcher stargazer count, as we're running without a storage backend");
|
trace!("Not caching launcher stargazer count, as we're running without a storage backend");
|
||||||
api::github::get_prism_stargazers_count().await?
|
api::github::get_prism_stargazers_count(octocrab).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
|
|
|
@ -32,7 +32,7 @@ module_macro!(moderation);
|
||||||
|
|
||||||
pub type Command = poise::Command<Data, Error>;
|
pub type Command = poise::Command<Data, Error>;
|
||||||
|
|
||||||
pub fn get() -> Vec<Command> {
|
pub fn all() -> Vec<Command> {
|
||||||
vec![
|
vec![
|
||||||
general!(help),
|
general!(help),
|
||||||
general!(joke),
|
general!(joke),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{fmt::Write, str::FromStr};
|
use std::{fmt::Write, str::FromStr};
|
||||||
|
|
||||||
use crate::{api, utils, Context, Error};
|
use crate::{api::HttpClientExt, utils, Context, Error};
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
@ -138,7 +138,12 @@ pub async fn set_welcome(
|
||||||
let downloaded = attachment.download().await?;
|
let downloaded = attachment.download().await?;
|
||||||
String::from_utf8(downloaded)?
|
String::from_utf8(downloaded)?
|
||||||
} else if let Some(url) = url {
|
} else if let Some(url) = url {
|
||||||
api::text_from_url(&url).await?
|
ctx.data()
|
||||||
|
.http_client
|
||||||
|
.get_request(&url)
|
||||||
|
.await?
|
||||||
|
.text()
|
||||||
|
.await?
|
||||||
} else {
|
} else {
|
||||||
ctx.say("A text file or URL must be provided!").await?;
|
ctx.say("A text file or URL must be provided!").await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
|
@ -192,19 +192,20 @@ async fn outdated_launcher(log: &str, data: &Data) -> Result<Issue> {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let octocrab = &data.octocrab;
|
||||||
let version_from_log = captures[0].replace("Prism Launcher version: ", "");
|
let version_from_log = captures[0].replace("Prism Launcher version: ", "");
|
||||||
|
|
||||||
let latest_version = if let Some(storage) = &data.storage {
|
let latest_version = if let Some(storage) = &data.storage {
|
||||||
if let Ok(version) = storage.launcher_version().await {
|
if let Ok(version) = storage.launcher_version().await {
|
||||||
version
|
version
|
||||||
} else {
|
} else {
|
||||||
let version = api::github::get_latest_prism_version().await?;
|
let version = api::github::get_latest_prism_version(octocrab).await?;
|
||||||
storage.cache_launcher_version(&version).await?;
|
storage.cache_launcher_version(&version).await?;
|
||||||
version
|
version
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
trace!("Not caching launcher version, as we're running without a storage backend");
|
trace!("Not caching launcher version, as we're running without a storage backend");
|
||||||
api::github::get_latest_prism_version().await?
|
api::github::get_latest_prism_version(octocrab).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
if version_from_log < latest_version {
|
if version_from_log < latest_version {
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
||||||
);
|
);
|
||||||
let channel = message.channel_id;
|
let channel = message.channel_id;
|
||||||
|
|
||||||
let log = find_log(message).await;
|
let log = find_log(&data.http_client, message).await;
|
||||||
|
|
||||||
if log.is_err() {
|
if log.is_err() {
|
||||||
let embed = CreateEmbed::new()
|
let embed = CreateEmbed::new()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api;
|
use crate::api::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ impl super::LogProvider for _0x0 {
|
||||||
.nth(0)
|
.nth(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let log = api::text_from_url(content).await?;
|
let log = http.get_request(content).await?.text().await?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::api::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use poise::serenity_prelude::Message;
|
use poise::serenity_prelude::Message;
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
|
|
||||||
pub struct Attachment;
|
pub struct Attachment;
|
||||||
|
|
||||||
impl super::LogProvider for Attachment {
|
impl super::LogProvider for Attachment {
|
||||||
|
@ -21,9 +21,10 @@ impl super::LogProvider for Attachment {
|
||||||
.nth(0)
|
.nth(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let attachment = api::bytes_from_url(content).await?;
|
let attachment = http.get_request(content).await?.bytes().await?.to_vec();
|
||||||
let log = String::from_utf8(attachment)?;
|
let log = String::from_utf8(attachment)?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api;
|
use crate::api::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ impl super::LogProvider for Haste {
|
||||||
super::get_first_capture(regex, &message.content)
|
super::get_first_capture(regex, &message.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let url = format!("{HASTE}{RAW}/{content}");
|
let url = format!("{HASTE}{RAW}/{content}");
|
||||||
let log = api::text_from_url(&url).await?;
|
let log = http.get_request(&url).await?.text().await?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api;
|
use crate::api::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
@ -21,9 +21,9 @@ impl super::LogProvider for MCLogs {
|
||||||
super::get_first_capture(regex, &message.content)
|
super::get_first_capture(regex, &message.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let url = format!("{MCLOGS}{RAW}/{content}");
|
let url = format!("{MCLOGS}{RAW}/{content}");
|
||||||
let log = api::text_from_url(&url).await?;
|
let log = http.get_request(&url).await?.text().await?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::api::HttpClient;
|
||||||
|
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
|
|
||||||
use enum_dispatch::enum_dispatch;
|
use enum_dispatch::enum_dispatch;
|
||||||
|
@ -21,7 +23,7 @@ mod pastebin;
|
||||||
#[enum_dispatch]
|
#[enum_dispatch]
|
||||||
pub trait LogProvider {
|
pub trait LogProvider {
|
||||||
async fn find_match(&self, message: &Message) -> Option<String>;
|
async fn find_match(&self, message: &Message) -> Option<String>;
|
||||||
async fn fetch(&self, content: &str) -> Result<String>;
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_first_capture(regex: &Regex, string: &str) -> Option<String> {
|
fn get_first_capture(regex: &Regex, string: &str) -> Option<String> {
|
||||||
|
@ -41,7 +43,7 @@ enum Provider {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Provider {
|
impl Provider {
|
||||||
pub fn interator() -> Iter<'static, Provider> {
|
pub fn iterator() -> Iter<'static, Provider> {
|
||||||
static PROVIDERS: [Provider; 6] = [
|
static PROVIDERS: [Provider; 6] = [
|
||||||
Provider::_0x0st(_0x0st),
|
Provider::_0x0st(_0x0st),
|
||||||
Provider::Attachment(Attachment),
|
Provider::Attachment(Attachment),
|
||||||
|
@ -54,12 +56,12 @@ impl Provider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_log(message: &Message) -> Result<Option<String>> {
|
pub async fn find_log(http: &HttpClient, message: &Message) -> Result<Option<String>> {
|
||||||
let providers = Provider::interator();
|
let providers = Provider::iterator();
|
||||||
|
|
||||||
for provider in providers {
|
for provider in providers {
|
||||||
if let Some(found) = provider.find_match(message).await {
|
if let Some(found) = provider.find_match(message).await {
|
||||||
let log = provider.fetch(&found).await?;
|
let log = provider.fetch(http, &found).await?;
|
||||||
return Ok(Some(log));
|
return Ok(Some(log));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api::paste_gg;
|
use crate::api::{paste_gg, HttpClient};
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ impl super::LogProvider for PasteGG {
|
||||||
super::get_first_capture(regex, &message.content)
|
super::get_first_capture(regex, &message.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let files = paste_gg::files_from(content).await?;
|
let files = paste_gg::files_from(http, content).await?;
|
||||||
let result = files
|
let result = files
|
||||||
.result
|
.result
|
||||||
.ok_or_eyre("Got an empty result from paste.gg!")?;
|
.ok_or_eyre("Got an empty result from paste.gg!")?;
|
||||||
|
@ -30,7 +30,7 @@ impl super::LogProvider for PasteGG {
|
||||||
.nth(0)
|
.nth(0)
|
||||||
.ok_or_eyre("Couldn't get file id from empty paste.gg response!")?;
|
.ok_or_eyre("Couldn't get file id from empty paste.gg response!")?;
|
||||||
|
|
||||||
let log = paste_gg::get_raw_file(content, file_id).await?;
|
let log = paste_gg::get_raw_file(http, content, file_id).await?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api;
|
use crate::api::{HttpClient, HttpClientExt};
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ impl super::LogProvider for PasteBin {
|
||||||
super::get_first_capture(regex, &message.content)
|
super::get_first_capture(regex, &message.content)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch(&self, content: &str) -> Result<String> {
|
async fn fetch(&self, http: &HttpClient, content: &str) -> Result<String> {
|
||||||
let url = format!("{PASTEBIN}{RAW}/{content}");
|
let url = format!("{PASTEBIN}{RAW}/{content}");
|
||||||
let log = api::text_from_url(&url).await?;
|
let log = http.get_request(&url).await?.text().await?;
|
||||||
|
|
||||||
Ok(log)
|
Ok(log)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
use crate::{api::HttpClient, utils};
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use poise::serenity_prelude::{Context, CreateAllowedMentions, CreateMessage, Message};
|
use poise::serenity_prelude::{Context, CreateAllowedMentions, CreateMessage, Message};
|
||||||
|
|
||||||
use crate::utils;
|
pub async fn handle(ctx: &Context, http: &HttpClient, message: &Message) -> Result<()> {
|
||||||
|
let embeds = utils::messages::from_message(ctx, http, message).await?;
|
||||||
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
|
||||||
let embeds = utils::messages::from_message(ctx, message).await?;
|
|
||||||
|
|
||||||
if !embeds.is_empty() {
|
if !embeds.is_empty() {
|
||||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);
|
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);
|
||||||
|
|
|
@ -23,7 +23,8 @@ pub async fn handle(
|
||||||
FullEvent::Ready { data_about_bot } => {
|
FullEvent::Ready { data_about_bot } => {
|
||||||
info!("Logged in as {}!", data_about_bot.user.name);
|
info!("Logged in as {}!", data_about_bot.user.name);
|
||||||
|
|
||||||
let latest_minecraft_version = api::prism_meta::latest_minecraft_version().await?;
|
let latest_minecraft_version =
|
||||||
|
api::prism_meta::latest_minecraft_version(&data.http_client).await?;
|
||||||
let activity = ActivityData::playing(format!("Minecraft {latest_minecraft_version}"));
|
let activity = ActivityData::playing(format!("Minecraft {latest_minecraft_version}"));
|
||||||
|
|
||||||
info!("Setting presence to activity {activity:#?}");
|
info!("Setting presence to activity {activity:#?}");
|
||||||
|
@ -37,7 +38,7 @@ pub async fn handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
FullEvent::Message { new_message } => {
|
FullEvent::Message { new_message } => {
|
||||||
trace!("Recieved message {}", new_message.content);
|
trace!("Received message {}", new_message.content);
|
||||||
|
|
||||||
// ignore new messages from bots
|
// ignore new messages from bots
|
||||||
// note: the webhook_id check allows us to still respond to PK users
|
// note: the webhook_id check allows us to still respond to PK users
|
||||||
|
@ -49,11 +50,12 @@ pub async fn handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(storage) = &data.storage {
|
if let Some(storage) = &data.storage {
|
||||||
|
let http = &data.http_client;
|
||||||
// detect PK users first to make sure we don't respond to unproxied messages
|
// detect PK users first to make sure we don't respond to unproxied messages
|
||||||
pluralkit::handle(ctx, new_message, storage).await?;
|
pluralkit::handle(ctx, http, storage, new_message).await?;
|
||||||
|
|
||||||
if storage.is_user_plural(new_message.author.id).await?
|
if storage.is_user_plural(new_message.author.id).await?
|
||||||
&& pluralkit::is_message_proxied(new_message).await?
|
&& pluralkit::is_message_proxied(http, new_message).await?
|
||||||
{
|
{
|
||||||
debug!("Not replying to unproxied PluralKit message");
|
debug!("Not replying to unproxied PluralKit message");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -61,13 +63,13 @@ pub async fn handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
eta::handle(ctx, new_message).await?;
|
eta::handle(ctx, new_message).await?;
|
||||||
expand_link::handle(ctx, new_message).await?;
|
expand_link::handle(ctx, &data.http_client, new_message).await?;
|
||||||
analyze_logs::handle(ctx, new_message, data).await?;
|
analyze_logs::handle(ctx, new_message, data).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
FullEvent::ReactionAdd { add_reaction } => {
|
FullEvent::ReactionAdd { add_reaction } => {
|
||||||
trace!(
|
trace!(
|
||||||
"Recieved reaction {} on message {} from {}",
|
"Received reaction {} on message {} from {}",
|
||||||
add_reaction.emoji,
|
add_reaction.emoji,
|
||||||
add_reaction.message_id.to_string(),
|
add_reaction.message_id.to_string(),
|
||||||
add_reaction.user_id.unwrap_or_default().to_string()
|
add_reaction.user_id.unwrap_or_default().to_string()
|
||||||
|
@ -78,7 +80,7 @@ pub async fn handle(
|
||||||
}
|
}
|
||||||
|
|
||||||
FullEvent::ThreadCreate { thread } => {
|
FullEvent::ThreadCreate { thread } => {
|
||||||
trace!("Recieved thread {}", thread.id);
|
trace!("Received thread {}", thread.id);
|
||||||
support_onboard::handle(ctx, thread).await?;
|
support_onboard::handle(ctx, thread).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::{api, storage::Storage};
|
use crate::{
|
||||||
|
api::{self, HttpClient},
|
||||||
|
storage::Storage,
|
||||||
|
};
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
@ -8,19 +12,24 @@ use tokio::time::sleep;
|
||||||
|
|
||||||
const PK_DELAY: Duration = Duration::from_secs(1);
|
const PK_DELAY: Duration = Duration::from_secs(1);
|
||||||
|
|
||||||
pub async fn is_message_proxied(message: &Message) -> Result<bool> {
|
pub async fn is_message_proxied(http: &HttpClient, message: &Message) -> Result<bool> {
|
||||||
trace!(
|
trace!(
|
||||||
"Waiting on PluralKit API for {} seconds",
|
"Waiting on PluralKit API for {} seconds",
|
||||||
PK_DELAY.as_secs()
|
PK_DELAY.as_secs()
|
||||||
);
|
);
|
||||||
sleep(PK_DELAY).await;
|
sleep(PK_DELAY).await;
|
||||||
|
|
||||||
let proxied = api::pluralkit::sender_from(message.id).await.is_ok();
|
let proxied = api::pluralkit::sender_from(http, message.id).await.is_ok();
|
||||||
|
|
||||||
Ok(proxied)
|
Ok(proxied)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle(_: &Context, msg: &Message, storage: &Storage) -> Result<()> {
|
pub async fn handle(
|
||||||
|
_: &Context,
|
||||||
|
http: &HttpClient,
|
||||||
|
storage: &Storage,
|
||||||
|
msg: &Message,
|
||||||
|
) -> Result<()> {
|
||||||
if msg.webhook_id.is_none() {
|
if msg.webhook_id.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -36,7 +45,7 @@ pub async fn handle(_: &Context, msg: &Message, storage: &Storage) -> Result<()>
|
||||||
);
|
);
|
||||||
sleep(PK_DELAY).await;
|
sleep(PK_DELAY).await;
|
||||||
|
|
||||||
if let Ok(sender) = api::pluralkit::sender_from(msg.id).await {
|
if let Ok(sender) = api::pluralkit::sender_from(http, msg.id).await {
|
||||||
storage.store_user_plurality(sender).await?;
|
storage.store_user_plurality(sender).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -30,6 +30,8 @@ type Context<'a> = poise::Context<'a, Data, Error>;
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
config: Config,
|
config: Config,
|
||||||
storage: Option<Storage>,
|
storage: Option<Storage>,
|
||||||
|
http_client: api::HttpClient,
|
||||||
|
octocrab: Arc<octocrab::Octocrab>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup(
|
async fn setup(
|
||||||
|
@ -55,7 +57,15 @@ async fn setup(
|
||||||
trace!("Redis connection looks good!");
|
trace!("Redis connection looks good!");
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = Data { config, storage };
|
let http_client = api::HttpClient::default();
|
||||||
|
let octocrab = octocrab::instance();
|
||||||
|
|
||||||
|
let data = Data {
|
||||||
|
config,
|
||||||
|
storage,
|
||||||
|
http_client,
|
||||||
|
octocrab,
|
||||||
|
};
|
||||||
|
|
||||||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||||
info!("Registered global commands!");
|
info!("Registered global commands!");
|
||||||
|
@ -82,7 +92,7 @@ async fn main() -> eyre::Result<()> {
|
||||||
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
|
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
|
||||||
|
|
||||||
let options = FrameworkOptions {
|
let options = FrameworkOptions {
|
||||||
commands: commands::get(),
|
commands: commands::all(),
|
||||||
|
|
||||||
on_error: |error| Box::pin(handlers::handle_error(error)),
|
on_error: |error| Box::pin(handlers::handle_error(error)),
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::api::pluralkit;
|
use crate::api::{pluralkit, HttpClient};
|
||||||
|
|
||||||
use std::{str::FromStr, sync::OnceLock};
|
use std::{str::FromStr, sync::OnceLock};
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ fn find_first_image(message: &Message) -> Option<String> {
|
||||||
.map(|res| res.url.clone())
|
.map(|res| res.url.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_real_author_id(message: &Message) -> UserId {
|
async fn find_real_author_id(http: &HttpClient, message: &Message) -> UserId {
|
||||||
if let Ok(sender) = pluralkit::sender_from(message.id).await {
|
if let Ok(sender) = pluralkit::sender_from(http, message.id).await {
|
||||||
sender
|
sender
|
||||||
} else {
|
} else {
|
||||||
message.author.id
|
message.author.id
|
||||||
|
@ -109,7 +109,11 @@ pub async fn to_embed(
|
||||||
Ok(embed)
|
Ok(embed)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_message(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
pub async fn from_message(
|
||||||
|
ctx: &Context,
|
||||||
|
http: &HttpClient,
|
||||||
|
msg: &Message,
|
||||||
|
) -> Result<Vec<CreateEmbed>> {
|
||||||
static MESSAGE_PATTERN: OnceLock<Regex> = OnceLock::new();
|
static MESSAGE_PATTERN: OnceLock<Regex> = OnceLock::new();
|
||||||
let message_pattern = MESSAGE_PATTERN.get_or_init(|| Regex::new(r"(?:https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<server_id>\d+)\/(?<channel_id>\d+)\/(?<message_id>\d+)").unwrap());
|
let message_pattern = MESSAGE_PATTERN.get_or_init(|| Regex::new(r"(?:https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<server_id>\d+)\/(?<channel_id>\d+)\/(?<message_id>\d+)").unwrap());
|
||||||
|
|
||||||
|
@ -121,7 +125,7 @@ pub async fn from_message(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbe
|
||||||
// if the message was sent through pluralkit, we'll want
|
// if the message was sent through pluralkit, we'll want
|
||||||
// to reference the Member of the unproxied account
|
// to reference the Member of the unproxied account
|
||||||
let author_id = if msg.webhook_id.is_some() {
|
let author_id = if msg.webhook_id.is_some() {
|
||||||
find_real_author_id(msg).await
|
find_real_author_id(http, msg).await
|
||||||
} else {
|
} else {
|
||||||
msg.author.id
|
msg.author.id
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue