mirror of
https://git.ondrovo.com/MightyPork/group-actor.git
synced 2024-12-21 22:56:33 +00:00
fix hashtag not working in mention
This commit is contained in:
parent
7f14dbc215
commit
0d37425c32
3 changed files with 92 additions and 59 deletions
|
@ -2,19 +2,25 @@ use std::cmp::Ordering;
|
|||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use elefren::{FediClient, SearchType, StatusBuilder};
|
||||
use elefren::entities::account::Account;
|
||||
use elefren::entities::prelude::Status;
|
||||
use elefren::status_builder::Visibility;
|
||||
use elefren::{FediClient, SearchType, StatusBuilder};
|
||||
|
||||
use crate::command::{StatusCommand, RE_NOBOT_TAG};
|
||||
use crate::command::{RE_NOBOT_TAG, StatusCommand};
|
||||
use crate::error::GroupError;
|
||||
use crate::group_handler::GroupHandle;
|
||||
use crate::store::group_config::GroupConfig;
|
||||
use crate::store::CommonConfig;
|
||||
use crate::store::group_config::GroupConfig;
|
||||
use crate::tr::TranslationTable;
|
||||
use crate::utils;
|
||||
use crate::utils::{normalize_acct, LogError};
|
||||
use crate::utils::{LogError, normalize_acct, VisExt};
|
||||
|
||||
use crate::{
|
||||
grp_debug,
|
||||
grp_warn,
|
||||
grp_info
|
||||
};
|
||||
|
||||
pub struct ProcessMention<'a> {
|
||||
status: Status,
|
||||
|
@ -38,7 +44,7 @@ impl<'a> ProcessMention<'a> {
|
|||
}
|
||||
|
||||
async fn lookup_acct_id(&self, acct: &str, followed: bool) -> Result<Option<String>, GroupError> {
|
||||
debug!("Looking up user ID by acct: {}", acct);
|
||||
grp_debug!(self, "Looking up user ID by acct: {}", acct);
|
||||
|
||||
match tokio::time::timeout(
|
||||
Duration::from_secs(5),
|
||||
|
@ -48,7 +54,7 @@ impl<'a> ProcessMention<'a> {
|
|||
.await
|
||||
{
|
||||
Err(_) => {
|
||||
warn!("Account lookup timeout!");
|
||||
grp_warn!(self, "Account lookup timeout!");
|
||||
Err(GroupError::ApiTimeout)
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
|
@ -60,14 +66,14 @@ impl<'a> ProcessMention<'a> {
|
|||
// XXX limit is 1!
|
||||
let acct_normalized = normalize_acct(&item.acct, &self.group_acct)?;
|
||||
if acct_normalized == acct {
|
||||
debug!("Search done, account found: {}", item.acct);
|
||||
grp_debug!(self, "Search done, account found: {}", item.acct);
|
||||
return Ok(Some(item.id));
|
||||
} else {
|
||||
warn!("Found wrong account: {}", item.acct);
|
||||
grp_warn!(self, "Found wrong account: {}", item.acct);
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Search done, nothing found");
|
||||
grp_debug!(self, "Search done, nothing found");
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
@ -101,14 +107,14 @@ impl<'a> ProcessMention<'a> {
|
|||
}
|
||||
|
||||
async fn follow_user_by_id(&self, id: &str) -> Result<(), GroupError> {
|
||||
debug!("Trying to follow user #{}", id);
|
||||
grp_debug!(self, "Trying to follow user #{}", id);
|
||||
self.client.follow(id).await?;
|
||||
self.delay_after_post().await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unfollow_user_by_id(&self, id: &str) -> Result<(), GroupError> {
|
||||
debug!("Trying to unfollow user #{}", id);
|
||||
grp_debug!(self, "Trying to unfollow user #{}", id);
|
||||
self.client.unfollow(id).await?;
|
||||
self.delay_after_post().await;
|
||||
Ok(())
|
||||
|
@ -119,7 +125,7 @@ impl<'a> ProcessMention<'a> {
|
|||
let status_acct = normalize_acct(&status.account.acct, &group_acct)?.to_string();
|
||||
|
||||
if gh.config.is_banned(&status_acct) {
|
||||
warn!("Status author {} is banned!", status_acct);
|
||||
grp_warn!(gh, "Status author {} is banned!", status_acct);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -163,7 +169,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.handle_post_with_no_commands().await;
|
||||
} else {
|
||||
if commands.contains(&StatusCommand::Ignore) {
|
||||
debug!("Notif ignored because of ignore command");
|
||||
grp_debug!(self, "Notif ignored because of ignore command");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -259,7 +265,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.delay_after_post().await;
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Can't reblog: {}", e);
|
||||
grp_warn!(self, "Can't reblog: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,9 +273,9 @@ impl<'a> ProcessMention<'a> {
|
|||
|
||||
if !self.replies.is_empty() {
|
||||
let mut msg = std::mem::take(&mut self.replies);
|
||||
debug!("r={}", msg);
|
||||
grp_debug!(self, "r={}", msg);
|
||||
|
||||
apply_trailing_hashtag_pleroma_bug_workaround(&mut msg);
|
||||
self.apply_trailing_hashtag_pleroma_bug_workaround(&mut msg);
|
||||
|
||||
let mention = crate::tr!(self, "mention_prefix", user = &self.status_acct);
|
||||
self.send_reply_multipart(mention, msg).await?;
|
||||
|
@ -277,9 +283,9 @@ impl<'a> ProcessMention<'a> {
|
|||
|
||||
if !self.announcements.is_empty() {
|
||||
let mut msg = std::mem::take(&mut self.announcements);
|
||||
debug!("a={}", msg);
|
||||
grp_debug!(self, "a={}", msg);
|
||||
|
||||
apply_trailing_hashtag_pleroma_bug_workaround(&mut msg);
|
||||
self.apply_trailing_hashtag_pleroma_bug_workaround(&mut msg);
|
||||
|
||||
let msg = crate::tr!(self, "group_announcement", message = &msg);
|
||||
self.send_announcement_multipart(&msg).await?;
|
||||
|
@ -339,24 +345,45 @@ impl<'a> ProcessMention<'a> {
|
|||
}
|
||||
|
||||
async fn handle_post_with_no_commands(&mut self) {
|
||||
debug!("No commands in post");
|
||||
if self.status.in_reply_to_id.is_none() {
|
||||
if self.can_write {
|
||||
grp_debug!(self, "No commands in post");
|
||||
|
||||
if self.status.visibility.is_private() {
|
||||
grp_debug!(self, "Mention is private, discard");
|
||||
return;
|
||||
}
|
||||
|
||||
if self.can_write {
|
||||
if self.status.in_reply_to_id.is_none() {
|
||||
// Someone tagged the group in OP, boost it.
|
||||
info!("Boosting OP mention");
|
||||
grp_info!(self, "Boosting OP mention");
|
||||
// tokio::time::sleep(DELAY_BEFORE_ACTION).await;
|
||||
self.reblog_status().await;
|
||||
// Otherwise, don't react
|
||||
} else {
|
||||
warn!("User @{} can't post to group!", self.status_acct);
|
||||
// Check for tags
|
||||
let tags = crate::command::parse_status_tags(&self.status.content);
|
||||
grp_debug!(self, "Tags in mention: {:?}", tags);
|
||||
|
||||
for t in tags {
|
||||
if self.config.is_tag_followed(&t) {
|
||||
grp_info!(self, "REBLOG #{} STATUS", t);
|
||||
self.client.reblog(&self.status.id).await.log_error("Failed to reblog");
|
||||
self.delay_after_post().await;
|
||||
return;
|
||||
} else {
|
||||
grp_debug!(self, "#{} is not a group tag", t);
|
||||
}
|
||||
}
|
||||
|
||||
grp_debug!(self, "Not OP & no tags, ignore mention");
|
||||
}
|
||||
// Otherwise, don't react
|
||||
} else {
|
||||
debug!("Not OP, ignore mention");
|
||||
grp_warn!(self, "User @{} can't post to group!", self.status_acct);
|
||||
}
|
||||
}
|
||||
|
||||
async fn cmd_announce(&mut self, msg: String) {
|
||||
info!("Sending PSA");
|
||||
grp_info!(self, "Sending PSA");
|
||||
self.add_announcement(msg);
|
||||
}
|
||||
|
||||
|
@ -364,7 +391,7 @@ impl<'a> ProcessMention<'a> {
|
|||
if self.can_write {
|
||||
self.do_boost_prev_post = self.status.in_reply_to_id.is_some();
|
||||
} else {
|
||||
warn!("User @{} can't share to group!", self.status_acct);
|
||||
grp_warn!(self, "User @{} can't share to group!", self.status_acct);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,19 +425,19 @@ impl<'a> ProcessMention<'a> {
|
|||
// This is a post sent by the group user, likely an announcement.
|
||||
// Undo here means delete it.
|
||||
if self.is_admin {
|
||||
info!("Deleting group post #{}", parent_status_id);
|
||||
grp_info!(self, "Deleting group post #{}", parent_status_id);
|
||||
self.client.delete_status(parent_status_id).await?;
|
||||
self.delay_after_post().await;
|
||||
} else {
|
||||
warn!("Only admin can delete posts made by the group user");
|
||||
grp_warn!(self, "Only admin can delete posts made by the group user");
|
||||
}
|
||||
} else if self.is_admin || parent_account_id == &self.status_user_id {
|
||||
info!("Un-reblogging post #{}", parent_status_id);
|
||||
grp_info!(self, "Un-reblogging post #{}", parent_status_id);
|
||||
// User unboosting own post boosted by accident, or admin doing it
|
||||
self.client.unreblog(parent_status_id).await?;
|
||||
self.delay_after_post().await;
|
||||
} else {
|
||||
warn!("Only the author and admins can undo reblogs");
|
||||
grp_warn!(self, "Only the author and admins can undo reblogs");
|
||||
// XXX this means when someone /b's someone else's post to a group,
|
||||
// they then can't reverse that (only admin or the post's author can)
|
||||
}
|
||||
|
@ -436,7 +463,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_ban_user_fail_already", user = &u));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -458,7 +485,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_unban_user_fail_already", user = &u));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -478,7 +505,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_ban_server_fail_already", server = s));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -497,7 +524,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_unban_server_fail_already", server = s));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,7 +543,7 @@ impl<'a> ProcessMention<'a> {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -534,7 +561,7 @@ impl<'a> ProcessMention<'a> {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -548,7 +575,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_add_tag_fail_already", tag = &tag));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,7 +588,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_remove_tag_fail_already", tag = &tag));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -584,7 +611,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_admin_fail_already", user = &u));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -605,7 +632,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_unadmin_fail_already", user = &u));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -619,7 +646,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_open_resp_already"));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,7 +659,7 @@ impl<'a> ProcessMention<'a> {
|
|||
self.add_reply(crate::tr!(self, "cmd_close_resp_already"));
|
||||
}
|
||||
} else {
|
||||
warn!("Ignore cmd, user not admin");
|
||||
grp_warn!(self, "Ignore cmd, user not admin");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -701,7 +728,7 @@ impl<'a> ProcessMention<'a> {
|
|||
|
||||
async fn cmd_join(&mut self) {
|
||||
if self.config.is_member_or_admin(&self.status_acct) {
|
||||
debug!("Already member or admin, try to follow-back again");
|
||||
grp_debug!(self, "Already member or admin, try to follow-back again");
|
||||
// Already a member, so let's try to follow the user
|
||||
// again, maybe first time it failed
|
||||
self.follow_user_by_id(&self.status_user_id).await.log_error("Failed to follow");
|
||||
|
@ -765,13 +792,13 @@ impl<'a> ProcessMention<'a> {
|
|||
async fn delay_after_post(&self) {
|
||||
tokio::time::sleep(Duration::from_secs_f64(self.cc.delay_after_post_s)).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_trailing_hashtag_pleroma_bug_workaround(msg: &mut String) {
|
||||
if crate::command::RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match(msg) {
|
||||
// if a status ends with a hashtag, pleroma will fuck it up
|
||||
debug!("Adding \" .\" to fix pleroma hashtag eating bug!");
|
||||
msg.push_str(" .");
|
||||
fn apply_trailing_hashtag_pleroma_bug_workaround(&self, msg: &mut String) {
|
||||
if crate::command::RE_HASHTAG_TRIGGERING_PLEROMA_BUG.is_match(msg) {
|
||||
// if a status ends with a hashtag, pleroma will fuck it up
|
||||
grp_debug!(self, "Adding \" .\" to fix pleroma hashtag eating bug!");
|
||||
msg.push_str(" .");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -785,29 +812,29 @@ fn smart_split(msg: &str, prefix: Option<String>, limit: usize) -> Vec<String> {
|
|||
let mut parts_to_send = vec![];
|
||||
let mut this_piece = prefix.clone();
|
||||
for l in msg.split('\n') {
|
||||
println!("* Line: {:?}", l);
|
||||
// println!("* Line: {:?}", l);
|
||||
|
||||
match (this_piece.len() + l.len()).cmp(&limit) {
|
||||
Ordering::Less => {
|
||||
println!("append line");
|
||||
// println!("append line");
|
||||
// this line still fits comfortably
|
||||
this_piece.push_str(l);
|
||||
this_piece.push('\n');
|
||||
}
|
||||
Ordering::Equal => {
|
||||
println!("exactly fits within limit");
|
||||
// println!("exactly fits within limit");
|
||||
// this line exactly reaches the limit
|
||||
this_piece.push_str(l);
|
||||
parts_to_send.push(std::mem::take(&mut this_piece).trim().to_owned());
|
||||
this_piece.push_str(&prefix);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
println!("too long to append (already {} + new {})", this_piece.len(), l.len());
|
||||
// println!("too long to append (already {} + new {})", this_piece.len(), l.len());
|
||||
// line too long to append
|
||||
if this_piece != prefix {
|
||||
let trimmed = this_piece.trim();
|
||||
if !trimmed.is_empty() {
|
||||
println!("flush buffer: {:?}", trimmed);
|
||||
// println!("flush buffer: {:?}", trimmed);
|
||||
parts_to_send.push(trimmed.to_owned());
|
||||
}
|
||||
}
|
||||
|
@ -818,18 +845,18 @@ fn smart_split(msg: &str, prefix: Option<String>, limit: usize) -> Vec<String> {
|
|||
while this_piece.len() > limit {
|
||||
// line too long, try splitting at the last space, if any
|
||||
let to_send = if let Some(last_space) = (&this_piece[..=limit]).rfind(' ') {
|
||||
println!("line split at word boundary");
|
||||
// println!("line split at word boundary");
|
||||
let mut p = this_piece.split_off(last_space + 1);
|
||||
std::mem::swap(&mut p, &mut this_piece);
|
||||
p
|
||||
} else {
|
||||
println!("line split at exact len (no word boundary found)");
|
||||
// println!("line split at exact len (no word boundary found)");
|
||||
let mut p = this_piece.split_off(limit);
|
||||
std::mem::swap(&mut p, &mut this_piece);
|
||||
p
|
||||
};
|
||||
let part_trimmed = to_send.trim();
|
||||
println!("flush buffer: {:?}", part_trimmed);
|
||||
// println!("flush buffer: {:?}", part_trimmed);
|
||||
parts_to_send.push(part_trimmed.to_owned());
|
||||
this_piece = format!("{}{}", prefix, this_piece.trim());
|
||||
}
|
||||
|
@ -841,7 +868,7 @@ fn smart_split(msg: &str, prefix: Option<String>, limit: usize) -> Vec<String> {
|
|||
if this_piece != prefix {
|
||||
let leftover_trimmed = this_piece.trim();
|
||||
if !leftover_trimmed.is_empty() {
|
||||
println!("flush buffer: {:?}", leftover_trimmed);
|
||||
// println!("flush buffer: {:?}", leftover_trimmed);
|
||||
parts_to_send.push(leftover_trimmed.to_owned());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ impl Default for GroupInternal {
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! grp_debug {
|
||||
($self:ident, $f:expr) => {
|
||||
::log::debug!(concat!("(@{}) ", $f), $self.config.get_acct());
|
||||
|
@ -56,6 +57,7 @@ macro_rules! grp_debug {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! grp_info {
|
||||
($self:ident, $f:expr) => {
|
||||
::log::info!(concat!("(@{}) ", $f), $self.config.get_acct());
|
||||
|
@ -65,6 +67,7 @@ macro_rules! grp_info {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! grp_trace {
|
||||
($self:ident, $f:expr) => {
|
||||
::log::trace!(concat!("(@{}) ", $f), $self.config.get_acct());
|
||||
|
@ -74,6 +77,7 @@ macro_rules! grp_trace {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! grp_warn {
|
||||
($self:ident, $f:expr) => {
|
||||
::log::warn!(concat!("(@{}) ", $f), $self.config.get_acct());
|
||||
|
@ -83,6 +87,7 @@ macro_rules! grp_warn {
|
|||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! grp_error {
|
||||
($self:ident, $f:expr) => {
|
||||
::log::error!(concat!("(@{}) ", $f), $self.config.get_acct());
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::utils::acct_to_server;
|
|||
|
||||
mod command;
|
||||
mod error;
|
||||
#[macro_use]
|
||||
mod group_handler;
|
||||
mod store;
|
||||
mod utils;
|
||||
|
|
Loading…
Reference in a new issue