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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::hooks::{Hook, format_hook};
|
use crate::hooks::{Hook, IssueAction, MergeRequestAction, format_hook};
|
||||||
|
|
||||||
|
use chrono::TimeDelta;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use tokio::{signal::ctrl_c, sync::mpsc};
|
use tokio::{signal::ctrl_c, sync::mpsc};
|
||||||
use xmpp::jid::{BareJid, Jid, ResourcePart};
|
use xmpp::jid::{BareJid, Jid, ResourcePart};
|
||||||
|
|
@ -31,6 +32,9 @@ pub struct XmppClient {
|
||||||
rooms: Vec<BareJid>,
|
rooms: Vec<BareJid>,
|
||||||
nickname: ResourcePart,
|
nickname: ResourcePart,
|
||||||
admins: Vec<BareJid>,
|
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 {
|
impl XmppClient {
|
||||||
|
|
@ -59,6 +63,7 @@ impl XmppClient {
|
||||||
rooms,
|
rooms,
|
||||||
nickname,
|
nickname,
|
||||||
admins,
|
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) {
|
pub async fn hook(&mut self, wh: Hook) {
|
||||||
debug!("XMPP Bot Processing 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) {
|
if let Some(display) = format_hook(&wh) {
|
||||||
debug!("Hook: {}", display);
|
debug!("Hook: {}", display);
|
||||||
for room in &self.rooms {
|
for room in &self.rooms {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue