Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::handlers::ReactionHandler;
use crate::models::ServerSetting;
use crate::schema;
use crate::schema::{server_settings, starboard_mappings};
use anyhow::Context as AnyhowContext;
use bigdecimal::{BigDecimal, FromPrimitive, ToPrimitive};
use diesel::{BoolExpressionMethods, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl};
use serenity::async_trait;
use serenity::model::prelude::{
ChannelId, Embed, EmojiId, GuildChannel, GuildId, Message, Reaction, ReactionType,
};
use serenity::prelude::*;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct StarboardHandler {
conn: Arc<Mutex<PgConnection>>,
}
impl StarboardHandler {
pub fn new(conn: Arc<Mutex<PgConnection>>) -> Self {
Self { conn }
}
async fn handle_reaction(&self, ctx: &Context, reaction: &Reaction) -> anyhow::Result<()> {
let guild = match reaction.channel_id.to_channel(ctx).await?.guild() {
Some(x) => x,
None => return Ok(()),
};
// get corresponding guild settings
let gs: Vec<ServerSetting> = server_settings::dsl::server_settings
.filter(
schema::server_settings::columns::guild_id
.eq(BigDecimal::from_u64(guild.guild_id.0).unwrap()),
)
.limit(1)
.get_results(&*self.conn.lock().await)?;
let gs = match gs.first() {
Some(x) => x,
None => return Ok(()),
};
let emoji = EmojiId(
gs.starboard_emoji_id
.to_u64()
.context("Could not convert emoji id to u64")?,
);
if Self::emoji_match(&reaction.emoji, emoji) {
let msg = reaction.message(ctx).await?;
for mr in &msg.reactions {
if Self::emoji_match(&mr.reaction_type, emoji) {
self.handle_matching_reaction(ctx, gs, mr.count, &msg, &guild.guild_id)
.await?;
break;
}
}
}
Ok(())
}
async fn handle_matching_reaction(
&self,
ctx: &Context,
gs: &ServerSetting,
count: u64,
msg: &Message,
guild: &GuildId,
) -> anyhow::Result<()> {
if count >= gs.starboard_threshold as u64 {
let original_id = BigDecimal::from(msg.id.0);
diesel::insert_into(starboard_mappings::dsl::starboard_mappings)
.values(starboard_mappings::columns::original_id.eq(&original_id))
.returning(starboard_mappings::columns::repost_id)
.on_conflict_do_nothing()
.execute(&*self.conn.lock().await)?;
let repost_id = starboard_mappings::dsl::starboard_mappings
.filter(starboard_mappings::columns::original_id.eq(&original_id))
.select(starboard_mappings::columns::repost_id)
.limit(1)
.get_results(&*self.conn.lock().await)?;
let repost_id: &Option<BigDecimal> =
repost_id.get(0).context("Insert of mapping failed")?;
if repost_id.is_none() {
// post to repost channel
let name = msg
.author
.nick_in(&ctx, guild)
.await
.unwrap_or_else(|| msg.author.tag());
let image = msg
.attachments
.iter()
.filter(|a| a.width.is_some())
.map(|a| &a.url)
.next();
let repost = ChannelId(
gs.starboard_channel
.to_u64()
.context("Could not convert starboard channel to a u64")?,
)
.send_message(ctx, |m| {
m.embed(|e| {
let mut e = e
.description(format!(
"[Jump to source]({})\n{}",
msg.link(),
msg.content
))
.timestamp(&msg.timestamp);
if let Some(url) = image {
e = e.image(url);
}
e
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
})
})
.await?;
// update the DB
let repost_id = BigDecimal::from_u64(repost.id.0);
diesel::update(
starboard_mappings::dsl::starboard_mappings
.filter(starboard_mappings::columns::original_id.eq(original_id)),
)
.set(starboard_mappings::columns::repost_id.eq(repost_id))
.execute(&*self.conn.lock().await)?;
}
}
Ok(())
}
fn emoji_match(rt: &ReactionType, em: EmojiId) -> bool {
matches!(rt, ReactionType::Custom { id, .. } if *id == em)
}
}
#[async_trait]
impl ReactionHandler for StarboardHandler {
async fn reaction(&self, ctx: &Context, reaction: &Reaction) {
if let Err(e) = self.handle_reaction(ctx, reaction).await {
eprintln!("Error in starboard: {:?}", e);
}
}
}