mirror of
https://github.com/pragma-/pbot.git
synced 2024-12-27 13:12:42 +01:00
ParseDate: complete rewrite, replacing Time::ParseDate with DateTime::Format::Flexible
Now supports more types of input, including those containing timezones! Converted PBot::Utils::ParseDate to an internal PBot module.
This commit is contained in:
parent
9ca14113e9
commit
b1729a13e2
@ -15,8 +15,6 @@ use strict;
|
||||
use Carp ();
|
||||
use Time::Duration;
|
||||
|
||||
use PBot::Utils::ParseDate;
|
||||
|
||||
sub new {
|
||||
if (ref($_[1]) eq 'HASH') {
|
||||
Carp::croak("Options to ChanOpCommands should be key/value pairs, not hash reference");
|
||||
@ -75,7 +73,7 @@ sub ban_user {
|
||||
$length = 60 * 60 * 24; # 24 hours
|
||||
} else {
|
||||
my $error;
|
||||
($length, $error) = parsedate($length);
|
||||
($length, $error) = $self->{pbot}->{parsedate}->parsedate($length);
|
||||
return $error if defined $error;
|
||||
}
|
||||
|
||||
@ -183,7 +181,7 @@ sub mute_user {
|
||||
$length = 60 * 60 * 24; # 24 hours
|
||||
} else {
|
||||
my $error;
|
||||
($length, $error) = parsedate($length);
|
||||
($length, $error) = $self->{pbot}->{parsedate}->parsedate($length);
|
||||
return $error if defined $error;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,6 @@ use Time::HiRes qw(gettimeofday);
|
||||
use Time::Duration;
|
||||
use Carp ();
|
||||
|
||||
use PBot::Utils::ParseDate;
|
||||
|
||||
sub new {
|
||||
if (ref($_[1]) eq 'HASH') {
|
||||
Carp::croak("Options to IgnoreListCommands should be key/value pairs, not hash reference");
|
||||
@ -77,7 +75,7 @@ sub ignore_user {
|
||||
$length = -1; # permanently
|
||||
} else {
|
||||
my $error;
|
||||
($length, $error) = parsedate($length);
|
||||
($length, $error) = $self->{pbot}->{parsedate}->parsedate($length);
|
||||
return $error if defined $error;
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ use PBot::Timer;
|
||||
use PBot::Refresher;
|
||||
use PBot::Plugins;
|
||||
use PBot::WebPaste;
|
||||
use PBot::Utils::ParseDate;
|
||||
|
||||
sub new {
|
||||
if (ref($_[1]) eq 'HASH') {
|
||||
@ -120,6 +121,7 @@ sub initialize {
|
||||
$self->{chanops} = PBot::ChanOps->new(pbot => $self, %conf);
|
||||
$self->{nicklist} = PBot::NickList->new(pbot => $self, %conf);
|
||||
$self->{webpaste} = PBot::WebPaste->new(pbot => $self, %conf);
|
||||
$self->{parsedate} = PBot::Utils::ParseDate->new(pbot => $self, %conf);
|
||||
|
||||
$self->{interpreter} = PBot::Interpreter->new(pbot => $self, %conf);
|
||||
$self->{interpreter}->register(sub { return $self->{commands}->interpreter(@_); });
|
||||
|
@ -2,11 +2,6 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# This module is intended to provide a "magic" command that allows
|
||||
# the bot owner to trigger special arbitrary code (by editing this
|
||||
# module and refreshing loaded modules before running the magical
|
||||
# command).
|
||||
|
||||
# Just a quick interface to test/play with PBot::Utils::ParseDate
|
||||
|
||||
package PBot::Plugins::ParseDate;
|
||||
@ -16,7 +11,6 @@ use strict;
|
||||
|
||||
use Carp ();
|
||||
|
||||
use PBot::Utils::ParseDate;
|
||||
use Time::Duration qw/duration/;
|
||||
|
||||
sub new {
|
||||
@ -29,7 +23,6 @@ sub new {
|
||||
|
||||
sub initialize {
|
||||
my ($self, %conf) = @_;
|
||||
|
||||
$self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
|
||||
$self->{pbot}->{commands}->register(sub { return $self->pd(@_)}, "pd", 0);
|
||||
}
|
||||
@ -42,13 +35,9 @@ sub unload {
|
||||
sub pd {
|
||||
my $self = shift;
|
||||
my ($from, $nick, $user, $host, $arguments) = @_;
|
||||
|
||||
my ($length, $error) = parsedate($arguments);
|
||||
my ($seconds, $error) = $self->{pbot}->{parsedate}->parsedate($arguments);
|
||||
return $error if defined $error;
|
||||
|
||||
$length = duration $length;
|
||||
return $length;
|
||||
return duration $seconds;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
@ -15,7 +15,6 @@ use DBI;
|
||||
use Time::Duration qw/concise duration/;
|
||||
use Time::HiRes qw/gettimeofday/;
|
||||
use Getopt::Long qw(GetOptionsFromString);
|
||||
use PBot::Utils::ParseDate;
|
||||
|
||||
Getopt::Long::Configure ("bundling");
|
||||
|
||||
@ -316,7 +315,7 @@ sub remindme {
|
||||
|
||||
print "alarm: $alarm\n";
|
||||
|
||||
my ($length, $error) = parsedate($alarm);
|
||||
my ($length, $error) = $self->{pbot}->{parsedate}->parsedate($alarm);
|
||||
|
||||
print "length: $length, error: $error!\n";
|
||||
return $error if $error;
|
||||
|
@ -9,34 +9,110 @@ use strict;
|
||||
|
||||
package PBot::Utils::ParseDate;
|
||||
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
our @EXPORT = qw/parsedate/;
|
||||
use DateTime;
|
||||
use DateTime::Format::Flexible;
|
||||
use DateTime::Format::Duration;
|
||||
|
||||
use Time::HiRes qw/gettimeofday/;
|
||||
sub new {
|
||||
Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference") if ref $_[1] eq 'HASH';
|
||||
my ($class, %conf) = @_;
|
||||
my $self = bless {}, $class;
|
||||
$self->initialize(%conf);
|
||||
return $self;
|
||||
}
|
||||
|
||||
require Time::ParseDate;
|
||||
sub initialize {
|
||||
my ($self, %conf) = @_;
|
||||
$self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
|
||||
}
|
||||
|
||||
# parses English natural language date strings into seconds
|
||||
# does not accept times or dates in the past
|
||||
sub parsedate {
|
||||
my $input = shift @_;
|
||||
my $now = gettimeofday;
|
||||
my ($self, $input) = @_;
|
||||
|
||||
# make some aliases
|
||||
$input =~ s/\bsecs?\b/seconds/g;
|
||||
$input =~ s/\bmins?\b/minutes/g;
|
||||
$input =~ s/\bhrs?\b/hours/g;
|
||||
$input =~ s/\bwks?\b/weeks/g;
|
||||
$input =~ s/\byrs?\b/years/g;
|
||||
|
||||
# sanitizers
|
||||
$input =~ s/\s+(am?|pm?)/$1/; # remove leading spaces from am/pm
|
||||
|
||||
# split input on "and" or comma, then we'll add up the seconds
|
||||
my @inputs = split /(?:,?\s+and\s+|\s*,\s*)/, $input;
|
||||
|
||||
# adjust timezone to user-override if user provides a timezone
|
||||
# we don't know if a timezone was provided until it is parsed
|
||||
my $timezone;
|
||||
my $tz_override = 'UTC';
|
||||
|
||||
ADJUST_TIMEZONE:
|
||||
$timezone = $tz_override;
|
||||
my $now = DateTime->now(time_zone => $timezone);
|
||||
|
||||
my $seconds = 0;
|
||||
my $from_now_added = 0;
|
||||
|
||||
foreach my $input (@inputs) {
|
||||
return -1 if $input =~ m/forever/i;
|
||||
$input .= ' seconds' if $input =~ m/^\s*\d+\s*$/;
|
||||
|
||||
my $parse = Time::ParseDate::parsedate($input, NOW => $now);
|
||||
|
||||
print "parsedate: now => $now, input => $input, parse => $parse\n";
|
||||
|
||||
if (not defined $parse) {
|
||||
$input =~ s/\s+$//;
|
||||
return (0, "I don't know what '$input' means. I expected a time duration like '5 minutes' or '24 hours' or 'next tuesday'.\n");
|
||||
} else {
|
||||
$seconds += $parse - $now;
|
||||
# DateTime::Format::Flexible doesn't support seconds, but that's okay;
|
||||
# we can take care of that easily here!
|
||||
if ($input =~ m/^\s*(\d+)\s+seconds$/) {
|
||||
$seconds += $1;
|
||||
next;
|
||||
}
|
||||
|
||||
# First, attempt to parse as-is...
|
||||
my $to = eval { return DateTime::Format::Flexible->parse_datetime($input, lang => ['en'], base => $now); };
|
||||
|
||||
# If there was an error, then append "from now" and attempt to parse as a relative time...
|
||||
if ($@) {
|
||||
$from_now_added = 1;
|
||||
$input .= ' from now';
|
||||
$to = eval { return DateTime::Format::Flexible->parse_datetime($input, lang => ['en'], base => $now); };
|
||||
|
||||
# If there's still an error, it's bad input
|
||||
if ($@) {
|
||||
$@ =~ s/ from now at PBot.*$//;
|
||||
return (0, $@);
|
||||
}
|
||||
}
|
||||
|
||||
# there was a timezone parsed, set the override and try again
|
||||
if ($to->time_zone_short_name ne 'floating' and $to->time_zone_short_name ne 'UTC' and $tz_override eq 'UTC') {
|
||||
$tz_override = $to->time_zone_long_name;
|
||||
goto ADJUST_TIMEZONE;
|
||||
}
|
||||
|
||||
$to->set_time_zone('UTC');
|
||||
my $duration = $to->subtract_datetime_absolute($now);
|
||||
|
||||
# If the time is in the past, prepend "tomorrow" and reparse
|
||||
if ($duration->is_negative) {
|
||||
$input = "tomorrow $input";
|
||||
$to = eval { return DateTime::Format::Flexible->parse_datetime($input, lang => ['en'], base => $now); };
|
||||
|
||||
if ($@) {
|
||||
$@ =~ s/format: tomorrow /format: /;
|
||||
if ($from_now_added) {
|
||||
$@ =~ s/ from now at PBot.*//;
|
||||
} else {
|
||||
$@ =~ s/ at PBot.*//;
|
||||
}
|
||||
return (0, $@);
|
||||
}
|
||||
|
||||
$to->set_time_zone('UTC');
|
||||
$duration = $to->subtract_datetime_absolute($now);
|
||||
}
|
||||
|
||||
# add the seconds from this input chunk
|
||||
$seconds += $duration->seconds;
|
||||
}
|
||||
|
||||
return $seconds;
|
||||
|
Loading…
Reference in New Issue
Block a user