mirror of
https://github.com/pragma-/pbot.git
synced 2025-01-11 04:22:35 +01:00
EventQueue: rearrange subroutines in file more logically
This commit is contained in:
parent
af0938dfc3
commit
20134def36
@ -128,208 +128,14 @@ sub cmd_eventqueue {
|
|||||||
return "Unknown command '$command'. $usage";
|
return "Unknown command '$command'. $usage";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub find_enqueue_position {
|
# returns seconds until upcoming event.
|
||||||
my ($self, $time) = @_;
|
|
||||||
|
|
||||||
# no events in queue yet, early-return first position
|
|
||||||
return 0 if not @{$self->{event_queue}};
|
|
||||||
|
|
||||||
# early-return first position if event's time is less
|
|
||||||
# than first position's
|
|
||||||
if ($time < $self->{event_queue}->[0]->{timeout}) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
# early-return last position if event's time is greater
|
|
||||||
if ($time > $self->{event_queue}->[@{$self->{event_queue}} - 1]->{timeout}) {
|
|
||||||
return scalar @{$self->{event_queue}};
|
|
||||||
}
|
|
||||||
|
|
||||||
# binary search to find enqueue position
|
|
||||||
|
|
||||||
my $lo = 0;
|
|
||||||
my $hi = scalar @{$self->{event_queue}} - 1;
|
|
||||||
|
|
||||||
while ($lo <= $hi) {
|
|
||||||
my $mid = int (($hi + $lo) / 2);
|
|
||||||
|
|
||||||
if ($time < $self->{event_queue}->[$mid]->{timeout}) {
|
|
||||||
$hi = $mid - 1;
|
|
||||||
} elsif ($time > $self->{event_queue}->[$mid]->{timeout}) {
|
|
||||||
$lo = $mid + 1;
|
|
||||||
} else {
|
|
||||||
while ($mid < @{$self->{event_queue}} and $self->{event_queue}->[$mid]->{timeout} == $time) {
|
|
||||||
# found a slot with the same time. we "slide" down the array
|
|
||||||
# to append this event to the end of this region of same-times.
|
|
||||||
$mid++;
|
|
||||||
}
|
|
||||||
return $mid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $lo;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub replace_subref_or_enqueue_event {
|
|
||||||
my ($self, $subref, $interval, $id, $repeating) = @_;
|
|
||||||
|
|
||||||
# find events matching id
|
|
||||||
my @events = grep { $_->{id} eq $id } @{$self->{event_queue}};
|
|
||||||
|
|
||||||
# no events found, enqueue new event
|
|
||||||
if (not @events) {
|
|
||||||
$self->enqueue_event($subref, $interval, $id, $repeating);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
# otherwise update existing events
|
|
||||||
foreach my $event (@events) {
|
|
||||||
$event->{subref} = $subref;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub replace_or_enqueue_event {
|
|
||||||
my ($self, $subref, $interval, $id, $repeating) = @_;
|
|
||||||
|
|
||||||
# remove event if it exists
|
|
||||||
$self->dequeue_event($id) if $self->exists($id);
|
|
||||||
|
|
||||||
# enqueue new event
|
|
||||||
$self->enqueue_event($subref, $interval, $id, $repeating);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub enqueue_event_unless_exists {
|
|
||||||
my ($self, $subref, $interval, $id, $repeating) = @_;
|
|
||||||
|
|
||||||
# event already exists, bail out
|
|
||||||
return if $self->exists($id);
|
|
||||||
|
|
||||||
# enqueue new event
|
|
||||||
$self->enqueue_event($subref, $interval, $id, $repeating);
|
|
||||||
}
|
|
||||||
|
|
||||||
# adds an event to the event queue
|
|
||||||
sub enqueue_event {
|
|
||||||
my ($self, $subref, $interval, $id, $repeating) = @_;
|
|
||||||
|
|
||||||
$id //= 'an event';
|
|
||||||
$repeating //= 0;
|
|
||||||
$interval //= 0;
|
|
||||||
|
|
||||||
my $event = {
|
|
||||||
id => $id,
|
|
||||||
subref => $subref,
|
|
||||||
interval => $interval,
|
|
||||||
timeout => time + $interval,
|
|
||||||
repeating => $repeating,
|
|
||||||
};
|
|
||||||
|
|
||||||
my $i = $self->find_enqueue_position($event->{timeout});
|
|
||||||
splice @{$self->{event_queue}}, $i, 0, $event;
|
|
||||||
|
|
||||||
my $debug = $self->{pbot}->{registry}->get_value('eventqueue', 'debug') // 0;
|
|
||||||
if ($debug > 1) {
|
|
||||||
$self->{pbot}->{logger}->log("Enqueued new event $id at position $i: timeout=$event->{timeout} interval=$interval repeating=$repeating\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# removes an event from the event queue, optionally invoking it.
|
|
||||||
# `id` can contain `.*` and `.*?` for wildcard-matching/globbing.
|
|
||||||
sub dequeue_event {
|
|
||||||
my ($self, $id, $execute) = @_;
|
|
||||||
|
|
||||||
my $result = eval {
|
|
||||||
$id = quotemeta $id;
|
|
||||||
$id =~ s/\\\.\\\*\\\?/.*?/g;
|
|
||||||
$id =~ s/\\\.\\\*/.*/g;
|
|
||||||
my $regex = qr/^$id$/i;
|
|
||||||
my $count = @{$self->{event_queue}};
|
|
||||||
my @removed = grep { $_->{id} =~ /$regex/i; } @{$self->{event_queue}};
|
|
||||||
@{$self->{event_queue}} = grep { $_->{id} !~ /$regex/i; } @{$self->{event_queue}};
|
|
||||||
$count -= @{$self->{event_queue}};
|
|
||||||
|
|
||||||
if ($execute) {
|
|
||||||
foreach my $event (@removed) {
|
|
||||||
$event->{subref}->($event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "No matching events." if not $count;
|
|
||||||
return "Removed $count event" . ($count == 1 ? '' : 's') . ': ' . join(', ', map { $_->{id} } @removed);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($@) {
|
|
||||||
my $error = $@;
|
|
||||||
$self->{pbot}->{logger}->log("Error in dequeue_event: $error\n");
|
|
||||||
$error =~ s/ at PBot.*//;
|
|
||||||
return "$error";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
# invoke and remove all events matching `id`, which can
|
|
||||||
# contain `.*` and `.*?` for wildcard-matching/globbing
|
|
||||||
sub execute_and_dequeue_event {
|
|
||||||
my ($self, $id) = @_;
|
|
||||||
return $self->dequeue_event($id, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
# adds an event, with repeating defaulted to enabled
|
|
||||||
sub enqueue {
|
|
||||||
my ($self, $subref, $interval, $id, $repeating) = @_;
|
|
||||||
$repeating //= 1;
|
|
||||||
$self->enqueue_event($subref, $interval, $id, $repeating);
|
|
||||||
}
|
|
||||||
|
|
||||||
# alias to dequeue_event, for consistency
|
|
||||||
sub dequeue {
|
|
||||||
my ($self, $id) = @_;
|
|
||||||
$self->dequeue_event($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub exists {
|
|
||||||
my ($self, $id) = @_;
|
|
||||||
return scalar grep { $_->{id} eq $id } @{$self->{event_queue}};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_repeating {
|
|
||||||
my ($self, $id, $repeating) = @_;
|
|
||||||
|
|
||||||
for (my $i = 0; $i < @{$self->{event_queue}}; $i++) {
|
|
||||||
if ($self->{event_queue}->[$i]->{id} eq $id) {
|
|
||||||
$self->{event_queue}->[$i]->{repeating} = $repeating;
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub update_interval {
|
|
||||||
my ($self, $id, $interval, $dont_enqueue) = @_;
|
|
||||||
|
|
||||||
for (my $i = 0; $i < @{$self->{event_queue}}; $i++) {
|
|
||||||
if ($self->{event_queue}->[$i]->{id} eq $id) {
|
|
||||||
if ($dont_enqueue) {
|
|
||||||
# update interval in-place without moving event to new place in queue
|
|
||||||
# (allows event to fire at expected time, then updates to new timeout afterwards)
|
|
||||||
$self->{event_queue}->[$i]->{interval} = $interval;
|
|
||||||
} else {
|
|
||||||
# remove and add event in new position in queue
|
|
||||||
my $event = splice(@{$self->{event_queue}}, $i, 1);
|
|
||||||
$self->enqueue_event($event->{subref}, $interval, $id, $event->{repeating});
|
|
||||||
}
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub duration_until_next_event {
|
sub duration_until_next_event {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return 0 if not @{$self->{event_queue}};
|
return 0 if not @{$self->{event_queue}};
|
||||||
return $self->{event_queue}->[0]->{timeout} - time;
|
return $self->{event_queue}->[0]->{timeout} - time;
|
||||||
}
|
}
|
||||||
|
|
||||||
# invokes the current events and then returns seconds until next upcoming event
|
# invokes any current events and then returns seconds until upcoming event.
|
||||||
sub do_events {
|
sub do_events {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
@ -375,4 +181,228 @@ sub do_events {
|
|||||||
return $self->duration_until_next_event;
|
return $self->duration_until_next_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# check if an event is in the event queue.
|
||||||
|
sub exists {
|
||||||
|
my ($self, $id) = @_;
|
||||||
|
return scalar grep { $_->{id} eq $id } @{$self->{event_queue}};
|
||||||
|
}
|
||||||
|
|
||||||
|
# quickly and efficiently find the best position in the event
|
||||||
|
# queue array for a given time value
|
||||||
|
sub find_enqueue_position {
|
||||||
|
my ($self, $time) = @_;
|
||||||
|
|
||||||
|
# no events in queue yet, early-return first position
|
||||||
|
return 0 if not @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# early-return first position if event's time is less
|
||||||
|
# than first position's
|
||||||
|
if ($time < $self->{event_queue}->[0]->{timeout}) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# early-return last position if event's time is greater
|
||||||
|
if ($time > $self->{event_queue}->[@{$self->{event_queue}} - 1]->{timeout}) {
|
||||||
|
return scalar @{$self->{event_queue}};
|
||||||
|
}
|
||||||
|
|
||||||
|
# binary search to find enqueue position
|
||||||
|
|
||||||
|
my $lo = 0;
|
||||||
|
my $hi = scalar @{$self->{event_queue}} - 1;
|
||||||
|
|
||||||
|
while ($lo <= $hi) {
|
||||||
|
my $mid = int (($hi + $lo) / 2);
|
||||||
|
|
||||||
|
if ($time < $self->{event_queue}->[$mid]->{timeout}) {
|
||||||
|
$hi = $mid - 1;
|
||||||
|
} elsif ($time > $self->{event_queue}->[$mid]->{timeout}) {
|
||||||
|
$lo = $mid + 1;
|
||||||
|
} else {
|
||||||
|
while ($mid < @{$self->{event_queue}} and $self->{event_queue}->[$mid]->{timeout} == $time) {
|
||||||
|
# found a slot with the same time. we "slide" down the array
|
||||||
|
# to append this event to the end of this region of same-times.
|
||||||
|
$mid++;
|
||||||
|
}
|
||||||
|
return $mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
# adds an event to the event queue, optionally repeating
|
||||||
|
sub enqueue_event {
|
||||||
|
my ($self, $subref, $interval, $id, $repeating) = @_;
|
||||||
|
|
||||||
|
# default values
|
||||||
|
$id //= "unnamed (${interval}s $subref)";
|
||||||
|
$repeating //= 0;
|
||||||
|
$interval //= 0;
|
||||||
|
|
||||||
|
# create event structure
|
||||||
|
my $event = {
|
||||||
|
id => $id,
|
||||||
|
subref => $subref,
|
||||||
|
interval => $interval,
|
||||||
|
timeout => time + $interval,
|
||||||
|
repeating => $repeating,
|
||||||
|
};
|
||||||
|
|
||||||
|
# find position to add event
|
||||||
|
my $i = $self->find_enqueue_position($event->{timeout});
|
||||||
|
|
||||||
|
# add the event to the event queue array
|
||||||
|
splice @{$self->{event_queue}}, $i, 0, $event;
|
||||||
|
|
||||||
|
# debugging noise
|
||||||
|
my $debug = $self->{pbot}->{registry}->get_value('eventqueue', 'debug') // 0;
|
||||||
|
if ($debug > 1) {
|
||||||
|
$self->{pbot}->{logger}->log("Enqueued new event $id at position $i: timeout=$event->{timeout} interval=$interval repeating=$repeating\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# convenient alias to add an event with repeating defaulted to enabled.
|
||||||
|
sub enqueue {
|
||||||
|
my ($self, $subref, $interval, $id, $repeating) = @_;
|
||||||
|
$self->enqueue_event($subref, $interval, $id, $repeating // 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
# removes an event from the event queue, optionally invoking it.
|
||||||
|
# `id` can contain `.*` and `.*?` for wildcard-matching/globbing.
|
||||||
|
sub dequeue_event {
|
||||||
|
my ($self, $id, $execute) = @_;
|
||||||
|
|
||||||
|
my $result = eval {
|
||||||
|
# escape special characters
|
||||||
|
$id = quotemeta $id;
|
||||||
|
|
||||||
|
# unescape .* and .*?
|
||||||
|
$id =~ s/\\\.\\\*\\\?/.*?/g;
|
||||||
|
$id =~ s/\\\.\\\*/.*/g;
|
||||||
|
|
||||||
|
# compile regex
|
||||||
|
my $regex = qr/^$id$/i;
|
||||||
|
|
||||||
|
# count total events before removal
|
||||||
|
my $count = @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# collect events to be removed
|
||||||
|
my @removed = grep { $_->{id} =~ /$regex/i; } @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# remove events from event queue
|
||||||
|
@{$self->{event_queue}} = grep { $_->{id} !~ /$regex/i; } @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# set count to total events removed
|
||||||
|
$count -= @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# invoke removed events, if requested
|
||||||
|
if ($execute) {
|
||||||
|
foreach my $event (@removed) {
|
||||||
|
$event->{subref}->($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# nothing removed
|
||||||
|
return "No matching events." if not $count;
|
||||||
|
|
||||||
|
# list all removed events
|
||||||
|
return "Removed $count event" . ($count == 1 ? '' : 's') . ': ' . join(', ', map { $_->{id} } @removed);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($@) {
|
||||||
|
my $error = $@;
|
||||||
|
$self->{pbot}->{logger}->log("Error in dequeue_event: $error\n");
|
||||||
|
$error =~ s/ at PBot.*//;
|
||||||
|
return "$error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
# alias to dequeue_event, for consistency.
|
||||||
|
sub dequeue {
|
||||||
|
my ($self, $id) = @_;
|
||||||
|
$self->dequeue_event($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
# invoke and remove all events matching `id`, which can
|
||||||
|
# contain `.*` and `.*?` for wildcard-matching/globbing.
|
||||||
|
sub execute_and_dequeue_event {
|
||||||
|
my ($self, $id) = @_;
|
||||||
|
return $self->dequeue_event($id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
# replace code subrefs for matching events. if no events
|
||||||
|
# were found, then add the event to the event queue.
|
||||||
|
sub replace_subref_or_enqueue_event {
|
||||||
|
my ($self, $subref, $interval, $id, $repeating) = @_;
|
||||||
|
|
||||||
|
# find events matching id
|
||||||
|
my @events = grep { $_->{id} eq $id } @{$self->{event_queue}};
|
||||||
|
|
||||||
|
# no events found, enqueue new event
|
||||||
|
if (not @events) {
|
||||||
|
$self->enqueue_event($subref, $interval, $id, $repeating);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# otherwise update existing events
|
||||||
|
foreach my $event (@events) {
|
||||||
|
$event->{subref} = $subref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove existing events of this id then enqueue new event.
|
||||||
|
sub replace_or_enqueue_event {
|
||||||
|
my ($self, $subref, $interval, $id, $repeating) = @_;
|
||||||
|
|
||||||
|
# remove event if it exists
|
||||||
|
$self->dequeue_event($id) if $self->exists($id);
|
||||||
|
|
||||||
|
# enqueue new event
|
||||||
|
$self->enqueue_event($subref, $interval, $id, $repeating);
|
||||||
|
}
|
||||||
|
|
||||||
|
# add event unless it already had been added.
|
||||||
|
sub enqueue_event_unless_exists {
|
||||||
|
my ($self, $subref, $interval, $id, $repeating) = @_;
|
||||||
|
|
||||||
|
# event already exists, bail out
|
||||||
|
return if $self->exists($id);
|
||||||
|
|
||||||
|
# enqueue new event
|
||||||
|
$self->enqueue_event($subref, $interval, $id, $repeating);
|
||||||
|
}
|
||||||
|
|
||||||
|
# update the `repeating` flag for all events matching `id`.
|
||||||
|
sub update_repeating {
|
||||||
|
my ($self, $id, $repeating) = @_;
|
||||||
|
|
||||||
|
for (my $i = 0; $i < @{$self->{event_queue}}; $i++) {
|
||||||
|
if ($self->{event_queue}->[$i]->{id} eq $id) {
|
||||||
|
$self->{event_queue}->[$i]->{repeating} = $repeating;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# update the `interval` value for all events matching `id`.
|
||||||
|
sub update_interval {
|
||||||
|
my ($self, $id, $interval, $dont_enqueue) = @_;
|
||||||
|
|
||||||
|
for (my $i = 0; $i < @{$self->{event_queue}}; $i++) {
|
||||||
|
if ($self->{event_queue}->[$i]->{id} eq $id) {
|
||||||
|
if ($dont_enqueue) {
|
||||||
|
# update interval in-place without moving event to new place in queue
|
||||||
|
# (allows event to fire at expected time, then updates to new timeout afterwards)
|
||||||
|
$self->{event_queue}->[$i]->{interval} = $interval;
|
||||||
|
} else {
|
||||||
|
# remove and add event in new position in queue
|
||||||
|
my $event = splice(@{$self->{event_queue}}, $i, 1);
|
||||||
|
$self->enqueue_event($event->{subref}, $interval, $id, $event->{repeating});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
Loading…
Reference in New Issue
Block a user