First Version of Drunk-Venti-Rust
This commit is contained in:
commit
c8a846cd5a
22 changed files with 1620 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
25
Cargo.toml
Normal file
25
Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "drunk-venti-rust"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
authors = ["Evann Regnault"]
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serenity = { version = "0.10.9", default-features = false, features = ["client", "gateway", "rustls_backend", "model", "unstable_discord_api", "collector"] }
|
||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
|
||||
reqwest = "0.11.7"
|
||||
serde_derive = "1.0.132"
|
||||
serde = "1.0.132"
|
||||
serde_json = {version = "1.0.73", features = ["indexmap"]}
|
||||
levenshtein = "1.0.5"
|
||||
regex = "1.5.4"
|
||||
linked-hash-map = "0.5.4"
|
||||
getset = "0.1.2"
|
||||
mongodb = "2.1.0"
|
||||
futures = "0.3.19"
|
||||
rand = "0.8.4"
|
||||
dotenv = "0.15.0"
|
||||
dotenv_codegen = "0.15.0"
|
91
src/data/artifacts.rs
Normal file
91
src/data/artifacts.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use serenity::builder::CreateEmbed;
|
||||
use crate::data::domains::Domain;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Set {
|
||||
pub goblet: Option<Box<str>>,
|
||||
pub plume: Option<Box<str>>,
|
||||
pub circlet: Option<Box<str>>,
|
||||
pub flower: Option<Box<str>>,
|
||||
pub sands: Option<Box<str>>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Artifact {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
pub set_piece: Vec<u32>,
|
||||
pub sets: Set,
|
||||
pub bonuses: Vec<Box<str>>,
|
||||
pub rarity: Vec<u8>,
|
||||
pub domain: Option<Box<str>>
|
||||
}
|
||||
|
||||
impl Artifact{
|
||||
pub async fn get(artifact: &str) -> Artifact {
|
||||
let url = format!("http://localhost:3000/api/artifacts/{}", artifact);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Artifact>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/artifacts");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn search(artifact: &str) -> Vec<Artifact> {
|
||||
let url = format!("http://localhost:3000/api/artifacts/search/{}", artifact);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Artifact>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn to_embed(&self) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
|
||||
embed.title(format!("{} | {}", self.name, ":star:".repeat(self.rarity.get(0).expect("").to_owned() as usize)));
|
||||
|
||||
embed.thumbnail(format!("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/static/images/artifacts/{}_circlet.png", self.id));
|
||||
|
||||
for i in 0..self.set_piece.len(){
|
||||
embed.field(format!("{}-Pieces", self.set_piece.get(i).expect("No such piece set")),
|
||||
format!("{}", self.bonuses.get(i).expect("Mo such effects")),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
match &self.domain {
|
||||
Some(d) => {
|
||||
let domain = Domain::get(d).await;
|
||||
embed.field("Domain", domain.name(), true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn id(&self) -> &str {
|
||||
return self.id.as_ref()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn name(&self) -> &str {
|
||||
return self.name.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_artifacts() {
|
||||
for a in Artifact::get_all().await {
|
||||
println!("{}", a);
|
||||
let arti = Artifact::get(&a).await;
|
||||
println!("Name : {}\nCirclet : {}\nBonuses {:?}", arti.name, arti.sets.goblet.unwrap_or(Box::from("")), arti.bonuses);
|
||||
}
|
||||
|
||||
}
|
61
src/data/builds.rs
Normal file
61
src/data/builds.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RoleWeapon {
|
||||
pub id: Box<str>,
|
||||
pub refine: Option<Vec<u8>>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RoleStat {
|
||||
pub sands: Box<str>,
|
||||
pub goblet: Box<str>,
|
||||
pub circlet: Box<str>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Role{
|
||||
pub name: Box<str>,
|
||||
pub recommended: bool,
|
||||
pub weapons: Vec<RoleWeapon>,
|
||||
pub artifacts: Vec<Vec<Box<str>>>,
|
||||
pub main_stats: RoleStat,
|
||||
pub sub_stats: Vec<Box<str>>,
|
||||
pub talent: Vec<Box<str>>,
|
||||
pub tip: Box<str>,
|
||||
pub note: Box<str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Builds {
|
||||
pub roles: Vec<Role>
|
||||
}
|
||||
|
||||
impl Builds {
|
||||
pub(crate) async fn get(build: &str) -> Builds {
|
||||
let url = format!("http://localhost:3000/api/builds/{}", build);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Builds>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/builds");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn test_builds() {
|
||||
for a in Builds::get_all().await {
|
||||
println!("------------------");
|
||||
let builds = Builds::get(&a).await;
|
||||
println!("Roles for {} : {:?}\n", a, builds.roles.into_iter().map(|x| x.name).collect::<Vec<Box<str>>>());
|
||||
}
|
||||
|
||||
}
|
76
src/data/characters.rs
Normal file
76
src/data/characters.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use crate::data::builds::{Role};
|
||||
use crate::data::elements::Element;
|
||||
use crate::data::items::Item;
|
||||
use crate::data::shared_structs::{Ascension, WeaponType};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CharacterStats {
|
||||
pub hp: u64,
|
||||
pub atk: u64,
|
||||
pub def: u64
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CharacterMaterials {
|
||||
pub book: Vec<Item>,
|
||||
pub material: Vec<Item>,
|
||||
pub boss: Item
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Character {
|
||||
pub name: Box<str>,
|
||||
pub id: Box<str>,
|
||||
pub rarity: u8,
|
||||
pub element: Element,
|
||||
pub weapon: WeaponType,
|
||||
pub ascension: Vec<Ascension>,
|
||||
pub stats: CharacterStats,
|
||||
pub material: CharacterMaterials,
|
||||
pub builds: Vec<Role>
|
||||
}
|
||||
|
||||
impl Clone for Character {
|
||||
fn clone(&self) -> Self {
|
||||
let x = serde_json::to_string(self).expect("str");
|
||||
serde_json::from_str::<Character>(x.as_str()).expect("An error occurred")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Character {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn get(character: &str) -> Character {
|
||||
let url = format!("http://localhost:3000/api/characters/{}", character);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Character>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/characters");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn search(character: &str) -> Vec<Character> {
|
||||
let url = format!("http://localhost:3000/api/characters/search/{}", character);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Character>>().await.expect("Wrong json format");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn test_character () {
|
||||
for a in Character::get_all().await {
|
||||
println!("{}", a);
|
||||
let char = Character::get(&a).await;
|
||||
println!("Name : {}", char.name);
|
||||
}
|
||||
}
|
70
src/data/domains.rs
Normal file
70
src/data/domains.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DomainMonster {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
pub count: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DomainReward {
|
||||
pub adventure_exp: Box<str>,
|
||||
pub mora: Box<str>,
|
||||
pub friendship_exp: Box<str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DomainDifficulty {
|
||||
pub s: u64,
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
pub ar: u8,
|
||||
pub level: u8,
|
||||
pub reward: DomainReward,
|
||||
pub monsters: Vec<DomainMonster>,
|
||||
pub disorder: Vec<Box<str>>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Domain {
|
||||
pub name: Box<str>,
|
||||
pub domains: Vec<DomainDifficulty>,
|
||||
pub artifacts: Vec<Box<str>>,
|
||||
}
|
||||
|
||||
impl Domain {
|
||||
#[allow(dead_code)]
|
||||
pub async fn get(domain: &str) -> Domain {
|
||||
let url = format!("http://localhost:3000/api/domains/{}", domain);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Domain>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/domains");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn name(&self) -> String {
|
||||
return self.name.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn test_domains () {
|
||||
for a in Domain::get_all().await {
|
||||
println!("{}", a);
|
||||
let domain = Domain::get(&a).await;
|
||||
println!("Name : {}", domain.name);
|
||||
}
|
||||
}
|
32
src/data/elements.rs
Normal file
32
src/data/elements.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Element {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
pub simple_name: Box<str>,
|
||||
pub color: u64
|
||||
}
|
||||
|
||||
impl Element{
|
||||
async fn get(element: &str) -> Element {
|
||||
let url = format!("http://localhost:3000/api/elements/{}", element);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Element>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/elements");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_elem() {
|
||||
println!("All elems : {:?}", Element::get_all().await);
|
||||
|
||||
let geo = Element::get("geo").await;
|
||||
println!("Name : {}\nColor : #{:x}", geo.name, geo.color);
|
||||
}
|
62
src/data/events.rs
Normal file
62
src/data/events.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Event {
|
||||
pub name: Box<str>,
|
||||
pub image: Option<Box<str>>,
|
||||
pub start: Box<str>,
|
||||
pub end: Box<str>,
|
||||
pub url: Option<Box<str>>,
|
||||
pub start_timestamp: u64,
|
||||
pub end_timestamp: u64,
|
||||
pub show_on_home: Option<bool>
|
||||
}
|
||||
|
||||
impl Clone for Event {
|
||||
fn clone(&self) -> Self {
|
||||
let x = serde_json::to_string(&self).expect("");
|
||||
serde_json::from_str::<Event>(x.as_str()).expect("")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Event {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn get_current() -> Vec<Event> {
|
||||
let url = format!("http://localhost:3000/api/events/current");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Event>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) async fn get_upcoming() -> Vec<Event> {
|
||||
let url = format!("http://localhost:3000/api/events/upcoming");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Event>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_all() -> Vec<Event> {
|
||||
let url = format!("http://localhost:3000/api/events");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Event>>().await.expect("Wrong json format");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn test_events() {
|
||||
let upcoming = Event::get_upcoming().await;
|
||||
println!("UPCOMING EVENTS\n--------");
|
||||
for event in upcoming {
|
||||
println!("Name : {}", event.name);
|
||||
}
|
||||
|
||||
let current = Event::get_current().await;
|
||||
println!("CURRENT EVENTS\n--------");
|
||||
for event in current {
|
||||
println!("Name : {}", event.name);
|
||||
}
|
||||
}
|
43
src/data/items.rs
Normal file
43
src/data/items.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Item {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
pub day: Option<Vec<Box<str>>>,
|
||||
pub rarity: Option<u8>,
|
||||
pub parent: Option<Box<str>>
|
||||
}
|
||||
|
||||
impl Item{
|
||||
async fn get(item: &str) -> Item {
|
||||
let url = format!("http://localhost:3000/api/items/{}", item);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Item>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/items");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn search(item: &str) -> Vec<Item> {
|
||||
let url = format!("http://localhost:3000/api/items/search/{}", item);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Item>>().await.expect("Wrong json format");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_items() {
|
||||
println!("List all items : {:?}", Item::get_all().await);
|
||||
|
||||
let item = Item::get("relic_from_guyun").await;
|
||||
println!("Name : {}\nDays : {:?}", item.name, item.day.unwrap_or(vec![]));
|
||||
|
||||
let item = Item::get("mora").await;
|
||||
println!("Name : {}\nDays : {:?}", item.name, item.day.unwrap_or(vec![]));
|
||||
}
|
20
src/data/mod.rs
Normal file
20
src/data/mod.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
pub mod elements;
|
||||
pub mod artifacts;
|
||||
pub mod items;
|
||||
pub mod weapons;
|
||||
pub mod builds;
|
||||
pub mod characters;
|
||||
pub mod events;
|
||||
pub mod shared_structs;
|
||||
pub mod domains;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn test() {
|
||||
elements::test_elem().await;
|
||||
artifacts::test_artifacts().await;
|
||||
items::test_items().await;
|
||||
weapons::test_weapons().await;
|
||||
builds::test_builds().await;
|
||||
characters::test_character().await;
|
||||
events::test_events().await;
|
||||
}
|
24
src/data/shared_structs.rs
Normal file
24
src/data/shared_structs.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use serde_derive::{Serialize, Deserialize};
|
||||
use crate::data::items::Item;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WeaponType {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AscensionItem {
|
||||
pub item: Item,
|
||||
pub amount: u32
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Ascension {
|
||||
pub items: Vec<AscensionItem>,
|
||||
pub mora: u64
|
||||
}
|
121
src/data/weapons.rs
Normal file
121
src/data/weapons.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use reqwest::Url;
|
||||
use serde_derive::{Serialize, Deserialize};
|
||||
use serenity::builder::CreateEmbed;
|
||||
use regex::Regex;
|
||||
use crate::data::shared_structs::Ascension;
|
||||
use crate::data::shared_structs::WeaponType;
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Skill {
|
||||
pub name: Option<Box<str>>,
|
||||
pub description: Option<Box<str>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Secondary {
|
||||
pub name: Option<Box<str>>,
|
||||
pub stats: Option<Vec<Option<f64>>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Extras {
|
||||
pub id: Box<str>,
|
||||
pub name: Box<str>,
|
||||
#[serde(rename = "type")]
|
||||
pub weapon_type: Box<str>,
|
||||
pub rarity: u8,
|
||||
pub description: Box<str>,
|
||||
pub skill: Skill,
|
||||
pub secondary: Secondary,
|
||||
pub atk: Vec<Option<f64>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Weapon {
|
||||
pub name: Box<str>,
|
||||
pub id: Box<str>,
|
||||
pub rarity: u8,
|
||||
pub atk: u64,
|
||||
pub secondary: Box<str>,
|
||||
#[serde(rename = "type")]
|
||||
pub weapon_type: WeaponType,
|
||||
pub source: Box<str>,
|
||||
pub ascension: Vec<Ascension>,
|
||||
pub extras: Extras,
|
||||
}
|
||||
|
||||
impl Weapon {
|
||||
#[allow(dead_code)]
|
||||
pub async fn get(weapon: &str) -> Weapon {
|
||||
let url = format!("http://localhost:3000/api/weapons/{}", weapon);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Weapon>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_all() -> Vec<Box<str>> {
|
||||
let url = format!("http://localhost:3000/api/weapons");
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Box<str>>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn search(weapon: &str) -> Vec<Weapon> {
|
||||
let url = format!("http://localhost:3000/api/weapons/search/{}", weapon);
|
||||
let url = Url::parse(&*url).expect("Can't convert url");
|
||||
return reqwest::get(url).await.expect("Can't access Url").json::<Vec<Weapon>>().await.expect("Wrong json format");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn name(&self) -> &str {
|
||||
return &self.name;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn id(&self) -> &str {
|
||||
return &self.id;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_embed(&self) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(format!("{} | {}", self.name, ":star:".repeat(self.rarity as usize)));
|
||||
|
||||
embed.description(format!("{}", self.extras.description));
|
||||
|
||||
embed.thumbnail(format!("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/static/images/weapons/{}.png", self.id));
|
||||
|
||||
// Fields
|
||||
match self.extras.skill.name.as_ref() {
|
||||
Some(t) => {
|
||||
let re = Regex::new("(<|</)span(?: [^>]*)?>").expect("Unknown regex");
|
||||
embed.field(format!("Passive : {}", t),
|
||||
format!("{}",
|
||||
re.replace_all(self.extras.skill.description.as_ref()
|
||||
.unwrap_or(&Box::from(""))
|
||||
.to_string().as_str(), "**")).replace("\\n","\n"),
|
||||
false);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
embed.field("Main Stat", format!("{}", self.secondary), true);
|
||||
embed.field("Source", format!("{}", self.source), true);
|
||||
embed.field("\u{200b}", "\u{200b}", true);
|
||||
embed.field("Type", format!("{}", self.weapon_type.name), true);
|
||||
embed.field("Ascension Item", format!("{}", self.ascension.get(0).expect("").items.get(0).expect("").item.name), true);
|
||||
embed.field("\u{200b}", "\u{200b}", true);
|
||||
return embed;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_weapons() {
|
||||
for w in Weapon::get_all().await {
|
||||
println!("------------------");
|
||||
println!("{}", w);
|
||||
}
|
||||
}
|
44
src/interactions/genshin/artifacts.rs
Normal file
44
src/interactions/genshin/artifacts.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use linked_hash_map::LinkedHashMap;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::interactions::{InteractionApplicationCommandCallbackDataFlags, InteractionResponseType};
|
||||
use serenity::model::interactions::application_command::{ApplicationCommandInteraction, ApplicationCommandInteractionDataOption};
|
||||
use serenity::model::interactions::message_component::{MessageComponentInteraction};
|
||||
use crate::data::artifacts::Artifact;
|
||||
use crate::interactions::utils::create_action_row_basic;
|
||||
|
||||
pub async fn genshin_artifact_interaction(ctx: &Context, command: &ApplicationCommandInteraction, opt: &ApplicationCommandInteractionDataOption) {
|
||||
|
||||
let weapon = opt.options.get(0).expect("No argument for command Genshin artifact")
|
||||
.value.as_ref().expect("").as_str().expect("Not a string");
|
||||
|
||||
let artifacts = Artifact::search(weapon).await;
|
||||
let mut artifact_list: LinkedHashMap<String, String> = LinkedHashMap::new();
|
||||
|
||||
for a in artifacts {
|
||||
artifact_list.insert(a.id().to_string(), a.name().to_string());
|
||||
}
|
||||
|
||||
let ar = create_action_row_basic(artifact_list, "artifact");
|
||||
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|d| {
|
||||
d.content("Select an Artifact")
|
||||
.components(|c| c.add_action_row(ar))
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.expect("Message didn't got sent");
|
||||
}
|
||||
|
||||
|
||||
pub async fn show_artifact_embed(ctx: &Context, command: &MessageComponentInteraction, artifact_name: String){
|
||||
let artifact = Artifact::get(artifact_name.as_str()).await;
|
||||
let embed = artifact.to_embed().await;
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|d| {
|
||||
d.embeds(vec!(embed).into_iter())
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.expect("Interaction failed");
|
||||
}
|
416
src/interactions/genshin/build.rs
Normal file
416
src/interactions/genshin/build.rs
Normal file
|
@ -0,0 +1,416 @@
|
|||
use std::borrow::{Borrow};
|
||||
use linked_hash_map::LinkedHashMap;
|
||||
use serenity::builder::{CreateActionRow, CreateButton, CreateEmbed};
|
||||
use serenity::client::Context;
|
||||
use serenity::model::interactions::{InteractionApplicationCommandCallbackDataFlags, InteractionResponseType};
|
||||
use serenity::model::interactions::application_command::{ApplicationCommandInteraction, ApplicationCommandInteractionDataOption};
|
||||
use serenity::model::interactions::message_component::{ButtonStyle, MessageComponentInteraction};
|
||||
use crate::data::artifacts::Artifact;
|
||||
use crate::data::builds::{Builds, Role};
|
||||
use crate::data::characters::Character;
|
||||
use crate::data::weapons::Weapon;
|
||||
use crate::interactions::utils::create_action_row_basic;
|
||||
|
||||
pub async fn genshin_build_interaction(ctx: &Context, command: &ApplicationCommandInteraction, opt: &ApplicationCommandInteractionDataOption) {
|
||||
let char = opt.options.get(0).expect("No argument for command Genshin builds")
|
||||
.value.as_ref().expect("").as_str().expect("Not a string");
|
||||
|
||||
let characters = Character::search(char).await;
|
||||
let mut character_list: LinkedHashMap<String, String> = LinkedHashMap::new();
|
||||
|
||||
for c in characters {
|
||||
character_list.insert(c.id.to_string(), c.name.to_string());
|
||||
}
|
||||
|
||||
let ar = create_action_row_basic(character_list, "build");
|
||||
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|d| {
|
||||
d.content("Select a Character")
|
||||
.components(|c| c.add_action_row(ar))
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.expect("Message didn't got sent");
|
||||
}
|
||||
|
||||
fn is_menu(arg: &str) -> bool {
|
||||
match arg {
|
||||
"home" | "artifacts" | "weapons" | "notes" => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn build_interact(ctx: &Context, interaction: &MessageComponentInteraction, arguments: String) {
|
||||
let mut args = arguments.split("_");
|
||||
let command = args.next();
|
||||
|
||||
|
||||
match command.expect("") {
|
||||
c if is_menu(c) => {
|
||||
let role_index = args.next().expect("").parse::<usize>().expect("");
|
||||
let character_name = args.collect::<Vec<_>>().join("_");
|
||||
let character = Character::get(character_name.as_str()).await;
|
||||
let builds = Builds::get(character_name.as_str()).await;
|
||||
let role = builds.roles.get(role_index).expect("");
|
||||
let ar = nav_button(character_name, role_index);
|
||||
|
||||
let embed = match c {
|
||||
"home" => home_embed(role, character).await,
|
||||
"artifacts" => artifact_embed(role, character).await,
|
||||
"weapons" => weapons_embed(role, character).await,
|
||||
"notes" => note_embed(role, character).await,
|
||||
_ => CreateEmbed::default()
|
||||
};
|
||||
|
||||
interaction.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|d| {
|
||||
d.add_embed(embed)
|
||||
.components(|c| c.add_action_row(ar))
|
||||
})
|
||||
}).await.expect("Can't send response");
|
||||
}
|
||||
_ => {
|
||||
let mut character = args.collect::<Vec<_>>().join("_");
|
||||
character = [command.expect(""), character.as_str()].join("_");
|
||||
character = character.strip_suffix("_").unwrap_or(character.as_str()).parse().unwrap();
|
||||
build_select(&ctx, &interaction, character).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn nav_button(character_name: String, role_index: usize) -> CreateActionRow {
|
||||
let mut ar = CreateActionRow::default();
|
||||
|
||||
let mut btn_home = CreateButton::default();
|
||||
btn_home
|
||||
.custom_id(format!("build_home_{}_{}", role_index, character_name))
|
||||
.style(ButtonStyle::Primary)
|
||||
.label("Home");
|
||||
|
||||
let mut btn_artifacts = CreateButton::default();
|
||||
btn_artifacts
|
||||
.custom_id(format!("build_artifacts_{}_{}", role_index, character_name))
|
||||
.style(ButtonStyle::Primary)
|
||||
.label("Artifacts");
|
||||
|
||||
let mut btn_weapons = CreateButton::default();
|
||||
btn_weapons
|
||||
.custom_id(format!("build_weapons_{}_{}", role_index, character_name))
|
||||
.style(ButtonStyle::Primary)
|
||||
.label("Weapons");
|
||||
|
||||
let mut btn_notes = CreateButton::default();
|
||||
btn_notes
|
||||
.custom_id(format!("build_notes_{}_{}", role_index, character_name))
|
||||
.style(ButtonStyle::Primary)
|
||||
.label("Notes");
|
||||
|
||||
let mut btn_builds = CreateButton::default();
|
||||
btn_builds
|
||||
.custom_id(format!("build_{}", character_name))
|
||||
.style(ButtonStyle::Success)
|
||||
.label("Other builds");
|
||||
|
||||
ar.add_button(btn_home);
|
||||
ar.add_button(btn_artifacts);
|
||||
ar.add_button(btn_weapons);
|
||||
ar.add_button(btn_notes);
|
||||
ar.add_button(btn_builds);
|
||||
|
||||
ar
|
||||
}
|
||||
|
||||
async fn build_select(ctx: &Context, command: &MessageComponentInteraction, character: String) {
|
||||
let char = Character::get(character.as_str()).await;
|
||||
|
||||
let roles = &char.builds;
|
||||
|
||||
let roles = roles.into_iter().map(|c| c.name.to_string()).collect::<Vec<String>>();
|
||||
|
||||
let mut rolemap = LinkedHashMap::<String, String>::new();
|
||||
for i in 0..roles.len() {
|
||||
rolemap.insert(format!("{}_{}", i, character), roles.get(i).expect("").to_string());
|
||||
}
|
||||
|
||||
let ar = create_action_row_basic(rolemap, "build_home");
|
||||
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|d| {
|
||||
d.components(|c| c.add_action_row(ar))
|
||||
.create_embed(|e| {
|
||||
e.title(format!("{}", char.name))
|
||||
.thumbnail(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/characters/{}.png", char.id))
|
||||
.color(char.element.color)
|
||||
.description("Select a build !")
|
||||
.footer(|f| {
|
||||
f.text(format!("Data from : https://paimon.moe/characters/{}", char.id))
|
||||
})
|
||||
})
|
||||
})
|
||||
}).await.expect("Can't send response");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pub async fn home_embed(role: &Role, character: Character) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(format!("{} | {}", character.name, role.name));
|
||||
embed.thumbnail(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/characters/{}.png", character.id));
|
||||
embed.color(character.element.color);
|
||||
|
||||
match role.weapons.get(0) {
|
||||
Some(t) => {
|
||||
embed.field("Best Weapon",
|
||||
Weapon::get(t.id.borrow()).await.name,
|
||||
true,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
embed.field("Best Weapon", "TBD", true);
|
||||
}
|
||||
}
|
||||
|
||||
embed.field("Skill Order",
|
||||
role.talent.to_owned().into_iter().map(|t| format!("- {}\n", t))
|
||||
.collect::<Vec<String>>().join("").strip_suffix("\n").expect(""),
|
||||
true,
|
||||
);
|
||||
|
||||
|
||||
match role.artifacts.get(0) {
|
||||
Some(a) => {
|
||||
let mut artifact_string: Vec<String> = vec![];
|
||||
let str;
|
||||
match a.len() {
|
||||
1 => {
|
||||
let artifact_name = match a.get(0).expect("").to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
|
||||
str = format!("(4) {}", artifact_name);
|
||||
}
|
||||
2 => {
|
||||
for art in a {
|
||||
let artifact_name = match art.to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
artifact_string.push(format!("(2) {}", artifact_name));
|
||||
}
|
||||
str = artifact_string.join(" & ");
|
||||
}
|
||||
_ => {
|
||||
artifact_string.push("Choose 2 sets :".to_string());
|
||||
for art in a {
|
||||
let artifact_name = match art.to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
artifact_string.push(format!("(2) {}", artifact_name));
|
||||
}
|
||||
str = artifact_string.join(", ")
|
||||
}
|
||||
}
|
||||
embed.field("Best Artifacts", str, false);
|
||||
}
|
||||
_ => { embed.field("Best Artifacts", "TBD", false); }
|
||||
}
|
||||
|
||||
embed.field("Circlet", &role.main_stats.circlet, true);
|
||||
embed.field("Goblet", &role.main_stats.goblet, true);
|
||||
embed.field("Sands", &role.main_stats.sands, true);
|
||||
|
||||
embed.footer(|f| {
|
||||
f.text(format!("Data from : https://paimon.moe/characters/{}", character.id))
|
||||
});
|
||||
|
||||
embed
|
||||
}
|
||||
|
||||
|
||||
async fn artifact_embed(role: &Role, character: Character) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(format!("{} | {}", character.name, role.name));
|
||||
|
||||
match role.artifacts.get(0) {
|
||||
Some(t) if t.get(0).expect("").to_string() != "TBD" => {
|
||||
embed.thumbnail(format!("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/static/images/artifacts/{}_circlet.png", t.get(0).expect("")));
|
||||
}
|
||||
|
||||
_ => {
|
||||
embed.thumbnail(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/characters/{}.png", character.id));
|
||||
}
|
||||
}
|
||||
|
||||
embed.color(character.element.color);
|
||||
|
||||
let mut all_artefacts_string: Vec<String> = vec![];
|
||||
for i in 0..role.artifacts.len() {
|
||||
match role.artifacts.get(i) {
|
||||
Some(a) => {
|
||||
let mut artifact_string: Vec<String> = vec![];
|
||||
let str;
|
||||
match a.len() {
|
||||
1 => {
|
||||
let artifact_name = match a.get(0).expect("").to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
|
||||
str = format!("(4) {}", artifact_name);
|
||||
}
|
||||
2 => {
|
||||
for art in a {
|
||||
let artifact_name = match art.to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
artifact_string.push(format!("(2) {}", artifact_name));
|
||||
}
|
||||
str = artifact_string.join(" & ");
|
||||
}
|
||||
_ => {
|
||||
artifact_string.push("Choose 2 sets :".to_string());
|
||||
for art in a {
|
||||
let artifact_name = match art.to_string().as_str() {
|
||||
"+18%_atk_set" => "+18% Atk Set".to_string(),
|
||||
t => Artifact::get(t).await.name.to_string(),
|
||||
};
|
||||
artifact_string.push(format!("(2) {}", artifact_name));
|
||||
}
|
||||
str = artifact_string.join(", ")
|
||||
}
|
||||
}
|
||||
all_artefacts_string.push(format!("- {}", str));
|
||||
}
|
||||
_ => { all_artefacts_string.push(format!("- TBD")); }
|
||||
}
|
||||
}
|
||||
if all_artefacts_string.len() > 0 {
|
||||
embed.field("Best Artifacts", all_artefacts_string.join("\n"), false);
|
||||
} else {
|
||||
embed.field("Best Artifacts", "- TBD", false);
|
||||
}
|
||||
|
||||
embed.field("Sub-stats", {
|
||||
format!("- {}", role.sub_stats.join("\n-"))
|
||||
}, false);
|
||||
|
||||
embed.field("Circlet", &role.main_stats.circlet, true);
|
||||
embed.field("Goblet", &role.main_stats.goblet, true);
|
||||
embed.field("Sands", &role.main_stats.sands, true);
|
||||
|
||||
embed.footer(|f| {
|
||||
f.text(format!("Data from : https://paimon.moe/characters/{}", character.id))
|
||||
});
|
||||
|
||||
embed
|
||||
}
|
||||
|
||||
async fn weapons_embed(role: &Role, character: Character) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(format!("{} | {}", character.name, role.name));
|
||||
|
||||
match role.weapons.get(0) {
|
||||
Some(t) if t.id.to_string() != "TBD" => {
|
||||
embed.thumbnail(format!("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/static/images/weapons/{}.png", t.id));
|
||||
}
|
||||
|
||||
_ => {
|
||||
embed.thumbnail(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/characters/{}.png", character.id));
|
||||
}
|
||||
}
|
||||
|
||||
embed.color(character.element.color);
|
||||
|
||||
let mut all_weapons_string: Vec<String> = vec![];
|
||||
for i in 0..role.weapons.len() {
|
||||
match role.weapons.get(i) {
|
||||
Some(a) if a.id.to_string() != "TBD" => {
|
||||
let weapon = Weapon::get(a.id.borrow()).await;
|
||||
all_weapons_string.push(format!("- {}", weapon.name));
|
||||
}
|
||||
_ => { all_weapons_string.push(format!("- TBD")); }
|
||||
}
|
||||
}
|
||||
if all_weapons_string.len() > 0 {
|
||||
embed.field("Best Weapons", all_weapons_string.join("\n"), false);
|
||||
} else {
|
||||
embed.field("Best Weapons", "- TBD", false);
|
||||
}
|
||||
|
||||
embed.footer(|f| {
|
||||
f.text(format!("Data from : https://paimon.moe/characters/{}", character.id))
|
||||
});
|
||||
|
||||
|
||||
embed
|
||||
}
|
||||
|
||||
async fn note_embed(role: &Role, character: Character) -> CreateEmbed {
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(format!("{} | {}", character.name, &role.name));
|
||||
embed.thumbnail(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/characters/{}.png", character.id));
|
||||
embed.color(character.element.color);
|
||||
|
||||
match &role.note {
|
||||
n=> {
|
||||
let x = n.split("\n");
|
||||
let mut first = true;
|
||||
let mut add_before = "";
|
||||
for note_paragraph in x.collect::<Vec<&str>>() {
|
||||
if note_paragraph.len() == 0 {continue};
|
||||
if note_paragraph.len() < 64 && add_before.len() == 0{
|
||||
add_before = note_paragraph;
|
||||
continue;
|
||||
}
|
||||
if add_before.len() > 0 {
|
||||
let note_paragraph = format!("**{}**\n{}", add_before, note_paragraph);
|
||||
embed.field(if first { "Notes" } else { "\u{200b}" }, note_paragraph, false);
|
||||
add_before = "";
|
||||
} else {
|
||||
embed.field(if first { "Notes" } else { "\u{200b}" }, note_paragraph, false);
|
||||
first = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match &role.tip {
|
||||
n=> {
|
||||
let x = n.split("\n");
|
||||
let mut first = true;
|
||||
let mut add_before = "";
|
||||
for tip_paragraph in x.collect::<Vec<&str>>() {
|
||||
if tip_paragraph.len() == 0 {continue};
|
||||
if tip_paragraph.len() < 64 {
|
||||
add_before = tip_paragraph;
|
||||
continue;
|
||||
}
|
||||
if add_before.len() > 0 {
|
||||
let tip_paragraph = format!("**{}**\n{}", add_before, tip_paragraph);
|
||||
embed.field(if first { "Tips" } else { "\u{200b}" }, tip_paragraph, false);
|
||||
add_before = "";
|
||||
} else {
|
||||
embed.field(if first { "Tips" } else { "\u{200b}" }, tip_paragraph, false);
|
||||
first = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
embed.footer(|f| {
|
||||
f.text(format!("Data from : https://paimon.moe/characters/{}", character.id))
|
||||
});
|
||||
|
||||
embed
|
||||
}
|
21
src/interactions/genshin/mod.rs
Normal file
21
src/interactions/genshin/mod.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use serenity::client::Context;
|
||||
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
|
||||
use crate::interactions::genshin::artifacts::genshin_artifact_interaction;
|
||||
use crate::interactions::genshin::build::genshin_build_interaction;
|
||||
use crate::interactions::genshin::weapons::genshin_weapon_interaction;
|
||||
|
||||
pub mod build;
|
||||
pub mod weapons;
|
||||
pub mod artifacts;
|
||||
|
||||
|
||||
|
||||
pub async fn genshin_interaction(ctx: Context, command: ApplicationCommandInteraction) {
|
||||
let sub_command = command.data.options.get(0).expect("No command provided");
|
||||
match sub_command.name.as_str() {
|
||||
"builds" => genshin_build_interaction(&ctx, &command, sub_command).await,
|
||||
"artifact" => genshin_artifact_interaction(&ctx, &command, sub_command).await,
|
||||
"weapon" => genshin_weapon_interaction(&ctx, &command, sub_command).await,
|
||||
_ => println!("Unknown Command")
|
||||
}
|
||||
}
|
45
src/interactions/genshin/weapons.rs
Normal file
45
src/interactions/genshin/weapons.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use linked_hash_map::LinkedHashMap;
|
||||
use serenity::client::Context;
|
||||
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
|
||||
use serenity::model::interactions::application_command::ApplicationCommandInteractionDataOption;
|
||||
use serenity::model::interactions::InteractionResponseType;
|
||||
use serenity::model::prelude::InteractionApplicationCommandCallbackDataFlags;
|
||||
use serenity::model::prelude::message_component::MessageComponentInteraction;
|
||||
use crate::data::weapons::Weapon;
|
||||
use crate::interactions::utils::create_action_row_basic;
|
||||
|
||||
pub async fn genshin_weapon_interaction(ctx: &Context, command: &ApplicationCommandInteraction, opt: &ApplicationCommandInteractionDataOption) {
|
||||
|
||||
let weapon = opt.options.get(0).expect("No argument for command Genshin build")
|
||||
.value.as_ref().expect("").as_str().expect("Not a string");
|
||||
|
||||
let weapons = Weapon::search(weapon).await;
|
||||
let mut weapon_list: LinkedHashMap<String, String> = LinkedHashMap::new();
|
||||
|
||||
for w in weapons {
|
||||
weapon_list.insert(w.id().to_string(), w.name().to_string());
|
||||
}
|
||||
|
||||
let ar = create_action_row_basic(weapon_list, "weapon");
|
||||
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|d| {
|
||||
d.content("Select a Weapon")
|
||||
.components(|c| c.add_action_row(ar))
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.expect("The response didn't get sent");
|
||||
}
|
||||
|
||||
|
||||
pub async fn show_weapon_embed(ctx: &Context, command: &MessageComponentInteraction, weapon_name: String){
|
||||
let weapon = Weapon::get(weapon_name.as_str()).await;
|
||||
command.create_interaction_response(&ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::UpdateMessage)
|
||||
.interaction_response_data(|d| {
|
||||
d.embeds(vec!(weapon.to_embed()).into_iter())
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.expect("The message didn't got sent");
|
||||
}
|
30
src/interactions/mod.rs
Normal file
30
src/interactions/mod.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
pub mod status_message;
|
||||
pub mod genshin;
|
||||
|
||||
#[path = "../data/mod.rs"]
|
||||
mod data;
|
||||
#[path = "../utils/mod.rs"]
|
||||
pub mod utils;
|
||||
|
||||
|
||||
|
||||
use serenity::client::Context;
|
||||
use serenity::model::interactions::{InteractionApplicationCommandCallbackDataFlags, InteractionResponseType};
|
||||
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
|
||||
|
||||
pub async fn pong(ctx : Context, command : ApplicationCommandInteraction) {
|
||||
let res = command.create_interaction_response(ctx.http, |res| {
|
||||
res.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|response| {
|
||||
response.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
.content("An error has occurred")
|
||||
})
|
||||
}).await;
|
||||
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(e) =>{
|
||||
println!("An error has occured : {}", e)
|
||||
}
|
||||
}
|
||||
}
|
177
src/interactions/status_message.rs
Normal file
177
src/interactions/status_message.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
use std::time::Duration;
|
||||
use rand::Rng;
|
||||
use serenity::{
|
||||
client::{
|
||||
Context
|
||||
}
|
||||
};
|
||||
use serenity::builder::CreateEmbed;
|
||||
use serenity::model::id::{ChannelId, MessageId};
|
||||
|
||||
use serenity::model::interactions::application_command::ApplicationCommandInteraction;
|
||||
use serenity::model::interactions::InteractionApplicationCommandCallbackDataFlags;
|
||||
use serenity::model::prelude::{InteractionResponseType};
|
||||
use serenity::utils::Colour;
|
||||
use crate::interactions::data::events::Event;
|
||||
use crate::utils::mongo::{add_discord_status_message, get_all_status_messages, get_discord_status_message, StatusMessage};
|
||||
|
||||
|
||||
pub fn copy_embed(from: &Vec<CreateEmbed>) -> Vec<CreateEmbed> {
|
||||
let mut cln: Vec<CreateEmbed> = vec![];
|
||||
for x in from {
|
||||
cln.push(x.clone());
|
||||
}
|
||||
cln
|
||||
}
|
||||
|
||||
pub async fn update_status_message(ctx: Context) {
|
||||
let forever = tokio::task::spawn(async move {
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(60*60));
|
||||
loop {
|
||||
let mut x = get_all_status_messages().await;
|
||||
x.reverse();
|
||||
|
||||
let embeds = create_status_embed().await;
|
||||
for sm in x {
|
||||
let msg = ChannelId::from(sm.channel_id as u64).message(&ctx.http, sm.message_id as u64).await;
|
||||
match msg {
|
||||
Ok(mut m) => {
|
||||
let copies = copy_embed(&embeds);
|
||||
m.edit(&ctx.http, |f| {
|
||||
f.set_embeds(copies)
|
||||
}).await.unwrap();
|
||||
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
interval.tick().await;
|
||||
}
|
||||
});
|
||||
forever.await.expect("Stopped for some reasons");
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn create_status_interaction(ctx: Context, command: ApplicationCommandInteraction) {
|
||||
let embeds = create_status_embed().await;
|
||||
let channel_option = &command.data.resolved.channels;
|
||||
let channel_id = channel_option.keys().next().expect("No options passed");
|
||||
|
||||
let message = get_discord_status_message(&command.guild_id.expect("Not in guild").as_u64().to_owned()).await;
|
||||
match message {
|
||||
Some(e) => {
|
||||
let rm = ChannelId::from(e.channel_id as u64)
|
||||
.message(&ctx.http, MessageId::from(e.message_id as u64))
|
||||
.await;
|
||||
match rm {
|
||||
msg if rm.is_ok() => {
|
||||
msg.unwrap().delete(&ctx.http).await.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let msg = channel_id.send_message(&ctx.http, |f| {
|
||||
f.add_embeds(embeds)
|
||||
}).await.expect("Can't send Message");
|
||||
|
||||
let sm = StatusMessage {
|
||||
message_id: *msg.id.as_u64() as i64,
|
||||
channel_id: *msg.channel_id.as_u64() as i64,
|
||||
guild_id: *command.guild_id.expect("Not in guild").as_u64() as i64,
|
||||
};
|
||||
|
||||
add_discord_status_message(sm).await;
|
||||
|
||||
|
||||
command.create_interaction_response(&ctx.http, |r| {
|
||||
r.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(|d| {
|
||||
d.create_embed(|e| {
|
||||
e.title("Command Successful !");
|
||||
e.color(Colour::new(0x00ff00));
|
||||
e.description(format!("Status message created !"))
|
||||
})
|
||||
.flags(InteractionApplicationCommandCallbackDataFlags::EPHEMERAL)
|
||||
})
|
||||
}).await.unwrap();
|
||||
}
|
||||
|
||||
async fn create_status_embed() -> Vec<CreateEmbed> {
|
||||
let mut embeds: Vec<CreateEmbed> = vec![];
|
||||
|
||||
let mut current = Event::get_current().await;
|
||||
let mut others = current.clone();
|
||||
|
||||
let mut question_marks = format!("");
|
||||
|
||||
let mut upcoming = Event::get_upcoming().await;
|
||||
|
||||
current = current.into_iter().filter(|p| p.show_on_home.unwrap_or(false)).collect::<Vec<Event>>();
|
||||
others = others.into_iter().filter(|p| !p.show_on_home.unwrap_or(false)).collect::<Vec<Event>>();
|
||||
|
||||
|
||||
upcoming = upcoming.into_iter().filter(|p| p.show_on_home.unwrap_or(false)).collect::<Vec<Event>>();
|
||||
let upcoming_event: Option<&Event> = upcoming.get(0);
|
||||
|
||||
for e in current {
|
||||
question_marks = format!("{}?", question_marks);
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed.title(e.name);
|
||||
embed.color(Colour::new(rand::thread_rng().gen_range(0x000000..0xffffff)));
|
||||
|
||||
match &e.image {
|
||||
Some(url) => {
|
||||
embed.image(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/events/{}", url));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match e.url {
|
||||
Some(t) => { embed.url(format!("{}{}", t, question_marks)); }
|
||||
_ => {}
|
||||
};
|
||||
|
||||
embed.description(format!("Ends : <t:{}:R>", e.end_timestamp));
|
||||
embeds.push(embed);
|
||||
}
|
||||
|
||||
|
||||
// Other events embed
|
||||
let mut other_embed = CreateEmbed::default();
|
||||
other_embed.title("Other Events");
|
||||
other_embed.color(Colour::new(rand::thread_rng().gen_range(0x000000..0xffffff)));
|
||||
|
||||
for e in others {
|
||||
other_embed.field(e.name, format!("Ends : <t:{}:R>", e.end_timestamp), false);
|
||||
}
|
||||
embeds.push(other_embed);
|
||||
|
||||
match upcoming_event {
|
||||
Some(e) => {
|
||||
let mut upcoming_embed = CreateEmbed::default();
|
||||
question_marks = format!("{}?", question_marks);
|
||||
upcoming_embed.title(&e.name);
|
||||
upcoming_embed.description(format!("Starts : <t:{}:R>", e.start_timestamp));
|
||||
upcoming_embed.color(Colour::new(rand::thread_rng().gen_range(0x000000..0xffffff)));
|
||||
|
||||
match &e.image {
|
||||
Some(url) => {
|
||||
upcoming_embed.image(format!("https://github.com/MadeBaruna/paimon-moe/raw/main/static/images/events/{}", url));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match &e.url {
|
||||
Some(url) => { upcoming_embed.url(format!("{}{}", url, question_marks)); }
|
||||
_ => {}
|
||||
};
|
||||
embeds.push(upcoming_embed);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
embeds
|
||||
}
|
164
src/main.rs
Normal file
164
src/main.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
mod interactions;
|
||||
mod data;
|
||||
pub mod utils;
|
||||
|
||||
use std::env;
|
||||
use dotenv::dotenv;
|
||||
use serenity::{
|
||||
async_trait,
|
||||
model::{
|
||||
gateway::Ready,
|
||||
interactions::{
|
||||
application_command::{
|
||||
ApplicationCommand,
|
||||
ApplicationCommandOptionType,
|
||||
},
|
||||
Interaction,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
use serenity::client::bridge::gateway::GatewayIntents;
|
||||
use serenity::model::gateway::Activity;
|
||||
use crate::interactions::genshin::artifacts::show_artifact_embed;
|
||||
use crate::interactions::genshin::build::build_interact;
|
||||
use crate::interactions::genshin::weapons::show_weapon_embed;
|
||||
use crate::interactions::status_message::update_status_message;
|
||||
|
||||
struct Handler;
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for Handler {
|
||||
async fn ready(&self, _ctx: Context, _bot: Ready) {
|
||||
println!("{} connected!", _bot.user.name);
|
||||
update_status_message(_ctx.clone()).await;
|
||||
_ctx.set_activity(Activity::playing("Drinking")).await;
|
||||
|
||||
let x = ApplicationCommand::get_global_application_commands(&_ctx.http).await.unwrap();
|
||||
for i in x {
|
||||
match i.name.as_str() {
|
||||
"genshin" => (),
|
||||
"createstatusmessage" => (),
|
||||
_ => {
|
||||
let _result_delete = ApplicationCommand::delete_global_application_command(&_ctx.http, i.id).await;
|
||||
match _result_delete {
|
||||
Ok(()) => { println!("Deleted command {}", i.name) }
|
||||
Err(f) => { println!("An error occurred deleting {} : {}", i.name, f) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let x = ApplicationCommand::create_global_application_command(&_ctx.http, |command| {
|
||||
command.name("genshin").description("Get Informations about Genshin Impact.").create_option(|option| {
|
||||
option.name("builds")
|
||||
.description("Shows builds for a Genshin Impact Characters")
|
||||
.kind(ApplicationCommandOptionType::SubCommand)
|
||||
.create_sub_option(|so| {
|
||||
so.name("character").description("Character to get builds for")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
})
|
||||
.create_option(|option| {
|
||||
option.name("weapon")
|
||||
.description("Shows infos on a Genshin Impact weapon.")
|
||||
.kind(ApplicationCommandOptionType::SubCommand)
|
||||
.create_sub_option(|so| {
|
||||
so.name("name").description("Weapon you want infos on.")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
})
|
||||
.create_option(|option| {
|
||||
option.name("artifact")
|
||||
.description("Shows infos on a Genshin Impact artifact set.")
|
||||
.kind(ApplicationCommandOptionType::SubCommand)
|
||||
.create_sub_option(|so| {
|
||||
so.name("artifact")
|
||||
.description("Artifact Set you want infos on.")
|
||||
.kind(ApplicationCommandOptionType::String)
|
||||
.required(true)
|
||||
})
|
||||
})
|
||||
}).await;
|
||||
if x.is_err() {
|
||||
println!("{}", x.unwrap_err());
|
||||
}
|
||||
|
||||
ApplicationCommand::create_global_application_command(&_ctx.http, |command| {
|
||||
command.name("createstatusmessage").create_option(|opt| {
|
||||
opt.name("channel").description("Channel where to put the status message")
|
||||
.kind(ApplicationCommandOptionType::Channel)
|
||||
.required(true)
|
||||
}).description("Creates a status message of all the current events on Genshin Impact")
|
||||
}).await.expect("Can't create the createstatusmessage command");
|
||||
}
|
||||
|
||||
|
||||
async fn interaction_create(&self, _ctx: Context, _interaction: Interaction) {
|
||||
if let Interaction::ApplicationCommand(command) = _interaction {
|
||||
match command.data.name.as_str() {
|
||||
// Ping command
|
||||
"genshin" => interactions::genshin::genshin_interaction(_ctx, command).await,
|
||||
"createstatusmessage" => interactions::status_message::create_status_interaction(_ctx, command).await,
|
||||
// Unknown command
|
||||
_ => interactions::pong(_ctx, command).await
|
||||
}
|
||||
} else if let Interaction::MessageComponent(component) = _interaction {
|
||||
let mut args = component.data.custom_id.split("_");
|
||||
let command = args.next();
|
||||
match command.unwrap() {
|
||||
"weapon" => {
|
||||
let weapon = args.collect::<Vec<_>>().join("_");
|
||||
show_weapon_embed(&_ctx, &component, weapon).await;
|
||||
}
|
||||
"artifact" => {
|
||||
let artifact = args.collect::<Vec<_>>().join("_");
|
||||
show_artifact_embed(&_ctx, &component, artifact).await;
|
||||
}
|
||||
"build" => {
|
||||
let character = args.collect::<Vec<_>>().join("_");
|
||||
build_interact(&_ctx, &component, character).await;
|
||||
}
|
||||
_ => println!("Unknown interaction")
|
||||
}
|
||||
} else {
|
||||
println!("ERROR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenv().ok();
|
||||
let mut token= "".to_string();
|
||||
for (k, v) in env::vars() {
|
||||
if k.eq("DISCORD_TOKEN") {
|
||||
token = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let application_id: u64 = "860553396578811914".parse().expect("Wrong format");
|
||||
|
||||
let needed_intents = [
|
||||
GatewayIntents::GUILDS,
|
||||
GatewayIntents::GUILD_MESSAGES,
|
||||
GatewayIntents::GUILD_EMOJIS,
|
||||
GatewayIntents::GUILD_WEBHOOKS,
|
||||
GatewayIntents::GUILD_INTEGRATIONS
|
||||
];
|
||||
|
||||
let mut client = Client::builder(token)
|
||||
.event_handler(Handler)
|
||||
.intents(GatewayIntents::from_iter(needed_intents.into_iter()))
|
||||
.application_id(application_id)
|
||||
.await
|
||||
.expect("Error creating the client");
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error {}", why);
|
||||
}
|
||||
}
|
22
src/utils/mod.rs
Normal file
22
src/utils/mod.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use linked_hash_map::LinkedHashMap;
|
||||
use serenity::builder::{CreateActionRow, CreateButton};
|
||||
use serenity::model::interactions::message_component::ButtonStyle;
|
||||
|
||||
pub mod mongo;
|
||||
|
||||
pub fn create_action_row_basic (dict: LinkedHashMap<String, String>, command: &str) -> CreateActionRow {
|
||||
let mut buttons: Vec<CreateButton> = vec!();
|
||||
for d in dict {
|
||||
let mut b = CreateButton::default();
|
||||
b.custom_id(format!("{}_{}", command, &d.0));
|
||||
b.label(d.1);
|
||||
b.style(ButtonStyle::Primary);
|
||||
buttons.push(b)
|
||||
}
|
||||
|
||||
let mut ar = CreateActionRow::default();
|
||||
for btn in buttons {
|
||||
ar.add_button(btn);
|
||||
}
|
||||
ar
|
||||
}
|
67
src/utils/mongo.rs
Normal file
67
src/utils/mongo.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use mongodb::bson::{Bson, doc, Document, from_bson, to_bson};
|
||||
use mongodb::{Client, Collection, options::ClientOptions};
|
||||
use futures::stream::{TryStreamExt};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct StatusMessage {
|
||||
pub(crate) guild_id: i64,
|
||||
pub(crate) message_id: i64,
|
||||
pub(crate) channel_id: i64
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn add_discord_status_message(status_message: StatusMessage) -> bool {
|
||||
let client = get_mongo_client().await;
|
||||
|
||||
let serialized = to_bson(&status_message).expect("Can't serialize status_message");
|
||||
let document = serialized.as_document().unwrap();
|
||||
|
||||
let collection: Collection<Document> = client.database("drunk_venti").collection("StatusMessages");
|
||||
let updated = collection.update_one(doc! {"guild_id": status_message.guild_id}, doc! {"$set" : document}, None).await;
|
||||
let is_ok = &updated.is_ok();
|
||||
match updated.unwrap() {
|
||||
e if e.matched_count == 0 => {
|
||||
let inserted = collection.insert_one(document, None).await;
|
||||
inserted.is_ok()
|
||||
}
|
||||
_ => *is_ok
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_discord_status_message(gid: &u64) -> Option<StatusMessage> {
|
||||
let client = get_mongo_client().await;
|
||||
|
||||
let status_messages: Collection<Document> = client.database("drunk_venti").collection("StatusMessages");
|
||||
|
||||
let infos = status_messages.find_one(doc! {"guild_id" : *gid as i64}, None).await.expect("Can't find one");
|
||||
|
||||
|
||||
match infos {
|
||||
Some(i) => {
|
||||
let m_infos: StatusMessage = from_bson(Bson::Document(i)).expect("Can't get");
|
||||
return Some(m_infos);
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_all_status_messages() -> Vec<StatusMessage> {
|
||||
let client = get_mongo_client().await;
|
||||
|
||||
let collection: Collection<StatusMessage> = client.database("drunk_venti").collection::<StatusMessage>("StatusMessages");
|
||||
let documents = collection.find(None, None).await.expect("Can't get everything");
|
||||
let all_docs: Vec<StatusMessage> = documents.try_collect().await.unwrap_or_else(|_| vec![]);
|
||||
return all_docs;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn get_mongo_client() -> Client {
|
||||
let mut client_options = ClientOptions::parse("mongodb://localhost:27017/?readPreference=primary&appname=MongoDB%20Compass&directConnection=true&ssl=false").await.expect("Can't connect to db");
|
||||
client_options.app_name = Some("Drunk Venti".to_string());
|
||||
client_options.default_database = Some("drunk_venti".to_string());
|
||||
|
||||
Client::with_options(client_options).expect("Can't add options")
|
||||
}
|
Loading…
Reference in a new issue