Skip to content

Commit

Permalink
Basic starboard functionality (#18)
Browse files Browse the repository at this point in the history
* functionality for getting the vote status

* backbone for reaction event handling

* ability to convert ReactionType to SimpleEmoji

* add list_for_channel to StarboardConfig

* cache channel nsfws

* Put guild emojis inside CachedGuild struct

* create async-safe wrappers for DashMap/DashSet

* fix constant name

* progress for reaction handling

* Add `contains_key` to AsyndDashMap

* more progress for creating votes on reaction_add

* formatting

* user caching

* finish logic for vote creation

* actually get the channels nsfw info

* don't cache channel nsfw info

* implement vote removal

* unused import

* VoteStatus takes ownership of configs

* send messages to starboard 🎉

* send the points rather than "hello"

* edit the original if it exists
  • Loading branch information
circuitsacul authored Jul 26, 2022
1 parent 95d999b commit 6ecb946
Show file tree
Hide file tree
Showing 29 changed files with 1,366 additions and 86 deletions.
450 changes: 411 additions & 39 deletions sqlx-data.json

Large diffs are not rendered by default.

67 changes: 51 additions & 16 deletions src/cache/cache.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
use dashmap::{DashMap, DashSet};
use stretto::ValueRef;
use twilight_gateway::Event;
use twilight_model::id::{
marker::{ChannelMarker, EmojiMarker, GuildMarker, MessageMarker},
marker::{ChannelMarker, EmojiMarker, GuildMarker, MessageMarker, UserMarker},
Id,
};

use crate::constants;
use crate::{
client::bot::StarboardBot,
constants,
utils::async_dash::{AsyncDashMap, AsyncDashSet},
};

use super::{models::message::CachedMessage, update::UpdateCache};
use super::{
models::{guild::CachedGuild, message::CachedMessage, user::CachedUser},
update::UpdateCache,
};

pub struct Cache {
// discord side
pub guild_emojis: DashMap<Id<GuildMarker>, DashSet<Id<EmojiMarker>>>,
pub guilds: AsyncDashMap<Id<GuildMarker>, CachedGuild>,
pub users: AsyncDashMap<Id<UserMarker>, CachedUser>,
pub messages: stretto::AsyncCache<Id<MessageMarker>, CachedMessage>,

// database side
pub autostar_channel_ids: DashSet<Id<ChannelMarker>>,
pub autostar_channel_ids: AsyncDashSet<Id<ChannelMarker>>,

// autocomplete
pub guild_autostar_channel_names: stretto::AsyncCache<Id<GuildMarker>, Vec<String>>,
Expand All @@ -25,23 +34,24 @@ pub struct Cache {
impl Cache {
pub fn new(autostar_channel_ids: DashSet<Id<ChannelMarker>>) -> Self {
Self {
guild_emojis: DashMap::new(),
guilds: DashMap::new().into(),
users: DashMap::new().into(),
messages: stretto::AsyncCache::new(
(constants::MAX_MESSAGES * 10).try_into().unwrap(),
constants::MAX_MESSAGES.into(),
tokio::spawn,
)
.unwrap(),
autostar_channel_ids,
autostar_channel_ids: autostar_channel_ids.into(),
guild_autostar_channel_names: stretto::AsyncCache::new(
(constants::MAX_AUTOSTAR_NAMES * 10).try_into().unwrap(),
constants::MAX_AUTOSTAR_NAMES.into(),
(constants::MAX_NAMES * 10).try_into().unwrap(),
constants::MAX_NAMES.into(),
tokio::spawn,
)
.unwrap(),
guild_starboard_names: stretto::AsyncCache::new(
(constants::MAX_AUTOSTAR_NAMES * 10).try_into().unwrap(),
constants::MAX_AUTOSTAR_NAMES.into(),
(constants::MAX_NAMES * 10).try_into().unwrap(),
constants::MAX_NAMES.into(),
tokio::spawn,
)
.unwrap(),
Expand All @@ -57,18 +67,43 @@ impl Cache {
Event::GuildCreate(event) => event.update_cache(self).await,
Event::GuildDelete(event) => event.update_cache(self).await,
Event::GuildEmojisUpdate(event) => event.update_cache(self).await,
Event::MemberChunk(event) => event.update_cache(self).await,
Event::MemberAdd(event) => event.update_cache(self).await,
_ => {}
}
}

// helper methods
pub fn guild_emoji_exists(&self, guild_id: Id<GuildMarker>, emoji_id: Id<EmojiMarker>) -> bool {
match self.guild_emojis.get(&guild_id) {
self.guilds.with(&guild_id, |_, guild| match guild {
None => false,
Some(guild_emojis) => match guild_emojis.get(&emoji_id) {
None => false,
Some(_) => true,
},
Some(guild) => guild.emojis.contains(&emoji_id),
})
}

// "fetch or get" methods
pub async fn fog_message(
&self,
bot: &StarboardBot,
channel_id: Id<ChannelMarker>,
message_id: Id<MessageMarker>,
) -> ValueRef<CachedMessage> {
if let Some(cached) = self.messages.get(&message_id) {
return cached;
}

let msg = bot
.http
.message(channel_id, message_id)
.exec()
.await
.unwrap()
.model()
.await
.unwrap();
self.messages.insert(message_id, msg.into(), 1).await;

self.messages.wait().await.unwrap();
self.messages.get(&message_id).unwrap()
}
}
28 changes: 18 additions & 10 deletions src/cache/events/guild.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
use async_trait::async_trait;
use dashmap::DashSet;
use twilight_model::gateway::payload::incoming::{GuildCreate, GuildDelete, GuildEmojisUpdate};

use crate::cache::{cache::Cache, update::UpdateCache};
use crate::cache::{cache::Cache, models::guild::CachedGuild, update::UpdateCache};

#[async_trait]
impl UpdateCache for GuildCreate {
async fn update_cache(&self, cache: &Cache) {
// update emojis
cache
.guild_emojis
.insert(self.id, self.emojis.iter().map(|e| e.id).collect());
let guild = CachedGuild {
emojis: self
.emojis
.iter()
.map(|e| e.id)
.collect::<DashSet<_>>()
.into(),
};
cache.guilds.insert(self.id, guild);
}
}

#[async_trait]
impl UpdateCache for GuildDelete {
async fn update_cache(&self, cache: &Cache) {
// delete emojis
cache.guild_emojis.remove(&self.id);
cache.guilds.remove(&self.id);
}
}

#[async_trait]
impl UpdateCache for GuildEmojisUpdate {
async fn update_cache(&self, cache: &Cache) {
cache
.guild_emojis
.insert(self.guild_id, self.emojis.iter().map(|e| e.id).collect());
cache.guilds.alter(&self.guild_id, |_, guild| {
for emoji in &self.emojis {
guild.emojis.insert(emoji.id);
}
guild
});
}
}
20 changes: 20 additions & 0 deletions src/cache/events/member.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use async_trait::async_trait;
use twilight_model::gateway::payload::incoming::{MemberAdd, MemberChunk};

use crate::cache::{cache::Cache, update::UpdateCache};

#[async_trait]
impl UpdateCache for MemberChunk {
async fn update_cache(&self, cache: &Cache) {
for member in &self.members {
cache.users.insert(member.user.id, (&member.user).into());
}
}
}

#[async_trait]
impl UpdateCache for MemberAdd {
async fn update_cache(&self, cache: &Cache) {
cache.users.insert(self.user.id, (&self.user).into());
}
}
2 changes: 2 additions & 0 deletions src/cache/events/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ impl UpdateCache for MessageCreate {
}

let message = CachedMessage {
author_id: self.author.id,
attachments: self.attachments.clone(),
embeds: self.embeds.clone(),
};
Expand Down Expand Up @@ -58,6 +59,7 @@ impl UpdateCache for MessageUpdate {
};

let message = CachedMessage {
author_id: cached.value().author_id,
attachments,
embeds,
};
Expand Down
1 change: 1 addition & 0 deletions src/cache/events/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod guild;
pub mod member;
pub mod message;
7 changes: 7 additions & 0 deletions src/cache/models/guild.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use twilight_model::id::{marker::EmojiMarker, Id};

use crate::utils::async_dash::AsyncDashSet;

pub struct CachedGuild {
pub emojis: AsyncDashSet<Id<EmojiMarker>>,
}
16 changes: 15 additions & 1 deletion src/cache/models/message.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
use twilight_model::channel::{embed::Embed, Attachment};
use twilight_model::{
channel::{embed::Embed, Attachment, Message},
id::{marker::UserMarker, Id},
};

pub struct CachedMessage {
pub author_id: Id<UserMarker>,
pub attachments: Vec<Attachment>,
pub embeds: Vec<Embed>,
}

impl From<Message> for CachedMessage {
fn from(msg: Message) -> Self {
Self {
author_id: msg.author.id,
attachments: msg.attachments,
embeds: msg.embeds,
}
}
}
2 changes: 2 additions & 0 deletions src/cache/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod guild;
pub mod message;
pub mod user;
11 changes: 11 additions & 0 deletions src/cache/models/user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use twilight_model::user::User;

pub struct CachedUser {
pub is_bot: bool,
}

impl From<&User> for CachedUser {
fn from(user: &User) -> Self {
Self { is_bot: user.bot }
}
}
2 changes: 1 addition & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub const BOT_COLOR: u32 = 0xFFE19C;

// Cache size
pub const MAX_MESSAGES: u32 = 10_000;
pub const MAX_AUTOSTAR_NAMES: u32 = 100;
pub const MAX_NAMES: u32 = 100;

// Cooldowns
pub const AUTOSTAR_COOLDOWN: (u32, Duration) = (5, Duration::from_secs(20));
Expand Down
12 changes: 9 additions & 3 deletions src/core/embedder/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use crate::core::starboard::config::StarboardConfig;

pub struct Embedder {
pub points: i16,
pub config: StarboardConfig,
pub struct Embedder<'config> {
pub points: i32,
pub config: &'config StarboardConfig,
}

impl<'config> Embedder<'config> {
pub fn new(points: i32, config: &'config StarboardConfig) -> Self {
Self { points, config }
}
}
23 changes: 20 additions & 3 deletions src/core/embedder/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use twilight_model::id::Id;
use twilight_model::id::{marker::MessageMarker, Id};

use crate::client::bot::StarboardBot;

use super::Embedder;

impl Embedder {
impl Embedder<'_> {
pub fn get_top_text(&self) -> String {
"Hello".to_string()
self.points.to_string()
}

pub async fn send(
Expand All @@ -23,4 +23,21 @@ impl Embedder {
.exec()
.await
}

pub async fn edit(
&self,
bot: &StarboardBot,
message_id: Id<MessageMarker>,
) -> Result<twilight_http::Response<twilight_model::channel::Message>, twilight_http::Error>
{
bot.http
.update_message(
Id::new(self.config.starboard.channel_id.try_into().unwrap()),
message_id,
)
.content(Some(&self.get_top_text()))
.unwrap()
.exec()
.await
}
}
28 changes: 24 additions & 4 deletions src/core/emoji.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ use std::str::FromStr;
use async_trait::async_trait;
use twilight_http::request::channel::reaction::RequestReactionType;
use twilight_mention::Mention;
use twilight_model::id::{
marker::{EmojiMarker, GuildMarker},
Id,
use twilight_model::{
channel::ReactionType,
id::{
marker::{EmojiMarker, GuildMarker},
Id,
},
};

use crate::client::bot::StarboardBot;

pub struct SimpleEmoji {
pub is_custom: bool,
pub raw: String,
as_id: Option<Id<EmojiMarker>>,
pub as_id: Option<Id<EmojiMarker>>,
}

#[async_trait]
Expand Down Expand Up @@ -157,3 +160,20 @@ impl EmojiCommon for Vec<SimpleEmoji> {
arr
}
}

impl From<ReactionType> for SimpleEmoji {
fn from(reaction: ReactionType) -> Self {
match reaction {
ReactionType::Custom { id, .. } => SimpleEmoji {
is_custom: true,
raw: id.to_string(),
as_id: Some(id),
},
ReactionType::Unicode { name } => SimpleEmoji {
is_custom: false,
raw: name,
as_id: None,
},
}
}
}
Loading

0 comments on commit 6ecb946

Please sign in to comment.