Recreate webhooks when they go missing, reuse older webhooks, and initiate connections to Redis cluster

This commit is contained in:
2025-08-20 23:20:21 -03:00
parent ee49b0a984
commit aba11cb2da
15 changed files with 553 additions and 34 deletions
Generated
+301 -1
View File
@@ -363,7 +363,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core",
"memchr", "memchr",
"pin-project-lite",
"tokio",
"tokio-util",
] ]
[[package]] [[package]]
@@ -382,6 +386,12 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crc16"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff"
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@@ -467,6 +477,36 @@ dependencies = [
"parking_lot_core", "parking_lot_core",
] ]
[[package]]
name = "deadpool"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f"
dependencies = [
"deadpool-runtime",
"num_cpus",
"tokio",
]
[[package]]
name = "deadpool-redis"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0965b977f1244bc3783bb27cd79cfcff335a8341da18f79232d00504b18eb1a"
dependencies = [
"deadpool",
"redis",
]
[[package]]
name = "deadpool-runtime"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b"
dependencies = [
"tokio",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.4.0" version = "0.4.0"
@@ -476,6 +516,17 @@ dependencies = [
"powerfmt", "powerfmt",
] ]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@@ -726,6 +777,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "http" name = "http"
version = "1.3.1" version = "1.3.1"
@@ -861,12 +918,119 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "icu_collections"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"potential_utf",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
"icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]] [[package]]
name = "ident_case" name = "ident_case"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]] [[package]]
name = "include_dir" name = "include_dir"
version = "0.7.4" version = "0.7.4"
@@ -1045,6 +1209,12 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "litemap"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.13" version = "0.4.13"
@@ -1146,12 +1316,31 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@@ -1161,6 +1350,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]] [[package]]
name = "num_threads" name = "num_threads"
version = "0.1.7" version = "0.1.7"
@@ -1238,13 +1437,14 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]] [[package]]
name = "payphone" name = "payphone"
version = "4.0.25" version = "4.0.26"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
"clap", "clap",
"colored", "colored",
"csv", "csv",
"deadpool-redis",
"dotenvy", "dotenvy",
"include_dir", "include_dir",
"itertools", "itertools",
@@ -1296,6 +1496,15 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "potential_utf"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
dependencies = [
"zerovec",
]
[[package]] [[package]]
name = "powerfmt" name = "powerfmt"
version = "0.2.0" version = "0.2.0"
@@ -1373,6 +1582,31 @@ dependencies = [
"rand_core", "rand_core",
] ]
[[package]]
name = "redis"
version = "0.32.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd3650deebc68526b304898b192fa4102a4ef0b9ada24da096559cb60e0eef8"
dependencies = [
"bytes",
"cfg-if",
"combine",
"crc16",
"futures-sink",
"futures-util",
"itoa",
"log",
"num-bigint",
"percent-encoding",
"pin-project-lite",
"rand",
"ryu",
"socket2 0.6.0",
"tokio",
"tokio-util",
"url",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.5.17" version = "0.5.17"
@@ -1942,6 +2176,16 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tinystr"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.47.1" version = "1.47.1"
@@ -2256,6 +2500,23 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@@ -2706,6 +2967,12 @@ dependencies = [
"bitflags 2.9.1", "bitflags 2.9.1",
] ]
[[package]]
name = "writeable"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]] [[package]]
name = "yoke" name = "yoke"
version = "0.8.0" version = "0.8.0"
@@ -2776,3 +3043,36 @@ name = "zeroize"
version = "1.8.1" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]]
name = "zerotrie"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
+2 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "payphone" name = "payphone"
version = "4.0.25" version = "4.0.26"
edition = "2024" edition = "2024"
default-run = "payphone" default-run = "payphone"
@@ -10,6 +10,7 @@ axum = "0.8.4"
clap = { version = "4.5.42", features = ["derive", "env", "string"] } clap = { version = "4.5.42", features = ["derive", "env", "string"] }
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"] }
dotenvy = "0.15.7" dotenvy = "0.15.7"
include_dir = "0.7.4" include_dir = "0.7.4"
itertools = "0.14.0" itertools = "0.14.0"
+36
View File
@@ -31,6 +31,36 @@ services:
scylla-net: scylla-net:
ipv4_address: 172.20.0.4 ipv4_address: 172.20.0.4
redis-node-1:
image: redis:latest
container_name: redis-node-1
command: redis-server --port 7000 --cluster-enabled yes --cluster-config-file nodes.conf --appendonly yes
ports:
- "7000:7000"
networks:
redis-net:
ipv4_address: 172.20.1.2
redis-node-2:
image: redis:latest
container_name: redis-node-2
command: redis-server --port 7001 --cluster-enabled yes --cluster-config-file nodes.conf --appendonly yes
ports:
- "7001:7001"
networks:
redis-net:
ipv4_address: 172.20.1.3
redis-node-3:
image: redis:latest
container_name: redis-node-3
command: redis-server --port 7002 --cluster-enabled yes --cluster-config-file nodes.conf --appendonly yes
ports:
- "7002:7002"
networks:
redis-net:
ipv4_address: 172.20.1.4
volumes: volumes:
scylla-data-1: scylla-data-1:
scylla-data-2: scylla-data-2:
@@ -42,3 +72,9 @@ networks:
ipam: ipam:
config: config:
- subnet: 172.20.0.0/24 - subnet: 172.20.0.0/24
redis-net:
driver: bridge
ipam:
config:
- subnet: 172.20.1.0/24
+9
View File
@@ -212,6 +212,15 @@ pub enum Commands {
default_value_t = Shard::default()) default_value_t = Shard::default())
] ]
shard: Shard, shard: Shard,
/// The Redis URLs to connect to.
#[arg(
long,
env = "PAYPHONE_REDIS_URLS",
value_name = "URLS",
default_values = &["redis://127.0.0.1:6379"],
)]
redis_urls: Vec<String>,
}, },
/// Starts a documentation server for the project which serves `cargo doc` and refreshes on /// Starts a documentation server for the project which serves `cargo doc` and refreshes on
+51 -10
View File
@@ -13,7 +13,9 @@ use thiserror::Error;
use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset}; use time::{Duration, OffsetDateTime, UtcDateTime, UtcOffset};
use twilight_http::Client; use twilight_http::Client;
use twilight_model::{ use twilight_model::{
channel::Message as TwilightMessage, guild::Guild as TwilightGuild, id::Id, channel::{Message as TwilightMessage, Webhook},
guild::Guild as TwilightGuild,
id::Id,
user::User as TwilightUser, user::User as TwilightUser,
}; };
@@ -478,6 +480,38 @@ impl<'a> Object<'a> for Channel {
} }
} }
async fn get_or_create_channel_webhook(
channel_id: u64,
http: &Client,
) -> Result<Webhook, DatabaseOrDiscordError> {
let webhooks = http
.channel_webhooks(Id::new(channel_id))
.await
.inspect_err(|e| log::error!("Failed to get webhooks from channel {channel_id}: {e:?}"))
.map_err(DatabaseOrDiscordError::Discord)?
.model()
.await
.inspect_err(|e| log::error!("Failed to get model for webhooks {channel_id}: {e:?}"))
.map_err(|_| DatabaseOrDiscordError::DiscordDeserializationFailed)?;
let webhook = if let Some(item) = webhooks.into_iter().find(|item| item.token.is_some()) {
item
} else {
http.create_webhook(Id::new(channel_id), "Payphone Messages")
.await
.inspect_err(|e| {
log::error!("Failed to create webhook for channel {channel_id}: {e:?}")
})
.map_err(DatabaseOrDiscordError::Discord)?
.model()
.await
.inspect_err(|e| log::error!("Failed to get model for webhook {channel_id}: {e:?}"))
.map_err(|_| DatabaseOrDiscordError::DiscordDeserializationFailed)?
};
Ok(webhook)
}
impl Channel { impl Channel {
pub async fn get_by_id(id: u64, session: &Session) -> Result<Option<Self>, DatabaseError> { pub async fn get_by_id(id: u64, session: &Session) -> Result<Option<Self>, DatabaseError> {
let id = u64_to_i64(id); let id = u64_to_i64(id);
@@ -520,15 +554,7 @@ impl Channel {
{ {
Ok(channel) Ok(channel)
} else { } else {
let webhook = http let webhook = get_or_create_channel_webhook(id, http).await?;
.create_webhook(Id::new(id), "Payphone Messages")
.await
.inspect_err(|e| log::error!("Failed to create webhook for channel {id}: {e:?}"))
.map_err(DatabaseOrDiscordError::Discord)?
.model()
.await
.inspect_err(|e| log::error!("Failed to get model for webhook {id}: {e:?}"))
.map_err(|_| DatabaseOrDiscordError::DiscordDeserializationFailed)?;
let response = http let response = http
.channel(Id::new(id)) .channel(Id::new(id))
@@ -593,6 +619,21 @@ impl Channel {
Ok(()) Ok(())
} }
pub async fn update_webhook(
&mut self,
session: &Session,
http: &Client,
) -> Result<(), DatabaseOrDiscordError> {
let webhook = get_or_create_channel_webhook(self.id, http).await?;
self.webhook_id = Some(webhook.id.get());
self.webhook_token = webhook.token;
self.upsert(session).await?;
Ok(())
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
+32 -7
View File
@@ -2,6 +2,7 @@ use std::{error::Error, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use itertools::Itertools; use itertools::Itertools;
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},
id::Id, id::Id,
@@ -151,7 +152,7 @@ impl OnMessageHandler<State> for CallMessageHandler {
return Ok(()); return Ok(());
} }
let Some(opposite_channel) = Channel::get_by_id( let Some(mut opposite_channel) = Channel::get_by_id(
call.other_party_channel_id(event.channel_id.get()), call.other_party_channel_id(event.channel_id.get()),
&rt.state.db, &rt.state.db,
) )
@@ -167,7 +168,7 @@ impl OnMessageHandler<State> for CallMessageHandler {
let Some((webhook_id, webhook_token)) = opposite_channel let Some((webhook_id, webhook_token)) = opposite_channel
.webhook_id .webhook_id
.zip(opposite_channel.webhook_token) .zip(opposite_channel.webhook_token.clone())
else { else {
// Handle DM calls in the future. // Handle DM calls in the future.
return Ok(()); return Ok(());
@@ -202,14 +203,14 @@ impl OnMessageHandler<State> for CallMessageHandler {
rt.http rt.http
.create_message(event.channel_id) .create_message(event.channel_id)
.content( .content(
"❌ Your message has been blocked because it broke Payphone's code of conduct. Check your DMs for more information.", "🚩 Your message has been blocked because it broke Payphone's code of conduct. Check your DMs for more information.",
) )
.await?; .await?;
rt.http rt.http
.create_message(Id::new(opposite_channel.id)) .create_message(Id::new(opposite_channel.id))
.content(&format!( .content(&format!(
"❌ **{}** Sent a message that was blocked.\n-# If you see an inappropiate message, please report it using `Message > Apps > Report Message`.", "🚩 **{}** Sent a message that was blocked.\n-# If you see an inappropiate message, please report it using `Message > Apps > Report Message`.",
profile.name.sanitize(false) profile.name.sanitize(false)
)) ))
.await?; .await?;
@@ -241,7 +242,7 @@ impl OnMessageHandler<State> for CallMessageHandler {
); );
} }
let message_sent = rt let message_sent = match rt
.http .http
.execute_webhook(Id::new(webhook_id), &webhook_token) .execute_webhook(Id::new(webhook_id), &webhook_token)
.allowed_mentions(None) .allowed_mentions(None)
@@ -257,7 +258,24 @@ impl OnMessageHandler<State> for CallMessageHandler {
call.id, call.id,
opposite_channel.id opposite_channel.id
); );
})? }) {
Ok(message) => message,
Err(e) => {
if let ErrorType::Response {
error: ApiError::General(error),
..
} = e.kind()
{
if error.code == 10015 {
log::debug!("Webhook not found, updating...");
log::trace!("Webhook ID before the update: {}", opposite_channel.webhook_id.unwrap_or(0));
opposite_channel.update_webhook(&rt.state.db, &rt.http).await?;
log::trace!("Webhook ID after the update: {}", opposite_channel.webhook_id.unwrap_or(0));
}
}
return Ok(());
}
}
.model() .model()
.await?; .await?;
@@ -306,6 +324,13 @@ pub struct CallMessageEditHandler;
#[async_trait] #[async_trait]
impl OnMessageEditHandler<State> for CallMessageEditHandler { impl OnMessageEditHandler<State> for CallMessageEditHandler {
async fn on_message_edit(&self, event: &MessageUpdate, rt: Arc<Runtime<State>>) -> EventResult { async fn on_message_edit(&self, event: &MessageUpdate, rt: Arc<Runtime<State>>) -> EventResult {
log::trace!(
"Edited message was '{}' sent by {} (bot? {:?})",
event.content,
event.author.id.get(),
event.author.bot
);
let Some(channel) = Channel::get_by_id(event.channel_id.get(), &rt.state.db).await? else { let Some(channel) = Channel::get_by_id(event.channel_id.get(), &rt.state.db).await? else {
return Ok(()); return Ok(());
}; };
@@ -346,7 +371,7 @@ impl OnMessageEditHandler<State> for CallMessageEditHandler {
rt.http rt.http
.create_message(event.channel_id) .create_message(event.channel_id)
.content( .content(
"❌ Your message's edit has been blocked because it broke Payphone's code of conduct. Check your DMs for more information.", "🚩 Your message's edit has been blocked because it broke Payphone's code of conduct. Check your DMs for more information.",
) )
.reply(event.id) .reply(event.id)
.await?; .await?;
@@ -1,5 +1,7 @@
sex sex
segs
esex esex
esegs
give head give head
gave head gave head
give me head give me head
@@ -35,7 +37,11 @@ stick it in yr pussy
cock cock
dick dick
dih dih
pussy pusy
posay
vagina
vgina
vgna
dck dck
psy psy
pu-c pu-c
@@ -92,6 +98,7 @@ masturbate
masturbating masturbating
masterbating masterbating
masterbaiting masterbaiting
masturbatigng
horny horny
hrny hrny
whore whore
@@ -8,6 +8,7 @@ qiga
niqa niqa
nqa nqa
nlger nlger
ngiga
digga digga
fag fag
fagot fagot
@@ -46,3 +47,4 @@ get the fck out
get tfo get tfo
blackie blackie
motherfucker motherfucker
chinese ape
@@ -12,6 +12,3 @@ rape u
slit slit
grape grape
graped graped
throat slit
slit your throat
slit ur throat
+80
View File
@@ -0,0 +1,80 @@
use std::sync::Arc;
use async_trait::async_trait;
use deadpool_redis::redis::{AsyncTypedCommands, IntegerReplyOrNoOp};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use time::{Date, Month, OffsetDateTime, Time};
use twilight_model::gateway::payload::incoming::MessageCreate;
use crate::{
database::models::Channel,
framework::{
events::traits::{EventResult, OnMessageHandler},
runtime::Runtime,
},
state::State,
};
const KEY_LEADERBOARD_MONTHLY: &str = "lvl:monthly";
fn seconds_until_end_of_month() -> i64 {
let now = OffsetDateTime::now_utc();
let year = now.year();
let month = now.month();
// figure out the first day of the next month
let (next_year, next_month) = if month == Month::December {
(year + 1, Month::January)
} else {
(year, month.next())
};
let first_of_next_month = Date::from_calendar_date(next_year, next_month, 1)
.unwrap()
.with_time(Time::MIDNIGHT)
.assume_utc();
(first_of_next_month - now).whole_seconds()
}
#[derive(Debug)]
pub struct MessageLevelingHandler;
#[async_trait]
impl OnMessageHandler<State> for MessageLevelingHandler {
async fn on_message(&self, event: &MessageCreate, rt: Arc<Runtime<State>>) -> EventResult {
let Some(channel) = Channel::get_by_id(event.channel_id.get(), &rt.state.db).await? else {
return Ok(());
};
if channel.call_id.is_none() {
return Ok(());
}
let mut redis = rt.state.redis.get().await?;
let key = format!("lvl:{}", event.author.id);
let lock = redis.exists(&key).await?;
if lock {
return Ok(());
}
let xp = ChaCha20Rng::from_os_rng().random_range(10..=25);
redis.set_ex(&key, true, 60).await?;
redis
.zincr(KEY_LEADERBOARD_MONTHLY, event.author.id.to_string(), xp)
.await?;
let leaderboard_exat = redis.ttl(KEY_LEADERBOARD_MONTHLY).await?;
if leaderboard_exat == IntegerReplyOrNoOp::ExistsButNotRelevant {
redis.expire(key, seconds_until_end_of_month()).await?;
}
Ok(())
}
}
+1
View File
@@ -0,0 +1 @@
pub mod events;
+2 -1
View File
@@ -17,5 +17,6 @@
pub mod about; pub mod about;
pub mod calls; pub mod calls;
pub mod gateway; pub mod gateway;
pub mod servers; pub mod leveling;
pub mod moderation; pub mod moderation;
pub mod servers;
+16 -7
View File
@@ -125,16 +125,13 @@ use crate::{
runtime::Runtime, runtime::Runtime,
}, },
groups::{ groups::{
about::{commands::HelpCommand, components::HelpGoBackButton}, about::{commands::HelpCommand, components::HelpGoBackButton}, calls::{
calls::{
commands::{CallCommand, FriendRequestCommand, HangupCommand, SkipCommand}, commands::{CallCommand, FriendRequestCommand, HangupCommand, SkipCommand},
events::{CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler}, events::{CallMessageDeleteHandler, CallMessageEditHandler, CallMessageHandler},
}, }, gateway::events::{ReadyHandler, ReconnectHandler}, leveling::events::MessageLevelingHandler, servers::{
gateway::events::{ReadyHandler, ReconnectHandler},
servers::{
commands::PrefixCommand, commands::PrefixCommand,
events::{ServerCreateHandler, ServerDeleteHandler, ServerUpdateHandler}, events::{ServerCreateHandler, ServerDeleteHandler, ServerUpdateHandler},
}, }
}, },
state::State, state::State,
}; };
@@ -146,6 +143,7 @@ mod discord;
mod framework; mod framework;
mod groups; mod groups;
mod logging; mod logging;
mod redis;
mod state; mod state;
mod utils; mod utils;
@@ -169,6 +167,7 @@ pub async fn main() {
database_username, database_username,
database_password, database_password,
shard, shard,
redis_urls,
} => { } => {
start( start(
token, token,
@@ -176,6 +175,7 @@ pub async fn main() {
database_username, database_username,
database_password, database_password,
shard.into(), shard.into(),
redis_urls,
) )
.await; .await;
} }
@@ -217,6 +217,7 @@ async fn start(
database_username: Option<String>, database_username: Option<String>,
database_password: Option<String>, database_password: Option<String>,
shard: ShardId, shard: ShardId,
redis_urls: Vec<String>,
) { ) {
log::info!("Welcome to Payphone, Nyeki!"); log::info!("Welcome to Payphone, Nyeki!");
log::info!( log::info!(
@@ -230,7 +231,14 @@ async fn start(
log::info!("Database connection established successfully."); log::info!("Database connection established successfully.");
let state = State { db }; let redis_pool = redis::init(redis_urls).expect("Could not connect to Redis!");
log::info!("Redis connection established successfully.");
let state = State {
db,
redis: redis_pool,
};
let bot = Bot::new(state, ServerPrefixOrPayphoneMention) let bot = Bot::new(state, ServerPrefixOrPayphoneMention)
.shard(shard) .shard(shard)
@@ -252,6 +260,7 @@ async fn start(
.on_ready(ReadyHandler) .on_ready(ReadyHandler)
.on_reconnect(ReconnectHandler) .on_reconnect(ReconnectHandler)
.on_message(CallMessageHandler) .on_message(CallMessageHandler)
.on_message(MessageLevelingHandler)
.on_message_edit(CallMessageEditHandler) .on_message_edit(CallMessageEditHandler)
.on_message_delete(CallMessageDeleteHandler) .on_message_delete(CallMessageDeleteHandler)
.on_server_create(ServerCreateHandler) .on_server_create(ServerCreateHandler)
+8
View File
@@ -0,0 +1,8 @@
use deadpool_redis::{
Runtime,
cluster::{Config, CreatePoolError, Pool},
};
pub fn init(urls: Vec<String>) -> Result<Pool, CreatePoolError> {
Config::from_urls(urls).create_pool(Some(Runtime::Tokio1))
}
+2
View File
@@ -18,8 +18,10 @@
//! } //! }
//! ``` //! ```
use deadpool_redis::cluster::Pool;
use scylla::client::session::Session; use scylla::client::session::Session;
pub struct State { pub struct State {
pub db: Session, pub db: Session,
pub redis: Pool,
} }