Add colors update
This commit is contained in:
Generated
+1057
-19
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -1,17 +1,20 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "payphone"
|
name = "payphone"
|
||||||
version = "4.3.4"
|
version = "4.3.6"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
default-run = "payphone"
|
default-run = "payphone"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.99"
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
clap = { version = "4.5.42", features = ["derive", "env", "string"] }
|
clap = { version = "4.5.42", features = ["derive", "env", "string"] }
|
||||||
|
color-thief = "0.2.2"
|
||||||
colored = "3.0.0"
|
colored = "3.0.0"
|
||||||
csv = "1.3.1"
|
csv = "1.3.1"
|
||||||
deadpool-redis = { version = "0.22.0", features = ["cluster", "cluster-async"] }
|
deadpool-redis = { version = "0.22.0", features = ["cluster", "cluster-async"] }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
|
image = "0.25.6"
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
linkify = "0.10.0"
|
linkify = "0.10.0"
|
||||||
@@ -22,6 +25,8 @@ open = "5.3.2"
|
|||||||
rand = "0.9.2"
|
rand = "0.9.2"
|
||||||
rand_chacha = { version = "0.9.0", features = ["os_rng"] }
|
rand_chacha = { version = "0.9.0", features = ["os_rng"] }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
reqwest = { version = "0.12.23", features = ["gzip", "brotli", "zstd"] }
|
||||||
|
rgb = "0.8.52"
|
||||||
scc = "2.3.4"
|
scc = "2.3.4"
|
||||||
scylla = { version = "1.3.1", features = ["time-03"] }
|
scylla = { version = "1.3.1", features = ["time-03"] }
|
||||||
serde_json = "1.0.143"
|
serde_json = "1.0.143"
|
||||||
|
|||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
use std::{io::Cursor, sync::Arc};
|
||||||
|
|
||||||
|
use color_thief::ColorFormat;
|
||||||
|
use image::{GenericImageView, ImageReader};
|
||||||
|
use reqwest::header::USER_AGENT;
|
||||||
|
use rgb::Rgb;
|
||||||
|
use tokio::sync::{Semaphore, mpsc::Receiver};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
constants::VERSION, database::models::User,
|
||||||
|
discord::images::get_user_custom_avatar_url_or_none, state::State,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct AvatarColorFetchingRequest {
|
||||||
|
pub hash: String,
|
||||||
|
pub user_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AvatarColorFetchingRequest {
|
||||||
|
pub fn new(hash: impl ToString, user_id: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
hash: hash.to_string(),
|
||||||
|
user_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_color_fetching_queue_listener(
|
||||||
|
mut receiver: Receiver<AvatarColorFetchingRequest>,
|
||||||
|
state: Arc<State>,
|
||||||
|
) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let semaphore = Arc::new(Semaphore::new(5));
|
||||||
|
|
||||||
|
while let Some(request) = receiver.recv().await {
|
||||||
|
let task_semaphore = semaphore.clone();
|
||||||
|
let task_state = state.clone();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let _permit = task_semaphore
|
||||||
|
.acquire()
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| log::error!("Failed to acquire semaphore permit: {e}"))
|
||||||
|
.expect("Failed to acquire semaphore permit");
|
||||||
|
|
||||||
|
let avatar_url = get_user_custom_avatar_url_or_none(request.user_id, request.hash);
|
||||||
|
|
||||||
|
let image_bytes = reqwest::Client::new()
|
||||||
|
.get(avatar_url)
|
||||||
|
.header(USER_AGENT, format!("Payphone/v{VERSION}"))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| log::error!("Failed to fetch avatar color: {e}"))?
|
||||||
|
.error_for_status()?
|
||||||
|
.bytes()
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| log::error!("Failed to read avatar image bytes: {e}"))?;
|
||||||
|
|
||||||
|
let color = tokio::task::spawn_blocking(move || {
|
||||||
|
let img = ImageReader::new(Cursor::new(image_bytes))
|
||||||
|
.with_guessed_format()?
|
||||||
|
.decode()?;
|
||||||
|
|
||||||
|
let mut pixels = Vec::new();
|
||||||
|
|
||||||
|
for (_x, _y, rgba) in img.pixels() {
|
||||||
|
pixels.extend_from_slice(&rgba.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(img);
|
||||||
|
|
||||||
|
let palette = color_thief::get_palette(&pixels, ColorFormat::Rgba, 1, 2)?;
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(palette.first().cloned())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
if let Some(color) = color {
|
||||||
|
let Rgb { r, g, b } = color;
|
||||||
|
|
||||||
|
let color_int: u32 = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
|
||||||
|
|
||||||
|
let mut user = User::get_by_id(request.user_id, &task_state.db)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
log::error!("Failed to fetch user to set their new avatar color: {e}")
|
||||||
|
})?
|
||||||
|
.ok_or(anyhow::anyhow!(
|
||||||
|
"User with ID {} not found when fetching their avatar URL color",
|
||||||
|
request.user_id
|
||||||
|
))
|
||||||
|
.inspect_err(|e| log::error!("{e}"))?;
|
||||||
|
|
||||||
|
user.color = Some(color_int as i32);
|
||||||
|
|
||||||
|
user.insert(&task_state.db).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
//! Constants used throughout the Payphone project.
|
//! Constants used throughout the Payphone project.
|
||||||
|
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
/// The default command prefix used by the bot for new servers.
|
/// The default command prefix used by the bot for new servers.
|
||||||
pub const DEFAULT_PREFIX: &str = "p.";
|
pub const DEFAULT_PREFIX: &str = "p.";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
ALTER TABLE payphone.users ADD avatar_hash TEXT;
|
||||||
|
ALTER TABLE payphone.users ADD color INT;
|
||||||
|
ALTER TABLE payphone.users ADD is_color_set BOOLEAN;
|
||||||
|
|
||||||
|
CREATE MATERIALIZED VIEW payphone.users_with_no_color AS
|
||||||
|
SELECT user_id, avatar_hash, is_color_set FROM payphone.users
|
||||||
|
WHERE user_id IS NOT NULL AND is_color_set IS NOT NULL
|
||||||
|
PRIMARY KEY (is_color_set, user_id);
|
||||||
+54
-5
@@ -16,6 +16,7 @@ use scylla::{
|
|||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset};
|
use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset};
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
use twilight_http::Client;
|
use twilight_http::Client;
|
||||||
use twilight_model::{
|
use twilight_model::{
|
||||||
channel::{Message as TwilightMessage, Webhook},
|
channel::{Message as TwilightMessage, Webhook},
|
||||||
@@ -25,9 +26,10 @@ use twilight_model::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
colors::AvatarColorFetchingRequest,
|
||||||
constants::DEFAULT_PREFIX,
|
constants::DEFAULT_PREFIX,
|
||||||
groups::calls::masks::{get_mask_avatar_url, get_mask_display_name, get_random_mask},
|
groups::calls::masks::{get_mask_avatar_url, get_mask_display_name, get_random_mask},
|
||||||
utils::{get_random_u64, i64_to_u64, u64_to_i64},
|
utils::{Bye, get_random_u64, i64_to_u64, u64_to_i64},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
@@ -105,6 +107,8 @@ pub struct User {
|
|||||||
pub supporter_until: Option<UtcDateTime>,
|
pub supporter_until: Option<UtcDateTime>,
|
||||||
pub is_banned_from_leaderboard: Option<bool>,
|
pub is_banned_from_leaderboard: Option<bool>,
|
||||||
pub findings: HashSet<String>,
|
pub findings: HashSet<String>,
|
||||||
|
pub avatar_hash: Option<String>,
|
||||||
|
pub color: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Object<'a> for User {
|
impl<'a> Object<'a> for User {
|
||||||
@@ -120,6 +124,9 @@ impl<'a> Object<'a> for User {
|
|||||||
Option<OffsetDateTime>,
|
Option<OffsetDateTime>,
|
||||||
Option<bool>,
|
Option<bool>,
|
||||||
HashSet<String>,
|
HashSet<String>,
|
||||||
|
Option<String>,
|
||||||
|
Option<i32>,
|
||||||
|
Option<bool>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn from_row(row: Self::Row) -> Self {
|
fn from_row(row: Self::Row) -> Self {
|
||||||
@@ -135,6 +142,8 @@ impl<'a> Object<'a> for User {
|
|||||||
supporter_until: row.8.map(|i| i.into()),
|
supporter_until: row.8.map(|i| i.into()),
|
||||||
is_banned_from_leaderboard: row.9,
|
is_banned_from_leaderboard: row.9,
|
||||||
findings: row.10,
|
findings: row.10,
|
||||||
|
avatar_hash: row.11,
|
||||||
|
color: row.12,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +160,9 @@ impl<'a> Object<'a> for User {
|
|||||||
value.supporter_until.map(|i| i.into()),
|
value.supporter_until.map(|i| i.into()),
|
||||||
Some(value.is_banned_from_leaderboard.unwrap_or(false)),
|
Some(value.is_banned_from_leaderboard.unwrap_or(false)),
|
||||||
value.findings,
|
value.findings,
|
||||||
|
value.avatar_hash,
|
||||||
|
value.color,
|
||||||
|
Some(value.color.is_some()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +190,10 @@ impl User {
|
|||||||
supporter_since,
|
supporter_since,
|
||||||
supporter_until,
|
supporter_until,
|
||||||
is_banned_from_leaderboard,
|
is_banned_from_leaderboard,
|
||||||
findings
|
findings,
|
||||||
|
avatar_hash,
|
||||||
|
color,
|
||||||
|
is_color_set
|
||||||
FROM
|
FROM
|
||||||
payphone.users
|
payphone.users
|
||||||
WHERE
|
WHERE
|
||||||
@@ -218,6 +233,7 @@ impl User {
|
|||||||
pub async fn create_or_update(
|
pub async fn create_or_update(
|
||||||
user: TwilightUser,
|
user: TwilightUser,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
|
sender: &Sender<AvatarColorFetchingRequest>,
|
||||||
) -> Result<Self, DatabaseError> {
|
) -> Result<Self, DatabaseError> {
|
||||||
let db_user = User::get_by_id(user.id.get(), session).await?;
|
let db_user = User::get_by_id(user.id.get(), session).await?;
|
||||||
|
|
||||||
@@ -225,10 +241,38 @@ impl User {
|
|||||||
db_user.last_seen_at = UtcDateTime::now();
|
db_user.last_seen_at = UtcDateTime::now();
|
||||||
db_user.name = user.name;
|
db_user.name = user.name;
|
||||||
|
|
||||||
|
if user.avatar.map(|hash| hash.to_string()) != db_user.avatar_hash {
|
||||||
|
db_user.avatar_hash = user.avatar.map(|hash| hash.to_string());
|
||||||
|
db_user.color = None;
|
||||||
|
|
||||||
|
if let Some(avatar_hash) = &db_user.avatar_hash {
|
||||||
|
sender
|
||||||
|
.send(AvatarColorFetchingRequest::new(avatar_hash, user.id.get()))
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
log::error!(
|
||||||
|
"Failed to send avatar color fetching request to queue! {e}"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.bye();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(db_user);
|
return Ok(db_user);
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_user = User::from(user);
|
let new_user = User::from(user);
|
||||||
|
|
||||||
|
if let Some(avatar_hash) = &new_user.avatar_hash {
|
||||||
|
sender
|
||||||
|
.send(AvatarColorFetchingRequest::new(avatar_hash, new_user.id))
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
log::error!("Failed to send avatar color fetching request to queue! {e}")
|
||||||
|
})
|
||||||
|
.bye();
|
||||||
|
}
|
||||||
|
|
||||||
new_user.insert(session).await?;
|
new_user.insert(session).await?;
|
||||||
|
|
||||||
Ok(new_user)
|
Ok(new_user)
|
||||||
@@ -249,8 +293,11 @@ impl User {
|
|||||||
supporter_since,
|
supporter_since,
|
||||||
supporter_until,
|
supporter_until,
|
||||||
is_banned_from_leaderboard,
|
is_banned_from_leaderboard,
|
||||||
findings
|
findings,
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
avatar_hash,
|
||||||
|
color,
|
||||||
|
is_color_set
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||||
"#,
|
"#,
|
||||||
Self::to_row(self.clone()),
|
Self::to_row(self.clone()),
|
||||||
)
|
)
|
||||||
@@ -338,6 +385,8 @@ impl From<TwilightUser> for User {
|
|||||||
supporter_until: None,
|
supporter_until: None,
|
||||||
is_banned_from_leaderboard: None,
|
is_banned_from_leaderboard: None,
|
||||||
findings: HashSet::new(),
|
findings: HashSet::new(),
|
||||||
|
avatar_hash: value.avatar.map(|a| a.to_string()),
|
||||||
|
color: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1865,7 +1914,7 @@ impl<'a> Object<'a> for Participant {
|
|||||||
|
|
||||||
fn make_name_valid(name: String) -> String {
|
fn make_name_valid(name: String) -> String {
|
||||||
if name.len() == 1 {
|
if name.len() == 1 {
|
||||||
format!("{name}\u{2800}")
|
format!("{name}⠀⠀") // Add two invisible characters
|
||||||
} else if name.len() > 80 {
|
} else if name.len() > 80 {
|
||||||
format!("{name:.80}")
|
format!("{name:.80}")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+19
-2
@@ -13,10 +13,17 @@ use twilight_model::util::ImageHash;
|
|||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
/// [`String`] - The URL of the user's avatar.
|
/// [`String`] - The URL of the user's avatar.
|
||||||
pub fn get_avatar_url(hash: Option<ImageHash>, user_id: u64, discriminator: u64, guild_id: Option<u64>) -> String {
|
pub fn get_avatar_url(
|
||||||
|
hash: Option<ImageHash>,
|
||||||
|
user_id: u64,
|
||||||
|
discriminator: u64,
|
||||||
|
guild_id: Option<u64>,
|
||||||
|
) -> String {
|
||||||
if let Some(hash) = hash {
|
if let Some(hash) = hash {
|
||||||
if let Some(guild_id) = guild_id {
|
if let Some(guild_id) = guild_id {
|
||||||
return format!("https://cdn.discordapp.com/guilds/{guild_id}/users/{user_id}/avatars/{hash}.webp?size=256");
|
return format!(
|
||||||
|
"https://cdn.discordapp.com/guilds/{guild_id}/users/{user_id}/avatars/{hash}.webp?size=256"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("https://cdn.discordapp.com/avatars/{user_id}/{hash}.webp?size=256")
|
format!("https://cdn.discordapp.com/avatars/{user_id}/{hash}.webp?size=256")
|
||||||
@@ -30,3 +37,13 @@ pub fn get_avatar_url(hash: Option<ImageHash>, user_id: u64, discriminator: u64,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the user's avatar URL only if it's custom (i.e. not the default avatar). It doesn't
|
||||||
|
/// take into account custom per-server avatars, i.e. only the global avatar is returned if any.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// * `user_id` - The user's ID.
|
||||||
|
/// * `hash` - The hex-encoded hash string, if any.
|
||||||
|
pub fn get_user_custom_avatar_url_or_none(user_id: u64, hash: String) -> String {
|
||||||
|
format!("https://cdn.discordapp.com/avatars/{user_id}/{hash}.webp?size=256")
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,13 +119,13 @@ where
|
|||||||
E: 'static + Send + Sync,
|
E: 'static + Send + Sync,
|
||||||
{
|
{
|
||||||
pub fn new(
|
pub fn new(
|
||||||
state: T,
|
state: Arc<T>,
|
||||||
prefixes: impl PrefixGetter<T, E> + Send + Sync + 'static,
|
prefixes: impl PrefixGetter<T, E> + Send + Sync + 'static,
|
||||||
extras: impl ExtrasGetter<T, Extras = E> + 'static,
|
extras: impl ExtrasGetter<T, Extras = E> + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
shard: ShardId::new(0, 1),
|
shard: ShardId::new(0, 1),
|
||||||
state: Arc::new(state),
|
state,
|
||||||
presence: None,
|
presence: None,
|
||||||
prefixes: Arc::new(prefixes),
|
prefixes: Arc::new(prefixes),
|
||||||
intents: Intents::empty(),
|
intents: Intents::empty(),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use twilight_util::builder::embed::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{COLOR_RED, COLOR_WHITE},
|
constants::{COLOR_RED, COLOR_WHITE, VERSION},
|
||||||
discord::{images, sanitizing::SanitizeDiscordMarkdown},
|
discord::{images, sanitizing::SanitizeDiscordMarkdown},
|
||||||
extras::Extras,
|
extras::Extras,
|
||||||
framework::{
|
framework::{
|
||||||
@@ -106,7 +106,7 @@ pub(super) fn get_all_command_help_embed(
|
|||||||
.unwrap())
|
.unwrap())
|
||||||
.color(COLOR_WHITE)
|
.color(COLOR_WHITE)
|
||||||
.field(EmbedFieldBuilder::new("Commands", get_commands_table(rt, extras)).build())
|
.field(EmbedFieldBuilder::new("Commands", get_commands_table(rt, extras)).build())
|
||||||
.footer(EmbedFooterBuilder::new(format!("v{} - Use {}help <command> to get more information about a specific command.", env!("CARGO_PKG_VERSION"), prefix)))
|
.footer(EmbedFooterBuilder::new(format!("v{VERSION} - Use {}help <command> to get more information about a specific command.", prefix)))
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use time::{Duration, UtcDateTime};
|
|||||||
use twilight_http::{api_error::ApiError, error::ErrorType};
|
use twilight_http::{api_error::ApiError, error::ErrorType};
|
||||||
use twilight_model::{
|
use twilight_model::{
|
||||||
gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate},
|
gateway::payload::incoming::{MessageCreate, MessageDelete, MessageUpdate},
|
||||||
guild,
|
|
||||||
id::Id,
|
id::Id,
|
||||||
};
|
};
|
||||||
use twilight_util::{
|
use twilight_util::{
|
||||||
@@ -165,7 +164,12 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut user = User::create_or_update(event.author.clone(), &rt.state.db).await?;
|
let mut user = User::create_or_update(
|
||||||
|
event.author.clone(),
|
||||||
|
&rt.state.db,
|
||||||
|
&rt.state.color_queue_sender,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let user_incidents = UserIncident::get_for_user(event.author.id.get(), &rt.state.db)
|
let user_incidents = UserIncident::get_for_user(event.author.id.get(), &rt.state.db)
|
||||||
.await?
|
.await?
|
||||||
@@ -307,18 +311,18 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
.zip(replying_to.data.propagation_message_id)
|
.zip(replying_to.data.propagation_message_id)
|
||||||
{
|
{
|
||||||
let (display_name, avatar_url) =
|
let (display_name, avatar_url) =
|
||||||
if let Some(mask) = replying_to.data.author_mask {
|
if let Some(mask) = &replying_to.data.author_mask {
|
||||||
(
|
(
|
||||||
if i64_to_u64(propagation_channel_id) != event.channel_id.get() {
|
if i64_to_u64(propagation_channel_id) != event.channel_id.get() {
|
||||||
get_mask_display_name(&mask)
|
get_mask_display_name(mask)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{} ({})",
|
"{} ({})",
|
||||||
get_mask_display_name(&mask),
|
get_mask_display_name(mask),
|
||||||
replying_to.data.author_name
|
replying_to.data.author_name
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
get_mask_avatar_url(&mask).expect("Failed to get mask avatar URL"),
|
get_mask_avatar_url(mask).expect("Failed to get mask avatar URL"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
@@ -339,6 +343,9 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let replying_to_user =
|
||||||
|
User::get_by_id(replying_to.author_id, &rt.state.db).await?;
|
||||||
|
|
||||||
embeds.push(
|
embeds.push(
|
||||||
EmbedBuilder::new()
|
EmbedBuilder::new()
|
||||||
.author(
|
.author(
|
||||||
@@ -346,7 +353,15 @@ impl OnMessageHandler<State, Extras> for CallMessageHandler {
|
|||||||
.icon_url(ImageSource::url(avatar_url).unwrap())
|
.icon_url(ImageSource::url(avatar_url).unwrap())
|
||||||
.url(message_link),
|
.url(message_link),
|
||||||
)
|
)
|
||||||
.color(COLOR_WHITE)
|
.color(
|
||||||
|
if replying_to.data.author_mask.is_some() {
|
||||||
|
COLOR_WHITE
|
||||||
|
} else {
|
||||||
|
replying_to_user
|
||||||
|
.and_then(|i| i.color.map(|c| c as u32))
|
||||||
|
.unwrap_or(COLOR_WHITE)
|
||||||
|
}
|
||||||
|
)
|
||||||
.description(ellipse(replying_to.data.content, 100))
|
.description(ellipse(replying_to.data.content, 100))
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
@@ -508,7 +523,12 @@ impl OnMessageEditHandler<State, Extras> for CallMessageEditHandler {
|
|||||||
.reply(event.id)
|
.reply(event.id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let user = User::create_or_update(event.author.clone(), &rt.state.db).await?;
|
let user = User::create_or_update(
|
||||||
|
event.author.clone(),
|
||||||
|
&rt.state.db,
|
||||||
|
&rt.state.color_queue_sender,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
penalize_user(user, message, rules, &rt).await?;
|
penalize_user(user, message, rules, &rt).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ ngiga
|
|||||||
ngigas
|
ngigas
|
||||||
negaras
|
negaras
|
||||||
nigy
|
nigy
|
||||||
|
nyga
|
||||||
diga
|
diga
|
||||||
digas
|
digas
|
||||||
fag
|
fag
|
||||||
|
|||||||
+18
-11
@@ -105,7 +105,7 @@ use std::{
|
|||||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||||
path::Path,
|
path::Path,
|
||||||
process::Command,
|
process::Command,
|
||||||
sync::mpsc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@@ -126,7 +126,8 @@ use twilight_model::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cli::{Args, Commands},
|
cli::{Args, Commands},
|
||||||
constants::DEFAULT_PREFIX,
|
colors::init_color_fetching_queue_listener,
|
||||||
|
constants::{DEFAULT_PREFIX, VERSION},
|
||||||
database::models::{Server, User},
|
database::models::{Server, User},
|
||||||
extras::Extras,
|
extras::Extras,
|
||||||
framework::{
|
framework::{
|
||||||
@@ -137,7 +138,9 @@ use crate::{
|
|||||||
groups::{
|
groups::{
|
||||||
about::{commands::HelpCommand, components::HelpGoBackButton},
|
about::{commands::HelpCommand, components::HelpGoBackButton},
|
||||||
calls::{
|
calls::{
|
||||||
commands::{CallCommand, FriendRequestCommand, HangupCommand, MaskCommand, SkipCommand},
|
commands::{
|
||||||
|
CallCommand, FriendRequestCommand, HangupCommand, MaskCommand, SkipCommand,
|
||||||
|
},
|
||||||
events::{CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler},
|
events::{CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler},
|
||||||
},
|
},
|
||||||
gateway::events::{ReadyHandler, ReconnectHandler},
|
gateway::events::{ReadyHandler, ReconnectHandler},
|
||||||
@@ -152,6 +155,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
mod colors;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod database;
|
mod database;
|
||||||
mod discord;
|
mod discord;
|
||||||
@@ -241,7 +245,8 @@ impl ExtrasGetter<State> for PayphoneExtrasGetter {
|
|||||||
) -> Result<Self::Extras, Box<dyn Error>> {
|
) -> Result<Self::Extras, Box<dyn Error>> {
|
||||||
log::trace!("Getting extras!");
|
log::trace!("Getting extras!");
|
||||||
|
|
||||||
let user = User::create_or_update(user.clone(), &rt.state.db).await?;
|
let user = User::create_or_update(user.clone(), &rt.state.db, &rt.state.color_queue_sender)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(Extras { user })
|
Ok(Extras { user })
|
||||||
}
|
}
|
||||||
@@ -258,10 +263,7 @@ async fn start(
|
|||||||
bot_owner_id: u64,
|
bot_owner_id: u64,
|
||||||
) {
|
) {
|
||||||
log::info!("Welcome to Payphone, Nyeki!");
|
log::info!("Welcome to Payphone, Nyeki!");
|
||||||
log::info!(
|
log::info!("Payphone is running on version v{VERSION}");
|
||||||
"Payphone is running on version v{}",
|
|
||||||
env!("CARGO_PKG_VERSION")
|
|
||||||
);
|
|
||||||
|
|
||||||
let db = database::init(database_addr, database_username, database_password)
|
let db = database::init(database_addr, database_username, database_password)
|
||||||
.await
|
.await
|
||||||
@@ -273,11 +275,16 @@ async fn start(
|
|||||||
|
|
||||||
log::info!("Redis connection established successfully.");
|
log::info!("Redis connection established successfully.");
|
||||||
|
|
||||||
let state = State {
|
let (color_queue_sender, color_queue_receiver) = tokio::sync::mpsc::channel(100);
|
||||||
|
|
||||||
|
let state = Arc::new(State {
|
||||||
db,
|
db,
|
||||||
redis: redis_pool,
|
redis: redis_pool,
|
||||||
bot_owner_id,
|
bot_owner_id,
|
||||||
};
|
color_queue_sender,
|
||||||
|
});
|
||||||
|
|
||||||
|
init_color_fetching_queue_listener(color_queue_receiver, state.clone());
|
||||||
|
|
||||||
let bot = Bot::new(state, ServerPrefixOrPayphoneMention, PayphoneExtrasGetter)
|
let bot = Bot::new(state, ServerPrefixOrPayphoneMention, PayphoneExtrasGetter)
|
||||||
.shard(shard)
|
.shard(shard)
|
||||||
@@ -352,7 +359,7 @@ async fn docs(bind: SocketAddr, open_on_start: bool) {
|
|||||||
.expect("Failed to run `cargo doc`");
|
.expect("Failed to run `cargo doc`");
|
||||||
|
|
||||||
tokio::spawn(async {
|
tokio::spawn(async {
|
||||||
let (tx, rx) = mpsc::channel::<notify::Result<Event>>();
|
let (tx, rx) = std::sync::mpsc::channel::<notify::Result<Event>>();
|
||||||
|
|
||||||
let mut watcher = notify::recommended_watcher(tx).expect("Failed to create watcher");
|
let mut watcher = notify::recommended_watcher(tx).expect("Failed to create watcher");
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,13 @@
|
|||||||
|
|
||||||
use deadpool_redis::cluster::Pool;
|
use deadpool_redis::cluster::Pool;
|
||||||
use scylla::client::session::Session;
|
use scylla::client::session::Session;
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
use crate::colors::AvatarColorFetchingRequest;
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub db: Session,
|
pub db: Session,
|
||||||
pub redis: Pool,
|
pub redis: Pool,
|
||||||
pub bot_owner_id: u64,
|
pub bot_owner_id: u64,
|
||||||
|
pub color_queue_sender: Sender<AvatarColorFetchingRequest>,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user