feat: log analysis
Signed-off-by: seth <getchoo@tuta.io>
This commit is contained in:
parent
026d4cb607
commit
c6f4295d6a
18 changed files with 487 additions and 20 deletions
|
@ -9,7 +9,7 @@ const DADJOKE: &str = "https://icanhazdadjoke.com";
|
||||||
pub async fn get_joke() -> Result<String> {
|
pub async fn get_joke() -> Result<String> {
|
||||||
let req = REQWEST_CLIENT.get(DADJOKE).build()?;
|
let req = REQWEST_CLIENT.get(DADJOKE).build()?;
|
||||||
|
|
||||||
info!("Making request to {}", req.url());
|
debug!("Making request to {}", req.url());
|
||||||
let resp = REQWEST_CLIENT.execute(req).await?;
|
let resp = REQWEST_CLIENT.execute(req).await?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub async fn get_sender(message_id: MessageId) -> Result<UserId> {
|
||||||
.get(format!("{PLURAL_KIT}{MESSAGES_ENDPOINT}/{message_id}"))
|
.get(format!("{PLURAL_KIT}{MESSAGES_ENDPOINT}/{message_id}"))
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
info!("Making request to {}", req.url());
|
debug!("Making request to {}", req.url());
|
||||||
let resp = REQWEST_CLIENT.execute(req).await?;
|
let resp = REQWEST_CLIENT.execute(req).await?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub async fn get_latest_minecraft_version() -> Result<String> {
|
||||||
.get(format!("{PRISM_META}{MINECRAFT_PACKAGEJSON_ENDPOINT}"))
|
.get(format!("{PRISM_META}{MINECRAFT_PACKAGEJSON_ENDPOINT}"))
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
info!("Making request to {}", req.url());
|
debug!("Making request to {}", req.url());
|
||||||
let resp = REQWEST_CLIENT.execute(req).await?;
|
let resp = REQWEST_CLIENT.execute(req).await?;
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
|
|
||||||
|
|
|
@ -16,31 +16,26 @@ const RORY: &str = "https://rory.cat";
|
||||||
const ENDPOINT: &str = "/purr";
|
const ENDPOINT: &str = "/purr";
|
||||||
|
|
||||||
pub async fn get_rory(id: Option<u64>) -> Result<RoryResponse> {
|
pub async fn get_rory(id: Option<u64>) -> Result<RoryResponse> {
|
||||||
let target = {
|
let target = id.map(|id| id.to_string()).unwrap_or_default();
|
||||||
if let Some(id) = id {
|
|
||||||
id.to_string()
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let req = REQWEST_CLIENT
|
let req = REQWEST_CLIENT
|
||||||
.get(format!("{RORY}{ENDPOINT}/{target}"))
|
.get(format!("{RORY}{ENDPOINT}/{target}"))
|
||||||
.build()
|
.build()
|
||||||
.wrap_err_with(|| "Couldn't build reqwest client!")?;
|
.wrap_err_with(|| "Couldn't build reqwest client!")?;
|
||||||
|
|
||||||
info!("Making request to {}", req.url());
|
debug!("Making request to {}", req.url());
|
||||||
let resp = REQWEST_CLIENT
|
let resp = REQWEST_CLIENT
|
||||||
.execute(req)
|
.execute(req)
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| "Couldn't make request for shiggy!")?;
|
.wrap_err_with(|| "Couldn't make request for rory!")?;
|
||||||
|
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
|
|
||||||
if let StatusCode::OK = status {
|
if let StatusCode::OK = status {
|
||||||
let data = resp
|
let data = resp
|
||||||
.json::<RoryResponse>()
|
.json::<RoryResponse>()
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| "Couldn't parse the shiggy response!")?;
|
.wrap_err_with(|| "Couldn't parse the rory response!")?;
|
||||||
|
|
||||||
Ok(data)
|
Ok(data)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,6 +10,7 @@ pub static COLORS: Lazy<HashMap<&str, Color>> = Lazy::new(|| {
|
||||||
("blue", Color::from((96, 165, 250))),
|
("blue", Color::from((96, 165, 250))),
|
||||||
("yellow", Color::from((253, 224, 71))),
|
("yellow", Color::from((253, 224, 71))),
|
||||||
("orange", Color::from((251, 146, 60))),
|
("orange", Color::from((251, 146, 60))),
|
||||||
|
// TODO purple & pink :D
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
||||||
e.title("Something went wrong!")
|
e.title("Something went wrong!")
|
||||||
.description("oopsie")
|
.description("oopsie")
|
||||||
.timestamp(Timestamp::now())
|
.timestamp(Timestamp::now())
|
||||||
.color(COLORS["orange"])
|
.color(COLORS["red"])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
181
src/handlers/event/analyze_logs/issues.rs
Normal file
181
src/handlers/event/analyze_logs/issues.rs
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
pub type Issue = Option<(String, String)>;
|
||||||
|
|
||||||
|
pub fn find_issues(log: &str) -> Vec<(String, String)> {
|
||||||
|
let issues = [
|
||||||
|
fabric_internal,
|
||||||
|
flatpak_nvidia,
|
||||||
|
forge_java,
|
||||||
|
intel_hd,
|
||||||
|
java_option,
|
||||||
|
macos_ns,
|
||||||
|
oom,
|
||||||
|
optinofine,
|
||||||
|
outdated_launcher,
|
||||||
|
wrong_java,
|
||||||
|
];
|
||||||
|
|
||||||
|
issues.iter().filter_map(|issue| issue(log)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fabric_internal(log: &str) -> Issue {
|
||||||
|
const CLASS_NOT_FOUND: &str = "Caused by: java.lang.ClassNotFoundException: ";
|
||||||
|
|
||||||
|
let issue = (
|
||||||
|
"Fabric Internal Access".to_string(),
|
||||||
|
"The mod you are using is using fabric internals that are not meant \
|
||||||
|
to be used by anything but the loader itself.
|
||||||
|
Those mods break both on Quilt and with fabric updates.
|
||||||
|
If you're using fabric, downgrade your fabric loader could work, \
|
||||||
|
on Quilt you can try updating to the latest beta version, \
|
||||||
|
but there's nothing much to do unless the mod author stops using them."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let errors = [
|
||||||
|
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.impl"),
|
||||||
|
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.mixin"),
|
||||||
|
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.impl"),
|
||||||
|
&format!("{CLASS_NOT_FOUND}net.fabricmc.fabric.loader.mixin"),
|
||||||
|
"org.quiltmc.loader.impl.FormattedException: java.lang.NoSuchMethodError:",
|
||||||
|
];
|
||||||
|
|
||||||
|
let found = errors.iter().any(|e| log.contains(e));
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flatpak_nvidia(log: &str) -> Issue {
|
||||||
|
let issue = (
|
||||||
|
"Outdated Nvidia Flatpak Driver".to_string(),
|
||||||
|
"The Nvidia driver for flatpak is outdated.
|
||||||
|
Please run `flatpak update` to fix this issue. \
|
||||||
|
If that does not solve it, \
|
||||||
|
please wait until the driver is added to Flathub and run it again."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let found = log.contains("org.lwjgl.LWJGLException: Could not choose GLX13 config")
|
||||||
|
|| log.contains("GLFW error 65545: GLX: Failed to find a suitable GLXFBConfig");
|
||||||
|
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn forge_java(log: &str) -> Issue {
|
||||||
|
let issue = (
|
||||||
|
"Forge Java Bug".to_string(),
|
||||||
|
"Old versions of Forge crash with Java 8u321+.
|
||||||
|
To fix this, update forge to the latest version via the Versions tab
|
||||||
|
(right click on Forge, click Change Version, and choose the latest one)
|
||||||
|
Alternatively, you can download 8u312 or lower. \
|
||||||
|
See [archive](https://github.com/adoptium/temurin8-binaries/releases/tag/jdk8u312-b07)"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.<init>(Ljava/util/jar/Manifest;)V");
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intel_hd(log: &str) -> Issue {
|
||||||
|
let issue =
|
||||||
|
(
|
||||||
|
"Intel HD Windows 10".to_string(),
|
||||||
|
"Your drivers don't support windows 10 officially
|
||||||
|
See https://prismlauncher.org/wiki/getting-started/installing-java/#a-note-about-intel-hd-20003000-on-windows-10 for more info".to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
let found = log.contains("java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.<init>(Ljava/util/jar/Manifest;)V");
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn java_option(log: &str) -> Issue {
|
||||||
|
static VM_OPTION_REGEX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"Unrecognized VM option '(.+)'[\r\n]").unwrap());
|
||||||
|
static OPTION_REGEX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"Unrecognized option: (.+)[\r\n]").unwrap());
|
||||||
|
|
||||||
|
if let Some(captures) = VM_OPTION_REGEX.captures(log) {
|
||||||
|
let title = if &captures[1] == "UseShenandoahGC" {
|
||||||
|
"Wrong Java Arguments"
|
||||||
|
} else {
|
||||||
|
"Java 8 and below don't support ShenandoahGC"
|
||||||
|
};
|
||||||
|
return Some((
|
||||||
|
title.to_string(),
|
||||||
|
format!("Remove `-XX:{}` from your Java arguments", &captures[1]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(captures) = OPTION_REGEX.captures(log) {
|
||||||
|
return Some((
|
||||||
|
"Wrong Java Arguments".to_string(),
|
||||||
|
format!("Remove `{}` from your Java arguments", &captures[1]),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn macos_ns(log: &str) -> Issue {
|
||||||
|
let issue = (
|
||||||
|
"MacOS NSInternalInconsistencyException".to_string(),
|
||||||
|
"You need to downgrade your Java 8 version. See https://prismlauncher.org/wiki/getting-started/installing-java/#older-minecraft-on-macos".to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
let found =
|
||||||
|
log.contains("Terminating app due to uncaught exception 'NSInternalInconsistencyException");
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn oom(log: &str) -> Issue {
|
||||||
|
let issue = (
|
||||||
|
"Out of Memory".to_string(),
|
||||||
|
"Allocating more RAM to your instance could help prevent this crash.".to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let found = log.contains("java.lang.OutOfMemoryError");
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn optinofine(log: &str) -> Issue {
|
||||||
|
let issue = (
|
||||||
|
"Potential OptiFine Incompatibilities".to_string(),
|
||||||
|
"OptiFine is known to cause problems when paired with other mods. \
|
||||||
|
Try to disable OptiFine and see if the issue persists.
|
||||||
|
Check `/tag optifine` for more info & some typically more compatible alternatives you can use."
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let found = log.contains("[✔] OptiFine_") || log.contains("[✔] optifabric-");
|
||||||
|
found.then_some(issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: @TheKodeToad
|
||||||
|
fn outdated_launcher(_log: &str) -> Issue {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrong_java(log: &str) -> Issue {
|
||||||
|
static SWITCH_VERSION_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
Regex::new(
|
||||||
|
r"(?m)Please switch to one of the following Java versions for this instance:[\r\n]+(Java version [\d.]+)",
|
||||||
|
).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(captures) = SWITCH_VERSION_REGEX.captures(log) {
|
||||||
|
let versions = captures[1].split('\n').collect::<Vec<&str>>().join(", ");
|
||||||
|
return Some((
|
||||||
|
"Wrong Java Version".to_string(),
|
||||||
|
format!("Please switch to one of the following: `{versions}`\nFor more information, type `/tag java`"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let issue = (
|
||||||
|
"Java compatibility check skipped".to_string(),
|
||||||
|
"The Java major version may not work with your Minecraft instance. Please switch to a compatible version".to_string()
|
||||||
|
);
|
||||||
|
|
||||||
|
log.contains("Java major version is incompatible. Things might break.")
|
||||||
|
.then_some(issue)
|
||||||
|
}
|
66
src/handlers/event/analyze_logs/mod.rs
Normal file
66
src/handlers/event/analyze_logs/mod.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use crate::consts::COLORS;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use log::*;
|
||||||
|
use poise::serenity_prelude::{Context, Message};
|
||||||
|
|
||||||
|
mod issues;
|
||||||
|
mod providers;
|
||||||
|
|
||||||
|
use issues::find_issues;
|
||||||
|
use providers::find_log;
|
||||||
|
|
||||||
|
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
||||||
|
let channel = message.channel_id;
|
||||||
|
|
||||||
|
let log = find_log(message).await;
|
||||||
|
|
||||||
|
if log.is_err() {
|
||||||
|
channel
|
||||||
|
.send_message(ctx, |m| {
|
||||||
|
m.reference_message(message)
|
||||||
|
.allowed_mentions(|am| am.replied_user(true))
|
||||||
|
.embed(|e| {
|
||||||
|
e.title("Analyze failed!")
|
||||||
|
.description("Couldn't download log")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(log) = log? else {
|
||||||
|
debug!("No log found in message! Skipping analysis");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let issues = find_issues(&log);
|
||||||
|
|
||||||
|
channel
|
||||||
|
.send_message(ctx, |m| {
|
||||||
|
m.reference_message(message)
|
||||||
|
.allowed_mentions(|am| am.replied_user(true))
|
||||||
|
.embed(|e| {
|
||||||
|
e.title("Log analysis");
|
||||||
|
|
||||||
|
if issues.is_empty() {
|
||||||
|
e.color(COLORS["green"]).field(
|
||||||
|
"Analyze failed!",
|
||||||
|
"No issues found automatically",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
e.color(COLORS["red"]);
|
||||||
|
|
||||||
|
for (title, description) in issues {
|
||||||
|
e.field(title, description, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
e
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
24
src/handlers/event/analyze_logs/providers/0x0.rs
Normal file
24
src/handlers/event/analyze_logs/providers/0x0.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
use crate::api::REQWEST_CLIENT;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"https://0x0\.st/\w*\.\w*").unwrap());
|
||||||
|
|
||||||
|
pub async fn find(content: &str) -> Result<Option<String>> {
|
||||||
|
let Some(url) = REGEX.find(content).map(|m| &content[m.range()]) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let request = REQWEST_CLIENT.get(url).build()?;
|
||||||
|
let response = REQWEST_CLIENT.execute(request).await?;
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if let StatusCode::OK = status {
|
||||||
|
Ok(Some(response.text().await?))
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Failed to fetch paste from {url} with {status}",))
|
||||||
|
}
|
||||||
|
}
|
18
src/handlers/event/analyze_logs/providers/attachment.rs
Normal file
18
src/handlers/event/analyze_logs/providers/attachment.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use poise::serenity_prelude::Message;
|
||||||
|
|
||||||
|
pub async fn find(message: &Message) -> Result<Option<String>> {
|
||||||
|
// find first uploaded text file
|
||||||
|
if let Some(attachment) = message.attachments.iter().find(|a| {
|
||||||
|
a.content_type
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|ct| ct.starts_with("text/").then_some(true))
|
||||||
|
.is_some()
|
||||||
|
}) {
|
||||||
|
let bytes = attachment.download().await?;
|
||||||
|
let res = String::from_utf8(bytes)?;
|
||||||
|
Ok(Some(res))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
26
src/handlers/event/analyze_logs/providers/haste.rs
Normal file
26
src/handlers/event/analyze_logs/providers/haste.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::api::REQWEST_CLIENT;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
static REGEX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"https://hst\.sh(?:/raw)?/(\w+(?:\.\w*)?)").unwrap());
|
||||||
|
|
||||||
|
pub async fn find(content: &str) -> Result<Option<String>> {
|
||||||
|
let Some(captures) = REGEX.captures(content) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = format!("https://hst.sh/raw/{}", &captures[1]);
|
||||||
|
let request = REQWEST_CLIENT.get(&url).build()?;
|
||||||
|
let response = REQWEST_CLIENT.execute(request).await?;
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if let StatusCode::OK = status {
|
||||||
|
Ok(Some(response.text().await?))
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Failed to fetch paste from {url} with {status}"))
|
||||||
|
}
|
||||||
|
}
|
25
src/handlers/event/analyze_logs/providers/mclogs.rs
Normal file
25
src/handlers/event/analyze_logs/providers/mclogs.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use crate::api::REQWEST_CLIENT;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"https://mclo\.gs/(\w+)").unwrap());
|
||||||
|
|
||||||
|
pub async fn find(content: &str) -> Result<Option<String>> {
|
||||||
|
let Some(captures) = REGEX.captures(content) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = format!("https://api.mclo.gs/1/raw/{}", &captures[1]);
|
||||||
|
let request = REQWEST_CLIENT.get(&url).build()?;
|
||||||
|
let response = REQWEST_CLIENT.execute(request).await?;
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if let StatusCode::OK = status {
|
||||||
|
Ok(Some(response.text().await?))
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Failed to fetch log from {url} with {status}"))
|
||||||
|
}
|
||||||
|
}
|
33
src/handlers/event/analyze_logs/providers/mod.rs
Normal file
33
src/handlers/event/analyze_logs/providers/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
|
use poise::serenity_prelude::Message;
|
||||||
|
|
||||||
|
#[path = "0x0.rs"]
|
||||||
|
mod _0x0;
|
||||||
|
mod attachment;
|
||||||
|
mod haste;
|
||||||
|
mod mclogs;
|
||||||
|
mod paste_gg;
|
||||||
|
mod pastebin;
|
||||||
|
|
||||||
|
pub type LogProvider = Result<Option<String>>;
|
||||||
|
|
||||||
|
pub async fn find_log(message: &Message) -> LogProvider {
|
||||||
|
macro_rules! provider_impl {
|
||||||
|
($provider:ident) => {
|
||||||
|
if let Some(content) = $provider::find(&message.content).await? {
|
||||||
|
return Ok(Some(content));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
provider_impl!(_0x0);
|
||||||
|
provider_impl!(mclogs);
|
||||||
|
provider_impl!(haste);
|
||||||
|
provider_impl!(paste_gg);
|
||||||
|
provider_impl!(pastebin);
|
||||||
|
|
||||||
|
if let Some(content) = attachment::find(message).await? {
|
||||||
|
return Ok(Some(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
70
src/handlers/event/analyze_logs/providers/paste_gg.rs
Normal file
70
src/handlers/event/analyze_logs/providers/paste_gg.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use crate::api::REQWEST_CLIENT;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
const PASTE_GG: &str = "https://api.paste.gg/v1";
|
||||||
|
const PASTES_ENDPOINT: &str = "/pastes";
|
||||||
|
static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"https://paste.gg/p/\w+/(\w+)").unwrap());
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
struct PasteResponse {
|
||||||
|
status: String,
|
||||||
|
result: Option<Vec<PasteResult>>,
|
||||||
|
error: Option<String>,
|
||||||
|
message: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
struct PasteResult {
|
||||||
|
id: String,
|
||||||
|
name: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
visibility: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn find(content: &str) -> Result<Option<String>> {
|
||||||
|
let Some(captures) = REGEX.captures(content) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let paste_id = &captures[1];
|
||||||
|
let files_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files");
|
||||||
|
|
||||||
|
let resp = REQWEST_CLIENT
|
||||||
|
.execute(REQWEST_CLIENT.get(&files_url).build()?)
|
||||||
|
.await?;
|
||||||
|
let status = resp.status();
|
||||||
|
|
||||||
|
if resp.status() != StatusCode::OK {
|
||||||
|
return Err(eyre!(
|
||||||
|
"Couldn't get paste {paste_id} from {PASTE_GG} with status {status}!"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let paste_files: PasteResponse = resp.json().await?;
|
||||||
|
let file_id = &paste_files
|
||||||
|
.result
|
||||||
|
.ok_or_else(|| eyre!("Couldn't find any files associated with paste {paste_id}!"))?[0]
|
||||||
|
.id;
|
||||||
|
|
||||||
|
let raw_url = format!("{PASTE_GG}{PASTES_ENDPOINT}/{paste_id}/files/{file_id}/raw");
|
||||||
|
|
||||||
|
let resp = REQWEST_CLIENT
|
||||||
|
.execute(REQWEST_CLIENT.get(&raw_url).build()?)
|
||||||
|
.await?;
|
||||||
|
let status = resp.status();
|
||||||
|
|
||||||
|
if status != StatusCode::OK {
|
||||||
|
return Err(eyre!(
|
||||||
|
"Couldn't get file {file_id} from paste {paste_id} with status {status}!"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = resp.text().await?;
|
||||||
|
|
||||||
|
Ok(Some(text))
|
||||||
|
}
|
26
src/handlers/event/analyze_logs/providers/pastebin.rs
Normal file
26
src/handlers/event/analyze_logs/providers/pastebin.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::api::REQWEST_CLIENT;
|
||||||
|
|
||||||
|
use color_eyre::eyre::{eyre, Result};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use regex::Regex;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
|
static REGEX: Lazy<Regex> =
|
||||||
|
Lazy::new(|| Regex::new(r"https://pastebin\.com(?:/raw)?/(\w+)").unwrap());
|
||||||
|
|
||||||
|
pub async fn find(content: &str) -> Result<Option<String>> {
|
||||||
|
let Some(captures) = REGEX.captures(content) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = format!("https://pastebin.com/raw/{}", &captures[1]);
|
||||||
|
let request = REQWEST_CLIENT.get(&url).build()?;
|
||||||
|
let response = REQWEST_CLIENT.execute(request).await?;
|
||||||
|
let status = response.status();
|
||||||
|
|
||||||
|
if let StatusCode::OK = status {
|
||||||
|
Ok(Some(response.text().await?))
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Failed to fetch paste from {url} with {status}"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,8 @@ use regex::Regex;
|
||||||
|
|
||||||
static ETA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\beta\b").unwrap());
|
static ETA_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\beta\b").unwrap());
|
||||||
|
|
||||||
pub async fn handle(ctx: &Context, msg: &Message) -> Result<()> {
|
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
||||||
if !ETA_REGEX.is_match(&msg.content) {
|
if !ETA_REGEX.is_match(&message.content) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,6 @@ pub async fn handle(ctx: &Context, msg: &Message) -> Result<()> {
|
||||||
utils::random_choice(consts::ETA_MESSAGES)?
|
utils::random_choice(consts::ETA_MESSAGES)?
|
||||||
);
|
);
|
||||||
|
|
||||||
msg.reply(ctx, response).await?;
|
message.reply(ctx, response).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,12 @@ use poise::serenity_prelude::{Context, Message};
|
||||||
|
|
||||||
use crate::utils;
|
use crate::utils;
|
||||||
|
|
||||||
pub async fn handle(ctx: &Context, msg: &Message) -> Result<()> {
|
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
||||||
let embeds = utils::resolve_message(ctx, msg).await?;
|
let embeds = utils::resolve_message(ctx, message).await?;
|
||||||
|
|
||||||
// TOOD getchoo: actually reply to user
|
// TOOD getchoo: actually reply to user
|
||||||
// ...not sure why Message doesn't give me a builder in reply() or equivalents
|
// ...not sure why Message doesn't give me a builder in reply() or equivalents
|
||||||
let our_channel = msg
|
let our_channel = message
|
||||||
.channel(ctx)
|
.channel(ctx)
|
||||||
.await
|
.await
|
||||||
.wrap_err_with(|| "Couldn't get channel from message!")?
|
.wrap_err_with(|| "Couldn't get channel from message!")?
|
||||||
|
|
|
@ -5,6 +5,7 @@ use log::*;
|
||||||
use poise::serenity_prelude::{Activity, Context, OnlineStatus};
|
use poise::serenity_prelude::{Activity, Context, OnlineStatus};
|
||||||
use poise::{Event, FrameworkContext};
|
use poise::{Event, FrameworkContext};
|
||||||
|
|
||||||
|
mod analyze_logs;
|
||||||
mod delete_on_reaction;
|
mod delete_on_reaction;
|
||||||
mod eta;
|
mod eta;
|
||||||
mod expand_link;
|
mod expand_link;
|
||||||
|
@ -52,6 +53,7 @@ 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, new_message).await?;
|
||||||
|
analyze_logs::handle(ctx, new_message).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Event::MessageDelete {
|
Event::MessageDelete {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue