From 805d21988157fa07fb35ead3a1553f9aacf8b071 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Thu, 20 Apr 2023 02:13:23 +0300 Subject: [PATCH] finished replacing embed with generated image in lobby start command --- src/bot/commands/lobby.rs | 168 ++++++++++++++++++++------------------ src/image_manipulation.rs | 33 +++++--- src/mixer/mixer.rs | 5 +- 3 files changed, 114 insertions(+), 92 deletions(-) diff --git a/src/bot/commands/lobby.rs b/src/bot/commands/lobby.rs index 197537e..1f76df4 100644 --- a/src/bot/commands/lobby.rs +++ b/src/bot/commands/lobby.rs @@ -1,28 +1,24 @@ use itertools::Itertools; -use sea_orm::{EntityTrait, QueryFilter, ColumnTrait}; use serenity::async_trait; -use serenity::builder::{CreateApplicationCommand, CreateEmbed}; +use serenity::builder::CreateApplicationCommand; use serenity::client::Context; use serenity::futures::future::join_all; use serenity::futures::StreamExt; use serenity::model::application::command::CommandOptionType; use serenity::model::application::component::ButtonStyle; -use serenity::model::application::interaction::message_component::MessageComponentInteraction; use serenity::model::application::interaction::{ application_command::ApplicationCommandInteraction, InteractionResponseType, }; use serenity::model::channel::{ChannelType, PermissionOverwrite, PermissionOverwriteType}; use serenity::model::id::{ChannelId, RoleId, UserId}; +use serenity::model::prelude::{AttachmentType, GuildId, Message}; use serenity::model::Permissions; -use serenity::model::prelude::{AttachmentType, Message, GuildId}; -use serenity::utils::Colour; use sqlx::types::chrono::Utc; use std::borrow::Cow; use std::time::Duration; use crate::bot::commands::MixerCommand; use crate::database::models::lobby::Model; -use crate::database::models::player; use crate::database::models::role::Role; use crate::database::queries::prelude::*; use crate::database::DatabaseContainer; @@ -251,11 +247,7 @@ impl LobbyCommand { let members = main_channel.members(ctx).await?; let users = members.iter().map(|m| m.user.id).collect::>(); - - let players = player::Entity::find() - .filter(player::Column::Id.is_in(1..11)) - .all(db.connection()).await.ok(); - // let players = PlayerQuery::players_by_user_ids(db.connection(), users).await; + let players = PlayerQuery::players_by_user_ids(db.connection(), users).await; let players = match players { Some(p) => p, @@ -348,12 +340,18 @@ impl LobbyCommand { let data = ctx.data.read().await; let image_gen = data.get::().unwrap(); - let player_names = team1_names.into_iter().chain(team2_names.into_iter()).collect_vec(); + let player_names = team1_names + .into_iter() + .chain(team2_names.into_iter()) + .collect_vec(); let team1_rank = team1.average_rating(&players); let team2_rank = team2.average_rating(&players); - image_gen.draw_teams(player_names, [team1_rank.value as i32, team2_rank.value as i32]) + image_gen.draw_teams_to_png( + player_names, + [team1_rank.value as i32, team2_rank.value as i32], + ) }; let attachment = AttachmentType::Bytes { @@ -361,35 +359,41 @@ impl LobbyCommand { filename: "teams.png".to_string(), }; - let msg = interaction.channel_id.send_message(ctx, |message| { - message - .add_file(attachment) - .components(|components| { - components.create_action_row(|row| { - row.create_button(|button| { - button - .custom_id("cancel") - .label("Cancel") - .style(ButtonStyle::Danger) - }); - row.create_button(|button| { - button - .custom_id("swap") - .label("Swap") - .disabled(true) - .style(ButtonStyle::Primary) - }); - row.create_button(|button| { - button - .custom_id("start") - .label("Start") - .style(ButtonStyle::Success) + let msg = interaction + .channel_id + .send_message(ctx, |message| { + message + .content(format!("<@{}>", interaction.user.id.0)) + .add_file(attachment) + .components(|components| { + components.create_action_row(|row| { + row.create_button(|button| { + button + .custom_id("cancel") + .label("Cancel") + .style(ButtonStyle::Danger) + }); + row.create_button(|button| { + button + .custom_id("swap") + .label("Swap") + .disabled(true) + .style(ButtonStyle::Primary) + }); + row.create_button(|button| { + button + .custom_id("start") + .label("Start") + .style(ButtonStyle::Success) + }) }) }) - }) - }).await?; + }) + .await?; - interaction.delete_original_interaction_response(ctx).await?; + interaction + .delete_original_interaction_response(ctx) + .await?; // interaction // .edit_original_interaction_response(ctx, |response| { @@ -436,8 +440,16 @@ impl LobbyCommand { if let Some(interaction) = interactions.first() { match interaction.data.custom_id.as_str() { "start" => { - self.process_valid_teams_start(ctx, lobby, &team1, &team2, players, interaction.user.id, msg) - .await? + self.process_valid_teams_start( + ctx, + lobby, + &team1, + &team2, + players, + interaction.user.id, + msg, + ) + .await? } "cancel" => { self.process_valid_teams_cancel(ctx, &team1, &team2, msg) @@ -464,7 +476,7 @@ impl LobbyCommand { team2: &Team, players: Vec, author: UserId, - mut message: Message + mut message: Message, ) -> serenity::Result<()> { let main_channel = ChannelId::from(lobby.main_voice_id as u64) .to_channel(ctx) @@ -493,51 +505,51 @@ impl LobbyCommand { .iter() .any(|(_, i)| *i == index && index.is_some()) { - member.move_to_voice_channel(ctx, red_channel.id).await?; + member.move_to_voice_channel(ctx, blue_channel.id).await?; } else if team2 .players .iter() .any(|(_, i)| *i == index && index.is_some()) { - member.move_to_voice_channel(ctx, blue_channel.id).await?; + member.move_to_voice_channel(ctx, red_channel.id).await?; } } - message.edit(ctx, |message| { - message - .content(format!("<@{}>", author.0)) - .components(|components| { - components - .create_action_row(|row| { - row.create_button(|button| { - button - .custom_id("win_team1") - .label("Team 1 win") - .style(ButtonStyle::Success) + message + .edit(ctx, |message| { + message.components(|components| { + components + .create_action_row(|row| { + row.create_button(|button| { + button + .custom_id("win_team1") + .label("Team 1 win") + .style(ButtonStyle::Success) + }) + .create_button(|button| { + button + .custom_id("draw") + .label("Draw") + .style(ButtonStyle::Secondary) + }) + .create_button(|button| { + button + .custom_id("win_team2") + .label("Team 2 win") + .style(ButtonStyle::Success) + }) }) - .create_button(|button| { - button - .custom_id("draw") - .label("Draw") - .style(ButtonStyle::Secondary) + .create_action_row(|row| { + row.create_button(|button| { + button + .custom_id("cancel") + .label("Cancel game") + .style(ButtonStyle::Danger) + }) }) - .create_button(|button| { - button - .custom_id("win_team2") - .label("Team 2 win") - .style(ButtonStyle::Success) - }) - }) - .create_action_row(|row| { - row.create_button(|button| { - button - .custom_id("cancel") - .label("Cancel game") - .style(ButtonStyle::Danger) - }) - }) + }) }) - }).await?; + .await?; // let msg = interaction.edit_original_interaction_response(ctx, |message| { // message.components(|components| { @@ -667,7 +679,7 @@ impl LobbyCommand { ctx: &Context, team1: &Team, team2: &Team, - message: Message + message: Message, ) -> serenity::Result<()> { message.delete(ctx).await } @@ -677,7 +689,7 @@ impl LobbyCommand { ctx: &Context, team1: &Team, team2: &Team, - message: Message + message: Message, ) -> serenity::Result<()> { Ok(()) } diff --git a/src/image_manipulation.rs b/src/image_manipulation.rs index 819882a..5e86f1a 100644 --- a/src/image_manipulation.rs +++ b/src/image_manipulation.rs @@ -1,26 +1,25 @@ -use std::{sync::Arc, io::Read}; -use image::EncodableLayout; +use image::{codecs::png, ImageEncoder}; use imageproc::drawing::text_size; -use itertools::Itertools; use rusttype::{Font, Scale}; use serenity::prelude::TypeMapKey; +use std::{io::BufWriter, sync::Arc}; pub struct ImageGenerator<'a> { pub player_font: Font<'a>, pub text_font: Font<'a>, - pub teams_image: image::ImageBuffer, Vec> + pub teams_image: image::ImageBuffer, Vec>, } impl<'a> ImageGenerator<'a> { - pub fn draw_teams(&self, player_names: Vec, teams_rating: [i32; 2]) -> Vec { - let mut image = self.teams_image.clone(); + pub fn draw_teams_to_png(&self, player_names: Vec, teams_rating: [i32; 2]) -> Vec { + let mut image: image::ImageBuffer, Vec> = self.teams_image.clone(); let player_text_scale = Scale::uniform(60.0); for i in 0..2 { for j in 0..5 { let player_name = match player_names.get(i * 5 + j) { Some(name) => name, - None => "Unknown" + None => "Unknown", }; let size = text_size(player_text_scale, &self.player_font, player_name); @@ -32,7 +31,8 @@ impl<'a> ImageGenerator<'a> { let size = text_size(scale, &self.player_font, player_name); let x: i32 = 83 + 540 * i as i32 - 2; - let y: i32 = 182 + 70 * j as i32 - size.1 + ((size.1 as f32 * 1.0 / 5.0) / 10.0) as i32; + let y: i32 = + 182 + 70 * j as i32 - size.1 + ((size.1 as f32 * 1.0 / 5.0) / 10.0) as i32; imageproc::drawing::draw_text_mut( &mut image, @@ -41,7 +41,7 @@ impl<'a> ImageGenerator<'a> { y, scale, &self.player_font, - player_name + player_name, ); } } @@ -54,7 +54,7 @@ impl<'a> ImageGenerator<'a> { imageproc::drawing::draw_text_mut( &mut image, image::Rgb([255, 255, 255]), - 365 - size.0 / 2 + 540 * i as i32, + 370 - size.0 / 2 + 540 * i as i32, 100 - size.1, rating_text_scale, &self.text_font, @@ -62,7 +62,16 @@ impl<'a> ImageGenerator<'a> { ); } - image.as_bytes().iter().cloned().collect_vec() + let mut buf = BufWriter::new(Vec::new()); + png::PngEncoder::new(&mut buf) + .write_image( + image.as_raw(), + image.width(), + image.height(), + image::ColorType::Rgb8, + ) + .unwrap(); + buf.into_inner().unwrap() } } @@ -70,4 +79,4 @@ pub struct ImageGeneratorContainer; impl TypeMapKey for ImageGeneratorContainer { type Value = Arc>; -} \ No newline at end of file +} diff --git a/src/mixer/mixer.rs b/src/mixer/mixer.rs index d6c358d..a838de1 100644 --- a/src/mixer/mixer.rs +++ b/src/mixer/mixer.rs @@ -5,6 +5,7 @@ use crate::database::models::role::Role; use crate::mixer::player::Player; use crate::mixer::team::Team; +#[derive(Debug)] struct PlayerRoleEntry { pub index: usize, pub role: Role, @@ -64,7 +65,7 @@ pub fn mix_players(players: &[Player], slots: Vec) -> Option<(Team, Team)> let mut best_team2 = None; let mut best_diff = None; - let threshold = 100.0; + let threshold = 150.0; // this is awful, but it works for tank1_combo in &tank_combos { @@ -192,7 +193,7 @@ pub fn mix_players(players: &[Player], slots: Vec) -> Option<(Team, Team)> .abs(); let diff = diff_rating; - if diff + threshold < best_diff.unwrap_or(f32::MAX) { + if diff < best_diff.unwrap_or(f32::MAX) { if diff < threshold { return Some((team1, team2)); }