feat: implement write_cards & support Qingque
This commit is contained in:
parent
bd1d54e202
commit
b16c0dadab
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2191,6 +2191,7 @@ dependencies = [
|
|||||||
"mongodb",
|
"mongodb",
|
||||||
"rusty-tesseract",
|
"rusty-tesseract",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
@ -10,6 +10,7 @@ leptess = "0.14.0"
|
|||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
rusty-tesseract = "1.1.9"
|
rusty-tesseract = "1.1.9"
|
||||||
serde = "1.0.195"
|
serde = "1.0.195"
|
||||||
|
tokio = "1.35.1"
|
||||||
tracing = "0.1.40"
|
tracing = "0.1.40"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
pub const KATANA_ID: u64 = 646937666251915264;
|
pub const KATANA_ID: u64 = 646937666251915264;
|
||||||
pub const SOFA_ID: u64 = 853629533855809596;
|
pub const SOFA_ID: u64 = 853629533855809596;
|
||||||
|
pub const QINGQUE_ID: u64 = 772642704257187840;
|
||||||
|
@ -3,8 +3,10 @@ use crate::structs::Card;
|
|||||||
use mongodb::Collection;
|
use mongodb::Collection;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use tokio::task;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
static KATANA: OnceLock<Collection<Card>> = OnceLock::new();
|
pub static KATANA: OnceLock<Collection<Card>> = OnceLock::new();
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Initialize the "katana" collection in MongoDB
|
/// Initialize the "katana" collection in MongoDB
|
||||||
@ -39,8 +41,7 @@ pub async fn query_card(name: &str, series: &str) -> Option<Card> {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_card(mut card: Card) {
|
pub async fn write_card(mut card: Card) -> Result<(), String> {
|
||||||
// todo!("Write card to database");
|
|
||||||
let old_card = KATANA
|
let old_card = KATANA
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -59,7 +60,7 @@ pub async fn write_card(mut card: Card) {
|
|||||||
.expect("Time went backwards");
|
.expect("Time went backwards");
|
||||||
card.last_update_ts = current_time_ts.as_secs() as i64;
|
card.last_update_ts = current_time_ts.as_secs() as i64;
|
||||||
if old_card.is_some() {
|
if old_card.is_some() {
|
||||||
KATANA
|
match KATANA
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.replace_one(
|
.replace_one(
|
||||||
@ -71,8 +72,96 @@ pub async fn write_card(mut card: Card) {
|
|||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Failed to update card: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
KATANA.get().unwrap().insert_one(card, None).await.unwrap();
|
match KATANA.get().unwrap().insert_one(card, None).await {
|
||||||
|
Ok(_) => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Failed to insert card: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn write_cards(cards: Vec<Card>) -> Result<(), String> {
|
||||||
|
let mut new_cards: Vec<Card> = Vec::new();
|
||||||
|
let mut handles: Vec<task::JoinHandle<Result<Option<Card>, String>>> = Vec::new();
|
||||||
|
for mut card in cards {
|
||||||
|
trace!("Writing card: {:?}", card);
|
||||||
|
handles.push(task::spawn(async {
|
||||||
|
let old_card = KATANA
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.find_one(
|
||||||
|
mongodb::bson::doc! {
|
||||||
|
"name": card.name.clone(),
|
||||||
|
"series": card.series.clone()
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let start = SystemTime::now();
|
||||||
|
let current_time_ts = start
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards");
|
||||||
|
card.last_update_ts = current_time_ts.as_secs() as i64;
|
||||||
|
if old_card.is_some() {
|
||||||
|
match KATANA
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.replace_one(
|
||||||
|
mongodb::bson::doc! {
|
||||||
|
"name": card.name.clone(),
|
||||||
|
"series": card.series.clone()
|
||||||
|
},
|
||||||
|
card,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Failed to update card: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(Some(card));
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for handle in handles {
|
||||||
|
match handle.await.unwrap() {
|
||||||
|
Ok(card) => {
|
||||||
|
if card.is_some() {
|
||||||
|
new_cards.push(card.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Failed to update card: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if new_cards.len() > 0 {
|
||||||
|
match KATANA.get().unwrap().insert_many(new_cards, None).await {
|
||||||
|
Ok(_) => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Failed to insert card: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#![feature(lazy_cell)]
|
#![feature(lazy_cell)]
|
||||||
|
#![feature(string_remove_matches)]
|
||||||
pub use log;
|
pub use log;
|
||||||
pub use tracing::{debug, error, info, trace, warn};
|
pub use tracing::{debug, error, info, trace, warn};
|
||||||
use tracing_subscriber::{self, fmt, EnvFilter};
|
use tracing_subscriber::{self, fmt, EnvFilter};
|
||||||
@ -6,6 +7,7 @@ pub mod constants;
|
|||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
pub mod tesseract;
|
pub mod tesseract;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
pub fn setup_logger(level: &str) -> Result<(), ()> {
|
pub fn setup_logger(level: &str) -> Result<(), ()> {
|
||||||
let formatter = fmt::format()
|
let formatter = fmt::format()
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
pub wishlist: Option<i32>,
|
pub wishlist: Option<u32>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub series: String,
|
pub series: String,
|
||||||
pub print: i32,
|
pub print: i32,
|
||||||
|
@ -1,3 +1,61 @@
|
|||||||
fn parse_card() {
|
use crate::structs::Card;
|
||||||
|
use log::{error, trace};
|
||||||
|
|
||||||
|
pub fn parse_cards_from_qingque_atopwl(content: &String) -> Vec<Card> {
|
||||||
|
let mut cards: Vec<Card> = Vec::new();
|
||||||
|
for line in content.split("\n") {
|
||||||
|
trace!("Parsing line: {}", line);
|
||||||
|
let mut line_split = line.split(" · ");
|
||||||
|
let wishlist = match line_split.nth(1) {
|
||||||
|
Some(wishlist_str) => {
|
||||||
|
let mut wl_string = wishlist_str.to_string();
|
||||||
|
// Remove `
|
||||||
|
wl_string.remove(0);
|
||||||
|
// Remove ❤ (Double because heart is 2 bytes)
|
||||||
|
wl_string.remove(0);
|
||||||
|
wl_string.remove(0);
|
||||||
|
// Remove last ``
|
||||||
|
wl_string.pop();
|
||||||
|
// Remove "," in the number
|
||||||
|
wl_string.remove_matches(",");
|
||||||
|
// Remove whitespace
|
||||||
|
wl_string = wl_string
|
||||||
|
.split_whitespace()
|
||||||
|
.collect::<String>()
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
trace!("Formatted wishlist number:{}", wl_string);
|
||||||
|
match wl_string.parse::<u32>() {
|
||||||
|
Ok(wishlist) => wishlist,
|
||||||
|
Err(_) => {
|
||||||
|
error!("Failed to parse wishlist number: {}", wishlist_str);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let series = match line_split.next() {
|
||||||
|
Some(series) => series.to_string(),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let name = match line_split.next() {
|
||||||
|
Some(name) => {
|
||||||
|
let mut name_string = name.to_string();
|
||||||
|
name_string.remove_matches("**");
|
||||||
|
name_string
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let card = Card {
|
||||||
|
wishlist: Some(wishlist),
|
||||||
|
name,
|
||||||
|
series,
|
||||||
|
print: 0,
|
||||||
|
last_update_ts: 0,
|
||||||
|
};
|
||||||
|
trace!("Parsed card: {:?}", card);
|
||||||
|
cards.push(card);
|
||||||
|
}
|
||||||
|
cards
|
||||||
}
|
}
|
1
swordfish-common/src/utils/mod.rs
Normal file
1
swordfish-common/src/utils/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod katana;
|
@ -17,10 +17,37 @@ pub struct Tesseract {
|
|||||||
pub backend: String,
|
pub backend: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct Debug {
|
||||||
|
pub allowed_users: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct List {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub servers: Vec<u64>,
|
||||||
|
pub channels: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct DropAnalyzer {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub blacklist: List,
|
||||||
|
pub whitelist: List,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct Features {
|
||||||
|
pub katana_drop_analysis: DropAnalyzer,
|
||||||
|
pub sofa_drop_analysis: DropAnalyzer,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub log: Log,
|
pub log: Log,
|
||||||
pub tesseract: Tesseract,
|
pub tesseract: Tesseract,
|
||||||
|
pub debug: Debug,
|
||||||
|
pub features: Features,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -36,6 +63,37 @@ impl Config {
|
|||||||
tesseract: Tesseract {
|
tesseract: Tesseract {
|
||||||
backend: "libtesseract".to_string(),
|
backend: "libtesseract".to_string(),
|
||||||
},
|
},
|
||||||
|
debug: Debug {
|
||||||
|
allowed_users: vec![],
|
||||||
|
},
|
||||||
|
features: Features {
|
||||||
|
katana_drop_analysis: DropAnalyzer {
|
||||||
|
enabled: false,
|
||||||
|
blacklist: List {
|
||||||
|
enabled: false,
|
||||||
|
servers: vec![],
|
||||||
|
channels: vec![],
|
||||||
|
},
|
||||||
|
whitelist: List {
|
||||||
|
enabled: false,
|
||||||
|
servers: vec![],
|
||||||
|
channels: vec![],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sofa_drop_analysis: DropAnalyzer {
|
||||||
|
enabled: false,
|
||||||
|
blacklist: List {
|
||||||
|
enabled: false,
|
||||||
|
servers: vec![],
|
||||||
|
channels: vec![],
|
||||||
|
},
|
||||||
|
whitelist: List {
|
||||||
|
enabled: false,
|
||||||
|
servers: vec![],
|
||||||
|
channels: vec![],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn save(&self, path: &str) {
|
pub fn save(&self, path: &str) {
|
||||||
|
@ -1,9 +1,44 @@
|
|||||||
|
use crate::config::List;
|
||||||
use crate::template::message;
|
use crate::template::message;
|
||||||
use serenity::builder::CreateMessage;
|
use serenity::builder::CreateMessage;
|
||||||
use serenity::client::Context;
|
use serenity::client::Context;
|
||||||
use serenity::model::channel::Message;
|
use serenity::model::channel::Message;
|
||||||
use swordfish_common::error;
|
use swordfish_common::error;
|
||||||
|
|
||||||
|
pub fn message_in_blacklist(msg: &Message, blacklist: &List) -> bool {
|
||||||
|
if !blacklist.enabled {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let guild_id = match msg.guild_id {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if blacklist.servers.contains(&guild_id.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if blacklist.channels.contains(&msg.channel_id.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn message_in_whitelist(msg: &Message, whitelist: &List) -> bool {
|
||||||
|
if !whitelist.enabled {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
let guild_id = match msg.guild_id {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if whitelist.servers.contains(&guild_id.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if whitelist.channels.contains(&msg.channel_id.get()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn error_message(ctx: &Context, msg: &Message, content: String, title: Option<String>) {
|
pub async fn error_message(ctx: &Context, msg: &Message, content: String, title: Option<String>) {
|
||||||
match msg
|
match msg
|
||||||
.channel_id
|
.channel_id
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(lazy_cell)]
|
#![feature(lazy_cell)]
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
|
use serenity::all::MessageUpdateEvent;
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::framework::standard::macros::{command, group};
|
use serenity::framework::standard::macros::{command, group};
|
||||||
use serenity::framework::standard::{CommandResult, Configuration, StandardFramework};
|
use serenity::framework::standard::{CommandResult, Configuration, StandardFramework};
|
||||||
@ -25,7 +26,7 @@ const GITHUB_URL: &str = "https://github.com/teppyboy/swordfish";
|
|||||||
static CONFIG: OnceLock<Config> = OnceLock::new();
|
static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||||
|
|
||||||
#[group]
|
#[group]
|
||||||
#[commands(ping, kdropanalyze, info)]
|
#[commands(ping, debug, info)]
|
||||||
struct General;
|
struct General;
|
||||||
struct Handler;
|
struct Handler;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -45,31 +46,145 @@ impl EventHandler for Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async fn message_update(
|
||||||
|
&self,
|
||||||
|
ctx: Context,
|
||||||
|
old_if_available: Option<Message>,
|
||||||
|
new: Option<Message>,
|
||||||
|
event: MessageUpdateEvent,
|
||||||
|
) {
|
||||||
|
let author = match event.author {
|
||||||
|
Some(ref v) => v,
|
||||||
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if author.id == ctx.cache.current_user().id {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let content = match event.content {
|
||||||
|
Some(ref v) => v,
|
||||||
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trace!("Message update: {}, sender: {}", content, author.id);
|
||||||
|
if author.id.get() == constants::QINGQUE_ID {
|
||||||
|
parse_qingque(&ctx, event).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn parse_katana(_ctx: &Context, msg: &Message) -> Result<(), String> {
|
async fn parse_qingque(ctx: &Context, event: MessageUpdateEvent) -> Result<(), String> {
|
||||||
|
if event.embeds.is_none() || event.embeds.clone().unwrap().len() == 0 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let embed = &event.embeds.unwrap()[0];
|
||||||
|
let embed_title = match embed.title {
|
||||||
|
Some(ref title) => title,
|
||||||
|
None => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match embed_title.as_str() {
|
||||||
|
"Top Wishlist" => {
|
||||||
|
let cards = utils::katana::parse_cards_from_qingque_atopwl(
|
||||||
|
&embed.description.as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
trace!("Begin importing cards");
|
||||||
|
match database::katana::write_cards(cards).await {
|
||||||
|
Ok(_) => {
|
||||||
|
trace!("Imported successully");
|
||||||
|
}
|
||||||
|
Err(why) => {
|
||||||
|
error!("Failed to import card: {:?}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn parse_katana(ctx: &Context, msg: &Message) -> Result<(), String> {
|
||||||
if msg.content.contains("is dropping 3 cards!")
|
if msg.content.contains("is dropping 3 cards!")
|
||||||
|| msg
|
|| msg
|
||||||
.content
|
.content
|
||||||
.contains("I'm dropping 3 cards since this server is currently active!")
|
.contains("I'm dropping 3 cards since this server is currently active!")
|
||||||
{
|
{
|
||||||
// trace!("Card drop detected, executing drop analyzer...");
|
let config = CONFIG.get().unwrap();
|
||||||
// match katana::analyze_drop_message(&LEPTESS_ARC, msg).await {
|
if !config.features.katana_drop_analysis.enabled {
|
||||||
// Ok(_) => {
|
return Ok(());
|
||||||
// // msg.reply(ctx, "Drop analysis complete").await?;
|
}
|
||||||
// }
|
if helper::message_in_blacklist(msg, &config.features.katana_drop_analysis.blacklist) {
|
||||||
// Err(why) => {
|
return Ok(());
|
||||||
// trace!("Failed to analyze drop: `{:?}`", why);
|
}
|
||||||
// // helper::error_message(ctx, msg, format!("Failed to analyze drop: `{:?}`", why)).await;
|
if !helper::message_in_whitelist(msg, &config.features.katana_drop_analysis.whitelist) {
|
||||||
// }
|
return Ok(());
|
||||||
// };
|
}
|
||||||
|
let start = Instant::now();
|
||||||
|
match katana::analyze_drop_message(msg).await {
|
||||||
|
Ok(cards) => {
|
||||||
|
let duration = start.elapsed();
|
||||||
|
let mut reply_str = String::new();
|
||||||
|
for card in cards {
|
||||||
|
// reply_str.push_str(&format!("{:?}\n", card));
|
||||||
|
let wishlist_str: String = match card.wishlist {
|
||||||
|
Some(wishlist) => {
|
||||||
|
let mut out_str = wishlist.to_string();
|
||||||
|
while out_str.len() < 5 {
|
||||||
|
out_str.push(' ');
|
||||||
|
}
|
||||||
|
out_str
|
||||||
|
}
|
||||||
|
None => "None ".to_string(),
|
||||||
|
};
|
||||||
|
let last_update_ts_str = match card.last_update_ts {
|
||||||
|
0 => "`Never`".to_string(),
|
||||||
|
ts => {
|
||||||
|
format!("<t:{}:R>", ts.to_string())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reply_str.push_str(
|
||||||
|
format!(
|
||||||
|
":heart: `{}` • `{}` • **{}** • {} • {}\n",
|
||||||
|
wishlist_str, card.print, card.name, card.series, last_update_ts_str
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
reply_str.push_str(&format!("Time taken (to analyze): `{:?}`", duration));
|
||||||
|
match msg.reply(ctx, reply_str).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(why) => {
|
||||||
|
error!("Failed to reply to message: {:?}", why);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to analyze drop: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
dotenv().unwrap();
|
match dotenv() {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(why) => {
|
||||||
|
eprintln!("Failed to load .env: {:?}", why);
|
||||||
|
}
|
||||||
|
}
|
||||||
let token = env::var("DISCORD_TOKEN").expect("Token not found");
|
let token = env::var("DISCORD_TOKEN").expect("Token not found");
|
||||||
let config: Config;
|
let config: Config;
|
||||||
if Path::new("./config.toml").exists() {
|
if Path::new("./config.toml").exists() {
|
||||||
@ -114,9 +229,298 @@ async fn ping(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
#[command]
|
||||||
async fn kdropanalyze(ctx: &Context, msg: &Message) -> CommandResult {
|
async fn debug(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let config = CONFIG.get().unwrap();
|
||||||
|
if !config.debug.allowed_users.contains(&msg.author.id.get()) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let mut args = msg.content.split(" ");
|
let mut args = msg.content.split(" ");
|
||||||
let target_channel_id = match args.nth(1) {
|
let subcommand = match args.nth(1) {
|
||||||
|
Some(content) => content,
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Usage: `debug <subcommand> [args...]`".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match subcommand {
|
||||||
|
"kdropanalyze" => dbg_kdropanalyze(ctx, msg).await?,
|
||||||
|
"kda" => dbg_kdropanalyze(ctx, msg).await?,
|
||||||
|
"embed" => dbg_embed(ctx, msg).await?,
|
||||||
|
"parse-qingque-atopwl" => dbg_parse_qingque_atopwl(ctx, msg).await?,
|
||||||
|
_ => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Unknown subcommand: `{}`", subcommand),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[command]
|
||||||
|
async fn info(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let reply_str = format!(
|
||||||
|
"Swordfish v{} - {}\n\
|
||||||
|
Log level: `{}`\n\
|
||||||
|
Build type: `{}`\n\n\
|
||||||
|
Like my work? Consider supporting me at my [Ko-fi](https://ko-fi.com/tretrauit) or [Patreon](https://patreon.com/tretrauit)!\n\n\
|
||||||
|
*Debug information*\n\
|
||||||
|
Tesseract backend: `{}`\n\
|
||||||
|
",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
GITHUB_URL,
|
||||||
|
CONFIG.get().unwrap().log.level.clone().as_str(),
|
||||||
|
env!("BUILD_PROFILE"),
|
||||||
|
CONFIG.get().unwrap().tesseract.backend.clone().as_str(),
|
||||||
|
);
|
||||||
|
helper::info_message(ctx, msg, reply_str, Some("Information".to_string())).await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dbg_parse_qingque_atopwl(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let mut args = msg.content.split(" ");
|
||||||
|
let target_channel_id = match args.nth(2) {
|
||||||
|
Some(content) => match content.parse::<u64>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to parse channel ID: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Usage: `parse-qingque-atopwl <channel ID> <message ID>`".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let target_msg_id = match args.nth(0) {
|
||||||
|
Some(content) => match content.parse::<u64>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to parse message ID: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Usage: `parse-qingque-atopwl <channel ID> <message ID>`".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let target_msg = match ctx
|
||||||
|
.http()
|
||||||
|
.get_message(
|
||||||
|
ChannelId::new(target_channel_id),
|
||||||
|
MessageId::new(target_msg_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(msg) => msg,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to get message: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if target_msg.embeds.len() == 0 {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Message does not contain any embeds".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let embed = &target_msg.embeds[0];
|
||||||
|
let embed_description = match embed.description {
|
||||||
|
Some(ref description) => description,
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Embed does not contain a description".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cards = utils::katana::parse_cards_from_qingque_atopwl(embed_description);
|
||||||
|
helper::info_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Parsed cards: ```\n{:?}\n```", cards),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dbg_embed(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let mut args = msg.content.split(" ");
|
||||||
|
let target_channel_id = match args.nth(2) {
|
||||||
|
Some(content) => match content.parse::<u64>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to parse channel ID: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Usage: `embed <channel ID> <message ID>`".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let target_msg_id = match args.nth(0) {
|
||||||
|
Some(content) => match content.parse::<u64>() {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to parse message ID: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Usage: `embed <channel ID> <message ID>`".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let target_msg = match ctx
|
||||||
|
.http()
|
||||||
|
.get_message(
|
||||||
|
ChannelId::new(target_channel_id),
|
||||||
|
MessageId::new(target_msg_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(msg) => msg,
|
||||||
|
Err(why) => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!("Failed to get message: `{:?}`", why),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if target_msg.embeds.len() == 0 {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Message does not contain any embeds".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let embed = &target_msg.embeds[0];
|
||||||
|
let embed_title = match embed.title {
|
||||||
|
Some(ref title) => title,
|
||||||
|
None => {
|
||||||
|
helper::error_message(ctx, msg, "Embed does not contain a title".to_string(), None)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let embed_description = match embed.description {
|
||||||
|
Some(ref description) => description,
|
||||||
|
None => {
|
||||||
|
helper::error_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
"Embed does not contain a description".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
helper::info_message(
|
||||||
|
ctx,
|
||||||
|
msg,
|
||||||
|
format!(
|
||||||
|
"Title: \n\
|
||||||
|
```\
|
||||||
|
{}\n\
|
||||||
|
```\n\
|
||||||
|
Description: \n\
|
||||||
|
```\n\
|
||||||
|
{}\n\
|
||||||
|
```",
|
||||||
|
embed_title, embed_description
|
||||||
|
),
|
||||||
|
Some("Embed information".to_string()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn dbg_kdropanalyze(ctx: &Context, msg: &Message) -> CommandResult {
|
||||||
|
let mut args = msg.content.split(" ");
|
||||||
|
let target_channel_id = match args.nth(2) {
|
||||||
Some(content) => match content.parse::<u64>() {
|
Some(content) => match content.parse::<u64>() {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
@ -232,23 +636,3 @@ async fn kdropanalyze(ctx: &Context, msg: &Message) -> CommandResult {
|
|||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[command]
|
|
||||||
async fn info(ctx: &Context, msg: &Message) -> CommandResult {
|
|
||||||
let reply_str = format!(
|
|
||||||
"Swordfish v{} - {}\n\
|
|
||||||
Log level: `{}`\n\
|
|
||||||
Build type: `{}`\n\n\
|
|
||||||
Like my work? Consider supporting me at my [Ko-fi](https://ko-fi.com/tretrauit) or [Patreon](https://patreon.com/tretrauit)!\n\n\
|
|
||||||
*Debug information*\n\
|
|
||||||
Tesseract backend: `{}`\n\
|
|
||||||
",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
GITHUB_URL,
|
|
||||||
CONFIG.get().unwrap().log.level.clone().as_str(),
|
|
||||||
env!("BUILD_PROFILE"),
|
|
||||||
CONFIG.get().unwrap().tesseract.backend.clone().as_str(),
|
|
||||||
);
|
|
||||||
helper::info_message(ctx, msg, reply_str, Some("Information".to_string())).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user