finished replacing embed with generated image in lobby start command

This commit is contained in:
Lionarius
2023-04-20 02:13:23 +03:00
parent ba041a91f0
commit 805d219881
3 changed files with 114 additions and 92 deletions

View File

@@ -1,28 +1,24 @@
use itertools::Itertools; use itertools::Itertools;
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait};
use serenity::async_trait; use serenity::async_trait;
use serenity::builder::{CreateApplicationCommand, CreateEmbed}; use serenity::builder::CreateApplicationCommand;
use serenity::client::Context; use serenity::client::Context;
use serenity::futures::future::join_all; use serenity::futures::future::join_all;
use serenity::futures::StreamExt; use serenity::futures::StreamExt;
use serenity::model::application::command::CommandOptionType; use serenity::model::application::command::CommandOptionType;
use serenity::model::application::component::ButtonStyle; use serenity::model::application::component::ButtonStyle;
use serenity::model::application::interaction::message_component::MessageComponentInteraction;
use serenity::model::application::interaction::{ use serenity::model::application::interaction::{
application_command::ApplicationCommandInteraction, InteractionResponseType, application_command::ApplicationCommandInteraction, InteractionResponseType,
}; };
use serenity::model::channel::{ChannelType, PermissionOverwrite, PermissionOverwriteType}; use serenity::model::channel::{ChannelType, PermissionOverwrite, PermissionOverwriteType};
use serenity::model::id::{ChannelId, RoleId, UserId}; use serenity::model::id::{ChannelId, RoleId, UserId};
use serenity::model::prelude::{AttachmentType, GuildId, Message};
use serenity::model::Permissions; use serenity::model::Permissions;
use serenity::model::prelude::{AttachmentType, Message, GuildId};
use serenity::utils::Colour;
use sqlx::types::chrono::Utc; use sqlx::types::chrono::Utc;
use std::borrow::Cow; use std::borrow::Cow;
use std::time::Duration; use std::time::Duration;
use crate::bot::commands::MixerCommand; use crate::bot::commands::MixerCommand;
use crate::database::models::lobby::Model; use crate::database::models::lobby::Model;
use crate::database::models::player;
use crate::database::models::role::Role; use crate::database::models::role::Role;
use crate::database::queries::prelude::*; use crate::database::queries::prelude::*;
use crate::database::DatabaseContainer; use crate::database::DatabaseContainer;
@@ -251,11 +247,7 @@ impl LobbyCommand {
let members = main_channel.members(ctx).await?; let members = main_channel.members(ctx).await?;
let users = members.iter().map(|m| m.user.id).collect::<Vec<UserId>>(); let users = members.iter().map(|m| m.user.id).collect::<Vec<UserId>>();
let players = PlayerQuery::players_by_user_ids(db.connection(), users).await;
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 = match players { let players = match players {
Some(p) => p, Some(p) => p,
@@ -348,12 +340,18 @@ impl LobbyCommand {
let data = ctx.data.read().await; let data = ctx.data.read().await;
let image_gen = data.get::<ImageGeneratorContainer>().unwrap(); let image_gen = data.get::<ImageGeneratorContainer>().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 team1_rank = team1.average_rating(&players);
let team2_rank = team2.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 { let attachment = AttachmentType::Bytes {
@@ -361,8 +359,11 @@ impl LobbyCommand {
filename: "teams.png".to_string(), filename: "teams.png".to_string(),
}; };
let msg = interaction.channel_id.send_message(ctx, |message| { let msg = interaction
.channel_id
.send_message(ctx, |message| {
message message
.content(format!("<@{}>", interaction.user.id.0))
.add_file(attachment) .add_file(attachment)
.components(|components| { .components(|components| {
components.create_action_row(|row| { components.create_action_row(|row| {
@@ -387,9 +388,12 @@ impl LobbyCommand {
}) })
}) })
}) })
}).await?; })
.await?;
interaction.delete_original_interaction_response(ctx).await?; interaction
.delete_original_interaction_response(ctx)
.await?;
// interaction // interaction
// .edit_original_interaction_response(ctx, |response| { // .edit_original_interaction_response(ctx, |response| {
@@ -436,7 +440,15 @@ impl LobbyCommand {
if let Some(interaction) = interactions.first() { if let Some(interaction) = interactions.first() {
match interaction.data.custom_id.as_str() { match interaction.data.custom_id.as_str() {
"start" => { "start" => {
self.process_valid_teams_start(ctx, lobby, &team1, &team2, players, interaction.user.id, msg) self.process_valid_teams_start(
ctx,
lobby,
&team1,
&team2,
players,
interaction.user.id,
msg,
)
.await? .await?
} }
"cancel" => { "cancel" => {
@@ -464,7 +476,7 @@ impl LobbyCommand {
team2: &Team, team2: &Team,
players: Vec<Player>, players: Vec<Player>,
author: UserId, author: UserId,
mut message: Message mut message: Message,
) -> serenity::Result<()> { ) -> serenity::Result<()> {
let main_channel = ChannelId::from(lobby.main_voice_id as u64) let main_channel = ChannelId::from(lobby.main_voice_id as u64)
.to_channel(ctx) .to_channel(ctx)
@@ -493,20 +505,19 @@ impl LobbyCommand {
.iter() .iter()
.any(|(_, i)| *i == index && index.is_some()) .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 } else if team2
.players .players
.iter() .iter()
.any(|(_, i)| *i == index && index.is_some()) .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 message
.content(format!("<@{}>", author.0)) .edit(ctx, |message| {
.components(|components| { message.components(|components| {
components components
.create_action_row(|row| { .create_action_row(|row| {
row.create_button(|button| { row.create_button(|button| {
@@ -537,7 +548,8 @@ impl LobbyCommand {
}) })
}) })
}) })
}).await?; })
.await?;
// let msg = interaction.edit_original_interaction_response(ctx, |message| { // let msg = interaction.edit_original_interaction_response(ctx, |message| {
// message.components(|components| { // message.components(|components| {
@@ -667,7 +679,7 @@ impl LobbyCommand {
ctx: &Context, ctx: &Context,
team1: &Team, team1: &Team,
team2: &Team, team2: &Team,
message: Message message: Message,
) -> serenity::Result<()> { ) -> serenity::Result<()> {
message.delete(ctx).await message.delete(ctx).await
} }
@@ -677,7 +689,7 @@ impl LobbyCommand {
ctx: &Context, ctx: &Context,
team1: &Team, team1: &Team,
team2: &Team, team2: &Team,
message: Message message: Message,
) -> serenity::Result<()> { ) -> serenity::Result<()> {
Ok(()) Ok(())
} }

View File

@@ -1,26 +1,25 @@
use std::{sync::Arc, io::Read}; use image::{codecs::png, ImageEncoder};
use image::EncodableLayout;
use imageproc::drawing::text_size; use imageproc::drawing::text_size;
use itertools::Itertools;
use rusttype::{Font, Scale}; use rusttype::{Font, Scale};
use serenity::prelude::TypeMapKey; use serenity::prelude::TypeMapKey;
use std::{io::BufWriter, sync::Arc};
pub struct ImageGenerator<'a> { pub struct ImageGenerator<'a> {
pub player_font: Font<'a>, pub player_font: Font<'a>,
pub text_font: Font<'a>, pub text_font: Font<'a>,
pub teams_image: image::ImageBuffer<image::Rgb<u8>, Vec<u8>> pub teams_image: image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
} }
impl<'a> ImageGenerator<'a> { impl<'a> ImageGenerator<'a> {
pub fn draw_teams(&self, player_names: Vec<String>, teams_rating: [i32; 2]) -> Vec<u8> { pub fn draw_teams_to_png(&self, player_names: Vec<String>, teams_rating: [i32; 2]) -> Vec<u8> {
let mut image = self.teams_image.clone(); let mut image: image::ImageBuffer<image::Rgb<u8>, Vec<u8>> = self.teams_image.clone();
let player_text_scale = Scale::uniform(60.0); let player_text_scale = Scale::uniform(60.0);
for i in 0..2 { for i in 0..2 {
for j in 0..5 { for j in 0..5 {
let player_name = match player_names.get(i * 5 + j) { let player_name = match player_names.get(i * 5 + j) {
Some(name) => name, Some(name) => name,
None => "Unknown" None => "Unknown",
}; };
let size = text_size(player_text_scale, &self.player_font, player_name); 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 size = text_size(scale, &self.player_font, player_name);
let x: i32 = 83 + 540 * i as i32 - 2; 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( imageproc::drawing::draw_text_mut(
&mut image, &mut image,
@@ -41,7 +41,7 @@ impl<'a> ImageGenerator<'a> {
y, y,
scale, scale,
&self.player_font, &self.player_font,
player_name player_name,
); );
} }
} }
@@ -54,7 +54,7 @@ impl<'a> ImageGenerator<'a> {
imageproc::drawing::draw_text_mut( imageproc::drawing::draw_text_mut(
&mut image, &mut image,
image::Rgb([255, 255, 255]), image::Rgb([255, 255, 255]),
365 - size.0 / 2 + 540 * i as i32, 370 - size.0 / 2 + 540 * i as i32,
100 - size.1, 100 - size.1,
rating_text_scale, rating_text_scale,
&self.text_font, &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()
} }
} }

View File

@@ -5,6 +5,7 @@ use crate::database::models::role::Role;
use crate::mixer::player::Player; use crate::mixer::player::Player;
use crate::mixer::team::Team; use crate::mixer::team::Team;
#[derive(Debug)]
struct PlayerRoleEntry { struct PlayerRoleEntry {
pub index: usize, pub index: usize,
pub role: Role, pub role: Role,
@@ -64,7 +65,7 @@ pub fn mix_players(players: &[Player], slots: Vec<Role>) -> Option<(Team, Team)>
let mut best_team2 = None; let mut best_team2 = None;
let mut best_diff = None; let mut best_diff = None;
let threshold = 100.0; let threshold = 150.0;
// this is awful, but it works // this is awful, but it works
for tank1_combo in &tank_combos { for tank1_combo in &tank_combos {
@@ -192,7 +193,7 @@ pub fn mix_players(players: &[Player], slots: Vec<Role>) -> Option<(Team, Team)>
.abs(); .abs();
let diff = diff_rating; let diff = diff_rating;
if diff + threshold < best_diff.unwrap_or(f32::MAX) { if diff < best_diff.unwrap_or(f32::MAX) {
if diff < threshold { if diff < threshold {
return Some((team1, team2)); return Some((team1, team2));
} }