Some changes and add links command, for now populated with eurosport.

This commit is contained in:
Tom 2022-02-21 23:21:57 +01:00
parent 9e03e8be42
commit 5d05c220ec
6 changed files with 750 additions and 76 deletions

70
command_permissions.py Normal file
View file

@ -0,0 +1,70 @@
import requests
import toml
import json
import sys
CONFIG_FILE = "data/config/01-discord.toml"
CONFIG_FILE2 = "data/config/30-high-tier.toml"
data1 = toml.load(CONFIG_FILE2)
ROLE_ID = data1.get("high_tier", {}).get("role", 294950710737502209)
GUILD = data1.get("high_tier", {}).get("guild", 117992911781494787)
HT_COMS = data1.get("high_tier", {}).get("coms", ["links"])
data = toml.load(CONFIG_FILE)
app_id = data["discord"].get("app_id", 936990391847223316)
print("Using token: ", data["discord"]["token"])
token = "Bot " + data["discord"]["token"]
print(token)
Headers = {
"Authorization": token,
"User=Agent": "DiscordBot https://github.com/ 1 manual browser requests",
}
r = requests.get(f"https://discord.com/api/v8/applications/{app_id}/guilds/{GUILD}/commands", headers=Headers)
print(json.dumps(r.json(), indent=2))
commands = r.json()
com_id = None
json = {
"permissions": [
{
"id": ROLE_ID,
"type": 1,
"permission": True
}
]
}
count = 0
for command in commands:
if command["name"] in HT_COMS:
id = command["id"]
url = f"https://discord.com/api/v8/applications/{app_id}/guilds/{GUILD}/commands/{str(id)}"
r = requests.patch(url, headers=Headers, json={"default_permission": False})
print(r.json())
r = requests.put(url+"/permissions", headers=Headers, json=json)
print(r.json())
count += 1
print("Updated", count, "commands")
# print("Which id to set")
# id = input()
# url = f"https://discord.com/api/v8/applications/{app_id}/guilds/{GUILD}/commands/{str(id)}"
# r = requests.patch(url, headers=Headers, json={"default_permission": False})
# print(r.json())
# r = requests.put(url+"/permissions", headers=Headers, json=json)
# print(r.json())

View file

