bot: Implement hook deduplication
Avoid sending similar (update) event within a certain time frame (5m). Signed-off-by: pep <pep@bouah.net>
This commit is contained in:
parent
990829d72c
commit
0746afb443
1 changed files with 77 additions and 1 deletions
78
src/bot.rs
78
src/bot.rs
|
|
@ -14,8 +14,9 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Error;
|
||||
use crate::hooks::{Hook, format_hook};
|
||||
use crate::hooks::{Hook, IssueAction, MergeRequestAction, format_hook};
|
||||
|
||||
use chrono::TimeDelta;
|
||||
use log::debug;
|
||||
use tokio::{signal::ctrl_c, sync::mpsc};
|
||||
use xmpp::jid::{BareJid, Jid, ResourcePart};
|
||||
|
|
@ -31,6 +32,9 @@ pub struct XmppClient {
|
|||
rooms: Vec<BareJid>,
|
||||
nickname: ResourcePart,
|
||||
admins: Vec<BareJid>,
|
||||
/// Keep around messages we've sent recently so we're able to prevent spamming the same type of
|
||||
/// messages
|
||||
recent_hooks: Vec<Hook>,
|
||||
}
|
||||
|
||||
impl XmppClient {
|
||||
|
|
@ -59,6 +63,7 @@ impl XmppClient {
|
|||
rooms,
|
||||
nickname,
|
||||
admins,
|
||||
recent_hooks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,8 +130,79 @@ impl XmppClient {
|
|||
}
|
||||
}
|
||||
|
||||
/// Compare incoming hook with recently sent hooks and update the list.
|
||||
/// Compare only hook type and author. If they match, check how long ago it was.
|
||||
/// Returns true if the hook is recent enough and thus not to be sent.
|
||||
fn update_recent_hooks(&mut self, new_hook: Hook) -> bool {
|
||||
// Do we need to remove/update the hook?
|
||||
let mut removal: Option<usize> = None;
|
||||
|
||||
for (i, old_hook) in self.recent_hooks.iter().enumerate() {
|
||||
match (old_hook, &new_hook) {
|
||||
(_, &Hook::MergeRequest(ref new))
|
||||
if new.action != Some(MergeRequestAction::Update) =>
|
||||
{
|
||||
return false;
|
||||
}
|
||||
(_, &Hook::Issue(ref new)) if new.action != Some(IssueAction::Update) => {
|
||||
return false;
|
||||
}
|
||||
(&Hook::MergeRequest(ref old), &Hook::MergeRequest(ref new)) => {
|
||||
// Action is MergeRequestAction::Update, otherwise it would have matched the other branch
|
||||
// and the method would have returned.
|
||||
if old.id == new.id {
|
||||
if old.author.name == new.author.name
|
||||
&& old.updated_at > (new.updated_at - TimeDelta::minutes(5))
|
||||
{
|
||||
// If everything matches and we're still within the time frame, let the old hook
|
||||
// expire, don't update it.
|
||||
return true;
|
||||
} else {
|
||||
// The old hook either doesn't match the new author (we want to announce messages
|
||||
// from different authors), or has expired, and in both cases can be replaced by the
|
||||
// new hook.
|
||||
removal = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(&Hook::Issue(ref old), &Hook::Issue(ref new)) => {
|
||||
// See the MergeRequest branch for comments
|
||||
if old.id == new.id {
|
||||
if old.author.name == new.author.name
|
||||
&& old.updated_at > (new.updated_at - TimeDelta::minutes(5))
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
removal = Some(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(index) = removal {
|
||||
self.recent_hooks.swap_remove(index);
|
||||
}
|
||||
|
||||
// The new hook hasn't been matched and needs to be added to the list, or it has and will
|
||||
// replace the matched hook.
|
||||
match new_hook {
|
||||
Hook::Issue(_) | Hook::MergeRequest(_) => self.recent_hooks.push(new_hook),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn hook(&mut self, wh: Hook) {
|
||||
debug!("XMPP Bot Processing Hook");
|
||||
if self.update_recent_hooks(wh.clone()) {
|
||||
debug!("Hook already sent recently");
|
||||
return;
|
||||
}
|
||||
if let Some(display) = format_hook(&wh) {
|
||||
debug!("Hook: {}", display);
|
||||
for room in &self.rooms {
|
||||
|
|
|
|||
Loading…
Reference in a new issue