|
|
|
@ -2,19 +2,19 @@ use std::collections::HashSet; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
use std::time::{Duration, Instant}; |
|
|
|
|
|
|
|
|
|
use elefren::{FediClient, StatusBuilder}; |
|
|
|
|
use elefren::debug::EventDisplay; |
|
|
|
|
use elefren::debug::NotificationDisplay; |
|
|
|
|
use elefren::entities::event::Event; |
|
|
|
|
use elefren::entities::notification::{Notification, NotificationType}; |
|
|
|
|
use elefren::status_builder::Visibility; |
|
|
|
|
use elefren::{FediClient, StatusBuilder}; |
|
|
|
|
use futures::StreamExt; |
|
|
|
|
|
|
|
|
|
use crate::command::StatusCommand; |
|
|
|
|
use crate::error::GroupError; |
|
|
|
|
use crate::store::ConfigStore; |
|
|
|
|
use crate::store::data::GroupConfig; |
|
|
|
|
use crate::utils::{LogError, normalize_acct}; |
|
|
|
|
use crate::store::ConfigStore; |
|
|
|
|
use crate::utils::{normalize_acct, LogError}; |
|
|
|
|
|
|
|
|
|
/// This is one group's config store capable of persistence
|
|
|
|
|
#[derive(Debug)] |
|
|
|
@ -91,22 +91,29 @@ impl GroupHandle { |
|
|
|
|
|
|
|
|
|
loop { |
|
|
|
|
if next_save < Instant::now() { |
|
|
|
|
self.save_if_needed().await |
|
|
|
|
self.save_if_needed() |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to save group"); |
|
|
|
|
next_save = Instant::now() + PERIODIC_SAVE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let timeout = next_save.saturating_duration_since(Instant::now()) |
|
|
|
|
let timeout = next_save |
|
|
|
|
.saturating_duration_since(Instant::now()) |
|
|
|
|
.min(PING_INTERVAL) |
|
|
|
|
.max(Duration::from_secs(1)); |
|
|
|
|
|
|
|
|
|
match tokio::time::timeout(timeout, events.next()).await { |
|
|
|
|
Ok(Some(event)) => { |
|
|
|
|
debug!("(@{}) Event: {}", self.config.get_acct(), EventDisplay(&event)); |
|
|
|
|
debug!( |
|
|
|
|
"(@{}) Event: {}", |
|
|
|
|
self.config.get_acct(), |
|
|
|
|
EventDisplay(&event) |
|
|
|
|
); |
|
|
|
|
match event { |
|
|
|
|
Event::Update(_status) => {} |
|
|
|
|
Event::Notification(n) => { |
|
|
|
|
self.handle_notification(n).await |
|
|
|
|
self.handle_notification(n) |
|
|
|
|
.await |
|
|
|
|
.log_error("Error handling a notification"); |
|
|
|
|
} |
|
|
|
|
Event::Delete(_id) => {} |
|
|
|
@ -114,7 +121,10 @@ impl GroupHandle { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Ok(None) => { |
|
|
|
|
warn!("Group @{} socket closed, restarting...", self.config.get_acct()); |
|
|
|
|
warn!( |
|
|
|
|
"Group @{} socket closed, restarting...", |
|
|
|
|
self.config.get_acct() |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
Err(_) => { |
|
|
|
@ -176,10 +186,13 @@ impl GroupHandle { |
|
|
|
|
// Someone tagged the group in OP, boost it.
|
|
|
|
|
info!("Boosting OP mention"); |
|
|
|
|
tokio::time::sleep(DELAY_BEFORE_ACTION).await; |
|
|
|
|
self.client.reblog(&status.id).await |
|
|
|
|
self.client |
|
|
|
|
.reblog(&status.id) |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to boost"); |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("You are not allowed to post to this group")); |
|
|
|
|
replies |
|
|
|
|
.push("You are not allowed to post to this group".to_string()); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
debug!("Not OP, ignore mention"); |
|
|
|
@ -200,7 +213,10 @@ impl GroupHandle { |
|
|
|
|
if can_write { |
|
|
|
|
do_boost_prev_post = status.in_reply_to_id.is_some(); |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("You are not allowed to share to this group")); |
|
|
|
|
replies.push( |
|
|
|
|
"You are not allowed to share to this group" |
|
|
|
|
.to_string(), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::BanUser(u) => { |
|
|
|
@ -210,17 +226,24 @@ impl GroupHandle { |
|
|
|
|
match self.config.ban_user(&u, true) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
replies.push(format!("User {} banned from group!", u)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"User {} banned from group!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
// no announcement here
|
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
replies.push(format!("Failed to ban user {}: {}", u, e)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Failed to ban user {}: {}", |
|
|
|
|
u, e |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage user bans")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can manage user bans".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::UnbanUser(u) => { |
|
|
|
@ -234,13 +257,14 @@ impl GroupHandle { |
|
|
|
|
|
|
|
|
|
// no announcement here
|
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
Err(_) => { |
|
|
|
|
unreachable!() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage user bans")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can manage user bans".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::BanServer(s) => { |
|
|
|
@ -249,16 +273,26 @@ impl GroupHandle { |
|
|
|
|
match self.config.ban_server(&s, true) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
announcements.push(format!("Server \"{}\" has been banned.", s)); |
|
|
|
|
replies.push(format!("Server {} banned from group!", s)); |
|
|
|
|
announcements.push(format!( |
|
|
|
|
"Server \"{}\" has been banned.", |
|
|
|
|
s |
|
|
|
|
)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Server {} banned from group!", |
|
|
|
|
s |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
replies.push(format!("Failed to ban server {}: {}", s, e)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Failed to ban server {}: {}", |
|
|
|
|
s, e |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage server bans")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can manage server bans".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::UnbanServer(s) => { |
|
|
|
@ -267,16 +301,21 @@ impl GroupHandle { |
|
|
|
|
match self.config.ban_server(&s, false) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
announcements.push(format!("Server \"{}\" has been un-banned.", s)); |
|
|
|
|
replies.push(format!("Server {} un-banned!", s)); |
|
|
|
|
announcements.push(format!( |
|
|
|
|
"Server \"{}\" has been un-banned.", |
|
|
|
|
s |
|
|
|
|
)); |
|
|
|
|
replies |
|
|
|
|
.push(format!("Server {} un-banned!", s)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
Err(_) => { |
|
|
|
|
unreachable!() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage server bans")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can manage server bans".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::AddMember(u) => { |
|
|
|
@ -286,19 +325,28 @@ impl GroupHandle { |
|
|
|
|
match self.config.set_member(&u, true) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
replies.push(format!("User {} added to the group!", u)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"User {} added to the group!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
|
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
announcements.push(format!("Welcome new member @{} to the group!", u)); |
|
|
|
|
announcements.push(format!( |
|
|
|
|
"Welcome new member @{} to the group!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
replies.push(format!("Failed to add user {} to group: {}", u, e)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Failed to add user {} to group: {}", |
|
|
|
|
u, e |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage members")); |
|
|
|
|
replies.push("Only admins can manage members".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::RemoveMember(u) => { |
|
|
|
@ -308,15 +356,18 @@ impl GroupHandle { |
|
|
|
|
match self.config.set_member(&u, false) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
replies.push(format!("User {} removed from the group.", u)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"User {} removed from the group.", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
Err(_) => { |
|
|
|
|
unreachable!() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage members")); |
|
|
|
|
replies.push("Only admins can manage members".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::GrantAdmin(u) => { |
|
|
|
@ -326,16 +377,25 @@ impl GroupHandle { |
|
|
|
|
match self.config.set_admin(&u, true) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
replies.push(format!("User {} is now a group admin!", u)); |
|
|
|
|
announcements.push(format!("User @{} can now manage this group!", u)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"User {} is now a group admin!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
announcements.push(format!( |
|
|
|
|
"User @{} can now manage this group!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
replies.push(format!("Failed to make user {} a group admin: {}", u, e)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Failed to make user {} a group admin: {}", |
|
|
|
|
u, e |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage admins")); |
|
|
|
|
replies.push("Only admins can manage admins".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::RemoveAdmin(u) => { |
|
|
|
@ -345,16 +405,25 @@ impl GroupHandle { |
|
|
|
|
match self.config.set_admin(&u, false) { |
|
|
|
|
Ok(_) => { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
replies.push(format!("User {} is no longer a group admin!", u)); |
|
|
|
|
announcements.push(format!("User @{} no longer manages this group.", u)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"User {} is no longer a group admin!", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
announcements.push(format!( |
|
|
|
|
"User @{} no longer manages this group.", |
|
|
|
|
u |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
Err(e) => { |
|
|
|
|
replies.push(format!("Failed to revoke {}'s group admin: {}", u, e)); |
|
|
|
|
replies.push(format!( |
|
|
|
|
"Failed to revoke {}'s group admin: {}", |
|
|
|
|
u, e |
|
|
|
|
)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can manage admins")); |
|
|
|
|
replies.push("Only admins can manage admins".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::OpenGroup => { |
|
|
|
@ -362,11 +431,14 @@ impl GroupHandle { |
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
self.config.set_member_only(false); |
|
|
|
|
replies.push(format!("Group changed to open-access")); |
|
|
|
|
announcements.push(format!("This group is now open-access!")); |
|
|
|
|
replies |
|
|
|
|
.push("Group changed to open-access".to_string()); |
|
|
|
|
announcements |
|
|
|
|
.push("This group is now open-access!".to_string()); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can set group options")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can set group options".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::CloseGroup => { |
|
|
|
@ -374,24 +446,27 @@ impl GroupHandle { |
|
|
|
|
if !self.config.is_member_only() { |
|
|
|
|
any_admin_cmd = true; |
|
|
|
|
self.config.set_member_only(true); |
|
|
|
|
replies.push(format!("Group changed to member-only")); |
|
|
|
|
announcements.push(format!("This group is now member-only!")); |
|
|
|
|
replies |
|
|
|
|
.push("Group changed to member-only".to_string()); |
|
|
|
|
announcements |
|
|
|
|
.push("This group is now member-only!".to_string()); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("Only admins can set group options")); |
|
|
|
|
replies |
|
|
|
|
.push("Only admins can set group options".to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
StatusCommand::Help => { |
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
let mut s = "This is a member-only group. ".to_string(); |
|
|
|
|
if self.config.can_write(¬if_acct) { |
|
|
|
|
s.push_str("*You are not a member, ask one of the admins to add you.*"); |
|
|
|
|
} else { |
|
|
|
|
if is_admin { |
|
|
|
|
s.push_str("*You are an admin.*"); |
|
|
|
|
} else { |
|
|
|
|
s.push_str("*You are a member.*"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
s.push_str("*You are not a member, ask one of the admins to add you.*"); |
|
|
|
|
} |
|
|
|
|
replies.push(s); |
|
|
|
|
} else { |
|
|
|
@ -409,7 +484,9 @@ impl GroupHandle { |
|
|
|
|
**Supported commands:**\n\ |
|
|
|
|
`/ignore, /i` - make the group completely ignore the post\n\ |
|
|
|
|
`/members, /who` - show group members / admins\n\ |
|
|
|
|
`/boost, /b` - boost the replied-to post into the group".to_string()); |
|
|
|
|
`/boost, /b` - boost the replied-to post into the group" |
|
|
|
|
.to_string(), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
replies.push("`/leave` - leave the group".to_string()); |
|
|
|
@ -433,8 +510,10 @@ impl GroupHandle { |
|
|
|
|
if is_admin { |
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
replies.push("Group members:".to_string()); |
|
|
|
|
let admins = self.config.get_admins().collect::<HashSet<_>>(); |
|
|
|
|
let mut members = self.config.get_members().collect::<Vec<_>>(); |
|
|
|
|
let admins = |
|
|
|
|
self.config.get_admins().collect::<HashSet<_>>(); |
|
|
|
|
let mut members = |
|
|
|
|
self.config.get_members().collect::<Vec<_>>(); |
|
|
|
|
members.extend(admins.iter()); |
|
|
|
|
members.sort(); |
|
|
|
|
members.dedup(); |
|
|
|
@ -442,7 +521,7 @@ impl GroupHandle { |
|
|
|
|
if admins.contains(&m) { |
|
|
|
|
replies.push(format!("{} [admin]", m)); |
|
|
|
|
} else { |
|
|
|
|
replies.push(format!("{}", m)); |
|
|
|
|
replies.push(m.to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
@ -454,10 +533,11 @@ impl GroupHandle { |
|
|
|
|
|
|
|
|
|
if show_admins { |
|
|
|
|
replies.push("Group admins:".to_string()); |
|
|
|
|
let mut admins = self.config.get_admins().collect::<Vec<_>>(); |
|
|
|
|
let mut admins = |
|
|
|
|
self.config.get_admins().collect::<Vec<_>>(); |
|
|
|
|
admins.sort(); |
|
|
|
|
for a in admins { |
|
|
|
|
replies.push(format!("{}", a)); |
|
|
|
|
replies.push(a.to_string()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -475,7 +555,9 @@ impl GroupHandle { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if do_boost_prev_post { |
|
|
|
|
self.client.reblog(&status.in_reply_to_id.as_ref().unwrap()).await |
|
|
|
|
self.client |
|
|
|
|
.reblog(&status.in_reply_to_id.as_ref().unwrap()) |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to boost"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -486,9 +568,14 @@ impl GroupHandle { |
|
|
|
|
.status(format!("@{user}\n{msg}", user = notif_acct, msg = r)) |
|
|
|
|
.content_type("text/markdown") |
|
|
|
|
.visibility(Visibility::Direct) |
|
|
|
|
.build().expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self.client.new_status(post).await.log_error("Failed to post"); |
|
|
|
|
.build() |
|
|
|
|
.expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self |
|
|
|
|
.client |
|
|
|
|
.new_status(post) |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to post"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !announcements.is_empty() { |
|
|
|
@ -497,9 +584,14 @@ impl GroupHandle { |
|
|
|
|
.status(format!("**📢 Group announcement**\n{msg}", msg = msg)) |
|
|
|
|
.content_type("text/markdown") |
|
|
|
|
.visibility(Visibility::Unlisted) |
|
|
|
|
.build().expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self.client.new_status(post).await.log_error("Failed to post"); |
|
|
|
|
.build() |
|
|
|
|
.expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self |
|
|
|
|
.client |
|
|
|
|
.new_status(post) |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to post"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if any_admin_cmd { |
|
|
|
@ -511,28 +603,36 @@ impl GroupHandle { |
|
|
|
|
info!("New follower!"); |
|
|
|
|
tokio::time::sleep(Duration::from_millis(500)).await; |
|
|
|
|
|
|
|
|
|
let text = if self.config.is_member_only() { |
|
|
|
|
// Admins are listed without @, so they won't become handles here.
|
|
|
|
|
// Tagging all admins would be annoying.
|
|
|
|
|
let mut admins = self.config.get_admins().cloned().collect::<Vec<_>>(); |
|
|
|
|
admins.sort(); |
|
|
|
|
format!( |
|
|
|
|
let text = |
|
|
|
|
if self.config.is_member_only() { |
|
|
|
|
// Admins are listed without @, so they won't become handles here.
|
|
|
|
|
// Tagging all admins would be annoying.
|
|
|
|
|
let mut admins = self.config.get_admins().cloned().collect::<Vec<_>>(); |
|
|
|
|
admins.sort(); |
|
|
|
|
format!( |
|
|
|
|
"@{user} welcome to the group! This is a member-only group, you won't be \ |
|
|
|
|
able to post. Ask the group admins if you wish to join!\n\n\ |
|
|
|
|
Admins: {admins}", user = notif_acct, admins = admins.join(", ")) |
|
|
|
|
} else { |
|
|
|
|
format!( |
|
|
|
|
"@{user} welcome to the group! \ |
|
|
|
|
To share a post, tag the group user. Use /help for more info.", user = notif_acct) |
|
|
|
|
}; |
|
|
|
|
} else { |
|
|
|
|
format!( |
|
|
|
|
"@{user} welcome to the group! \ |
|
|
|
|
To share a post, tag the group user. Use /help for more info.", |
|
|
|
|
user = notif_acct |
|
|
|
|
) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let post = StatusBuilder::new() |
|
|
|
|
.status(text) |
|
|
|
|
.content_type("text/markdown") |
|
|
|
|
.visibility(Visibility::Direct) |
|
|
|
|
.build().expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self.client.new_status(post).await.log_error("Failed to post"); |
|
|
|
|
.build() |
|
|
|
|
.expect("error build status"); |
|
|
|
|
|
|
|
|
|
let _ = self |
|
|
|
|
.client |
|
|
|
|
.new_status(post) |
|
|
|
|
.await |
|
|
|
|
.log_error("Failed to post"); |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
@ -575,10 +675,11 @@ impl GroupHandle { |
|
|
|
|
|
|
|
|
|
for n in notifs_to_handle { |
|
|
|
|
debug!("Handling missed notification: {}", NotificationDisplay(&n)); |
|
|
|
|
self.handle_notification(n).await |
|
|
|
|
self.handle_notification(n) |
|
|
|
|
.await |
|
|
|
|
.log_error("Error handling a notification"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Ok(true); |
|
|
|
|
Ok(true) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|