Many fixes and tweaks

* Fix tags (again?)

* Make tag names more consistent

* Remove prefix commands (not implemented well and not worth fixing)

* Allow '-'s in tags

* Fix /joke

* Fix /members

* Fix intel_hd issue match

* Fix log analysis reply

* Clearer log analysis messages

It's weird to say the process failed when no issues were found.

* Clippy

* Final doc cleanup

* Fix link expanding

* Fix duplicate event filtering

The other code simply does not work. ChannelId does have a method to grab members but I'm not sure whether it would work either.

* Remove message resolution

It's surprisingly hard to create an bug-free implementation.

* Fix pluralkit detection

* simplify tag codegen

* commands: improve error handling in members

unwrap() bad!!!11!!

* events: use debug logs for pk checks

* Revert "Remove message resolution"

This reverts commit 0d9f224a81917212adafdeb2213f3cc11b44cf88.

* Bring back prefix commands with "."

(it's easier to type)

* Add help

* Fix messsage resolution

* utils: factor out message resolution

* Improve tag message

* Disable VC support for message resolution for now

* Improve prefix command usage

Update on edit, display additional tip with wrong usage.

* Check invoke_on_edit to display tip

* Add defer in commands which make http requests

* Apply tag sorting to slash commands too

* handlers::error: `+=` -> `writeln!`

* handlers::event: ignore own new messages

* help: remove unneeded format!

* optimize for size in release builds

* nix: cleanup deployment expressions

* nix: use treefmt

* nix: update flake.lock

Flake lock file updates:

• Updated input 'fenix':
    'github:nix-community/fenix/eb683549b7d76b12d1a009f888b91b70ed34485f' (2024-01-27)
  → 'github:nix-community/fenix/c53bb4a32f2fce7acf4e8e160a54779c4460ffdb' (2024-03-17)
• Updated input 'fenix/rust-analyzer-src':
    'github:rust-lang/rust-analyzer/596e5c77cf5b2b660b3ac2ce732fa0596c246d9b' (2024-01-26)
  → 'github:rust-lang/rust-analyzer/5ecace48f693afaa6adf8cb23086b651db3aec96' (2024-03-16)
• Updated input 'nixpkgs':
    'github:nixos/nixpkgs/4fddc9be4eaf195d631333908f2a454b03628ee5' (2024-01-25)
  → 'github:nixos/nixpkgs/34ad8c9f29a18b4dd97a9ad40ceb16954f24afe7' (2024-03-17)
• Updated input 'pre-commit-hooks':
    'github:cachix/pre-commit-hooks.nix/f56597d53fd174f796b5a7d3ee0b494f9e2285cc' (2024-01-20)
  → 'github:cachix/pre-commit-hooks.nix/5df5a70ad7575f6601d91f0efec95dd9bc619431' (2024-02-15)
• Updated input 'procfile-nix':
    'github:getchoo/procfile-nix/31a33e4264e5c6214844993c5b508fb3500ef5cd' (2024-01-27)
  → 'github:getchoo/procfile-nix/7a0ab379a4ab71c9deccaca9fb463e9aaea363d8' (2024-03-14)

---------

Co-authored-by: seth <getchoo@tuta.io>
This commit is contained in:
TheKodeToad 2024-03-18 01:01:46 +00:00 committed by GitHub
parent 1ea08671fb
commit 9d0c022c68
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 492 additions and 296 deletions

View file

@ -1,3 +1 @@
mod resolve_message;
pub use resolve_message::resolve as resolve_message;
pub mod resolve_message;

View file

@ -4,17 +4,14 @@ use eyre::{eyre, Context as _, Result};
use log::{debug, trace};
use once_cell::sync::Lazy;
use poise::serenity_prelude::{
ChannelId, ChannelType, Colour, Context, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter,
Message, MessageId,
Cache, CacheHttp, ChannelId, ChannelType, Colour, Context, CreateEmbed, CreateEmbedAuthor,
CreateEmbedFooter, GuildChannel, Member, Message, MessageId, Permissions,
};
use regex::Regex;
static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"/(https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<serverId>\d+)\/(?<channelId>\d+)\/(?<messageId>\d+)/g;").unwrap()
});
fn find_first_image(msg: &Message) -> Option<String> {
msg.attachments
fn find_first_image(message: &Message) -> Option<String> {
message
.attachments
.iter()
.find(|a| {
a.content_type
@ -25,86 +22,130 @@ fn find_first_image(msg: &Message) -> Option<String> {
.map(|res| res.url.clone())
}
pub async fn resolve(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
async fn member_can_view_channel(
ctx: impl CacheHttp + AsRef<Cache>,
member: &Member,
channel: &GuildChannel,
) -> Result<bool> {
static REQUIRED_PERMISSIONS: Lazy<Permissions> =
Lazy::new(|| Permissions::VIEW_CHANNEL | Permissions::READ_MESSAGE_HISTORY);
let guild = ctx.http().get_guild(channel.guild_id).await?;
let channel_to_check = match &channel.kind {
ChannelType::PublicThread => {
let parent_id = channel
.parent_id
.ok_or_else(|| eyre!("Couldn't get parent of thread {}", channel.id))?;
parent_id
.to_channel(ctx)
.await?
.guild()
.ok_or_else(|| eyre!("Couldn't get GuildChannel from ChannelID {parent_id}!"))?
}
ChannelType::Text | ChannelType::News => channel.to_owned(),
_ => return Ok(false),
};
let can_view = guild
.user_permissions_in(&channel_to_check, member)
.contains(*REQUIRED_PERMISSIONS);
Ok(can_view)
}
pub async fn to_embed(
ctx: impl CacheHttp + AsRef<Cache>,
message: &Message,
) -> Result<CreateEmbed> {
let author = CreateEmbedAuthor::new(message.author.tag()).icon_url(
message
.author
.avatar_url()
.unwrap_or_else(|| message.author.default_avatar_url()),
);
let footer = CreateEmbedFooter::new(format!(
"#{}",
message.channel(ctx).await?.guild().unwrap_or_default().name
));
let mut embed = CreateEmbed::new()
.author(author)
.color(Colour::BLITZ_BLUE)
.timestamp(message.timestamp)
.footer(footer)
.description(format!(
"{}\n\n[Jump to original message]({})",
message.content,
message.link()
));
if !message.attachments.is_empty() {
embed = embed.fields(message.attachments.iter().map(|a| {
(
"Attachments".to_string(),
format!("[{}]({})", a.filename, a.url),
false,
)
}));
if let Some(image) = find_first_image(message) {
embed = embed.image(image);
}
}
Ok(embed)
}
pub async fn from_message(ctx: &Context, msg: &Message) -> Result<Vec<CreateEmbed>> {
static MESSAGE_PATTERN: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?:https?:\/\/)?(?:canary\.|ptb\.)?discord(?:app)?\.com\/channels\/(?<server_id>\d+)\/(?<channel_id>\d+)\/(?<message_id>\d+)").unwrap()
});
let Some(guild_id) = msg.guild_id else {
debug!("Not resolving message in DM");
return Ok(Vec::new());
};
let author = guild_id.member(ctx, msg.author.id).await?;
let matches = MESSAGE_PATTERN
.captures_iter(&msg.content)
.map(|capture| capture.extract());
let mut embeds: Vec<CreateEmbed> = vec![];
for (url, [_server_id, channel_id, message_id]) in matches {
trace!("Attempting to resolve message {message_id} from URL {url}");
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();
let author_can_view = if channel.kind == ChannelType::PublicThread
|| channel.kind == ChannelType::PrivateThread
{
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");
for (url, [target_guild_id, target_channel_id, target_message_id]) in matches {
if target_guild_id != guild_id.to_string() {
debug!("Not resolving message from other server");
continue;
}
trace!("Attempting to resolve message {target_message_id} from URL {url}");
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(|| {
format!("Couldn't get message from ID {message_id} in channel {channel_id}!")
let target_channel = ChannelId::from_str(target_channel_id)?
.to_channel(ctx)
.await?
.guild()
.ok_or_else(|| {
eyre!("Couldn't find GuildChannel from ChannelId {target_channel_id}!")
})?;
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(footer)
.description(format!(
"{}\n\n[Jump to original message]({})",
original_message.content,
original_message.link()
));
if !original_message.attachments.is_empty() {
embed = embed.fields(original_message.attachments.iter().map(|a| {
(
"Attachments".to_string(),
format!("[{}]({})", a.filename, a.url),
false,
)
}));
if let Some(image) = find_first_image(msg) {
embed = embed.image(image);
}
if !member_can_view_channel(ctx, &author, &target_channel).await? {
debug!("Not resolving message for author who can't see it");
continue;
}
let target_message_id = MessageId::from_str(target_message_id)?;
let target_message = target_channel
.message(ctx, target_message_id)
.await
.wrap_err_with(|| {
eyre!("Couldn't find channel message from ID {target_message_id}!")
})?;
let embed = to_embed(ctx, &target_message).await?;
embeds.push(embed);
}