refactor!: use poise 0.6.1
This commit is contained in:
parent
203ba111cc
commit
7252ced3cb
16 changed files with 700 additions and 512 deletions
642
Cargo.lock
generated
642
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
28
Cargo.toml
28
Cargo.toml
|
@ -10,30 +10,30 @@ build = "build.rs"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[build-dependencies]
|
||||
gray_matter = "0.2.6"
|
||||
poise = "0.5.7"
|
||||
serde = "1.0.193"
|
||||
serde_json = "1.0.108"
|
||||
poise = "0.6.1"
|
||||
serde = "1.0.196"
|
||||
serde_json = "1.0.112"
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.74"
|
||||
async-trait = "0.1.77"
|
||||
color-eyre = "0.6.2"
|
||||
dotenvy = "0.15.7"
|
||||
env_logger = "0.10.0"
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
poise = "0.5.7"
|
||||
octocrab = "0.32.0"
|
||||
once_cell = "1.18.0"
|
||||
poise = "0.6.1"
|
||||
octocrab = "0.33.3"
|
||||
once_cell = "1.19.0"
|
||||
rand = "0.8.5"
|
||||
redis = { version = "0.23.3", features = ["tokio-comp", "tokio-rustls-comp"] }
|
||||
redis = { version = "0.24.0", features = ["tokio-comp", "tokio-rustls-comp"] }
|
||||
redis-macros = "0.2.1"
|
||||
regex = "1.10.2"
|
||||
reqwest = { version = "0.11.22", default-features = false, features = [
|
||||
regex = "1.10.3"
|
||||
reqwest = { version = "0.11.23", default-features = false, features = [
|
||||
"rustls-tls",
|
||||
"json",
|
||||
] }
|
||||
serde = "1.0.193"
|
||||
serde_json = "1.0.108"
|
||||
tokio = { version = "1.33.0", features = [
|
||||
serde = "1.0.196"
|
||||
serde_json = "1.0.112"
|
||||
tokio = { version = "1.35.1", features = [
|
||||
"macros",
|
||||
"rt-multi-thread",
|
||||
"signal",
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
use crate::{consts, Context};
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
use poise::CreateReply;
|
||||
|
||||
/// Returns the number of members in the server
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
pub async fn members(ctx: Context<'_>) -> Result<()> {
|
||||
let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't fetch guild!"))?;
|
||||
let guild = ctx
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't fetch guild!"))?
|
||||
.to_owned();
|
||||
|
||||
let count = guild.member_count;
|
||||
let online = if let Some(count) = guild.approximate_presence_count {
|
||||
|
@ -14,13 +19,12 @@ pub async fn members(ctx: Context<'_>) -> Result<()> {
|
|||
"Undefined".to_string()
|
||||
};
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
e.title(format!("{count} total members!"))
|
||||
let embed = CreateEmbed::new()
|
||||
.title(format!("{count} total members!"))
|
||||
.description(format!("{online} online members"))
|
||||
.color(consts::COLORS["blue"])
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
.color(consts::COLORS["blue"]);
|
||||
let reply = CreateReply::default().embed(embed);
|
||||
|
||||
ctx.send(reply).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ use crate::api::rory::get_rory;
|
|||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use poise::serenity_prelude::{CreateEmbed, CreateEmbedFooter};
|
||||
use poise::CreateReply;
|
||||
|
||||
/// Gets a Rory photo!
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
|
@ -11,19 +13,22 @@ pub async fn rory(
|
|||
) -> Result<()> {
|
||||
let rory = get_rory(id).await?;
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
let embed = {
|
||||
let embed = CreateEmbed::new();
|
||||
if let Some(error) = rory.error {
|
||||
e.title("Error!").description(error)
|
||||
embed.title("Error!").description(error)
|
||||
} else {
|
||||
e.title("Rory :3")
|
||||
let footer = CreateEmbedFooter::new(format!("ID {}", rory.id));
|
||||
embed
|
||||
.title("Rory :3")
|
||||
.url(&rory.url)
|
||||
.image(rory.url)
|
||||
.footer(|f| f.text(format!("ID {}", rory.id)))
|
||||
.footer(footer)
|
||||
}
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
};
|
||||
|
||||
let reply = CreateReply::default().embed(embed);
|
||||
ctx.send(reply).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::Context;
|
||||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use poise::serenity_prelude::{CreateEmbed, CreateEmbedAuthor, CreateMessage};
|
||||
|
||||
/// Say something through the bot
|
||||
#[poise::command(
|
||||
|
@ -11,7 +12,10 @@ use color_eyre::eyre::{eyre, Result};
|
|||
required_permissions = "MODERATE_MEMBERS"
|
||||
)]
|
||||
pub async fn say(ctx: Context<'_>, #[description = "Just content?"] content: String) -> Result<()> {
|
||||
let guild = ctx.guild().ok_or_else(|| eyre!("Couldn't get guild!"))?;
|
||||
let guild = ctx
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't get guild!"))?
|
||||
.to_owned();
|
||||
let channel = ctx
|
||||
.guild_channel()
|
||||
.await
|
||||
|
@ -28,23 +32,16 @@ pub async fn say(ctx: Context<'_>, #[description = "Just content?"] content: Str
|
|||
.find(|c| c.0 == &channel_id)
|
||||
.ok_or_else(|| eyre!("Couldn't get log channel from guild!"))?;
|
||||
|
||||
log_channel
|
||||
.1
|
||||
.clone()
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't cast channel we found from guild as GuildChannel?????"))?
|
||||
.send_message(ctx, |m| {
|
||||
m.embed(|e| {
|
||||
e.title("Say command used!")
|
||||
let author = CreateEmbedAuthor::new(ctx.author().tag())
|
||||
.icon_url(ctx.author().avatar_url().unwrap_or("Undefined".to_string()));
|
||||
|
||||
let embed = CreateEmbed::default()
|
||||
.title("Say command used!")
|
||||
.description(content)
|
||||
.author(|a| {
|
||||
a.name(ctx.author().tag()).icon_url(
|
||||
ctx.author().avatar_url().unwrap_or("undefined".to_string()),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
.author(author);
|
||||
|
||||
let message = CreateMessage::new().embed(embed);
|
||||
log_channel.1.send_message(ctx, message).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{consts::COLORS, Context};
|
||||
use crate::{consts, Context};
|
||||
|
||||
use color_eyre::eyre::{Context as _, Result};
|
||||
use poise::serenity_prelude::CreateEmbed;
|
||||
use poise::CreateReply;
|
||||
|
||||
/// Returns GitHub stargazer count
|
||||
#[poise::command(slash_command, prefix_command)]
|
||||
|
@ -19,13 +21,12 @@ pub async fn stars(ctx: Context<'_>) -> Result<()> {
|
|||
"undefined".to_string()
|
||||
};
|
||||
|
||||
ctx.send(|m| {
|
||||
m.embed(|e| {
|
||||
e.title(format!("⭐ {count} total stars!"))
|
||||
.color(COLORS["yellow"])
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
let embed = CreateEmbed::new()
|
||||
.title(format!("⭐ {count} total stars!"))
|
||||
.color(consts::COLORS["yellow"]);
|
||||
let reply = CreateReply::default().embed(embed);
|
||||
|
||||
ctx.send(reply).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ use std::env;
|
|||
|
||||
use color_eyre::eyre::{eyre, Result};
|
||||
use once_cell::sync::Lazy;
|
||||
use poise::serenity_prelude::{Color, User};
|
||||
use poise::serenity_prelude::{Color, CreateEmbed, User};
|
||||
use poise::CreateReply;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
static TAGS: Lazy<Vec<Tag>> = Lazy::new(|| serde_json::from_str(env!("TAGS")).unwrap());
|
||||
|
@ -25,36 +26,40 @@ pub async fn tag(
|
|||
|
||||
let frontmatter = &tag.frontmatter;
|
||||
|
||||
ctx.send(|m| {
|
||||
if let Some(user) = user {
|
||||
m.content(format!("<@{}>", user.id));
|
||||
}
|
||||
|
||||
m.embed(|e| {
|
||||
e.title(&frontmatter.title);
|
||||
e.description(&tag.content);
|
||||
let embed = {
|
||||
let mut e = CreateEmbed::new();
|
||||
|
||||
if let Some(color) = &frontmatter.color {
|
||||
let color = *consts::COLORS
|
||||
.get(color.as_str())
|
||||
.unwrap_or(&Color::default());
|
||||
e.color(color);
|
||||
e = e.color(color);
|
||||
}
|
||||
|
||||
if let Some(image) = &frontmatter.image {
|
||||
e.image(image);
|
||||
e = e.image(image);
|
||||
}
|
||||
|
||||
if let Some(fields) = &frontmatter.fields {
|
||||
for field in fields {
|
||||
e.field(&field.name, &field.value, field.inline);
|
||||
e = e.field(&field.name, &field.value, field.inline);
|
||||
}
|
||||
}
|
||||
|
||||
e
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
};
|
||||
|
||||
let reply = {
|
||||
let mut r = CreateReply::default();
|
||||
|
||||
if let Some(user) = user {
|
||||
r = r.content(format!("<@{}>", user.id));
|
||||
}
|
||||
|
||||
r.embed(embed)
|
||||
};
|
||||
|
||||
ctx.send(reply).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ use crate::Data;
|
|||
|
||||
use color_eyre::eyre::Report;
|
||||
use log::*;
|
||||
use poise::serenity_prelude::Timestamp;
|
||||
use poise::FrameworkError;
|
||||
use poise::serenity_prelude::{CreateEmbed, Timestamp};
|
||||
use poise::{CreateReply, FrameworkError};
|
||||
|
||||
pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
||||
match error {
|
||||
|
@ -12,23 +12,23 @@ pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
|||
error, framework, ..
|
||||
} => {
|
||||
error!("Error setting up client! Bailing out");
|
||||
framework.shard_manager().lock().await.shutdown_all().await;
|
||||
framework.shard_manager().shutdown_all().await;
|
||||
|
||||
panic!("{error}")
|
||||
}
|
||||
|
||||
FrameworkError::Command { error, ctx } => {
|
||||
FrameworkError::Command { error, ctx, .. } => {
|
||||
error!("Error in command {}:\n{error:?}", ctx.command().name);
|
||||
ctx.send(|c| {
|
||||
c.embed(|e| {
|
||||
e.title("Something went wrong!")
|
||||
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Something went wrong!")
|
||||
.description("oopsie")
|
||||
.timestamp(Timestamp::now())
|
||||
.color(COLORS["red"])
|
||||
})
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
.color(COLORS["red"]);
|
||||
|
||||
let reply = CreateReply::default().embed(embed);
|
||||
|
||||
ctx.send(reply).await.ok();
|
||||
}
|
||||
|
||||
FrameworkError::EventHandler {
|
||||
|
@ -36,13 +36,17 @@ pub async fn handle(error: FrameworkError<'_, Data, Report>) {
|
|||
ctx: _,
|
||||
event,
|
||||
framework: _,
|
||||
..
|
||||
} => {
|
||||
error!("Error while handling event {}:\n{error:?}", event.name());
|
||||
error!(
|
||||
"Error while handling event {}:\n{error:?}",
|
||||
event.snake_case_name()
|
||||
);
|
||||
}
|
||||
|
||||
error => {
|
||||
if let Err(e) = poise::builtins::on_error(error).await {
|
||||
error!("Unhandled error occured:\n{e:#?}");
|
||||
error!("Unhandled error occurred:\n{e:#?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ use crate::Data;
|
|||
|
||||
use color_eyre::eyre::Result;
|
||||
use log::*;
|
||||
use poise::serenity_prelude::{Context, Message};
|
||||
use poise::serenity_prelude::{
|
||||
Context, CreateAllowedMentions, CreateEmbed, CreateMessage, Message,
|
||||
};
|
||||
|
||||
mod issues;
|
||||
mod providers;
|
||||
|
@ -17,16 +19,16 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
|||
let log = find_log(message).await;
|
||||
|
||||
if log.is_err() {
|
||||
channel
|
||||
.send_message(ctx, |m| {
|
||||
m.reference_message(message)
|
||||
.allowed_mentions(|am| am.replied_user(true))
|
||||
.embed(|e| {
|
||||
e.title("Analyze failed!")
|
||||
.description("Couldn't download log")
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
let embed = CreateEmbed::new()
|
||||
.title("Analyze failed!")
|
||||
.description("Couldn't download log");
|
||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
||||
let our_message = CreateMessage::new()
|
||||
.reference_message(message)
|
||||
.allowed_mentions(allowed_mentions)
|
||||
.embed(embed);
|
||||
|
||||
channel.send_message(ctx, our_message).await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -38,31 +40,32 @@ pub async fn handle(ctx: &Context, message: &Message, data: &Data) -> Result<()>
|
|||
|
||||
let issues = find_issues(&log, data).await?;
|
||||
|
||||
channel
|
||||
.send_message(ctx, |m| {
|
||||
m.reference_message(message)
|
||||
.allowed_mentions(|am| am.replied_user(true))
|
||||
.embed(|e| {
|
||||
e.title("Log analysis");
|
||||
let embed = {
|
||||
let mut e = CreateEmbed::new().title("Log analysis");
|
||||
|
||||
if issues.is_empty() {
|
||||
e.color(COLORS["green"]).field(
|
||||
e = e.color(COLORS["green"]).field(
|
||||
"Analyze failed!",
|
||||
"No issues found automatically",
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
e.color(COLORS["red"]);
|
||||
e = e.color(COLORS["red"]);
|
||||
|
||||
for (title, description) in issues {
|
||||
e.field(title, description, false);
|
||||
e = e.field(title, description, false);
|
||||
}
|
||||
}
|
||||
|
||||
e
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
};
|
||||
|
||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(true);
|
||||
let message = CreateMessage::new()
|
||||
.allowed_mentions(allowed_mentions)
|
||||
.embed(embed);
|
||||
|
||||
channel.send_message(ctx, message).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub async fn handle(ctx: &Context, reaction: &Reaction) -> Result<()> {
|
|||
.wrap_err_with(|| "Couldn't fetch message from reaction!")?;
|
||||
|
||||
if let Some(interaction) = &message.interaction {
|
||||
if interaction.kind == InteractionType::ApplicationCommand
|
||||
if interaction.kind == InteractionType::Command
|
||||
&& interaction.user == user
|
||||
&& reaction.emoji.unicode_eq("❌")
|
||||
{
|
||||
|
|
|
@ -1,27 +1,20 @@
|
|||
use color_eyre::eyre::{eyre, Context as _, Result};
|
||||
use poise::serenity_prelude::{Context, Message};
|
||||
use color_eyre::eyre::Result;
|
||||
use poise::serenity_prelude::{Context, CreateAllowedMentions, CreateMessage, Message};
|
||||
|
||||
use crate::utils;
|
||||
|
||||
pub async fn handle(ctx: &Context, message: &Message) -> Result<()> {
|
||||
let embeds = utils::resolve_message(ctx, message).await?;
|
||||
|
||||
// TOOD getchoo: actually reply to user
|
||||
// TODO getchoo: actually reply to user
|
||||
// ...not sure why Message doesn't give me a builder in reply() or equivalents
|
||||
let our_channel = message
|
||||
.channel(ctx)
|
||||
.await
|
||||
.wrap_err_with(|| "Couldn't get channel from message!")?
|
||||
.guild()
|
||||
.ok_or_else(|| eyre!("Couldn't convert to GuildChannel!"))?;
|
||||
|
||||
if !embeds.is_empty() {
|
||||
our_channel
|
||||
.send_message(ctx, |m| {
|
||||
m.set_embeds(embeds)
|
||||
.allowed_mentions(|am| am.replied_user(false))
|
||||
})
|
||||
.await?;
|
||||
let allowed_mentions = CreateAllowedMentions::new().replied_user(false);
|
||||
let reply = CreateMessage::new()
|
||||
.embeds(embeds)
|
||||
.allowed_mentions(allowed_mentions);
|
||||
|
||||
message.channel_id.send_message(ctx, reply).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::{api, Data};
|
|||
|
||||
use color_eyre::eyre::{Report, Result};
|
||||
use log::*;
|
||||
use poise::serenity_prelude::{Activity, Context, OnlineStatus};
|
||||
use poise::{Event, FrameworkContext};
|
||||
use poise::serenity_prelude::{ActivityData, Context, FullEvent, OnlineStatus};
|
||||
use poise::FrameworkContext;
|
||||
|
||||
mod analyze_logs;
|
||||
mod delete_on_reaction;
|
||||
|
@ -14,22 +14,22 @@ mod support_onboard;
|
|||
|
||||
pub async fn handle(
|
||||
ctx: &Context,
|
||||
event: &Event<'_>,
|
||||
event: &FullEvent,
|
||||
_framework: FrameworkContext<'_, Data, Report>,
|
||||
data: &Data,
|
||||
) -> Result<()> {
|
||||
match event {
|
||||
Event::Ready { data_about_bot } => {
|
||||
FullEvent::Ready { data_about_bot } => {
|
||||
info!("Logged in as {}!", data_about_bot.user.name);
|
||||
|
||||
let latest_minecraft_version = api::prism_meta::get_latest_minecraft_version().await?;
|
||||
let activity = Activity::playing(format!("Minecraft {}", latest_minecraft_version));
|
||||
let activity = ActivityData::playing(format!("Minecraft {}", latest_minecraft_version));
|
||||
|
||||
info!("Setting presence to activity {activity:#?}");
|
||||
ctx.set_presence(Some(activity), OnlineStatus::Online).await;
|
||||
ctx.set_presence(Some(activity), OnlineStatus::Online);
|
||||
}
|
||||
|
||||
Event::Message { new_message } => {
|
||||
FullEvent::Message { new_message } => {
|
||||
// ignore new messages from bots
|
||||
// NOTE: the webhook_id check allows us to still respond to PK users
|
||||
if new_message.author.bot && new_message.webhook_id.is_none() {
|
||||
|
@ -52,11 +52,13 @@ pub async fn handle(
|
|||
analyze_logs::handle(ctx, new_message, data).await?;
|
||||
}
|
||||
|
||||
Event::ReactionAdd { add_reaction } => {
|
||||
delete_on_reaction::handle(ctx, add_reaction).await?
|
||||
FullEvent::ReactionAdd { add_reaction } => {
|
||||
delete_on_reaction::handle(ctx, add_reaction).await?;
|
||||
}
|
||||
|
||||
Event::ThreadCreate { thread } => support_onboard::handle(ctx, thread).await?,
|
||||
FullEvent::ThreadCreate { thread } => {
|
||||
support_onboard::handle(ctx, thread).await?;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
use color_eyre::eyre::{eyre, Result};
|
||||
use log::*;
|
||||
use poise::serenity_prelude::{ChannelType, Context, GuildChannel};
|
||||
use poise::serenity_prelude::{
|
||||
ChannelType, Context, CreateAllowedMentions, CreateMessage, GuildChannel,
|
||||
};
|
||||
|
||||
pub async fn handle(ctx: &Context, thread: &GuildChannel) -> Result<()> {
|
||||
if thread.kind != ChannelType::PublicThread {
|
||||
debug!("Not doing support onboard in non-thread channel");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let parent_id = thread
|
||||
if thread
|
||||
.parent_id
|
||||
.ok_or_else(|| eyre!("Couldn't get parent ID from thread {}!", thread.name))?;
|
||||
|
||||
let parent_channel = ctx
|
||||
.cache
|
||||
.guild_channel(parent_id)
|
||||
.ok_or_else(|| eyre!("Couldn't get GuildChannel {}!", parent_id))?;
|
||||
|
||||
if parent_channel.name != "support" {
|
||||
.ok_or_else(|| eyre!("Couldn't get parent ID from thread {}!", thread.name))?
|
||||
.name(ctx)
|
||||
.await
|
||||
.unwrap_or("".to_string())
|
||||
!= "support"
|
||||
{
|
||||
debug!("Not posting onboarding message to threads outside of support");
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -32,12 +33,15 @@ pub async fn handle(ctx: &Context, thread: &GuildChannel) -> Result<()> {
|
|||
"Please don't ping people for support questions, unless you have their permission."
|
||||
);
|
||||
|
||||
thread
|
||||
.send_message(ctx, |m| {
|
||||
m.content(msg)
|
||||
.allowed_mentions(|am| am.replied_user(true).users(Vec::from([owner])))
|
||||
})
|
||||
.await?;
|
||||
let allowed_mentions = CreateAllowedMentions::new()
|
||||
.replied_user(true)
|
||||
.users(Vec::from([owner]));
|
||||
|
||||
let message = CreateMessage::new()
|
||||
.content(msg)
|
||||
.allowed_mentions(allowed_mentions);
|
||||
|
||||
thread.send_message(ctx, message).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -10,8 +10,6 @@ use poise::{
|
|||
serenity_prelude as serenity, EditTracker, Framework, FrameworkOptions, PrefixFrameworkOptions,
|
||||
};
|
||||
|
||||
use serenity::ShardManager;
|
||||
|
||||
use redis::ConnectionLike;
|
||||
|
||||
use tokio::signal::ctrl_c;
|
||||
|
@ -19,7 +17,6 @@ use tokio::signal::ctrl_c;
|
|||
use tokio::signal::unix::{signal, SignalKind};
|
||||
#[cfg(target_family = "windows")]
|
||||
use tokio::signal::windows::ctrl_close;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
mod api;
|
||||
mod commands;
|
||||
|
@ -78,9 +75,9 @@ async fn setup(
|
|||
Ok(data)
|
||||
}
|
||||
|
||||
async fn handle_shutdown(shard_manager: Arc<Mutex<ShardManager>>, reason: &str) {
|
||||
async fn handle_shutdown(shard_manager: Arc<serenity::ShardManager>, reason: &str) {
|
||||
warn!("{reason}! Shutting down bot...");
|
||||
shard_manager.lock().await.shutdown_all().await;
|
||||
shard_manager.shutdown_all().await;
|
||||
println!("{}", "Everything is shutdown. Goodbye!".green())
|
||||
}
|
||||
|
||||
|
@ -111,7 +108,9 @@ async fn main() -> Result<()> {
|
|||
|
||||
prefix_options: PrefixFrameworkOptions {
|
||||
prefix: Some("r".into()),
|
||||
edit_tracker: Some(EditTracker::for_timespan(Duration::from_secs(3600))),
|
||||
edit_tracker: Some(Arc::from(EditTracker::for_timespan(Duration::from_secs(
|
||||
3600,
|
||||
)))),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
|
@ -119,22 +118,23 @@ async fn main() -> Result<()> {
|
|||
};
|
||||
|
||||
let framework = Framework::builder()
|
||||
.token(token)
|
||||
.intents(intents)
|
||||
.options(options)
|
||||
.setup(|ctx, ready, framework| Box::pin(setup(ctx, ready, framework)))
|
||||
.build()
|
||||
.await
|
||||
.wrap_err_with(|| "Failed to build framework!")?;
|
||||
.build();
|
||||
|
||||
let mut client = serenity::ClientBuilder::new(token, intents)
|
||||
.framework(framework)
|
||||
.await?;
|
||||
|
||||
let shard_manager = client.shard_manager.clone();
|
||||
|
||||
let shard_manager = framework.shard_manager().clone();
|
||||
#[cfg(target_family = "unix")]
|
||||
let mut sigterm = signal(SignalKind::terminate())?;
|
||||
#[cfg(target_family = "windows")]
|
||||
let mut sigterm = ctrl_close()?;
|
||||
|
||||
tokio::select! {
|
||||
result = framework.start() => result.map_err(Report::from),
|
||||
result = client.start() => result.map_err(Report::from),
|
||||
_ = sigterm.recv() => {
|
||||
handle_shutdown(shard_manager, "Received SIGTERM").await;
|
||||
std::process::exit(0);
|
||||
|
|
|
@ -69,7 +69,7 @@ impl Storage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn expire_key(&self, key: &str, expire_seconds: usize) -> Result<()> {
|
||||
async fn expire_key(&self, key: &str, expire_seconds: i64) -> Result<()> {
|
||||
debug!("Expiring key {key} in {expire_seconds}");
|
||||
|
||||
let mut con = self.client.get_async_connection().await?;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use color_eyre::eyre::{eyre, Context as _, Result};
|
||||
use log::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use poise::serenity_prelude::{ChannelType, Colour, Context, CreateEmbed, Message};
|
||||
use poise::serenity_prelude::{
|
||||
ChannelId, ChannelType, Colour, Context, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
|
||||
Message, MessageId,
|
||||
};
|
||||
use regex::Regex;
|
||||
|
||||
static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
|
||||
|
@ -21,81 +26,63 @@ pub fn find_first_image(msg: &Message) -> Option<String> {
|
|||
}
|
||||
|
||||
pub async fn resolve(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
||||
let matches = MESSAGE_PATTERN.captures_iter(&msg.content);
|
||||
let matches = MESSAGE_PATTERN
|
||||
.captures_iter(&msg.content)
|
||||
.map(|capture| capture.extract());
|
||||
|
||||
let mut embeds: Vec<CreateEmbed> = vec![];
|
||||
|
||||
for captured in matches.take(3) {
|
||||
// don't leak messages from other servers
|
||||
if let Some(server_id) = captured.get(0) {
|
||||
let other_server: u64 = server_id.as_str().parse().unwrap_or_default();
|
||||
let current_id = msg.guild_id.unwrap_or_default();
|
||||
for (_, [_server_id, channel_id, message_id]) in matches {
|
||||
let channel = ChannelId::from_str(channel_id)
|
||||
.wrap_err_with(|| format!("Couldn't parse channel ID {channel_id}!"))?
|
||||
.to_channel_cached(ctx.as_ref())
|
||||
.ok_or_else(|| eyre!("Couldn't find Guild Channel from {channel_id}!"))?
|
||||
.to_owned();
|
||||
|
||||
if &other_server != current_id.as_u64() {
|
||||
debug!("Not resolving message of other guild.");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
warn!("Couldn't find server_id from Discord link! Not resolving message to be safe");
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(channel_id) = captured.get(1) {
|
||||
let parsed: u64 = channel_id.as_str().parse().unwrap_or_default();
|
||||
let req_channel = ctx
|
||||
.cache
|
||||
.channel(parsed)
|
||||
.ok_or_else(|| eyre!("Couldn't get channel_id from Discord regex!"))?
|
||||
.guild()
|
||||
.ok_or_else(|| {
|
||||
eyre!("Couldn't convert to GuildChannel from channel_id {parsed}!")
|
||||
})?;
|
||||
|
||||
if !req_channel.is_text_based() {
|
||||
debug!("Not resolving message is non-text-based channel.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if req_channel.kind == ChannelType::PrivateThread {
|
||||
if let Some(id) = req_channel.parent_id {
|
||||
let parent = ctx.cache.guild_channel(id).ok_or_else(|| {
|
||||
eyre!("Couldn't get parent channel {id} for thread {req_channel}!")
|
||||
})?;
|
||||
let parent_members = parent.members(ctx).await.unwrap_or_default();
|
||||
|
||||
if !parent_members.iter().any(|m| m.user.id == msg.author.id) {
|
||||
debug!("Not resolving message for user not a part of a private thread.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if req_channel
|
||||
.members(ctx)
|
||||
.await?
|
||||
.iter()
|
||||
.any(|m| m.user.id == msg.author.id)
|
||||
let author_can_view = if channel.kind == ChannelType::PublicThread
|
||||
|| channel.kind == ChannelType::PrivateThread
|
||||
{
|
||||
debug!("Not resolving for message for user not a part of a channel");
|
||||
continue;
|
||||
let thread_members = channel
|
||||
.id
|
||||
.get_thread_members(ctx)
|
||||
.await
|
||||
.wrap_err("Couldn't get members from thread!")?;
|
||||
|
||||
thread_members
|
||||
.iter()
|
||||
.any(|member| member.user_id == msg.author.id)
|
||||
} else {
|
||||
channel
|
||||
.members(ctx)
|
||||
.wrap_err_with(|| format!("Couldn't get members for channel {channel_id}!"))?
|
||||
.iter()
|
||||
.any(|member| member.user.id == msg.author.id)
|
||||
};
|
||||
|
||||
if !author_can_view {
|
||||
debug!("Not resolving message for author who can't see it");
|
||||
}
|
||||
|
||||
let message_id: u64 = captured
|
||||
.get(2)
|
||||
.ok_or_else(|| eyre!("Couldn't get message_id from Discord regex!"))?
|
||||
.as_str()
|
||||
.parse()
|
||||
let original_message = channel
|
||||
.message(
|
||||
ctx,
|
||||
MessageId::from_str(message_id)
|
||||
.wrap_err_with(|| format!("Couldn't parse message ID {message_id}!"))?,
|
||||
)
|
||||
.await
|
||||
.wrap_err_with(|| {
|
||||
eyre!("Couldn't parse message_id from Discord regex as a MessageId!")
|
||||
format!("Couldn't get message from ID {message_id} in channel {channel_id}!")
|
||||
})?;
|
||||
|
||||
let original_message = req_channel.message(ctx, message_id).await?;
|
||||
let mut embed = CreateEmbed::default();
|
||||
embed
|
||||
.author(|a| {
|
||||
a.name(original_message.author.tag())
|
||||
.icon_url(original_message.author.default_avatar_url())
|
||||
})
|
||||
let author = CreateEmbedAuthor::new(original_message.author.tag())
|
||||
.icon_url(original_message.author.default_avatar_url());
|
||||
let footer = CreateEmbedFooter::new(format!("#{}", channel.name));
|
||||
|
||||
let mut embed = CreateEmbed::new()
|
||||
.author(author)
|
||||
.color(Colour::BLITZ_BLUE)
|
||||
.timestamp(original_message.timestamp)
|
||||
.footer(|f| f.text(format!("#{}", req_channel.name)))
|
||||
.footer(footer)
|
||||
.description(format!(
|
||||
"{}\n\n[Jump to original message]({})",
|
||||
original_message.content,
|
||||
|
@ -103,7 +90,7 @@ pub async fn resolve(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
|||
));
|
||||
|
||||
if !original_message.attachments.is_empty() {
|
||||
embed.fields(original_message.attachments.iter().map(|a| {
|
||||
embed = embed.fields(original_message.attachments.iter().map(|a| {
|
||||
(
|
||||
"Attachments".to_string(),
|
||||
format!("[{}]({})", a.filename, a.url),
|
||||
|
@ -112,13 +99,12 @@ pub async fn resolve(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
|
|||
}));
|
||||
|
||||
if let Some(image) = find_first_image(msg) {
|
||||
embed.image(image);
|
||||
embed = embed.image(image);
|
||||
}
|
||||
}
|
||||
|
||||
embeds.push(embed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(embeds)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue