This commit is contained in:
2024-08-05 18:56:40 +02:00
parent 553adb59a7
commit 8b26768466
12 changed files with 467 additions and 176 deletions

View File

@@ -8,11 +8,15 @@ fn get_env(env: &'static str) -> String {
pub struct Config {
pub discord_bot_token: String,
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 telegram_bot_token: String,
pub telegram_channel_id: i128,
}

129
src/handler/mod.rs Normal file
View File

@@ -0,0 +1,129 @@
use serenity::prelude::*;
use serenity::all::{AutocompleteChoice, CreateAutocompleteResponse, CreateInteractionResponse, CreateInteractionResponseMessage, EditMessage, GuildId, Interaction};
use serenity::async_trait;
use serenity::model::channel::Message;
use chrono::offset::FixedOffset;
use crate::config;
use crate::notifiers::telegram::send_to_telegram;
use crate::utils::{add_game, delete_game, format_games_list, parse_games_list};
pub mod commands;
pub struct Handler;
#[async_trait]
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_game_list_channel_id {
return;
}
match command.data.name.as_str() {
"add" => {
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);
if let Err(why) = command.create_response(&ctx.http, builder).await {
println!("Cannot respond to slash command: {why}");
}
},
"delete" => {
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);
if let Err(why) = command.create_response(&ctx.http, builder).await {
println!("Cannot respond to slash command: {why}");
}
},
_ => (),
};
} else if let Interaction::Autocomplete(interaction) = interaction {
if interaction.channel_id != config::CONFIG.discord_game_list_channel_id {
return;
}
if interaction.data.name.as_str() == "delete" {
let message = interaction.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap();
let categories = parse_games_list(&message.content).await;
let games = categories.iter().flat_map(|category| category.games.iter()).collect::<Vec<&String>>();
let query = interaction.data.options[0].value.as_str().unwrap();
let autocompolete_response = CreateAutocompleteResponse::new().set_choices(
games
.iter()
.filter(|game| game.to_lowercase().contains(&query.to_lowercase()))
.map(|game| {
AutocompleteChoice::new(game.to_string(), game.to_string())
}).collect()
);
let _ = interaction.create_response(&ctx.http, serenity::builder::CreateInteractionResponse::Autocomplete(autocompolete_response)).await.unwrap();
};
}
}
async fn message(&self, _ctx: Context, msg: Message) {
if msg.guild_id != Some(config::CONFIG.discord_guild_id.into()) {
return;
}
if msg.channel_id == config::CONFIG.discord_channel_id {
send_to_telegram(&msg.content).await;
return;
}
}
async fn ready(&self, ctx: Context, _ready: serenity::model::gateway::Ready) {
let guild_id = GuildId::new(config::CONFIG.discord_guild_id);
let _ = guild_id
.set_commands(
&ctx.http,
vec![
commands::add_game::register(),
commands::delete_game::register(),
]
).await.unwrap();
}
}

View File

@@ -1,163 +1,32 @@
use reqwest::Url;
use serenity::all::{ActivityData, AutocompleteChoice, CreateAutocompleteResponse, CreateInteractionResponse, CreateInteractionResponseMessage, EditMessage, GuildId, Interaction};
use serenity::async_trait;
use serenity::model::channel::Message;
use serenity::all::ActivityData;
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 handler;
pub mod utils;
pub mod notifiers;
async fn send_to_telegram(msg: &str) {
let base_url = format!("https://api.telegram.org/bot{}/sendMessage", config::CONFIG.telegram_bot_token);
let url = Url::parse_with_params(
base_url.as_ref(),
&[
("chat_id", &config::CONFIG.telegram_channel_id.to_string().as_ref()),
("text", &msg)
]
).unwrap();
reqwest::get(url).await.expect("Error sending message to Telegram");
}
struct Handler;
#[async_trait]
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_game_list_channel_id {
return;
}
match command.data.name.as_str() {
"add" => {
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);
if let Err(why) = command.create_response(&ctx.http, builder).await {
println!("Cannot respond to slash command: {why}");
}
},
"delete" => {
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);
if let Err(why) = command.create_response(&ctx.http, builder).await {
println!("Cannot respond to slash command: {why}");
}
},
_ => (),
};
} else if let Interaction::Autocomplete(interaction) = interaction {
if interaction.channel_id != config::CONFIG.discord_game_list_channel_id {
return;
}
if interaction.data.name.as_str() == "delete" {
let message = interaction.channel_id.message(&ctx.http, config::CONFIG.discord_game_list_message_id).await.unwrap();
let categories = parse_games_list(&message.content).await;
let games = categories.iter().flat_map(|category| category.games.iter()).collect::<Vec<&String>>();
let query = interaction.data.options[0].value.as_str().unwrap();
let autocompolete_response = CreateAutocompleteResponse::new().set_choices(
games
.iter()
.filter(|game| game.to_lowercase().contains(&query.to_lowercase()))
.map(|game| {
AutocompleteChoice::new(game.to_string(), game.to_string())
}).collect()
);
let _ = interaction.create_response(&ctx.http, serenity::builder::CreateInteractionResponse::Autocomplete(autocompolete_response)).await.unwrap();
};
}
}
async fn message(&self, _ctx: Context, msg: Message) {
if msg.guild_id != Some(config::CONFIG.discord_guild_id.into()) {
return;
}
if msg.channel_id == config::CONFIG.discord_channel_id {
send_to_telegram(&msg.content).await;
return;
}
}
async fn ready(&self, ctx: Context, _ready: serenity::model::gateway::Ready) {
let guild_id = GuildId::new(config::CONFIG.discord_guild_id);
let _ = guild_id
.set_commands(
&ctx.http,
vec![
commands::add_game::register(),
commands::delete_game::register(),
]
).await.unwrap();
}
}
#[tokio::main]
async fn main() {
async fn start_discord_bot() {
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT;
let mut client =
Client::builder(&config::CONFIG.discord_bot_token, intents)
.event_handler(Handler)
.event_handler(handler::Handler)
.status(serenity::all::OnlineStatus::Online)
.activity(ActivityData::playing(&config::CONFIG.discord_bot_activity))
.await
.expect("Err creating client");
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
panic!("Client error: {why:?}");
}
}
#[tokio::main]
async fn main() {
start_discord_bot().await;
}

3
src/notifiers/discord.rs Normal file
View File

@@ -0,0 +1,3 @@
pub async fn send_to_discord(_msg: &str) {
todo!();
}

2
src/notifiers/mod.rs Normal file
View File

@@ -0,0 +1,2 @@
pub mod telegram;
pub mod discord;

18
src/notifiers/telegram.rs Normal file
View File

@@ -0,0 +1,18 @@
use reqwest::Url;
use crate::config;
pub async fn send_to_telegram(msg: &str) {
let base_url = format!("https://api.telegram.org/bot{}/sendMessage", config::CONFIG.telegram_bot_token);
let url = Url::parse_with_params(
base_url.as_ref(),
&[
("chat_id", &config::CONFIG.telegram_channel_id.to_string().as_ref()),
("text", &msg)
]
).unwrap();
reqwest::get(url).await.expect("Error sending message to Telegram");
}