From 4e27b036cd52e4d5ebb58c2d23cbf03fb9b2edc1 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Sat, 25 Jan 2020 14:13:57 -0800 Subject: [PATCH] Fix issues with Users and add `date` command --- PBot/Users.pm | 97 +++++++++++++++++++++++++++++-------------------- Plugins/Date.pm | 55 ++++++++++++++++++++++++++++ data/factoids | 13 +++++++ modules/date.sh | 13 +++++++ 4 files changed, 138 insertions(+), 40 deletions(-) create mode 100644 Plugins/Date.pm create mode 100755 modules/date.sh diff --git a/PBot/Users.pm b/PBot/Users.pm index 49afd166..ec63a63a 100644 --- a/PBot/Users.pm +++ b/PBot/Users.pm @@ -140,48 +140,55 @@ sub save { $self->{users}->save; } -sub hostmask_or_account_name { +sub find_user_account { my ($self, $channel, $hostmask) = @_; $channel = lc $channel; $hostmask = lc $hostmask; $channel = '.*' if $channel !~ /^#/; + my ($found_channel, $found_hostmask) = ($channel, $hostmask); - if (exists $self->{users}->{hash}->{$channel}) { - if (not exists $self->{users}->{hash}->{$channel}->{$hostmask}) { - my $last_level = 0; - # find hostmask by account name or wildcard - foreach my $mask (keys %{ $self->{users}->{hash}->{$channel} }) { - next if $mask eq '_name'; - if (lc $self->{users}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) { - if ($last_level < $self->{users}->{hash}->{$channel}->{$mask}->{level}) { - $hostmask = $mask; - $last_level = $self->{users}->{hash}->{$channel}->{$mask}->{level}; + foreach my $chan (keys %{ $self->{users}->{hash} }) { + if ($channel !~ m/^#/ or $channel =~ m/^$chan$/i) { + if (not exists $self->{users}->{hash}->{$chan}->{$hostmask}) { + my $last_level = 0; + # find hostmask by account name or wildcard + foreach my $mask (keys %{ $self->{users}->{hash}->{$chan} }) { + next if $mask eq '_name'; + if (lc $self->{users}->{hash}->{$chan}->{$mask}->{name} eq $hostmask) { + if ($last_level <= $self->{users}->{hash}->{$chan}->{$mask}->{level}) { + $found_hostmask = $mask; + $found_channel = $chan; + $last_level = $self->{users}->{hash}->{$chan}->{$mask}->{level}; + } } - } - if ($mask =~ /[*?]/) { - # contains * or ? so it's converted to a regex - my $mask_quoted = quotemeta $mask; - $mask_quoted =~ s/\\\*/.*?/g; - $mask_quoted =~ s/\\\?/./g; - if ($hostmask =~ m/^$mask_quoted$/i) { - if ($last_level < $self->{users}->{hash}->{$channel}->{$mask}->{level}) { - $hostmask = $mask; - $last_level = $self->{users}->{hash}->{$channel}->{$mask}->{level}; + if ($mask =~ /[*?]/) { + # contains * or ? so it's converted to a regex + my $mask_quoted = quotemeta $mask; + $mask_quoted =~ s/\\\*/.*?/g; + $mask_quoted =~ s/\\\?/./g; + if ($hostmask =~ m/^$mask_quoted$/i) { + if ($last_level <= $self->{users}->{hash}->{$chan}->{$mask}->{level}) { + $found_hostmask = $mask; + $found_channel = $chan; + $last_level = $self->{users}->{hash}->{$chan}->{$mask}->{level}; + } } } } } } } - return $hostmask; + return ($found_channel, $found_hostmask); } sub find_admin { my ($self, $channel, $hostmask, $min_level) = @_; $min_level //= 1; + ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); + $channel = $self->{pbot}->{registry}->get_value('irc', 'botnick') if not defined $channel; $hostmask = '.*' if not defined $hostmask; $hostmask = lc $hostmask; @@ -199,13 +206,13 @@ sub find_admin { $hostmask_quoted =~ s/\\\?/./g; if ($hostmask =~ m/^$hostmask_quoted$/i) { my $temp = $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; - $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} < $temp->{level}); + $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} <= $temp->{level}); } } else { # direct comparison if ($hostmask eq lc $hostmask_regex) { my $temp = $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; - $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} < $temp->{level}); + $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} <= $temp->{level}); } } } @@ -271,6 +278,15 @@ sub logout { delete $user->{loggedin} if defined $user; } +sub get_loggedin_user_metadata { + my ($self, $channel, $hostmask, $key) = @_; + my $user = $self->loggedin($channel, $hostmask); + if ($user) { + return $user->{lc $key}; + } + return undef; +} + sub logincmd { my ($self, $from, $nick, $user, $host, $arguments) = @_; my $channel = $from; @@ -314,6 +330,7 @@ sub useradd { my $admin = $self->{pbot}->{users}->find_admin($channel, "$nick!$user\@$host"); if (not $admin) { + $channel = 'global' if $channel eq '.*'; return "You are not an admin for $channel; cannot add users to that channel.\n"; } @@ -335,8 +352,7 @@ sub userdel { return "/msg $nick Usage: userdel "; } - $hostmask = $self->hostmask_or_account_name($channel, $hostmask); - $channel = '.*' if $channel !~ /^#/; + ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); return $self->remove_user($channel, $hostmask); } @@ -348,15 +364,16 @@ sub userset { return "Usage: userset [key] [value]"; } - $hostmask = $self->hostmask_or_account_name($channel, $hostmask); my $admin = $self->find_admin($channel, "$nick!$user\@$host"); my $target = $self->find_user($channel, $hostmask); if (not $admin) { + $channel = 'global' if $channel eq '.*'; return "You are not an admin for $channel; cannot modify their users."; } if (not $target) { + $channel = 'global' if $channel eq '.*'; return "There is no user $hostmask in channel $channel."; } @@ -369,7 +386,7 @@ sub userset { return "You may not modify users higher in level than you."; } - $channel = '.*' if $channel !~ /^#/; + ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); my $result = $self->{users}->set($channel, $hostmask, $key, $value); $result =~ s/^password => .*;$/password => ;/m; return $result; @@ -384,22 +401,23 @@ sub userunset { } my $admin = $self->find_admin($channel, "$nick!$user\@$host"); - $hostmask = $self->hostmask_or_account_name($channel, $hostmask); my $target = $self->find_user($channel, $hostmask); if (not $admin) { + $channel = 'global' if $channel eq '.*'; return "You are not an admin for $channel; cannot modify their users."; } if (not $target) { + $channel = 'global' if $channel eq '.*'; return "There is no user $hostmask in channel $channel."; } - if ($target->{level} >= $user->{level}) { - return "You may not modify users equal or higher in level than you."; + if ($target->{level} > $admin->{level}) { + return "You may not modify users higher in level than you."; } - $channel = '.*' if $channel !~ /^#/; + ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); return $self->{users}->unset($channel, $hostmask, $key); } @@ -411,14 +429,11 @@ sub mycmd { return "Usage: my [value]"; } + $key = lc $key; my $channel = $from; - $channel = '.*' if $channel !~ /^#/; - my $hostmask = $self->hostmask_or_account_name($channel, "$nick!$user\@$host"); - my $u = $self->find_user($channel, $hostmask); + my $hostmask = "$nick!$user\@$host"; - print "hostmask: $hostmask\n"; - use Data::Dumper; - print Dumper \$u; + my $u = $self->find_user($channel, $hostmask); if (not $u) { $channel = '.*'; @@ -428,13 +443,15 @@ sub mycmd { $u->{loggedin} = 1; } - if ($u->{level} == 0) { + if (defined $value and $u->{level} == 0) { my @disallowed = qw/level autoop autovoice/; - if (grep { lc $key } @disallowed) { + if (grep { $_ eq $key } @disallowed) { return "You must be an admin to set $key."; } } + + ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); my $result = $self->{users}->set($channel, $hostmask, $key, $value); $result =~ s/^password => .*;$/password => ;/m; return $result; diff --git a/Plugins/Date.pm b/Plugins/Date.pm new file mode 100644 index 00000000..ce7448c7 --- /dev/null +++ b/Plugins/Date.pm @@ -0,0 +1,55 @@ +# File: Date.pm +# Author: pragma- +# +# Purpose: Adds command to display time and date for timezones. + +# This Source Code Form is subject to the terms of the Mozilla Public +# 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/. + +package Plugins::Date; + +use warnings; +use strict; + +use feature 'unicode_strings'; + +use Carp (); + +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; +} + +sub initialize { + my ($self, %conf) = @_; + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot}->{registry}->add_default('text', 'date', 'default_timezone', 'UTC'); + $self->{pbot}->{commands}->register(sub { $self->datecmd(@_) }, "date", 0); +} + +sub unload { + my $self = shift; + $self->{pbot}->{commands}->unregister("date"); +} + +sub datecmd { + my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_; + + my $timezone = $self->{pbot}->{registry}->get_value('date', 'default_timezone') // 'UTC'; + my $tz_override = $self->{pbot}->{users}->get_loggedin_user_metadata($from, "$nick!$user\@$host", 'timezone'); + $timezone = $tz_override if $tz_override; + + my $newstuff = { + from => $from, nick => $nick, user => $user, host => $host, + command => "date_module $timezone", root_channel => $from, root_keyword => "date_module", + keyword => "date_module", arguments => "$timezone" + }; + + $self->{pbot}->{factoids}->{factoidmodulelauncher}->execute_module($newstuff); +} + +1; diff --git a/data/factoids b/data/factoids index cc01fc08..9f2b6096 100644 --- a/data/factoids +++ b/data/factoids @@ -5540,6 +5540,19 @@ "ref_user" : "esselfe!~bsfc@unaffiliated/esselfe", "type" : "text" }, + "date_module" : { + "_name" : "date_module", + "action" : "date.sh", + "add_nick" : 1, + "created_on" : 1579985843.56774, + "enabled" : 1, + "nooverride" : 1, + "owner" : "pragma-!~chaos@unaffiliated/pragmatic-chaos", + "rate_limit" : "15", + "ref_count" : 0, + "ref_user" : "nobody", + "type" : "module" + }, "ddd" : { "_name" : "ddd", "action" : "a graphical front end to gdb and other debuggers (http://www.gnu.org/software/ddd/)", diff --git a/modules/date.sh b/modules/date.sh new file mode 100755 index 00000000..4b41f986 --- /dev/null +++ b/modules/date.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +export TZ=UTC +if (( $# )) && ! read -r TZ < <(IFS=_; find -L /usr/share/zoneinfo/posix -type f -iname "*$**" -printf '%P\n' -quit); then + echo "No match for '$*'." >&2 + exit 1 +fi +if [[ $TZ != UTC ]]; then + _city=${TZ##*/} + echo "It's $(date) in ${_city//_/ }." +else + echo "It's $(date)." +fi