User-App support
This commit is contained in:
parent
f98eb4fd7b
commit
47453acecd
564
Cargo.lock
generated
564
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
19
Cargo.toml
@ -1,24 +1,27 @@
|
||||
[package]
|
||||
name = "kon"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bb8 = "0.8.5"
|
||||
bb8 = "0.8.6"
|
||||
bb8-redis = "0.17.0"
|
||||
cargo_toml = "0.20.5"
|
||||
feed-rs = "2.1.1"
|
||||
feed-rs = "2.2.0"
|
||||
once_cell = "1.20.2"
|
||||
poise = "0.6.1"
|
||||
regex = "1.11.0"
|
||||
reqwest = { version = "0.12.8", features = ["json", "native-tls-vendored"] }
|
||||
serde = "1.0.210"
|
||||
serde_json = "1.0.128"
|
||||
regex = "1.11.1"
|
||||
reqwest = { version = "0.12.9", features = ["json", "native-tls-vendored"] }
|
||||
serde = "1.0.215"
|
||||
serde_json = "1.0.133"
|
||||
sysinfo = "0.32.0"
|
||||
tokenservice-client = { version = "0.4.0", registry = "gitea" }
|
||||
tokio = { version = "1.40.0", features = ["macros", "signal", "rt-multi-thread"] }
|
||||
tokio = { version = "1.41.1", features = ["macros", "signal", "rt-multi-thread"] }
|
||||
uptime_lib = "0.3.1"
|
||||
|
||||
[patch.crates-io]
|
||||
poise = { git = "https://github.com/serenity-rs/poise", branch = "next" }
|
||||
|
||||
[features]
|
||||
production = []
|
||||
|
||||
|
@ -8,7 +8,7 @@ services:
|
||||
- cache
|
||||
cache:
|
||||
container_name: kon-redis
|
||||
image: redis/redis-stack-server:7.4.0-v0
|
||||
image: redis/redis-stack-server:7.4.0-v1
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 37935:6379/tcp
|
||||
|
@ -6,13 +6,15 @@ pub mod ping;
|
||||
pub mod status;
|
||||
pub mod uptime;
|
||||
|
||||
type PoiseCtx<'a> = poise::Context<'a, (), Error>;
|
||||
|
||||
/// Deploy the commands globally or in a guild
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
owners_only,
|
||||
guild_only
|
||||
)]
|
||||
pub async fn deploy(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn deploy(ctx: PoiseCtx<'_>) -> Result<(), Error> {
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -187,15 +187,17 @@ async fn ilo_data(endpoint: RedfishEndpoint) -> Result<Box<dyn std::any::Any + S
|
||||
/// Retrieve data from the HP iLO4 interface
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
install_context = "Guild|User",
|
||||
interaction_context = "Guild|BotDm|PrivateChannel",
|
||||
subcommands("temperature", "power", "system")
|
||||
)]
|
||||
pub async fn ilo(_: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn ilo(_: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve the server's temperature data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn temperature(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn temperature(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
ctx.defer().await?;
|
||||
let ilo = ilo_data(RedfishEndpoint::Thermal).await.unwrap();
|
||||
let data = ilo.downcast_ref::<Chassis>().unwrap();
|
||||
@ -246,7 +248,7 @@ pub async fn temperature(ctx: poise::Context<'_, (), Error>) -> Result<(), Error
|
||||
|
||||
/// Retrieve the server's power data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn power(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn power(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
ctx.defer().await?;
|
||||
let ilo = ilo_data(RedfishEndpoint::Power).await.unwrap();
|
||||
let data = ilo.downcast_ref::<Power>().unwrap();
|
||||
@ -272,7 +274,7 @@ pub async fn power(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
|
||||
/// Retrieve the server's system data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn system(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn system(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
ctx.defer().await?;
|
||||
|
||||
let (ilo_sys, ilo_event) = tokio::join!(
|
||||
|
@ -21,9 +21,13 @@ use poise::{
|
||||
};
|
||||
|
||||
/// Convert MIDI file to WAV
|
||||
#[poise::command(context_menu_command = "MIDI -> WAV")]
|
||||
#[poise::command(
|
||||
context_menu_command = "MIDI -> WAV",
|
||||
install_context = "User",
|
||||
interaction_context = "Guild|BotDm|PrivateChannel",
|
||||
)]
|
||||
pub async fn midi_to_wav(
|
||||
ctx: poise::Context<'_, (), Error>,
|
||||
ctx: super::PoiseCtx<'_>,
|
||||
#[description = "MIDI file to be converted"] message: poise::serenity_prelude::Message
|
||||
) -> Result<(), Error> {
|
||||
let re = Regex::new(r"(?i)\.mid$").unwrap();
|
||||
@ -46,7 +50,7 @@ pub async fn midi_to_wav(
|
||||
)
|
||||
.await.unwrap();
|
||||
|
||||
return Err(Error::from(format!("Failed to download the file: {}", y)))
|
||||
return Err(Error::from(format!("Failed to download the file: {y}")))
|
||||
}
|
||||
};
|
||||
|
||||
@ -63,7 +67,7 @@ pub async fn midi_to_wav(
|
||||
.output();
|
||||
|
||||
// Just to add an info to console to tell what the bot is doing when MIDI file is downloaded.
|
||||
println!("Discord[{}:{}]: Processing MIDI file: \"{}\"", ctx.guild().unwrap().name, ctx.command().qualified_name, midi_path);
|
||||
println!("Discord[{}]: Processing MIDI file: \"{}\"", ctx.command().qualified_name, midi_path);
|
||||
|
||||
match output {
|
||||
Ok(_) => {
|
||||
@ -73,8 +77,8 @@ pub async fn midi_to_wav(
|
||||
|
||||
if reply.is_err() {
|
||||
println!(
|
||||
"Discord[{}:{}]: Processed file couldn't be uploaded back to Discord channel due to upload limit",
|
||||
ctx.guild().unwrap().name, ctx.command().qualified_name
|
||||
"Discord[{}]: Processed file couldn't be uploaded back to Discord channel due to upload limit",
|
||||
ctx.command().qualified_name
|
||||
);
|
||||
|
||||
ctx.send(CreateReply::default()
|
||||
@ -93,7 +97,7 @@ pub async fn midi_to_wav(
|
||||
.content("Command didn't execute successfully, check console for more information!")
|
||||
).await.unwrap();
|
||||
|
||||
return Err(Error::from(format!("Midi conversion failed: {}", y)))
|
||||
return Err(Error::from(format!("Midi conversion failed: {y}")))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use crate::Error;
|
||||
|
||||
/// Check if the bot is alive
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn ping(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn ping(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
ctx.reply(format!("Powong! `{:?}`", ctx.ping().await)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -71,13 +71,13 @@ fn process_pms_statuses(servers: Vec<(String, Vec<Value>)>) -> Vec<(String, Stri
|
||||
slash_command,
|
||||
subcommands("wg")
|
||||
)]
|
||||
pub async fn status(_: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn status(_: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve the server statuses from Wargaming
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn wg(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn wg(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
let pms_asia = token_path().await.wg_pms;
|
||||
let pms_eu = pms_asia.replace("asia", "eu");
|
||||
let embed = CreateEmbed::new().color(BINARY_PROPERTIES.embed_color);
|
||||
|
@ -42,12 +42,12 @@ fn get_os_info() -> String {
|
||||
});
|
||||
}
|
||||
|
||||
format!("{} {}", name, version)
|
||||
format!("{name} {version}")
|
||||
}
|
||||
|
||||
/// Retrieve host and bot uptimes
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
pub async fn uptime(ctx: super::PoiseCtx<'_>) -> Result<(), Error> {
|
||||
let _bot = ctx.http().get_current_user().await.unwrap();
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
@ -68,7 +68,7 @@ pub async fn uptime(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
let stat_msg = [
|
||||
format!("**{} {}** `{}:{}`", _bot.name, BOT_VERSION.as_str(), GIT_COMMIT_HASH, GIT_COMMIT_BRANCH),
|
||||
format!("**{} {}** `{GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH}`", _bot.name, BOT_VERSION.as_str()),
|
||||
format!(">>> System: `{}`", format_duration(sys_uptime)),
|
||||
format!("Process: `{}`", format_duration(proc_uptime)),
|
||||
format!("CPU: `{}`", cpu[0].brand()),
|
||||
|
@ -27,6 +27,7 @@ impl RedisController {
|
||||
|
||||
async fn create_pool(manager: RedisConnectionManager) -> Pool<RedisConnectionManager> {
|
||||
let mut backoff = 1;
|
||||
let redis_err = "Redis[Error]: {{ e }}, retrying in {{ backoff }} seconds";
|
||||
|
||||
loop {
|
||||
match Pool::builder().max_size(20).retry_connection(true).build(manager.clone()).await {
|
||||
@ -40,19 +41,19 @@ impl RedisController {
|
||||
return pool.clone();
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Redis[Error]: {}, retrying in {} seconds", e, backoff);
|
||||
eprintln!("{}", redis_err.replace("{{ e }}", &e.to_string()).replace("{{ backoff }}", &backoff.to_string()));
|
||||
Self::apply_backoff(&mut backoff).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("Redis[ConnError]: {}, retrying in {} seconds", e, backoff);
|
||||
eprintln!("{}", redis_err.replace("{{ e }}", &e.to_string()).replace("{{ backoff }}", &backoff.to_string()));
|
||||
Self::apply_backoff(&mut backoff).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Redis[PoolError]: {}, retrying in {} seconds", e, backoff);
|
||||
eprintln!("Redis[PoolError]: {e}, retrying in {backoff} seconds");
|
||||
Self::apply_backoff(&mut backoff).await;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
pub struct ConfigMeta {
|
||||
pub env: String,
|
||||
pub embed_color: i32,
|
||||
pub ready_notify: u64,
|
||||
pub rss_channel: u64,
|
||||
@ -14,6 +15,7 @@ pub static BINARY_PROPERTIES: LazyLock<ConfigMeta> = LazyLock::new(ConfigMeta::n
|
||||
#[cfg(not(feature = "production"))]
|
||||
pub static BINARY_PROPERTIES: LazyLock<ConfigMeta> = LazyLock::new(||
|
||||
ConfigMeta::new()
|
||||
.env("dev")
|
||||
.embed_color(0xf1d63c)
|
||||
.ready_notify(865673694184996888)
|
||||
.rss_channel(865673694184996888)
|
||||
@ -22,6 +24,7 @@ pub static BINARY_PROPERTIES: LazyLock<ConfigMeta> = LazyLock::new(||
|
||||
impl ConfigMeta {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
env: "prod".to_string(),
|
||||
embed_color: 0x5a99c7,
|
||||
ready_notify: 865673694184996888,
|
||||
rss_channel: 865673694184996888,
|
||||
@ -33,6 +36,12 @@ impl ConfigMeta {
|
||||
}
|
||||
|
||||
// Scalable functions below;
|
||||
#[cfg(not(feature = "production"))]
|
||||
fn env(mut self, env: &str) -> Self {
|
||||
self.env = env.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "production"))]
|
||||
fn embed_color(mut self, color: i32) -> Self {
|
||||
self.embed_color = color;
|
||||
|
@ -17,7 +17,7 @@ impl HttpClient {
|
||||
pub async fn get(&self, url: &str, ua: &str) -> Result<Response, Error> {
|
||||
let response = self.0.get(url).header(
|
||||
reqwest::header::USER_AGENT,
|
||||
format!("Kon ({}-{}) - {}/reqwest", super::utils::BOT_VERSION.as_str(), crate::GIT_COMMIT_HASH, ua)
|
||||
format!("Kon ({}-{}) - {ua}/reqwest", super::utils::BOT_VERSION.as_str(), crate::GIT_COMMIT_HASH)
|
||||
)
|
||||
.timeout(Duration::from_secs(30))
|
||||
.send()
|
||||
@ -26,11 +26,11 @@ impl HttpClient {
|
||||
match response {
|
||||
Ok(res) => Ok(res),
|
||||
Err(y) if y.is_timeout() => {
|
||||
eprintln!("{ERROR_PREFIX} Request timed out for \"{}\"", url);
|
||||
eprintln!("{ERROR_PREFIX} Request timed out for \"{url}\"");
|
||||
Err(y)
|
||||
},
|
||||
Err(y) if y.is_connect() => {
|
||||
eprintln!("{ERROR_PREFIX} Connection failed for \"{}\"", url);
|
||||
eprintln!("{ERROR_PREFIX} Connection failed for \"{url}\"");
|
||||
Err(y)
|
||||
},
|
||||
Err(y) => Err(y)
|
||||
|
@ -16,11 +16,11 @@ use std::{
|
||||
};
|
||||
|
||||
fn task_info(name: &str, message: &str) {
|
||||
println!("TaskScheduler[{}]: {}", name, message)
|
||||
println!("TaskScheduler[{name}]: {message}")
|
||||
}
|
||||
|
||||
fn task_err(name: &str, message: &str) {
|
||||
eprintln!("TaskScheduler[{}:Error]: {}", name, message)
|
||||
eprintln!("TaskScheduler[{name}:Error]: {message}")
|
||||
}
|
||||
|
||||
static TASK_RUNNING: AtomicBool = AtomicBool::new(false);
|
||||
@ -36,9 +36,9 @@ where
|
||||
TASK_RUNNING.store(true, Ordering::SeqCst);
|
||||
spawn(async move {
|
||||
if let Err(y) = task(ctx_cl).await {
|
||||
eprintln!("TaskScheduler[Main:Error]: Failed to execute the task, error reason: {}", y);
|
||||
eprintln!("TaskScheduler[Main:Error]: Failed to execute the task, error reason: {y}");
|
||||
if let Some(source) = y.source() {
|
||||
eprintln!("TaskScheduler[Main:Error]: Failed to execute the task, this is caused by: {:#?}", source);
|
||||
eprintln!("TaskScheduler[Main:Error]: Failed to execute the task, this is caused by: {source:#?}");
|
||||
}
|
||||
}
|
||||
TASK_RUNNING.store(false, Ordering::SeqCst);
|
||||
|
@ -11,7 +11,7 @@ pub static BOT_VERSION: LazyLock<String> = LazyLock::new(|| {
|
||||
.unwrap()
|
||||
.version
|
||||
.unwrap();
|
||||
format!("v{}", cargo_version)
|
||||
format!("v{cargo_version}")
|
||||
});
|
||||
|
||||
static TSCLIENT: LazyLock<Mutex<TSClient>> = LazyLock::new(|| Mutex::new(TSClient::new()));
|
||||
@ -28,7 +28,7 @@ pub fn mention_dev(ctx: poise::Context<'_, (), crate::Error>) -> Option<String>
|
||||
|
||||
for dev in devs {
|
||||
if app_owners.contains(&UserId::new(dev)) {
|
||||
mentions.push(format!("<@{}>", dev));
|
||||
mentions.push(format!("<@{dev}>"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,15 +47,15 @@ pub fn format_duration(secs: u64) -> String {
|
||||
|
||||
let mut formatted_string = String::new();
|
||||
if days > 0 {
|
||||
formatted_string.push_str(&format!("{}d, ", days));
|
||||
formatted_string.push_str(&format!("{days}d, "));
|
||||
}
|
||||
if hours > 0 || days > 0 {
|
||||
formatted_string.push_str(&format!("{}h, ", hours));
|
||||
formatted_string.push_str(&format!("{hours}h, "));
|
||||
}
|
||||
if minutes > 0 || hours > 0 {
|
||||
formatted_string.push_str(&format!("{}m, ", minutes));
|
||||
formatted_string.push_str(&format!("{minutes}m, "));
|
||||
}
|
||||
formatted_string.push_str(&format!("{}s", seconds));
|
||||
formatted_string.push_str(&format!("{seconds}s"));
|
||||
|
||||
formatted_string
|
||||
}
|
||||
@ -75,8 +75,8 @@ pub fn format_bytes(bytes: u64) -> String {
|
||||
}
|
||||
|
||||
if unit == "B" {
|
||||
format!("{}{}", value, unit)
|
||||
format!("{value}{unit}")
|
||||
} else {
|
||||
format!("{:.2}{}", value, unit)
|
||||
format!("{value:.2}{unit}")
|
||||
}
|
||||
}
|
||||
|
37
src/main.rs
37
src/main.rs
@ -19,6 +19,7 @@ use crate::internals::{
|
||||
|
||||
use std::{
|
||||
sync::Arc,
|
||||
borrow::Cow,
|
||||
thread::current
|
||||
};
|
||||
use poise::serenity_prelude::{
|
||||
@ -57,7 +58,7 @@ async fn on_ready(
|
||||
println!("Event[Ready][Notice]: Session limit: {}/{}", session.remaining, session.total);
|
||||
}
|
||||
|
||||
println!("Event[Ready]: Build version: {} ({}:{})", *BOT_VERSION, GIT_COMMIT_HASH, GIT_COMMIT_BRANCH);
|
||||
println!("Event[Ready]: Build version: {} ({GIT_COMMIT_HASH}:{GIT_COMMIT_BRANCH})", *BOT_VERSION);
|
||||
println!("Event[Ready]: Connected to API as {}", ready.user.name);
|
||||
|
||||
let message = CreateMessage::new();
|
||||
@ -72,16 +73,15 @@ async fn on_ready(
|
||||
}
|
||||
|
||||
async fn event_processor(
|
||||
ctx: &Context,
|
||||
event: &FullEvent,
|
||||
_framework: poise::FrameworkContext<'_, (), Error>
|
||||
framework: poise::FrameworkContext<'_, (), Error>,
|
||||
event: &FullEvent
|
||||
) -> Result<(), Error> {
|
||||
if let FullEvent::Ready { .. } = event {
|
||||
let thread_id = format!("{:?}", current().id());
|
||||
let thread_num: String = thread_id.chars().filter(|c| c.is_ascii_digit()).collect();
|
||||
println!("Event[Ready]: Task Scheduler operating on thread {}", thread_num);
|
||||
println!("Event[Ready]: Task Scheduler operating on thread {thread_num}");
|
||||
|
||||
let ctx = Arc::new(ctx.clone());
|
||||
let ctx = Arc::new(framework.serenity_context.clone());
|
||||
run_task(ctx.clone(), rss).await;
|
||||
}
|
||||
|
||||
@ -90,6 +90,12 @@ async fn event_processor(
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let prefix = if BINARY_PROPERTIES.env.contains("dev") {
|
||||
Some(Cow::Borrowed("kon!"))
|
||||
} else {
|
||||
Some(Cow::Borrowed("k!"))
|
||||
};
|
||||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![
|
||||
@ -101,7 +107,7 @@ async fn main() {
|
||||
commands::uptime::uptime()
|
||||
],
|
||||
prefix_options: poise::PrefixFrameworkOptions {
|
||||
prefix: Some(String::from("konata")),
|
||||
prefix,
|
||||
mention_as_prefix: false,
|
||||
case_insensitive_commands: true,
|
||||
ignore_bots: true,
|
||||
@ -113,29 +119,32 @@ async fn main() {
|
||||
Some(guild) => guild.name.clone(),
|
||||
None => String::from("Direct Message")
|
||||
};
|
||||
println!("Discord[{}]: {} ran /{}", get_guild_name, ctx.author().name, ctx.command().qualified_name);
|
||||
println!(
|
||||
"Discord[{get_guild_name}]: {} ran /{}",
|
||||
ctx.author().name,
|
||||
ctx.command().qualified_name
|
||||
);
|
||||
}),
|
||||
on_error: |error| Box::pin(async move {
|
||||
match error {
|
||||
poise::FrameworkError::Command { error, ctx, .. } => {
|
||||
println!("PoiseCommandError({}): {}", ctx.command().qualified_name, error);
|
||||
println!("PoiseCommandError({}): {error}", ctx.command().qualified_name);
|
||||
ctx.reply(format!(
|
||||
"Encountered an error during command execution, ask {} to check console for more details!",
|
||||
mention_dev(ctx).unwrap_or_default()
|
||||
)).await.expect("Error sending message");
|
||||
},
|
||||
poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {}", event.snake_case_name(), error),
|
||||
poise::FrameworkError::Setup { error, .. } => println!("PoiseSetupError: {}", error),
|
||||
poise::FrameworkError::EventHandler { error, event, .. } => println!("PoiseEventHandlerError({}): {error}", event.snake_case_name()),
|
||||
poise::FrameworkError::UnknownInteraction { interaction, .. } => println!(
|
||||
"PoiseUnknownInteractionError: {} tried to execute an unknown interaction ({})",
|
||||
interaction.user.name,
|
||||
interaction.data.name
|
||||
),
|
||||
other => println!("PoiseOtherError: {}", other)
|
||||
other => println!("PoiseOtherError: {other}")
|
||||
}
|
||||
}),
|
||||
initialize_owners: true,
|
||||
event_handler: |ctx, event, framework, _| Box::pin(event_processor(ctx, event, framework)),
|
||||
event_handler: |framework, event| Box::pin(event_processor(framework, event)),
|
||||
..Default::default()
|
||||
})
|
||||
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework)))
|
||||
@ -151,6 +160,6 @@ async fn main() {
|
||||
.await.expect("Error creating client");
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Error starting client: {:#?}", why);
|
||||
println!("Error starting client: {why:#?}");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user