diff --git a/Cargo.lock b/Cargo.lock index 3676b95..92b96e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,14 +169,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.35" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf5903dcbc0a39312feb77df2ff4c76387d591b9fc7b04a238dcf8bb62639a" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-targets 0.52.4", ] @@ -294,6 +296,7 @@ dependencies = [ name = "discord-to-telegram-messages-resender" version = "0.1.0" dependencies = [ + "chrono", "once_cell", "reqwest", "serenity", diff --git a/Cargo.toml b/Cargo.toml index 208ac61..fa75bd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ once_cell = "1.19.0" tokio = { version = "1.35.1", features = ["rt-multi-thread", "macros", "time"] } serenity = { verstion = "0.12.1", features = ["collector"] } reqwest = { version = "0.11.23", features = ["json"] } +chrono = "0.4.38" diff --git a/src/config.rs b/src/config.rs index ca98d42..417ec67 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,8 +11,8 @@ pub struct Config { pub discord_guild_id: u64, pub discord_channel_id: u64, pub discord_bot_activity: String, - // pub discord_game_list_channel_id: u64, - // pub discord_game_list_message_id: u64, + pub discord_game_list_channel_id: u64, + pub discord_game_list_message_id: u64, pub telegram_bot_token: String, pub telegram_channel_id: i128, } @@ -25,8 +25,8 @@ impl Config { discord_guild_id: get_env("DISCORD_GUILD_ID").parse().unwrap(), discord_channel_id: get_env("DISCORD_CHANNEL_ID").parse().unwrap(), discord_bot_activity: get_env("DISCORD_BOT_ACTIVITY"), - // discord_game_list_channel_id: get_env("DISCORD_GAME_LIST_CHANNEL_ID").parse().unwrap(), - // discord_game_list_message_id: get_env("DISCORD_GAME_LIST_MESSAGE_ID").parse().unwrap(), + discord_game_list_channel_id: get_env("DISCORD_GAME_LIST_CHANNEL_ID").parse().unwrap(), + discord_game_list_message_id: get_env("DISCORD_GAME_LIST_MESSAGE_ID").parse().unwrap(), telegram_bot_token: get_env("TELEGRAM_BOT_TOKEN"), telegram_channel_id: get_env("TELEGRAM_CHANNEL_ID").parse().unwrap(), } diff --git a/src/main.rs b/src/main.rs index 2fa16a7..98b7b95 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,14 @@ use reqwest::Url; -use serenity::all::{ActivityData, CreateInteractionResponse, CreateInteractionResponseMessage, GuildId, Interaction}; +use serenity::all::{ActivityData, CommandDataOption, CreateInteractionResponse, CreateInteractionResponseMessage, EditMessage, GuildId, Interaction}; use serenity::async_trait; use serenity::model::channel::Message; use serenity::prelude::*; +use utils::{add_game, delete_game, format_games_list, parse_games_list}; +use chrono::offset::FixedOffset; pub mod config; pub mod commands; +pub mod utils; async fn send_to_telegram(msg: &str) { @@ -29,6 +32,10 @@ struct Handler; impl EventHandler for Handler { async fn interaction_create(&self, ctx: Context, interaction: Interaction) { if let Interaction::Command(command) = interaction { + if command.channel_id != config::CONFIG.discord_channel_id { + return; + } + match command.data.name.as_str() { "copy_message" => { let message_id = command.data.options[0].value.as_str().unwrap().parse::().unwrap(); @@ -41,7 +48,30 @@ impl EventHandler for Handler { } }, "add_game" => { - // let message = command.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap(); + let mut message = command.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap(); + + let utc_offset = FixedOffset::east_opt(3 * 3600); // UTC+3 offset in seconds + let current_time = chrono::Local::now().with_timezone(&utc_offset.unwrap()); + + let mut categories = parse_games_list(&message.content).await; + + categories = add_game( + categories, + command.data.options[0].value.as_str().unwrap(), + &format!( + "* {} ({}) | {}", + command.data.options[2].value.as_str().unwrap(), + command.data.options[1].value.as_str().unwrap(), + match command.data.options.get(3) { + Some(v) => v.value.as_str().unwrap().to_string(), + None => format!("{}", current_time.format("%d.%m.%Y")), + }, + ) + ).await; + + let new_content = format_games_list(categories).await; + + message.edit(&ctx.http, EditMessage::new().content(new_content)).await.unwrap(); let data = CreateInteractionResponseMessage::new().content("Игра добавлена!").ephemeral(true); let builder = CreateInteractionResponse::Message(data); @@ -50,7 +80,18 @@ impl EventHandler for Handler { } }, "delete_game" => { - // let message = command.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap(); + let mut message = command.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap(); + + let mut categories = parse_games_list(&message.content).await; + + categories = delete_game( + categories, + command.data.options[0].value.as_str().unwrap() + ).await; + + let new_content = format_games_list(categories).await; + + message.edit(&ctx.http, EditMessage::new().content(new_content)).await.unwrap(); let data = CreateInteractionResponseMessage::new().content("Игра удалена!").ephemeral(true); let builder = CreateInteractionResponse::Message(data); diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f47914f --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,85 @@ +use std::vec; + + +#[derive(Clone)] +pub struct Category { + pub name: String, + pub games: Vec +} + + +pub async fn parse_games_list(text: &str) -> Vec { + let mut categories = vec![]; + + let lines = text.lines(); + + let mut current_category: Option = None; + + for line in lines.into_iter() { + if line.is_empty() { + continue; + } + + if line.starts_with("* ") { + current_category.clone().unwrap().games.push(line.to_string()); + } else { + if let Some(category) = current_category { + categories.push(category); + } + + current_category = Some(Category { + name: line.to_string(), + games: vec![] + }); + } + } + + categories.push(current_category.unwrap()); + + categories +} + + +pub async fn add_game( + mut categories: Vec, + category: &str, + game_line: &str +) -> Vec { + let category_number = ["points", "paids", "gifts"] + .iter() + .position(|&x| x == category) + .unwrap(); + + categories[category_number].games.push(game_line.to_string()); + + categories +} + +pub async fn delete_game( + mut categories: Vec, + game_name: &str +) -> Vec { + let prefix = format!("* {}", game_name); + + for category in categories.iter_mut() { + category.games.retain(|game| !game.starts_with(&prefix)); + } + + categories +} + + +pub async fn format_games_list(categories: Vec) -> String { + let mut result = String::new(); + + for category in categories.iter() { + result.push_str(&format!("{}\n", category.name)); + + for game in category.games.iter() { + result.push_str(&format!("* {}\n", game)); + } + result.push_str("\n\n"); + } + + result +}