@ -0,0 +1,496 @@
use crate::{commands::utils, Context, Error};
use std::collections::HashMap;
use cached::proc_macro::cached;
use chrono::{DateTime, Utc};
use log::*;
use reqwest::header::{HeaderName, ACCEPT_LANGUAGE, COOKIE};
use serde::Deserialize;
use super::Timeframe;
mod es_date_time {
use chrono::{DateTime, TimeZone, Utc};
use serde::{self, Deserialize, Deserializer};
const FORMAT: &'static str = "%Y-%m-%dT%H:%M:%SZ";
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Utc.datetime_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
}
#[derive(Deserialize, Debug)]
struct VideoAttributes {
// #[serde(rename = "earliestPlayableStart")]
// #[serde(with = "es_date_time")]
// earliest_playable_start: DateTime<Utc>,
name: String,
#[serde(rename = "secondaryTitle")]
#[serde(default)]
secondary: String,
#[serde(rename = "publishEnd")]
#[serde(with = "es_date_time")]
publish_end: DateTime<Utc>,
#[serde(rename = "publishStart")]
#[serde(with = "es_date_time")]
publish_start: DateTime<Utc>,
#[serde(default)]
description: String,
// #[serde(rename = "drmEnabled")]
// drm_enabled: bool,
}
#[derive(Deserialize, Debug)]
struct SportVal {
id: String,
}
#[derive(Deserialize, Debug)]
struct SportID {
data: Vec<SportVal>,
}
#[derive(Deserialize, Debug)]
struct VideoRelations {
#[serde(alias = "txSports")]
#[serde(alias = "txOlympicssport")]
sport_id: Option<SportID>,
}
#[derive(Deserialize, Debug)]
struct TaxonomyNodeAttributes {
name: String,
// kind: String,
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
enum Includes {
#[serde(rename = "video")]
Video {
attributes: VideoAttributes,
id: String,
relationships: VideoRelations,
},
#[serde(rename = "taxonomyNode")]
TaxonomyNode {
attributes: TaxonomyNodeAttributes,
id: String,
},
#[serde(rename = "channel")]
Channels,
#[serde(rename = "collection")]
Collection,
#[serde(rename = "collectionItem")]
CollectionItem,
#[serde(rename = "image")]
Image,
#[serde(rename = "link")]
Link,
#[serde(rename = "package")]
Package,
#[serde(rename = "page")]
Page,
#[serde(rename = "pageItem")]
PageItem,
#[serde(rename = "route")]
Route,
#[serde(rename = "show")]
Show,
}
#[derive(Deserialize, Debug)]
struct Eurosport {
included: Vec<Includes>,
}
#[derive(Debug, Clone)]
struct ESEvents {
id: String,
sport: Option<String>,
name: String,
secondary: String,
description: String,
start: DateTime<Utc>,
end: DateTime<Utc>,
// early_start: DateTime<Utc>,
// drm_enabled: bool,
}
impl ESEvents {
fn filter(&self, filter: &str) -> bool {
if self.name.to_lowercase().contains(filter) {
return true;
};
if self.description.to_lowercase().contains(filter) {
return true;
};
if self.secondary.to_lowercase().contains(filter) {
return true;
};
if let Some(sport) = &self.sport {
if sport.to_lowercase().contains(filter) {
return true;
}
}
return false;
}
fn comp(&self, when: &Option<Timeframe>) -> bool {
let now = Utc::now();
match when {
Some(Timeframe::Everything) => true,
Some(Timeframe::Current) => self.start <= now && self.end >= now,
Some(Timeframe::Future) => self.end >= now,
Some(Timeframe::Past) => self.end <= now,
_ => self.end >= now,
}
}
}
fn get_events(v: Eurosport) -> Result<Vec<ESEvents>, serde_json::Error> {
let mut sports: HashMap<String, String> = HashMap::new();
let nodes = v.included.iter().filter(|x| match x {
Includes::TaxonomyNode { .. } => true,
_ => false,
});
for node in nodes {
if let Includes::TaxonomyNode { attributes, id } = node {
sports.insert(id.to_string(), attributes.name.to_string());
}
}
debug!("Sports: {:?}", sports);
let mut events: Vec<ESEvents> = vec![];
let nodes = v.included.iter().filter(|x| match x {
Includes::Video { .. } => true,
_ => false,
});
for node in nodes {
if let Includes::Video {
attributes,
id,
relationships,
} = node
{
// sports.insert(id.to_string(), attributes.name.to_string());
let sport_name = match &relationships.sport_id {
None => None,
Some(sportid) => match sportid.data.get(0) {
None => None,
Some(sv) => match sports.get(&sv.id) {
None => None,
Some(s) => Some(s.to_owned()),
},
},
};
events.push(ESEvents {
id: id.to_string(),
sport: sport_name,
name: attributes.name.to_string(),
secondary: attributes.secondary.to_string(),
description: attributes.description.to_string(),
start: attributes.publish_start,
end: attributes.publish_end,
// early_start: attributes.earliest_playable_start,
// drm_enabled: attributes.drm_enabled,
});
}
}
events.sort_by(|a, b| b.start.cmp(&a.start));
debug!("Events: {:?}", events);
return Ok(events);
}
fn stringify_events(events: Vec<ESEvents>) -> Vec<String> {
let mut result = vec![];
for event in events {
result.push(match event.sport {
// None => format!("```md\n({}) {}```\n", event.name, event.secondary),
None => format!(
"```md\n({}) {}```(<t:{}:R>-<t:{}:R>) {}\n https://tom.al/ms/euro/{} https://tom.al/ms/euro/{}",
event.name,
event.secondary,
event.start.timestamp(),
event.end.timestamp(),
event.description,
event.id,
event.id
),
Some(sport) => format!(
"```md\n[{}]({}) {}```(<t:{}:R>-<t:{}:R>) {}\n https://tom.al/ms/euro/{} https://tom.al/ms/euro/{}",
sport,
event.name,
event.secondary,
event.start.timestamp(),
event.end.timestamp(),
event.description,
event.id,
event.id
),
});
}
return result;
}
#[cached(time = 3600)]
#[allow(dead_code)]
async fn get_eurosport_events(url: String) -> Option<Vec<ESEvents>> {
let cookie = super::super::super::SETTINGS
.read()
.unwrap()
.get_table("eurosport")
.expect("Expecting an eurosport section in the config")
.get("cookie")
.expect("Config error, please set the eurosport[cookie] value")
.clone()
.into_str()
.expect("Config error, please make sure eurosport[cookie] is a string");
let client = reqwest::Client::new();
let x_disco_client = HeaderName::from_lowercase(b"x-disco-client").unwrap();
let req = client
.get(url)
.header(COOKIE, cookie)
.header(ACCEPT_LANGUAGE, "en-US,en;q=0.5")
.header(x_disco_client, "WEB:UNKNOWN:esplayer:prod")
.send()
.await;
let result: Option<Vec<ESEvents>> = match req {
Err(e) => {
warn!("Error getting Eurosport schedule {}", e);
None
}
Ok(req) if req.status().as_u16() == 404 => {
warn!("404 on getting ES events");
return None;
}
Ok(req) if req.status().as_u16() == 200 => {
let data = req.json::<Eurosport>().await;
match data {
Err(e) => {
warn!("Error getting Eurosport schedule {}", e);
None
}
Ok(es) => {
debug!("Eurosport data: {:?}", es);
match get_events(es) {
Err(e) => {
warn!("Error getting Eurosport schedule {}", e);
None
}
Ok(events) => {
// events.sort_by(|a: ESEvents, b: ESEvents| b.start.cmp(a));
Some(events)
}
}
}
}
}
_ => None,
};
return result;
}
// #[derive(Debug, poise::SlashChoiceParameter)]
// pub enum Timeframe {
// #[name = "Currently happening"]
// Current,
// #[name = "Currently happening or starting in the future"]
// Future,
// #[name = "Currently happening or already ended"]
// Past,
// #[name = "Everything"]
// Everything,
// }
// /// Eurosport player events
// #[poise::command(slash_command)]
// pub async fn eurosport(
// ctx: Context<'_>,
// #[description = "Filter sessions for when they are/were happening, defaults to future"]
// timeframe: Option<Timeframe>,
// #[description = "Content to filter on"] filter: Option<String>,
// ) -> Result<(), Error> {
// if !utils::high_tier(ctx.channel_id()) {
// ctx.say("This command can only be used in high tier channels for security")
// .await?;
// return Ok(());
// }
// let url = super::super::super::SETTINGS
// .read()
// .unwrap()
// .get_table("eurosport")
// .expect("Expecting an eurosport section in the config")
// .get("url")
// .expect("Config error, please set the eurosport[url] value")
// .clone()
// .into_str()
// .expect("Config error, please make sure eurosport[url] is a string");
// let events = get_eurosport_events(url).await;
// match events {
// None => {
// ctx.say("Oh no something went wrong").await?;
// }
// Some(evs) => {
// info!("Found {} events from eurosport", evs.len());
// let strings = stringify_events(
// evs.into_iter()
// .filter(|e| e.comp(&timeframe))
// .filter(|e| match &filter {
// None => true,
// Some(f) => e.filter(f.as_str()),
// })
// .collect(),
// );
// let pages = utils::paginator(strings, 1900, "\n".to_string());
// utils::paginate_string(ctx, pages).await?;
// }
// }
// Ok(())
// }
/// Eurosport player events
pub async fn proc_eurosport(
ctx: Context<'_>,
timeframe: Option<Timeframe>,
filter: Option<String>,
) -> Result<(), Error> {
let url = super::super::super::SETTINGS
.read()
.unwrap()
.get_table("eurosport")
.expect("Expecting an eurosport section in the config")
.get("url")
.expect("Config error, please set the eurosport[url] value")
.clone()
.into_str()
.expect("Config error, please make sure eurosport[url] is a string");
let events = get_eurosport_events(url).await;
match events {
None => {
ctx.say("Oh no something went wrong").await?;
}
Some(evs) => {
info!("Found {} events from eurosport", evs.len());
let strings = stringify_events(
evs.into_iter()
.filter(|e| e.comp(&timeframe))
.filter(|e| match &filter {
None => true,
Some(f) => e.filter(f.as_str()),
})
.collect(),
);
let pages = utils::paginator(strings, 1900, "\n".to_string());
utils::paginate_string(ctx, pages).await?;
}
}
Ok(())
}
// /// Eurosport olympics events
// #[poise::command(slash_command)]
// pub async fn olympics(
// ctx: Context<'_>,
// #[description = "Filter sessions for when they are/were happening, defaults to future"]
// timeframe: Option<Timeframe>,
// #[description = "Content to filter on"] filter: Option<String>,
// ) -> Result<(), Error> {
// // if !utils::high_tier(ctx.channel_id()) {
// // ctx.say("This command can only be used in high tier channels for security")
// // .await?;
// // return Ok(());
// // }
// if !utils::high_tier_mess(ctx).await {
// ctx.say("This command can only be used in high tier channels for security")
// .await?;
// return Ok(());
// }
// let url = super::super::super::SETTINGS
// .read()
// .unwrap()
// .get_table("eurosport")
// .expect("Expecting an eurosport section in the config")
// .get("olympics")
// .expect("Config error, please set the eurosport[olympics] value")
// .clone()
// .into_str()
// .expect("Config error, please make sure eurosport[olympics] is a string");
// let events = get_eurosport_events(url).await;
// match events {
// None => {
// ctx.say("Oh no something went wrong").await?;
// }
// Some(evs) => {
// info!("Found {} events from eurosport olympics ", evs.len());
// let strings = stringify_events(
// evs.into_iter()
// .filter(|e| e.comp(&timeframe))
// .filter(|e| match &filter {
// None => true,
// Some(f) => e.filter(f.as_str()),
// })
// .collect(),
// );
// let pages = utils::paginator(strings, 1900, "\n".to_string());
// utils::paginate_string(ctx, pages).await?;
// }
// }
// Ok(())
// }
/// Eurosport olympics events
pub async fn proc_olympics(
ctx: Context<'_>,
timeframe: Option<Timeframe>,
filter: Option<String>,
) -> Result<(), Error> {
let url = super::super::super::SETTINGS
.read()
.unwrap()
.get_table("eurosport")
.expect("Expecting an eurosport section in the config")
.get("olympics")
.expect("Config error, please set the eurosport[olympics] value")
.clone()
.into_str()
.expect("Config error, please make sure eurosport[olympics] is a string");
let events = get_eurosport_events(url).await;
match events {
None => {
ctx.say("Oh no something went wrong").await?;
}
Some(evs) => {
info!("Found {} events from eurosport olympics ", evs.len());
let strings = stringify_events(
evs.into_iter()
.filter(|e| e.comp(&timeframe))
.filter(|e| match &filter {
None => true,
Some(f) => e.filter(f.as_str()),
})
.collect(),
);
let pages = utils::paginator(strings, 1900, "\n".to_string());
utils::paginate_string(ctx, pages).await?;
}
}
Ok(())
}

46
src/commands/links/mod.rs Normal file
View file

@ -0,0 +1,46 @@
use crate::{commands::utils, Context, Error};
pub mod eurosport;
#[derive(Debug, poise::SlashChoiceParameter)]
pub enum Timeframe {
#[name = "Currently happening"]
Current,
#[name = "Currently happening or starting in the future"]
Future,
#[name = "Currently happening or already ended"]
Past,
#[name = "Everything"]
Everything,
}
#[derive(Debug, poise::SlashChoiceParameter)]
pub enum Source {
#[name = "Get links for the Eurosport player"]
Eurosport,
#[name = "Get links for the apocalympics Eurosport player"]
Olympics,
}
/// Get links for high tier commands.
#[poise::command(slash_command)]
pub async fn links(
ctx: Context<'_>,
#[description = "Where to git the juicy links from?"] source: Source,
#[description = "Filter sessions for when they are/were happening, defaults to future"]
timeframe: Option<Timeframe>,
#[description = "Content to filter on"] filter: Option<String>,
) -> Result<(), Error> {
if !utils::high_tier(ctx).await {
ctx.say("This command can only be used in high tier channels for security")
.await?;
return Ok(());
}
match source {
Source::Eurosport => eurosport::proc_eurosport(ctx, timeframe, filter).await,
Source::Olympics => eurosport::proc_olympics(ctx, timeframe, filter).await,
}
// Ok(())
}

View file

@ -2,6 +2,7 @@ use crate::{Context, Error};
use poise::serenity_prelude as serenity;
pub mod invites;
pub mod links;
pub mod planning;
pub mod schedule;
pub mod utils;
@ -12,7 +13,7 @@ pub async fn boop(ctx: Context<'_>) -> Result<(), Error> {
let uuid_boop = ctx.id();
ctx.send(|m| {
m.content("I want some boops!").components(|c| {
m.content("I want some boops! 🐇").components(|c| {
c.create_action_row(|ar| {
ar.create_button(|b| {
b.style(serenity::ButtonStyle::Primary)
@ -24,11 +25,10 @@ pub async fn boop(ctx: Context<'_>) -> Result<(), Error> {
})
.await?;
let mut boop_count = 0;
let mut boop_count: i32 = 0;
while let Some(mci) = serenity::CollectComponentInteraction::new(ctx.discord())
.author_id(ctx.author().id)
.channel_id(ctx.channel_id())
.timeout(std::time::Duration::from_secs(120))
// .timeout(std::time::Duration::from_secs(1200))
.filter(move |mci| mci.data.custom_id == uuid_boop.to_string())
.await
{
@ -36,7 +36,26 @@ pub async fn boop(ctx: Context<'_>) -> Result<(), Error> {
let mut msg = mci.message.clone();
msg.edit(ctx.discord(), |m| {
m.content(format!("Boop count: {}", boop_count))
m.content(match boop_count {
2 => "Boop count: <:HamSmile:738765923401596991>".to_string(),
3 => "Boop 3".to_string(),
4 => "Boop four".to_string(),
5 => "Boop five".to_string(),
42 => "Boop count: 42, but what is the question?".to_string(),
43 => "Boop count: 43 A wild <@230001507481681920> appeared".to_string(),
69 => "Boop count: 69 Nice!".to_string(),
77 => "Boop count: 77 <@547041420733841409> Approved".to_string(),
308 => "Redirect 308: Boop count is in another castle".to_string(),
400 => "ERROR 400: Bad booping".to_string(),
401 => "ERROR 401: Unauthorized booping".to_string(),
402 => "ERROR 402: Payment required, no free boops".to_string(),
403 => "ERROR 403: Forbidden boop".to_string(),
404 => "ERROR 404: Boop count not found".to_string(),
420 => "Boop count: 420 Blaze it".to_string(),
666 => "Boop count: 666 😈".to_string(),
777 => "Boop count: 777 A wild <@117992484310745097> appeared".to_string(),
_ => format!("Boop count: {}", boop_count),
})
})
.await?;
@ -48,3 +67,18 @@ pub async fn boop(ctx: Context<'_>) -> Result<(), Error> {
Ok(())
}
pub fn get_links(
) -> poise::Command<super::Data, Box<(dyn std::error::Error + Sync + std::marker::Send + 'static)>>
{
// poise::Command {
// subcommands: vec![
// links::eurosport::eurosport(),
// links::eurosport::olympics(),
// links::links(),
// // Let's make sure poise isn't confused by the duplicate names!
// ],
// ..links::links()
// };
links::links()
}

View file

@ -1,7 +1,28 @@
use std::vec;
use crate::{Context, Error};
use poise::serenity_prelude as serenity;
use poise::serenity_prelude::{self as serenity, ChannelId};
pub async fn high_tier(ctx: Context<'_>) -> bool {
let ChannelId(chan_id) = ctx.channel_id();
match chan_id {
117992911781494787 => return true, // private general
332337657113739265 => return true, // Testing
_ => (),
}
match ctx.discord().cache.guild_channel(ctx.channel_id()) {
None => return false,
Some(chan) => match chan.parent_id {
None => return false,
Some(cat_id) => match cat_id {
ChannelId(547551264498515978) => return true,
ChannelId(884698356360818708) => return true,
_ => return false,
},
},
}
}
pub fn paginator(input: Vec<String>, chunk_size: usize, join_string: String) -> Vec<String> {
if input.len() == 0 {

View file

@ -35,65 +35,57 @@ lazy_static! {
owners_only=true,
)]
async fn register(ctx: Context<'_>, #[flag] global: bool) -> Result<(), Error> {
poise::builtins::register_application_commands(ctx, global).await?;
// poise::builtins::register_application_commands(ctx, global).await?;
Ok(())
}
/// Boop the bot!
#[poise::command(prefix_command, track_edits, slash_command)]
pub async fn boop(ctx: Context<'_>) -> Result<(), Error> {
let uuid_boop = ctx.id();
ctx.send(|m| {
m.content("I want some boops! 🐇").components(|c| {
c.create_action_row(|ar| {
ar.create_button(|b| {
b.style(serenity::ButtonStyle::Primary)
.label("Boop me!")
.custom_id(uuid_boop)
})
})
})
})
.await?;
let mut boop_count = 0;
while let Some(mci) = serenity::CollectComponentInteraction::new(ctx.discord())
.channel_id(ctx.channel_id())
.timeout(std::time::Duration::from_secs(1200))
.filter(move |mci| mci.data.custom_id == uuid_boop.to_string())
.await
{
boop_count += 1;
let mut msg = mci.message.clone();
msg.edit(ctx.discord(), |m| {
m.content(match &boop_count {
2 => "Boop count: <:HamSmile:738765923401596991>".to_string(),
42 => "Boop count: 42, but what is the question?".to_string(),
43 => "Boop count: 43 A wild <@230001507481681920> appeared".to_string(),
69 => "Boop count: 69 Nice!".to_string(),
77 => "Boop count: 77 <@547041420733841409> Aproved".to_string(),
308 => "Redirect 308: Boop count is in another castle".to_string(),
400 => "ERROR 400: Bad booping".to_string(),
401 => "ERROR 401: Unauthorized booping".to_string(),
402 => "ERROR 402: Payment required, no free boops".to_string(),
403 => "ERROR 403: Forbidden boop".to_string(),
404 => "ERROR 404: Boop count not found".to_string(),
420 => "Boop count: 420 Blaze it".to_string(),
666 => "Boop count: 666 😈".to_string(),
777 => "Boop count: 777 A wild <@117992484310745097> appeared".to_string(),
_ => format!("Boop count: {}", boop_count),
})
})
.await?;
mci.create_interaction_response(ctx.discord(), |ir| {
ir.kind(serenity::InteractionResponseType::DeferredUpdateMessage)
})
.await?;
let mut commands_builder = serenity::CreateApplicationCommands::default();
let commands = &ctx.framework().options().commands;
for command in commands {
if let Some(slash_command) = command.create_as_slash_command() {
commands_builder.add_application_command(slash_command);
}
if let Some(context_menu_command) = command.create_as_context_menu_command() {
commands_builder.add_application_command(context_menu_command);
}
}
let commands_builder = serenity::json::Value::Array(commands_builder.0);
let is_bot_owner = ctx.framework().options().owners.contains(&ctx.author().id);
if global {
if !is_bot_owner {
ctx.say("Can only be used by bot owner").await?;
return Ok(());
}
ctx.say(format!("Registering {} commands...", commands.len()))
.await?;
ctx.discord()
.http
.create_global_application_commands(&commands_builder)
.await?;
} else {
let guild = match ctx.guild() {
Some(x) => x,
None => {
ctx.say("Must be called in guild").await?;
return Ok(());
}
};
let is_guild_owner = ctx.author().id == guild.owner_id;
if !is_guild_owner && !is_bot_owner {
ctx.say("Can only be used by server owner").await?;
return Ok(());
}
ctx.say(format!("Registering {} commands...", commands.len()))
.await?;
ctx.discord()
.http
.create_guild_application_commands(guild.id.0, &commands_builder)
.await?;
}
ctx.say("Done!").await?;
Ok(())
}
@ -162,8 +154,8 @@ async fn app() -> Result<(), Error> {
let prefix: String = discord
.get("prefix")
.expect("Config error, please set the discord[token] value")
.clone()
.into_str()
.to_owned()
.try_into()
.expect("Config error, please make sure discord[token] is a string");
let options = poise::FrameworkOptions {
commands: vec![
@ -173,16 +165,7 @@ async fn app() -> Result<(), Error> {
commands::schedule::schedule(),
commands::boop(),
commands::planning::get_command(),
// poise::Command {
// subcommands: vec![
// commands::planning::series(),
// commands::planning::sessions(),
// commands::planning::events(),
// // Let's make sure poise isn't confused by the duplicate names!
// // commands::planning::planning(),
// ],
// ..commands::planning::planning()
// },
commands::get_links(),
],
prefix_options: poise::PrefixFrameworkOptions {
prefix: Some(prefix.into()),
@ -247,7 +230,31 @@ async fn main() {
setup_config().unwrap();
// env_logger::init();
// init_log();
SimpleLogger::init(LevelFilter::Warn, simplelog::Config::default()).unwrap();
let log_default = LevelFilter::Warn;
let logger = match SETTINGS
.read()
.unwrap()
.get_table("logging")
.unwrap_or_default()
.get("level")
{
Some(val) => match val.clone().into_str() {
Ok(level) => match level.to_lowercase().as_str() {
"trace" => LevelFilter::Trace,
"debug" => LevelFilter::Debug,
"info" => LevelFilter::Info,
"warn" => LevelFilter::Warn,
"error" => LevelFilter::Error,
_ => log_default,
},
_ => log_default,
},
_ => log_default,
};
println!("Logging level: {:?}", logger);
SimpleLogger::init(logger, simplelog::Config::default()).unwrap();
if let Err(e) = app().await {
log::error!("{}", e);