|
|
|
@ -262,8 +262,30 @@ impl<'a> ProcessMention<'a> { |
|
|
|
|
apply_trailing_hashtag_pleroma_bug_workaround(&mut msg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mention = format!("@{user}", user = self.status_acct); |
|
|
|
|
self.send_reply_multipart(mention, msg).await?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !self.announcements.is_empty() { |
|
|
|
|
let mut msg = self.announcements.join("\n"); |
|
|
|
|
debug!("a={}", msg); |
|
|
|
|
|
|
|
|
|
if self.want_markdown { |
|
|
|
|
apply_trailing_hashtag_pleroma_bug_workaround(&mut msg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let msg = format!("**📢 Group announcement**\n{msg}", msg = msg); |
|
|
|
|
self.send_announcement_multipart(&msg).await?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async fn send_reply_multipart(&self, mention : String, msg : String) -> Result<(), GroupError> { |
|
|
|
|
let parts = smart_split(&msg, Some(mention), self.config.get_character_limit()); |
|
|
|
|
for p in parts { |
|
|
|
|
if let Ok(post) = StatusBuilder::new() |
|
|
|
|
.status(format!("@{user} {msg}", user = self.status_acct, msg = msg)) |
|
|
|
|
.status(p) |
|
|
|
|
.content_type(if self.want_markdown { |
|
|
|
|
"text/markdown" |
|
|
|
|
} else { |
|
|
|
@ -272,30 +294,31 @@ impl<'a> ProcessMention<'a> { |
|
|
|
|
.visibility(Visibility::Direct) |
|
|
|
|
.build() |
|
|
|
|
{ |
|
|
|
|
let _ = self.client.new_status(post) |
|
|
|
|
.await.log_error("Failed to post"); |
|
|
|
|
self.client.new_status(post).await?; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Sleep a bit to avoid throttling
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !self.announcements.is_empty() { |
|
|
|
|
let mut msg = self.announcements.join("\n"); |
|
|
|
|
debug!("a={}", msg); |
|
|
|
|
|
|
|
|
|
if self.want_markdown { |
|
|
|
|
apply_trailing_hashtag_pleroma_bug_workaround(&mut msg); |
|
|
|
|
Ok(()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async fn send_announcement_multipart(&self, msg : &str) -> Result<(), GroupError> { |
|
|
|
|
let parts = smart_split(msg, None, self.config.get_character_limit()); |
|
|
|
|
|
|
|
|
|
for p in parts { |
|
|
|
|
let post = StatusBuilder::new() |
|
|
|
|
.status(format!("**📢 Group announcement**\n{msg}", msg = msg)) |
|
|
|
|
.status(p) |
|
|
|
|
.content_type("text/markdown") |
|
|
|
|
.visibility(Visibility::Public) |
|
|
|
|
.build() |
|
|
|
|
.expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self.client.new_status(post) |
|
|
|
|
.await.log_error("Failed to post"); |
|
|
|
|
self.client.new_status(post).await?; |
|
|
|
|
|
|
|
|
|
// Sleep a bit to avoid throttling
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(()) |
|
|
|
@ -772,3 +795,95 @@ fn apply_trailing_hashtag_pleroma_bug_workaround(msg: &mut String) { |
|
|
|
|
msg.push_str(" ."); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn smart_split(msg : &str, prefix: Option<String>, limit: usize) -> Vec<String> { |
|
|
|
|
let prefix = prefix.unwrap_or_default(); |
|
|
|
|
|
|
|
|
|
if msg.len() + prefix.len() < limit { |
|
|
|
|
return vec![format!("{}{}", prefix, msg)]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let mut parts_to_send = vec![]; |
|
|
|
|
let mut this_piece = prefix.clone(); |
|
|
|
|
for l in msg.split("\n") { |
|
|
|
|
if this_piece.len() + l.len() == limit { |
|
|
|
|
this_piece.push_str(l); |
|
|
|
|
parts_to_send.push(std::mem::take(&mut this_piece).trim().to_owned()); |
|
|
|
|
this_piece.push_str(&prefix); |
|
|
|
|
} else if this_piece.len() + l.len() > limit { |
|
|
|
|
let trimmed = this_piece.trim(); |
|
|
|
|
if !trimmed.is_empty() { |
|
|
|
|
parts_to_send.push(trimmed.to_owned()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// start new piece
|
|
|
|
|
this_piece = format!("{}{}", prefix, l); |
|
|
|
|
|
|
|
|
|
while this_piece.len() > limit { |
|
|
|
|
let to_send = if let Some(last_space) = (&this_piece[..limit]).rfind(' ') { |
|
|
|
|
let mut p = this_piece.split_off(last_space); |
|
|
|
|
std::mem::swap(&mut p, &mut this_piece); |
|
|
|
|
p |
|
|
|
|
} else { |
|
|
|
|
let mut p = this_piece.split_off(limit); |
|
|
|
|
std::mem::swap(&mut p, &mut this_piece); |
|
|
|
|
p |
|
|
|
|
}; |
|
|
|
|
parts_to_send.push(to_send); |
|
|
|
|
this_piece = format!("{}{}", prefix, this_piece); |
|
|
|
|
} |
|
|
|
|
this_piece.push('\n'); |
|
|
|
|
} else { |
|
|
|
|
this_piece.push_str(l); |
|
|
|
|
this_piece.push('\n'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
parts_to_send |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod test { |
|
|
|
|
#[test] |
|
|
|
|
fn test_smart_split1() { |
|
|
|
|
let to_split = "a234567890\nb234567890\nc234567890\nd234\n67890\ne234567890\n"; |
|
|
|
|
|
|
|
|
|
let parts = super::smart_split(to_split, None, 10); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
"a234567890".to_string(), |
|
|
|
|
"b234567890".to_string(), |
|
|
|
|
"c234567890".to_string(), |
|
|
|
|
"d234\n67890".to_string(), |
|
|
|
|
"e234567890".to_string(), |
|
|
|
|
], parts); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_smart_split2() { |
|
|
|
|
let to_split = "foo\nbar\nbaz"; |
|
|
|
|
|
|
|
|
|
let parts = super::smart_split(to_split, None, 1000); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
"foo\nbar\nbaz".to_string(), |
|
|
|
|
], parts); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_smart_split3() { |
|
|
|
|
let to_split = "foo\nbar\nbaz"; |
|
|
|
|
let parts = super::smart_split(to_split, Some("PREFIX".to_string()), 1000); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
"PREFIXfoo\nbar\nbaz".to_string(), |
|
|
|
|
], parts); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn test_smart_split4() { |
|
|
|
|
let to_split = "1234\n56\n7"; |
|
|
|
|
let parts = super::smart_split(to_split, Some("PREFIX".to_string()), 10); |
|
|
|
|
assert_eq!(vec![ |
|
|
|
|
"PREFIX1234".to_string(), |
|
|
|
|
"PREFIX56\n7".to_string(), |
|
|
|
|
], parts); |
|
|
|
|
} |
|
|
|
|
} |