mirror of
				https://github.com/pragma-/pbot.git
				synced 2025-11-04 08:37:24 +01:00 
			
		
		
		
	Finished replacing admin-levels with user-capabilities [FIN commit 2 of 2]; misc clean-ups
This commit is contained in:
		
							parent
							
								
									2ca9634c66
								
							
						
					
					
						commit
						6267cc04d2
					
				@ -78,7 +78,7 @@ sub initialize {
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text',  'antiflood', 'debug_checkban',             $conf{debug_checkban}             //  0);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->unbanme(@_)   },  "unbanme",   0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->whitelist(@_) },  "whitelist", 10);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->whitelist(@_) },  "whitelist", 1);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.whoisaccount', sub { $self->on_whoisaccount(@_)  });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.whoisuser',    sub { $self->on_whoisuser(@_)     });
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ sub initialize {
 | 
			
		||||
  $self->{keywords}->load;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'antispam', 'enforce',  $conf{enforce_antispam} // 1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->antispam_cmd(@_) }, "antispam", 10);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->antispam_cmd(@_) }, "antispam", 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub is_spam {
 | 
			
		||||
 | 
			
		||||
@ -24,12 +24,8 @@ $Data::Dumper::Sortkeys = 1;
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to BanTracker should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to BanTracker 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;
 | 
			
		||||
@ -38,14 +34,14 @@ sub new {
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}    = delete $conf{pbot} // Carp::croak("Missing pbot reference to BanTracker");
 | 
			
		||||
  $self->{pbot}    = $conf{pbot} // Carp::croak("Missing pbot reference to BanTracker");
 | 
			
		||||
  $self->{banlist} = {};
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'bantracker', 'chanserv_ban_timeout', '604800');
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'bantracker', 'mute_timeout',         '604800');
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'bantracker', 'debug',                '0');
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->dumpbans(@_) }, "dumpbans", 60);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->dumpbans(@_) }, "dumpbans", 1);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.endofnames', sub { $self->get_banlist(@_) });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.banlist',    sub { $self->on_banlist_entry(@_) });
 | 
			
		||||
@ -54,7 +50,6 @@ sub initialize {
 | 
			
		||||
 | 
			
		||||
sub dumpbans {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  my $bans = Dumper($self->{banlist});
 | 
			
		||||
  return $bans;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ sub initialize {
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{filename} = $conf{filename};
 | 
			
		||||
  $self->{blacklist} = {};
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->blacklist(@_) }, "blacklist", 10);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->blacklist(@_) }, "blacklist", 1);
 | 
			
		||||
  $self->load_blacklist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -53,8 +53,9 @@ sub has {
 | 
			
		||||
  my ($self, $cap, $subcap, $depth) = @_;
 | 
			
		||||
  my $cap_data = $self->{caps}->get_data($cap);
 | 
			
		||||
  return 0 if not defined $cap_data;
 | 
			
		||||
  return 1 if $cap eq $subcap;
 | 
			
		||||
 | 
			
		||||
  $depth //= 2;
 | 
			
		||||
  $depth //= 10;
 | 
			
		||||
  if (--$depth <= 0) {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Max recursion reached for PBot::Capabilities->has()\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
@ -74,7 +75,7 @@ sub userhas {
 | 
			
		||||
  return 1 if $user->{$cap};
 | 
			
		||||
  foreach my $key (keys %{$user}) {
 | 
			
		||||
    next if $key eq '_name';
 | 
			
		||||
    return 1 if $self->has($key, $cap, 10);
 | 
			
		||||
    return 1 if $self->has($key, $cap);
 | 
			
		||||
  }
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
@ -109,6 +110,15 @@ sub add {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub remove {
 | 
			
		||||
  my ($self, $cap) = @_;
 | 
			
		||||
  $cap = lc $cap;
 | 
			
		||||
  foreach my $c (keys %{$self->{caps}->{hash}}) {
 | 
			
		||||
    next if $c eq '_name';
 | 
			
		||||
    foreach my $sub_cap (keys %{$self->{caps}->{hash}->{$c}}) {
 | 
			
		||||
      delete $self->{caps}->{hash}->{$c}->{$sub_cap} if $sub_cap eq $cap;
 | 
			
		||||
    }
 | 
			
		||||
    delete $self->{caps}->{hash}->{$c} if $c eq $cap;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub rebuild_botowner_capabilities {
 | 
			
		||||
@ -122,7 +132,7 @@ sub rebuild_botowner_capabilities {
 | 
			
		||||
 | 
			
		||||
sub list {
 | 
			
		||||
  my ($self, $capability) = @_;
 | 
			
		||||
  $capability = lc $capability;
 | 
			
		||||
  $capability = lc $capability if defined $capability;
 | 
			
		||||
  return "No such capability $capability." if defined $capability and not exists $self->{caps}->{hash}->{$capability};
 | 
			
		||||
 | 
			
		||||
  my @caps;
 | 
			
		||||
@ -135,7 +145,7 @@ sub list {
 | 
			
		||||
    $result = 'Capabilities: ';
 | 
			
		||||
  } else {
 | 
			
		||||
    @caps = sort keys %{$self->{caps}->{hash}->{$capability}};
 | 
			
		||||
    return "Capability $capability has no sub-capabilities." if @caps == 1;
 | 
			
		||||
    return "Capability $capability has no sub-capabilities." if not @caps or @caps == 1;
 | 
			
		||||
    $result = "Sub-capabilities for $capability: ";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -144,7 +154,7 @@ sub list {
 | 
			
		||||
  foreach my $cap (@caps) {
 | 
			
		||||
    next if $cap eq '_name';
 | 
			
		||||
    my $count = keys(%{$self->{caps}->{hash}->{$cap}}) - 1;
 | 
			
		||||
    if ($count) {
 | 
			
		||||
    if ($count > 0) {
 | 
			
		||||
      push @cap_group, "$cap [$count]" if $count;
 | 
			
		||||
    } else {
 | 
			
		||||
      push @cap_standalone, $cap;
 | 
			
		||||
@ -166,6 +176,41 @@ sub capcmd {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    when ('userhas') {
 | 
			
		||||
      my ($hostmask, $cap) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2);
 | 
			
		||||
      return "Usage: cap userhas <user> [capability]" if not defined $hostmask;
 | 
			
		||||
      $cap = lc $cap if defined $cap;
 | 
			
		||||
 | 
			
		||||
      my $u = $self->{pbot}->{users}->find_user('.*', $hostmask);
 | 
			
		||||
      return "No such user $hostmask." if not defined $u;
 | 
			
		||||
 | 
			
		||||
      if (defined $cap) {
 | 
			
		||||
        return "Try again. No such capability $cap." if not $self->exists($cap);
 | 
			
		||||
        if ($self->userhas($u, $cap)) {
 | 
			
		||||
          return "Yes. User $u->{name} has capability $cap.";
 | 
			
		||||
        } else {
 | 
			
		||||
          return "No. User $u->{name} does not have capability $cap.";
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        my $result = "User $u->{name} has capabilities: ";
 | 
			
		||||
        my @groups;
 | 
			
		||||
        my @single;
 | 
			
		||||
        foreach my $key (sort keys %{$u}) {
 | 
			
		||||
          next if $key eq '_name';
 | 
			
		||||
          next if not $self->exists($key);
 | 
			
		||||
          my $count = keys (%{$self->{caps}->{hash}->{$key}}) - 1;
 | 
			
		||||
          if ($count > 0) {
 | 
			
		||||
            push @groups, "$key [$count]";
 | 
			
		||||
          } else {
 | 
			
		||||
            push @single, $key;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (@groups or @single) {
 | 
			
		||||
          $result .= join ', ', @groups, @single;
 | 
			
		||||
        } else {
 | 
			
		||||
          $result = "User $u->{name} has no capabilities.";
 | 
			
		||||
        }
 | 
			
		||||
        return $result;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    when ('add') {
 | 
			
		||||
@ -175,7 +220,7 @@ sub capcmd {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default {
 | 
			
		||||
      $result = "Usage: cap list [capability] | cap add <capability> [sub-capability] | cap remove <capability> [sub-capability] | cap userhas <user> <capability>";
 | 
			
		||||
      $result = "Usage: cap list [capability] | cap add <capability> [sub-capability] | cap remove <capability> [sub-capability] | cap userhas <user> [capability]";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return $result;
 | 
			
		||||
 | 
			
		||||
@ -19,10 +19,7 @@ use Time::HiRes qw(gettimeofday);
 | 
			
		||||
use Time::Duration qw(concise duration);
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
@ -31,7 +28,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to ChanOps");
 | 
			
		||||
 | 
			
		||||
  $self->{unban_timeout} = PBot::DualIndexHashObject->new(
 | 
			
		||||
 | 
			
		||||
@ -27,19 +27,18 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{channels} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Channels', filename => $conf{filename});
 | 
			
		||||
  $self->load_channels;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->join(@_)   },  "join",      40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->part(@_)   },  "part",      40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->set(@_)    },  "chanset",   40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->unset(@_)  },  "chanunset", 40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->add(@_)    },  "chanadd",   40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->remove(@_) },  "chanrem",   40);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->list(@_)   },  "chanlist",  10);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->join(@_)   },  "join",      1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->part(@_)   },  "part",      1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->set(@_)    },  "chanset",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->unset(@_)  },  "chanunset", 1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->add(@_)    },  "chanadd",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->remove(@_) },  "chanrem",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->list(@_)   },  "chanlist",  1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub join {
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,6 @@ sub initialize {
 | 
			
		||||
 | 
			
		||||
sub load {
 | 
			
		||||
  my ($self, $filename) = @_;
 | 
			
		||||
 | 
			
		||||
  $filename = $self->{filename} if not defined $filename;
 | 
			
		||||
 | 
			
		||||
  if (not defined $filename) {
 | 
			
		||||
@ -104,7 +103,6 @@ sub load {
 | 
			
		||||
sub save {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my $filename;
 | 
			
		||||
 | 
			
		||||
  if (@_) { $filename = shift; } else { $filename = $self->{filename}; }
 | 
			
		||||
 | 
			
		||||
  if (not defined $filename) {
 | 
			
		||||
 | 
			
		||||
@ -25,26 +25,26 @@ use JSON;
 | 
			
		||||
 | 
			
		||||
use PBot::Utils::SafeFilename;
 | 
			
		||||
 | 
			
		||||
our %factoid_metadata_levels = (
 | 
			
		||||
  created_on                  => 90,
 | 
			
		||||
  enabled                     => 10,
 | 
			
		||||
  last_referenced_in          => 90,
 | 
			
		||||
  last_referenced_on          => 90,
 | 
			
		||||
  modulelauncher_subpattern   => 90,
 | 
			
		||||
  owner                       => 90,
 | 
			
		||||
  rate_limit                  => 10,
 | 
			
		||||
  ref_count                   => 90,
 | 
			
		||||
  ref_user                    => 90,
 | 
			
		||||
  type                        => 90,
 | 
			
		||||
  edited_by                   => 90,
 | 
			
		||||
  edited_on                   => 90,
 | 
			
		||||
  locked                      => 10,
 | 
			
		||||
  add_nick                    => 10,
 | 
			
		||||
  nooverride                  => 10,
 | 
			
		||||
  'effective-level'           => 40,
 | 
			
		||||
  'persist-key'               => 20,
 | 
			
		||||
  'interpolate'               => 10,
 | 
			
		||||
  # all others are allowed to be factset by anybody/default to level 0
 | 
			
		||||
our %factoid_metadata_capabilities = (
 | 
			
		||||
  created_on                  => 'botowner',
 | 
			
		||||
  enabled                     => 'chanop',
 | 
			
		||||
  last_referenced_in          => 'botowner',
 | 
			
		||||
  last_referenced_on          => 'botowner',
 | 
			
		||||
  modulelauncher_subpattern   => 'botowner',
 | 
			
		||||
  owner                       => 'botowner',
 | 
			
		||||
  rate_limit                  => 'chanop',
 | 
			
		||||
  ref_count                   => 'botowner',
 | 
			
		||||
  ref_user                    => 'botowner',
 | 
			
		||||
  type                        => 'botowner',
 | 
			
		||||
  edited_by                   => 'botowner',
 | 
			
		||||
  edited_on                   => 'botowner',
 | 
			
		||||
  locked                      => 'chanop',
 | 
			
		||||
  add_nick                    => 'chanop',
 | 
			
		||||
  nooverride                  => 'chanop',
 | 
			
		||||
  'cap-override'              => 'botowner',
 | 
			
		||||
  'persist-key'               => 'admin',
 | 
			
		||||
  'interpolate'               => 'chanop',
 | 
			
		||||
  # all others are allowed to be factset by anybody
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
@ -78,14 +78,14 @@ sub initialize {
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->call_factoid(@_)  },  "fact",         0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->factfind(@_)      },  "factfind",     0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->top20(@_)         },  "top20",        0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->load_module(@_)   },  "load",        90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->unload_module(@_) },  "unload",      90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->load_module(@_)   },  "load",         1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->unload_module(@_) },  "unload",       1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->histogram(@_)     },  "histogram",    0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->count(@_)         },  "count",        0);
 | 
			
		||||
 | 
			
		||||
  # the following commands have not yet been updated to use the new factoid structure
 | 
			
		||||
  # DO NOT USE!!  Factoid corruption may occur.
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->add_regex(@_)     },  "regex",        999);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->add_regex(@_)     },  "regex",        1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub call_factoid {
 | 
			
		||||
@ -406,13 +406,12 @@ sub factundo {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash};
 | 
			
		||||
  my $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
 | 
			
		||||
  if ($factoids->{$channel}->{$trigger}->{'locked'}) {
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo;
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be reverted." if not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin');
 | 
			
		||||
 | 
			
		||||
    if (exists $factoids->{$channel}->{$trigger}->{'effective-level'}
 | 
			
		||||
        and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
      return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted.";
 | 
			
		||||
    if (exists $factoids->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) {
 | 
			
		||||
      return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -499,13 +498,12 @@ sub factredo {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash};
 | 
			
		||||
  my $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
 | 
			
		||||
  if ($factoids->{$channel}->{$trigger}->{'locked'}) {
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo;
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be reverted." if not defined $self->{pbot}->{capabilities}->userhas($userinfo, 'admin');
 | 
			
		||||
 | 
			
		||||
    if (exists $factoids->{$channel}->{$trigger}->{'effective-level'}
 | 
			
		||||
        and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
      return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted.";
 | 
			
		||||
    if (exists $factoids->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) {
 | 
			
		||||
      return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -559,50 +557,39 @@ sub factset {
 | 
			
		||||
  $channel = '.*' if $channel !~ /^#/;
 | 
			
		||||
  my ($owner_channel, $owner_trigger) = $self->{pbot}->{factoids}->find_factoid($channel, $trigger, exact_channel => 1, exact_trigger => 1);
 | 
			
		||||
 | 
			
		||||
  my $admininfo;
 | 
			
		||||
  my $userinfo;
 | 
			
		||||
  if (defined $owner_channel) {
 | 
			
		||||
    $admininfo  = $self->{pbot}->{users}->loggedin_admin($owner_channel, "$nick!$user\@$host");
 | 
			
		||||
    $userinfo  = $self->{pbot}->{users}->loggedin($owner_channel, "$nick!$user\@$host");
 | 
			
		||||
  } else {
 | 
			
		||||
    $admininfo  = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $level = 0;
 | 
			
		||||
  my $meta_level = 0;
 | 
			
		||||
 | 
			
		||||
  if (defined $admininfo) {
 | 
			
		||||
    $level = $admininfo->{level};
 | 
			
		||||
    $userinfo  = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $meta_cap;
 | 
			
		||||
  if (defined $key) {
 | 
			
		||||
    if (defined $factoid_metadata_levels{$key}) {
 | 
			
		||||
      $meta_level = $factoid_metadata_levels{$key};
 | 
			
		||||
    if (defined $factoid_metadata_capabilities{$key}) {
 | 
			
		||||
      $meta_cap = $factoid_metadata_capabilities{$key};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($meta_level > 0) {
 | 
			
		||||
      if ($level == 0) {
 | 
			
		||||
        return "You must be an admin to set $key";
 | 
			
		||||
      } elsif ($level < $meta_level) {
 | 
			
		||||
        return "You must be at least level $meta_level to set $key";
 | 
			
		||||
    if (defined $meta_cap) {
 | 
			
		||||
      if (not $self->{pbot}->{capabilities}->userhas($userinfo, $meta_cap)) {
 | 
			
		||||
        return "Your user account must have the $meta_cap capability to set $key.";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (defined $value and !$level and $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'locked'}) {
 | 
			
		||||
    if (defined $value and !$self->{pbot}->{capabilities}->userhas($userinfo, 'admin') and $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'locked'}) {
 | 
			
		||||
      return "/say $trigger_name is locked; unlock before setting.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (lc $key eq 'effective-level' and defined $value and $level > 0) {
 | 
			
		||||
      if ($value > $level) {
 | 
			
		||||
        return "You cannot set `effective-level` greater than your level, which is $level.";
 | 
			
		||||
      } elsif ($value < 0) {
 | 
			
		||||
        return "You cannot set a negative effective-level.";
 | 
			
		||||
    if (lc $key eq 'cap-override' and defined $value) {
 | 
			
		||||
      if (not $self->{pbot}->{capabilities}->exists($value)) {
 | 
			
		||||
        return "No such capability $value.";
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      $self->{pbot}->{factoids}->{factoids}->set($channel, $trigger, 'locked', '1');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (lc $key eq 'locked' and exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
      if ($level < $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
        return "You cannot unlock this factoid because its effective-level is greater than your level.";
 | 
			
		||||
    if (lc $key eq 'locked' and exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'cap-override'}) {
 | 
			
		||||
      if (not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) {
 | 
			
		||||
        return "/say $trigger_name has a cap-override and cannot be unlocked until the override is removed.";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -622,7 +609,7 @@ sub factset {
 | 
			
		||||
      $mask = $nick;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ((defined $value and $key ne 'action' and $key ne 'action_with_args') and lc $mask ne lc $owner and $level == 0) {
 | 
			
		||||
    if ((defined $value and $key ne 'action' and $key ne 'action_with_args') and lc $mask ne lc $owner and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) {
 | 
			
		||||
      return "You are not the owner of $trigger_name.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -651,43 +638,30 @@ sub factunset {
 | 
			
		||||
 | 
			
		||||
  my ($owner_channel, $owner_trigger) = $self->{pbot}->{factoids}->find_factoid($channel, $trigger, exact_channel => 1, exact_trigger => 1);
 | 
			
		||||
 | 
			
		||||
  my $admininfo;
 | 
			
		||||
 | 
			
		||||
  my $userinfo;
 | 
			
		||||
  if (defined $owner_channel) {
 | 
			
		||||
    $admininfo = $self->{pbot}->{users}->loggedin_admin($owner_channel, "$nick!$user\@$host");
 | 
			
		||||
    $userinfo = $self->{pbot}->{users}->loggedin($owner_channel, "$nick!$user\@$host");
 | 
			
		||||
  } else {
 | 
			
		||||
    $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
    $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $level = 0;
 | 
			
		||||
  my $meta_level = 0;
 | 
			
		||||
 | 
			
		||||
  if (defined $admininfo) {
 | 
			
		||||
    $level = $admininfo->{level};
 | 
			
		||||
  my $meta_cap;
 | 
			
		||||
  if (exists $factoid_metadata_capabilities{$key}) {
 | 
			
		||||
    $meta_cap = $factoid_metadata_capabilities{$key};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (defined $factoid_metadata_levels{$key}) {
 | 
			
		||||
    $meta_level = $factoid_metadata_levels{$key};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ($meta_level > 0) {
 | 
			
		||||
    if ($level == 0) {
 | 
			
		||||
      return "You must be an admin to unset $key";
 | 
			
		||||
    } elsif ($level < $meta_level) {
 | 
			
		||||
      return "You must be at least level $meta_level to unset $key";
 | 
			
		||||
  if (defined $meta_cap) {
 | 
			
		||||
    if (not $self->{pbot}->{capabilities}->userhas($userinfo, $meta_cap)) {
 | 
			
		||||
      return "Your user account must have the $meta_cap capability to unset $key.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
  if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'cap-override'}) {
 | 
			
		||||
    if (lc $key eq 'locked') {
 | 
			
		||||
      if ($level >= $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
        $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, 'effective-level');
 | 
			
		||||
      if ($self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) {
 | 
			
		||||
        $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, 'cap-override');
 | 
			
		||||
      } else {
 | 
			
		||||
        return "You cannot unlock this factoid because its effective-level is higher than your level.";
 | 
			
		||||
      }
 | 
			
		||||
    } elsif (lc $key eq 'effective-level') {
 | 
			
		||||
      if ($level < $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
        return "You cannot unset the effective-level because it is higher than your level.";
 | 
			
		||||
        return "You cannot unlock this factoid because it has a cap-override. Remove the override first.";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -704,7 +678,7 @@ sub factunset {
 | 
			
		||||
 | 
			
		||||
    my ($owner) = $factoid->{'owner'} =~ m/([^!]+)/;
 | 
			
		||||
 | 
			
		||||
    if ($key ne 'action_with_args' and lc $nick ne lc $owner and $level == 0) {
 | 
			
		||||
    if ($key ne 'action_with_args' and lc $nick ne lc $owner and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) {
 | 
			
		||||
      return "You are not the owner of $trigger_name.";
 | 
			
		||||
    }
 | 
			
		||||
    $oldvalue = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{$key};
 | 
			
		||||
@ -717,7 +691,6 @@ sub factunset {
 | 
			
		||||
  if ($result =~ m/unset/) {
 | 
			
		||||
    $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "unset $key (value: $oldvalue)");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1623,13 +1596,12 @@ sub factchange {
 | 
			
		||||
    return "/say $trigger_name belongs to $channel_name, but this is $from_chan. Please switch to $channel_name or use /msg to change this factoid.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
 | 
			
		||||
  if ($factoids_hash->{$channel}->{$trigger}->{'locked'}) {
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be changed." if not defined $admininfo;
 | 
			
		||||
    return "/say $trigger_name is locked and cannot be changed." if not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin');
 | 
			
		||||
 | 
			
		||||
    if (exists $factoids_hash->{$channel}->{$trigger}->{'effective-level'}
 | 
			
		||||
        and $admininfo->{level} < $factoids_hash->{$channel}->{$trigger}->{'effective-level'}) {
 | 
			
		||||
      return "/say $trigger_name is locked with an effective-level higher than your level and cannot be changed.";
 | 
			
		||||
    if (exists $factoids_hash->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) {
 | 
			
		||||
      return "/say $trigger_name is locked with a cap-override set and cannot be changed until the override is removed.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1711,7 +1683,7 @@ sub factchange {
 | 
			
		||||
    return $ret if length $ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (length $action > 8000 and not defined $admininfo) {
 | 
			
		||||
  if (length $action > 8000 and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) {
 | 
			
		||||
    return "Change $trigger_name failed; result is too long.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -25,12 +25,8 @@ use Encode;
 | 
			
		||||
$SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) {} };
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to Commands should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@ -38,13 +34,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  my $pbot = delete $conf{pbot};
 | 
			
		||||
  if (not defined $pbot) {
 | 
			
		||||
    Carp::croak("Missing pbot reference to PBot::FactoidModuleLauncher");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $pbot;
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub execute_module {
 | 
			
		||||
 | 
			
		||||
@ -1009,12 +1009,12 @@ sub handle_action {
 | 
			
		||||
 | 
			
		||||
    $self->{pbot}->{logger}->log("[" . (defined $stuff->{from} ? $stuff->{from} : "stdin") . "] ($stuff->{nick}!$stuff->{user}\@$stuff->{host}) $trigger_name aliased to: $command\n");
 | 
			
		||||
 | 
			
		||||
    if (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) {
 | 
			
		||||
    if (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'}) {
 | 
			
		||||
        if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'locked'}) {
 | 
			
		||||
          $self->{pbot}->{logger}->log("Effective-level set to $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}\n");
 | 
			
		||||
          $stuff->{'effective-level'} = $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'};
 | 
			
		||||
          $self->{pbot}->{logger}->log("Capability override set to $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'}\n");
 | 
			
		||||
          $stuff->{'cap-override'} = $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'};
 | 
			
		||||
        } else {
 | 
			
		||||
          $self->{pbot}->{logger}->log("Ignoring effective-level of $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'} on unlocked factoid\n");
 | 
			
		||||
          $self->{pbot}->{logger}->log("Ignoring cap-override of $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'} on unlocked factoid\n");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1068,9 +1068,9 @@ sub handle_action {
 | 
			
		||||
    # Don't allow user-custom /msg factoids, unless factoid triggered by admin
 | 
			
		||||
    if ($action =~ m/^\/msg/i) {
 | 
			
		||||
      my $admin = $self->{pbot}->{users}->loggedin_admin($stuff->{from}, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}");
 | 
			
		||||
      if (not $admin or $admin->{level} < 60) {
 | 
			
		||||
      if (not $admin) {
 | 
			
		||||
        $self->{pbot}->{logger}->log("[ABUSE] Bad factoid (contains /msg): $action\n");
 | 
			
		||||
        return "You are not powerful enough to use /msg in a factoid.";
 | 
			
		||||
        return "You must be an admin to use /msg in a factoid.";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1084,19 +1084,6 @@ sub handle_action {
 | 
			
		||||
    } else {
 | 
			
		||||
      if ($action =~ m/^\/(?:say|me|msg)/i) {
 | 
			
		||||
        return $action;
 | 
			
		||||
      } elsif ($action =~ s/^\/kick\s+//) {
 | 
			
		||||
        if (not exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) {
 | 
			
		||||
          $stuff->{authorized} = 0;
 | 
			
		||||
          return "/say $stuff->{nick}: $trigger_name doesn't have the effective-level to do that.";
 | 
			
		||||
        }
 | 
			
		||||
        my $level = 10;
 | 
			
		||||
        if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'} >= $level) {
 | 
			
		||||
          $stuff->{authorized} = 1;
 | 
			
		||||
          return "/kick " . $action;
 | 
			
		||||
        } else {
 | 
			
		||||
          $stuff->{authorized} = 0;
 | 
			
		||||
          return "/say $stuff->{nick}: My effective-level isn't high enough to do that.";
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        return "/say $trigger_name is $action";
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -25,14 +25,10 @@ use warnings;
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
use feature 'unicode_strings';
 | 
			
		||||
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
@ -41,7 +37,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to FactoidCommands");
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->do_func(@_) }, 'func', 0);
 | 
			
		||||
  $self->init_funcs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,6 @@ sub initialize {
 | 
			
		||||
sub load {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my $filename;
 | 
			
		||||
 | 
			
		||||
  if (@_) { $filename = shift; } else { $filename = $self->{filename}; }
 | 
			
		||||
 | 
			
		||||
  if (not defined $filename) {
 | 
			
		||||
@ -85,7 +84,6 @@ sub load {
 | 
			
		||||
sub save {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my $filename;
 | 
			
		||||
 | 
			
		||||
  if (@_) { $filename = shift; } else { $filename = $self->{filename}; }
 | 
			
		||||
 | 
			
		||||
  if (not defined $filename) {
 | 
			
		||||
 | 
			
		||||
@ -20,12 +20,8 @@ use Data::Dumper;
 | 
			
		||||
$Data::Dumper::Sortkeys = 1;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@ -33,9 +29,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot};
 | 
			
		||||
  Carp::croak("Missing pbot parameter to " . __FILE__) if not defined $self->{pbot};
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot parameter to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.welcome',       sub { $self->on_connect(@_) });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.disconnect',    sub { $self->on_disconnect(@_) });
 | 
			
		||||
 | 
			
		||||
@ -19,12 +19,8 @@ use Time::Duration;
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to IgnoreListCommands should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@ -32,16 +28,9 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  my $pbot = delete $conf{pbot};
 | 
			
		||||
  if (not defined $pbot) {
 | 
			
		||||
    Carp::croak("Missing pbot reference to IgnoreListCommands");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $pbot;
 | 
			
		||||
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->ignore_user(@_)    },    "ignore",    10);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->unignore_user(@_)  },    "unignore",  10);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->ignore_user(@_)    }, "ignore",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->unignore_user(@_)  }, "unignore", 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub ignore_user {
 | 
			
		||||
 | 
			
		||||
@ -23,10 +23,7 @@ use Carp ();
 | 
			
		||||
use PBot::Utils::ValidateString;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
@ -35,10 +32,8 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->SUPER::initialize(%conf);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text',  'general', 'compile_blocks',                  $conf{compile_blocks}                  // 1);
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('array', 'general', 'compile_blocks_channels',         $conf{compile_blocks_channels}         // '.*');
 | 
			
		||||
@ -174,8 +169,8 @@ sub process_line {
 | 
			
		||||
    # check if user is ignored (and command isn't `login`)
 | 
			
		||||
    if ($command !~ /^login / && defined $from && $pbot->{ignorelist}->check_ignore($nick, $user, $host, $from)) {
 | 
			
		||||
      my $admin = $pbot->{users}->loggedin_admin($from, "$nick!$user\@$host");
 | 
			
		||||
      if (!defined $admin || $admin->{level} < 10) {
 | 
			
		||||
        # hostmask ignored
 | 
			
		||||
      if (!defined $admin) {
 | 
			
		||||
        # user is ignored
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -791,7 +786,7 @@ sub handle_result {
 | 
			
		||||
    $stuff->{prepend} = $1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ($stuff->{pipe} and not $stuff->{authorized}) {
 | 
			
		||||
  if ($stuff->{pipe}) {
 | 
			
		||||
    my ($pipe, $pipe_rest) = (delete $stuff->{pipe}, delete $stuff->{pipe_rest});
 | 
			
		||||
    if (not $stuff->{alldone}) {
 | 
			
		||||
      $stuff->{command} = "$pipe $result $pipe_rest";
 | 
			
		||||
@ -975,27 +970,6 @@ sub output_result {
 | 
			
		||||
      $pbot->{conn}->privmsg($to, $line) if $to ne $botnick;
 | 
			
		||||
      $pbot->{antiflood}->check_flood($to, $botnick, $pbot->{registry}->get_value('irc', 'username'), 'pbot', $line, 0, 0, 0) if $stuff->{checkflood};
 | 
			
		||||
    }
 | 
			
		||||
  } elsif ($stuff->{authorized} && $line =~ s/^\/kick\s+//) {
 | 
			
		||||
    $pbot->{antiflood}->check_flood($stuff->{from}, $botnick, $pbot->{registry}->get_value('irc', 'username'), 'pbot', '/kick ' . $line, 0, 0, 0) if $stuff->{checkflood};
 | 
			
		||||
    my ($victim, $reason) = split /\s+/, $line, 2;
 | 
			
		||||
 | 
			
		||||
    if (not defined $reason) {
 | 
			
		||||
      if (open my $fh, '<',  $self->{pbot}->{registry}->get_value('general', 'module_dir') . '/insults.txt') {
 | 
			
		||||
        my @insults = <$fh>;
 | 
			
		||||
        close $fh;
 | 
			
		||||
        $reason = $insults[rand @insults];
 | 
			
		||||
        chomp $reason;
 | 
			
		||||
      } else {
 | 
			
		||||
        $reason = 'Bye!';
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($self->{pbot}->{chanops}->can_gain_ops($stuff->{from})) {
 | 
			
		||||
      $self->{pbot}->{chanops}->add_op_command($stuff->{from}, "kick $stuff->{from} $victim $reason");
 | 
			
		||||
      $self->{pbot}->{chanops}->gain_ops($stuff->{from});
 | 
			
		||||
    } else {
 | 
			
		||||
      $pbot->{conn}->privmsg($stuff->{from}, "$victim: $reason") if defined $stuff->{from} && $stuff->{from} ne $botnick;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    if (defined $stuff->{nickoverride} and ($stuff->{no_nickoverride} == 0 or $stuff->{force_nickoverride} == 1)) {
 | 
			
		||||
      $line = "$stuff->{nickoverride}: $line";
 | 
			
		||||
@ -1087,9 +1061,9 @@ sub process_command_queue {
 | 
			
		||||
          preserve_whitespace => 0
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if (exists $command->{level}) {
 | 
			
		||||
          $self->{pbot}->{logger}->log("Override command effective-level to $command->{level}\n");
 | 
			
		||||
          $stuff->{'effective-level'} = $command->{level};
 | 
			
		||||
        if (exists $command->{'cap-override'}) {
 | 
			
		||||
          $self->{pbot}->{logger}->log("[command queue] Override command capability with $command->{'cap-override'}\n");
 | 
			
		||||
          $stuff->{'cap-override'} = $command->{'cap-override'};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        my $result = $self->interpret($stuff);
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}  = $conf{pbot} // Carp::croak("Missing pbot reference to LagChecker");
 | 
			
		||||
 | 
			
		||||
  $self->{lag_average}    = undef;     # average of entries in lag history, in seconds
 | 
			
		||||
 | 
			
		||||
@ -14,45 +14,32 @@ use File::Basename;
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to Logger should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to Logger should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH';
 | 
			
		||||
  my ($class, %conf) = @_;
 | 
			
		||||
  my $self = bless {}, $class;
 | 
			
		||||
 | 
			
		||||
  my $pbot = $conf{pbot} // Carp::croak "Missing pbot reference to " . __FILE__;
 | 
			
		||||
  my $logfile = $conf{filename} // Carp::croak "Missing logfile parameter in " . __FILE__;
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak "Missing pbot reference to " . __FILE__;
 | 
			
		||||
  $self->{logfile} = $conf{filename} // Carp::croak "Missing logfile parameter in " . __FILE__;
 | 
			
		||||
  $self->{start} = time;
 | 
			
		||||
 | 
			
		||||
  my $path = dirname $logfile;
 | 
			
		||||
  my $path = dirname $self->{logfile};
 | 
			
		||||
  if (not -d $path) {
 | 
			
		||||
    print "Creating new logfile path: $path\n";
 | 
			
		||||
    mkdir $path or Carp::croak "Couldn't create logfile path: $!\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  open LOGFILE, ">>$logfile" or Carp::croak "Couldn't open logfile $logfile: $!\n";
 | 
			
		||||
  open LOGFILE, ">>$self->{logfile}" or Carp::croak "Couldn't open logfile $self->{logfile}: $!\n";
 | 
			
		||||
  LOGFILE->autoflush(1);
 | 
			
		||||
 | 
			
		||||
  my $self = bless {
 | 
			
		||||
    logfile => $logfile,
 | 
			
		||||
    pbot => $pbot,
 | 
			
		||||
    start => time,
 | 
			
		||||
  }, $class;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{atexit}->register(sub { $self->rotate_log; return; });
 | 
			
		||||
 | 
			
		||||
  return $self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub log {
 | 
			
		||||
  my ($self, $text) = @_;
 | 
			
		||||
  my $time = localtime;
 | 
			
		||||
 | 
			
		||||
  $text =~ s/(\P{PosixGraph})/my $ch = $1; if ($ch =~ m{[\s]}) { $ch } else { sprintf "\\x%02X", ord $ch }/ge;
 | 
			
		||||
 | 
			
		||||
  if (openhandle *LOGFILE) {
 | 
			
		||||
    print LOGFILE "$time :: $text";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  print LOGFILE "$time :: $text" if openhandle *LOGFILE;
 | 
			
		||||
  print "$time :: $text";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,10 +26,7 @@ use Carp ();
 | 
			
		||||
use PBot::MessageHistory_SQLite;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
@ -38,8 +35,8 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{filename} = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3';
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{filename} = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3';
 | 
			
		||||
 | 
			
		||||
  $self->{database} = PBot::MessageHistory_SQLite->new(pbot => $self->{pbot}, filename => $self->{filename});
 | 
			
		||||
  $self->{database}->begin();
 | 
			
		||||
@ -55,9 +52,9 @@ sub initialize {
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->recall_message(@_)     },  "recall",          0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->list_also_known_as(@_) },  "aka",             0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->rebuild_aliases(@_)    },  "rebuildaliases", 90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->aka_link(@_)           },  "akalink",        60);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->aka_unlink(@_)         },  "akaunlink",      60);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->rebuild_aliases(@_)    },  "rebuildaliases",  1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->aka_link(@_)           },  "akalink",         1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->aka_unlink(@_)         },  "akaunlink",       1);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{atexit}->register(sub { $self->{database}->end(); return; });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,12 +22,8 @@ use Text::Levenshtein qw/fastdistance/;
 | 
			
		||||
use Time::Duration;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@ -36,8 +32,8 @@ sub new {
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__);
 | 
			
		||||
  $self->{filename}  = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3';
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__);
 | 
			
		||||
  $self->{filename} = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3';
 | 
			
		||||
  $self->{new_entries} = 0;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'messagehistory', 'debug_link',             0);
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,9 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}    = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{nicklist} = {};
 | 
			
		||||
 | 
			
		||||
  $self->{nicklist} = {};
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'nicklist', 'debug', '0');
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->show_nicklist(@_) }, "nicklist", 0);
 | 
			
		||||
@ -57,10 +56,7 @@ sub initialize {
 | 
			
		||||
sub show_nicklist {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
  my $nicklist;
 | 
			
		||||
 | 
			
		||||
  if (not length $arguments) {
 | 
			
		||||
    return "Usage: nicklist <channel> [nick]";
 | 
			
		||||
  }
 | 
			
		||||
  return "Usage: nicklist <channel> [nick]" if not length $arguments;
 | 
			
		||||
 | 
			
		||||
  my @args = split / /, $arguments;
 | 
			
		||||
 | 
			
		||||
@ -77,7 +73,6 @@ sub show_nicklist {
 | 
			
		||||
    }
 | 
			
		||||
    $nicklist = Dumper($self->{nicklist}->{lc $args[0]}->{lc $args[1]});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $nicklist;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -170,7 +165,6 @@ sub delete_meta {
 | 
			
		||||
      or not exists $self->{nicklist}->{$channel}->{$nick}->{$key}) {
 | 
			
		||||
    return undef;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return delete $self->{nicklist}->{$channel}->{$nick}->{$key};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -240,7 +234,6 @@ sub is_present_similar {
 | 
			
		||||
      return $self->{nicklist}->{$channel}->{$person}->{nick};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -290,7 +283,6 @@ sub on_namreply {
 | 
			
		||||
      $self->set_meta($channel, $stripped_nick, '+h', 1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -327,7 +319,6 @@ sub on_quit {
 | 
			
		||||
      $self->remove_nick($channel, $nick);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -350,7 +341,6 @@ sub on_nickchange {
 | 
			
		||||
      $self->{nicklist}->{$channel}->{lc $newnick} = $meta;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								PBot/PBot.pm
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								PBot/PBot.pm
									
									
									
									
									
								
							@ -49,10 +49,7 @@ use PBot::Utils::ParseDate;
 | 
			
		||||
use PBot::FuncCommand;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to PBot should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to PBot should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH';
 | 
			
		||||
  my ($class, %conf) = @_;
 | 
			
		||||
  my $self = bless {}, $class;
 | 
			
		||||
  $self->{atexit} = PBot::Registerable->new(%conf);
 | 
			
		||||
@ -63,7 +60,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{startup_timestamp} = time;
 | 
			
		||||
 | 
			
		||||
  my $data_dir   = $conf{data_dir};
 | 
			
		||||
@ -331,7 +327,7 @@ sub listcmd {
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
  my $text;
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: list <modules|commands|admins|users>";
 | 
			
		||||
  my $usage = "Usage: list <modules|commands|users>";
 | 
			
		||||
 | 
			
		||||
  if (not defined $arguments) {
 | 
			
		||||
    return $usage;
 | 
			
		||||
@ -353,8 +349,11 @@ sub listcmd {
 | 
			
		||||
  if ($arguments =~ /^commands$/i) {
 | 
			
		||||
    $text = "Registered commands: ";
 | 
			
		||||
    foreach my $command (sort { $a->{name} cmp $b->{name} } @{ $self->{commands}->{handlers} }) {
 | 
			
		||||
      $text .= "$command->{name} ";
 | 
			
		||||
      $text .= "($command->{level}) " if $command->{level} > 0;
 | 
			
		||||
      if ($command->{requires_cap}) {
 | 
			
		||||
        $text .= "+$command->{name} ";
 | 
			
		||||
      } else {
 | 
			
		||||
        $text .= "$command->{name} ";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return $text;
 | 
			
		||||
  }
 | 
			
		||||
@ -373,31 +372,17 @@ sub listcmd {
 | 
			
		||||
      foreach my $hostmask (sort { return 0 if $a eq '_name' or $b eq '_name'; $self->{users}->{users}->{hash}->{$channel}->{$a}->{name} cmp $self->{users}->{users}->{hash}->{$channel}->{$b}->{name} } keys %{ $self->{users}->{users}->{hash}->{$channel} }) {
 | 
			
		||||
        next if $hostmask eq '_name';
 | 
			
		||||
        $text .= $sep;
 | 
			
		||||
        my $has_cap = 0;
 | 
			
		||||
        foreach my $key (keys %{$self->{users}->{users}->{hash}->{$channel}->{$hostmask}}) {
 | 
			
		||||
          next if $key eq '_name';
 | 
			
		||||
          if ($self->{capabilities}->exists($key)) {
 | 
			
		||||
            print "has $key?\n";
 | 
			
		||||
            $has_cap = 1;
 | 
			
		||||
            last;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        $text .= '+' if $has_cap;
 | 
			
		||||
        $text .= $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{name};
 | 
			
		||||
        $text .= "(" . $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} . ")" if $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} > 0;
 | 
			
		||||
        $sep = " ";
 | 
			
		||||
      }
 | 
			
		||||
      $sep = "; ";
 | 
			
		||||
    }
 | 
			
		||||
    return $text;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ($arguments =~ /^admins$/i) {
 | 
			
		||||
    $text = "Admins: ";
 | 
			
		||||
    my $last_channel = "";
 | 
			
		||||
    my $sep = "";
 | 
			
		||||
    foreach my $channel (sort keys %{ $self->{users}->{users}->{hash} }) {
 | 
			
		||||
      next if $from =~ m/^#/ and $channel ne $from and $channel ne '.*';
 | 
			
		||||
      if ($last_channel ne $channel) {
 | 
			
		||||
        $text .= $sep . ($channel eq ".*" ? "global" : $channel) . ": ";
 | 
			
		||||
        $last_channel = $channel;
 | 
			
		||||
        $sep = "";
 | 
			
		||||
      }
 | 
			
		||||
      foreach my $hostmask (sort { return 0 if $a eq '_name' or $b eq '_name'; $self->{users}->{users}->{hash}->{$channel}->{$a}->{name} cmp $self->{users}->{users}->{hash}->{$channel}->{$b}->{name} } keys %{ $self->{users}->{users}->{hash}->{$channel} }) {
 | 
			
		||||
        next if $hostmask eq '_name';
 | 
			
		||||
        next if $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} <= 0;
 | 
			
		||||
        $text .= $sep;
 | 
			
		||||
        $text .= $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{name} . " (" . $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} . ")";
 | 
			
		||||
        $sep = " ";
 | 
			
		||||
      }
 | 
			
		||||
      $sep = "; ";
 | 
			
		||||
 | 
			
		||||
@ -18,12 +18,8 @@ use File::Basename;
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
@ -31,20 +27,16 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{plugins} = {};
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->load_cmd(@_)   },  "plug",     90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->unload_cmd(@_) },  "unplug",   90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->reload_cmd(@_) },  "replug",   90);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->list_cmd(@_)   },  "pluglist",  0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->load_cmd(@_)   },  "plug",     1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->unload_cmd(@_) },  "unplug",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->reload_cmd(@_) },  "replug",   1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->list_cmd(@_)   },  "pluglist", 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub autoload {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  return if $self->{pbot}->{registry}->get_value('plugins', 'noautoload');
 | 
			
		||||
 | 
			
		||||
  my $path = $self->{pbot}->{registry}->get_value('general', 'plugin_dir') // 'Plugins';
 | 
			
		||||
@ -71,7 +63,6 @@ sub autoload {
 | 
			
		||||
 | 
			
		||||
    $plugin_count++ if $self->load($plugin, %conf)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{logger}->log("$plugin_count plugin" . ($plugin_count == 1 ? '' : 's') . " loaded.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -112,7 +103,6 @@ sub load {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Error loading $plugin: $@\n");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -189,7 +179,6 @@ sub unload_cmd {
 | 
			
		||||
 | 
			
		||||
sub list_cmd {
 | 
			
		||||
   my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
   my $result = "Loaded plugins: ";
 | 
			
		||||
   my $count = 0;
 | 
			
		||||
   my $comma = '';
 | 
			
		||||
@ -201,7 +190,6 @@ sub list_cmd {
 | 
			
		||||
   }
 | 
			
		||||
 | 
			
		||||
   $result .= 'none' if $count == 0;
 | 
			
		||||
 | 
			
		||||
   return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,10 +22,7 @@ use PBot::DualIndexHashObject;
 | 
			
		||||
use PBot::RegistryCommands;
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be item/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to " . __FILE__ . " should be item/value pairs, not hash reference") if ref($_[1]) eq 'HASH';
 | 
			
		||||
  my ($class, %conf) = @_;
 | 
			
		||||
  my $self = bless {}, $class;
 | 
			
		||||
  $self->initialize(%conf);
 | 
			
		||||
@ -34,15 +31,11 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  my $filename  = $conf{filename} // Carp::croak("Missing filename reference in " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{registry} = PBot::DualIndexHashObject->new(name => 'Registry', filename => $filename, pbot => $self->{pbot});
 | 
			
		||||
  $self->{triggers} = {};
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{atexit}->register(sub { $self->save; return; });
 | 
			
		||||
 | 
			
		||||
  PBot::RegistryCommands->new(pbot => $self->{pbot});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -107,7 +100,6 @@ sub remove {
 | 
			
		||||
  if (not scalar keys %{ $self->{registry}->{hash}->{$section} }) {
 | 
			
		||||
    delete $self->{registry}->{hash}->{$section};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->save;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -139,7 +131,6 @@ sub set {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->save if !$dont_save && $result =~ m/set to/ && not $is_default;
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,10 +17,7 @@ use feature 'unicode_strings';
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
@ -29,23 +26,19 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  my $pbot = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = $pbot;
 | 
			
		||||
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regset(@_)         }, "regset",       60);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regunset(@_)       }, "regunset",     60);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regshow(@_)        }, "regshow",       0);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regsetmeta(@_)     }, "regsetmeta",   60);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regunsetmeta(@_)   }, "regunsetmeta", 60);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regchange(@_)      }, "regchange",    60);
 | 
			
		||||
  $pbot->{commands}->register(sub { return $self->regfind(@_)        }, "regfind",       0);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regset(@_)         }, "regset",        1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regunset(@_)       }, "regunset",      1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regshow(@_)        }, "regshow",       0);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regsetmeta(@_)     }, "regsetmeta",    1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regunsetmeta(@_)   }, "regunsetmeta",  1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regchange(@_)      }, "regchange",     1);
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->regfind(@_)        }, "regfind",       0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub regset {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regset <section>.<item> <value>";
 | 
			
		||||
 | 
			
		||||
  # support "<section>.<key>" syntax in addition to "<section> <key>"
 | 
			
		||||
@ -71,7 +64,6 @@ sub regset {
 | 
			
		||||
sub regunset {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regunset <section>.<item>";
 | 
			
		||||
 | 
			
		||||
  # support "<section>.<key>" syntax in addition to "<section> <key>"
 | 
			
		||||
@ -106,7 +98,6 @@ sub regunset {
 | 
			
		||||
sub regsetmeta {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regsetmeta <section>.<item> [key [value]]";
 | 
			
		||||
 | 
			
		||||
  # support "<section>.<key>" syntax in addition to "<section> <key>"
 | 
			
		||||
@ -125,14 +116,12 @@ sub regsetmeta {
 | 
			
		||||
 | 
			
		||||
  $key = undef if not length $key;
 | 
			
		||||
  $value = undef if not length $value;
 | 
			
		||||
 | 
			
		||||
  return $self->{pbot}->{registry}->set($section, $item, $key, $value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub regunsetmeta {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regunsetmeta <section>.<item> <key>";
 | 
			
		||||
 | 
			
		||||
  # support "<section>.<key>" syntax in addition to "<section> <key>"
 | 
			
		||||
@ -148,7 +137,6 @@ sub regunsetmeta {
 | 
			
		||||
  if (not defined $section or not defined $item or not defined $key) {
 | 
			
		||||
    return $usage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $self->{pbot}->{registry}->unset($section, $item, $key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -156,7 +144,6 @@ sub regshow {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
  my $registry = $self->{pbot}->{registry}->{registry}->{hash};
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regshow <section>.<item>";
 | 
			
		||||
 | 
			
		||||
  # support "<section>.<key>" syntax in addition to "<section> <key>"
 | 
			
		||||
@ -192,7 +179,6 @@ sub regshow {
 | 
			
		||||
  if ($registry->{$section}->{$item}->{type} eq 'array') {
 | 
			
		||||
    $result .= ' [array]';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -200,15 +186,11 @@ sub regfind {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
  my $registry = $self->{pbot}->{registry}->{registry}->{hash};
 | 
			
		||||
 | 
			
		||||
  my $usage = "Usage: regfind [-showvalues] [-section section] <regex>";
 | 
			
		||||
 | 
			
		||||
  if (not defined $arguments) {
 | 
			
		||||
    return $usage;
 | 
			
		||||
  }
 | 
			
		||||
  return $usage if not defined $arguments;
 | 
			
		||||
 | 
			
		||||
  my ($section, $showvalues);
 | 
			
		||||
 | 
			
		||||
  $section = $1 if $arguments =~ s/-section\s+([^\b\s]+)//i;
 | 
			
		||||
  $showvalues = 1 if $arguments =~ s/-showvalues?//i;
 | 
			
		||||
 | 
			
		||||
@ -216,9 +198,7 @@ sub regfind {
 | 
			
		||||
  $arguments =~ s/\s+$//;
 | 
			
		||||
  $arguments =~ s/\s+/ /g;
 | 
			
		||||
 | 
			
		||||
  if ($arguments eq "") {
 | 
			
		||||
    return $usage;
 | 
			
		||||
  }
 | 
			
		||||
  return $usage if $arguments eq "";
 | 
			
		||||
 | 
			
		||||
  $section = lc $section if defined $section;;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,12 +13,8 @@ use IO::Select;
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to SelectHandler should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to SelectHandler 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;
 | 
			
		||||
@ -26,8 +22,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in SelectHandler");
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in SelectHandler");
 | 
			
		||||
  $self->{select} = IO::Select->new();
 | 
			
		||||
  $self->{readers} = {};
 | 
			
		||||
  $self->{buffers} = {};
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in StdinReader");
 | 
			
		||||
 | 
			
		||||
  # create implicit bot-admin account for bot
 | 
			
		||||
@ -61,7 +60,6 @@ sub stdin_reader {
 | 
			
		||||
    $from = $self->{pbot}->{registry}->get_value('irc', 'botnick') . "!stdin\@pbot";
 | 
			
		||||
    $text = $self->{pbot}->{registry}->get_value('irc', 'botnick') . " $input";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $self->{pbot}->{interpreter}->process_line($from, $self->{pbot}->{registry}->get_value('irc', 'botnick'), "stdin", "pbot", $text);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,8 +27,6 @@ $SIG{ALRM} = sub {
 | 
			
		||||
  $seconds += $min_timeout;
 | 
			
		||||
  alarm $min_timeout;
 | 
			
		||||
 | 
			
		||||
  # print "ALARM! $seconds $min_timeout\n";
 | 
			
		||||
 | 
			
		||||
  # call timer func subroutines
 | 
			
		||||
  foreach my $func (@timer_funcs) { &$func; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										145
									
								
								PBot/Users.pm
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								PBot/Users.pm
									
									
									
									
									
								
							@ -27,9 +27,9 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot}     = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{users}   = PBot::DualIndexHashObject->new(name => 'Users', filename => $conf{filename}, pbot => $conf{pbot});
 | 
			
		||||
  $self->{users} = PBot::DualIndexHashObject->new(name => 'Users', filename => $conf{filename}, pbot => $conf{pbot});
 | 
			
		||||
  $self->load;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { return $self->logincmd(@_)   },  "login",      0);
 | 
			
		||||
@ -74,7 +74,7 @@ sub on_join {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ($u->{autologin}) {
 | 
			
		||||
      $self->{pbot}->{logger}->log("$nick!$user\@$host autologin to $u->{name} ($u->{level}) for $channel\n");
 | 
			
		||||
      $self->{pbot}->{logger}->log("$nick!$user\@$host autologin to $u->{name} for $channel\n");
 | 
			
		||||
      $u->{loggedin} = 1;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -82,19 +82,23 @@ sub on_join {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_user {
 | 
			
		||||
  my ($self, $name, $channel, $hostmask, $level, $password, $dont_save) = @_;
 | 
			
		||||
  my ($self, $name, $channel, $hostmask, $capabilities, $password, $dont_save) = @_;
 | 
			
		||||
  $channel = '.*' if $channel !~ m/^#/;
 | 
			
		||||
 | 
			
		||||
  $level //= 0;
 | 
			
		||||
  $capabilities //= 'none';
 | 
			
		||||
  $password //= $self->{pbot}->random_nick(16);
 | 
			
		||||
 | 
			
		||||
  my $data = {
 | 
			
		||||
    name => $name,
 | 
			
		||||
    level => $level,
 | 
			
		||||
    password => $password
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{logger}->log("Adding new user (level $level): name: $name hostmask: $hostmask channel: $channel\n");
 | 
			
		||||
  foreach my $cap (split /\s*,\s*/, $capabilities) {
 | 
			
		||||
    next if $cap eq 'none';
 | 
			
		||||
    $data->{$cap} = 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{logger}->log("Adding new user (caps: $capabilities): name: $name hostmask: $hostmask channel: $channel\n");
 | 
			
		||||
  $self->{users}->add($channel, $hostmask, $data, $dont_save);
 | 
			
		||||
  return $data;
 | 
			
		||||
}
 | 
			
		||||
@ -123,10 +127,9 @@ sub load {
 | 
			
		||||
      next if $hostmask eq '_name';
 | 
			
		||||
      $i++;
 | 
			
		||||
      my $name = $self->{users}->{hash}->{$channel}->{$hostmask}->{name};
 | 
			
		||||
      my $level = $self->{users}->{hash}->{$channel}->{$hostmask}->{level};
 | 
			
		||||
      my $password = $self->{users}->{hash}->{$channel}->{$hostmask}->{password};
 | 
			
		||||
 | 
			
		||||
      if (not defined $name or not defined $level or not defined $password) {
 | 
			
		||||
      if (not defined $name or not defined $password) {
 | 
			
		||||
        Carp::croak "A user in $filename is missing critical data\n";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -150,16 +153,13 @@ sub find_user_account {
 | 
			
		||||
  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};
 | 
			
		||||
            }
 | 
			
		||||
            $found_hostmask = $mask;
 | 
			
		||||
            $found_channel = $chan;
 | 
			
		||||
            last;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if ($mask =~ /[*?]/) {
 | 
			
		||||
@ -168,26 +168,23 @@ sub find_user_account {
 | 
			
		||||
            $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};
 | 
			
		||||
              }
 | 
			
		||||
              $found_hostmask = $mask;
 | 
			
		||||
              $found_channel = $chan;
 | 
			
		||||
              last;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        $found_channel = $chan;
 | 
			
		||||
        last;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ($found_channel, $found_hostmask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub find_admin {
 | 
			
		||||
  my ($self, $channel, $hostmask, $min_level) = @_;
 | 
			
		||||
  $min_level //= 1;
 | 
			
		||||
 | 
			
		||||
sub find_user {
 | 
			
		||||
  my ($self, $channel, $hostmask) = @_;
 | 
			
		||||
  ($channel, $hostmask) = $self->find_user_account($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
  $channel = '.*' if not defined $channel;
 | 
			
		||||
@ -195,7 +192,7 @@ sub find_admin {
 | 
			
		||||
  $hostmask = lc $hostmask;
 | 
			
		||||
 | 
			
		||||
  my $result = eval {
 | 
			
		||||
    my $admin;
 | 
			
		||||
    my $user;
 | 
			
		||||
    foreach my $channel_regex (keys %{ $self->{users}->{hash} }) {
 | 
			
		||||
      if ($channel !~ m/^#/ or $channel =~ m/^$channel_regex$/i) {
 | 
			
		||||
        foreach my $hostmask_regex (keys %{ $self->{users}->{hash}->{$channel_regex} }) {
 | 
			
		||||
@ -206,51 +203,45 @@ sub find_admin {
 | 
			
		||||
            $hostmask_quoted =~ s/\\\*/.*?/g;
 | 
			
		||||
            $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});
 | 
			
		||||
              return $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex};
 | 
			
		||||
            }
 | 
			
		||||
          } 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});
 | 
			
		||||
              return $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex};
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return $admin;
 | 
			
		||||
    return undef;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if ($@) {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Error in find_admin parameters: $@\n");
 | 
			
		||||
    $self->{pbot}->{logger}->log("Error in find_user parameters: $@\n");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub find_user {
 | 
			
		||||
sub find_admin {
 | 
			
		||||
  my ($self, $from, $hostmask) = @_;
 | 
			
		||||
  return $self->find_admin($from, $hostmask, 0);
 | 
			
		||||
  my $user = $self->find_user($from, $hostmask);
 | 
			
		||||
  return undef if not defined $user;
 | 
			
		||||
  return undef if not $self->{pbot}->{capabilities}->userhas($user, 'admin');
 | 
			
		||||
  return $user;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub loggedin {
 | 
			
		||||
  my ($self, $channel, $hostmask) = @_;
 | 
			
		||||
  my $user = $self->find_user($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
  if (defined $user and $user->{loggedin}) {
 | 
			
		||||
    return $user;
 | 
			
		||||
  }
 | 
			
		||||
  return $user if defined $user and $user->{loggedin};
 | 
			
		||||
  return undef;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub loggedin_admin {
 | 
			
		||||
  my ($self, $channel, $hostmask) = @_;
 | 
			
		||||
  my $user = $self->loggedin($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
  if (defined $user and $user->{level} > 0) {
 | 
			
		||||
    return $user;
 | 
			
		||||
  }
 | 
			
		||||
  return $user if defined $user and $self->{pbot}->{capabilities}->userhas($user, 'admin');
 | 
			
		||||
  return undef;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -293,9 +284,7 @@ sub logincmd {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
  my $channel = $from;
 | 
			
		||||
 | 
			
		||||
  if (not $arguments) {
 | 
			
		||||
    return "Usage: login [channel] password";
 | 
			
		||||
  }
 | 
			
		||||
  return "Usage: login [channel] password" if not $arguments;
 | 
			
		||||
 | 
			
		||||
  if ($arguments =~ m/^([^ ]+)\s+(.+)/) {
 | 
			
		||||
    $channel = $1;
 | 
			
		||||
@ -303,10 +292,7 @@ sub logincmd {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my ($user_channel, $user_hostmask) = $self->find_user_account($channel, "$nick!$user\@$host");
 | 
			
		||||
 | 
			
		||||
  if (not defined $user_channel) {
 | 
			
		||||
    return "/msg $nick You do not have a user account.";
 | 
			
		||||
  }
 | 
			
		||||
  return "/msg $nick You do not have a user account." if not defined $user_channel;
 | 
			
		||||
 | 
			
		||||
  my $u = $self->{users}->{hash}->{$user_channel}->{$user_hostmask};
 | 
			
		||||
  my $channel_text = $user_channel eq '.*' ? '' : " for $user_channel";
 | 
			
		||||
@ -343,29 +329,35 @@ sub logoutcmd {
 | 
			
		||||
sub useradd {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_;
 | 
			
		||||
 | 
			
		||||
  my ($name, $channel, $hostmask, $level, $password) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 5);
 | 
			
		||||
  $level //= 0;
 | 
			
		||||
  my ($name, $channel, $hostmask, $capabilities, $password) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 5);
 | 
			
		||||
  $capabilities //= 'none';
 | 
			
		||||
 | 
			
		||||
  if (not defined $name or not defined $channel or not defined $hostmask) {
 | 
			
		||||
    return "Usage: useradd <account name> <channel> <hostmask> [level [password]]";
 | 
			
		||||
    return "Usage: useradd <account name> <channel> <hostmask> [capabilities [password]]";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $channel = '.*' if $channel !~ /^#/;
 | 
			
		||||
 | 
			
		||||
  my $admin  = $self->{pbot}->{users}->find_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $u = $self->{pbot}->{users}->find_user($channel, "$nick!$user\@$host");
 | 
			
		||||
 | 
			
		||||
  if (not $admin) {
 | 
			
		||||
  if (not defined $u) {
 | 
			
		||||
    $channel = 'global' if $channel eq '.*';
 | 
			
		||||
    return "You are not an admin for $channel; cannot add users to that channel.\n";
 | 
			
		||||
    return "You do not have a user account for $channel; cannot add users to that channel.\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # don't allow non-bot-owners to add admins that can also add admins
 | 
			
		||||
  if ($admin->{level} < 90 and $level > 40) {
 | 
			
		||||
    return "You may not set admin level higher than 40.\n";
 | 
			
		||||
  if ($capabilities ne 'none' and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) {
 | 
			
		||||
    return "Your user account does not have the can-modify-capabilities capability. You cannot create user accounts with capabilities.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{users}->add_user($name, $channel, $hostmask, $level, $password);
 | 
			
		||||
  return !$level ? "User added." : "Admin added.";
 | 
			
		||||
  foreach my $cap (split /\s*,\s*/, $capabilities) {
 | 
			
		||||
    next if $cap eq 'none';
 | 
			
		||||
    return "There is no such capability $cap." if not $self->{pbot}->{capabilities}->exists($cap);
 | 
			
		||||
    if (not $self->{pbot}->{capabilities}->userhas($u, $cap)) {
 | 
			
		||||
      return "To set the $cap capability your user account must also have it.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  $self->{pbot}->{users}->add_user($name, $channel, $hostmask, $capabilities, $password);
 | 
			
		||||
  return "User added.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub userdel {
 | 
			
		||||
@ -395,12 +387,12 @@ sub userset {
 | 
			
		||||
    return "Usage: userset [channel] <hostmask or account name> [key [value]]";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $admin  = $self->find_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $u = $self->find_user($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $target = $self->find_user($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
  if (not $admin) {
 | 
			
		||||
  if (not $u) {
 | 
			
		||||
    $channel = 'global' if $channel eq '.*';
 | 
			
		||||
    return "You are not an admin for $channel; cannot modify their users.";
 | 
			
		||||
    return "You do not have a user account for $channel; cannot modify their users.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not $target) {
 | 
			
		||||
@ -411,13 +403,14 @@ sub userset {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  # don't allow non-bot-owners to add admins that can also add admins
 | 
			
		||||
  if (defined $key and $key eq 'level' and $admin->{level} < 90 and $value > 40) {
 | 
			
		||||
    return "You may not set user level higher than 40.\n";
 | 
			
		||||
  if (defined $value and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) {
 | 
			
		||||
    if ($key =~ m/^can-/i or $self->{pbot}->{capabilities}->exists($key)) {
 | 
			
		||||
      return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (defined $key and $target->{level} > $admin->{level}) {
 | 
			
		||||
    return "You may not modify users higher in level than you.";
 | 
			
		||||
  if ($self->{pbot}->{capabilities}->exists($key) and not $self->{pbot}->{capabilities}->userhas($u, $key)) {
 | 
			
		||||
    return "To set the $key capability your user account must also have it.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask);
 | 
			
		||||
@ -440,12 +433,12 @@ sub userunset {
 | 
			
		||||
    return "Usage: userunset [channel] <hostmask or account name> <key>";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my $admin  = $self->find_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $u = $self->find_admin($channel, "$nick!$user\@$host");
 | 
			
		||||
  my $target = $self->find_user($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
  if (not $admin) {
 | 
			
		||||
  if (not $u) {
 | 
			
		||||
    $channel = 'global' if $channel eq '.*';
 | 
			
		||||
    return "You are not an admin for $channel; cannot modify their users.";
 | 
			
		||||
    return "You do not have a user account for $channel; cannot modify their users.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not $target) {
 | 
			
		||||
@ -456,8 +449,14 @@ sub userunset {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ($target->{level} > $admin->{level}) {
 | 
			
		||||
    return "You may not modify users higher in level than you.";
 | 
			
		||||
  if (defined $key and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) {
 | 
			
		||||
    if ($key =~ m/^can-/i or $self->{pbot}->{capabilities}->exists($key)) {
 | 
			
		||||
      return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have.";
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (defined $key and $self->{pbot}->{capabilities}->exists($key) and not $self->{pbot}->{capabilities}->userhas($u, $key)) {
 | 
			
		||||
    return "To unset the $key capability your user account must also have it.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask);
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,6 @@ sub version_cmd {
 | 
			
		||||
  if ($self->{last_check}->{version} > BUILD_REVISION) {
 | 
			
		||||
    $result .= "; new version available: $self->{last_check}->{version} $self->{last_check}->{date}!";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}    = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'antiaway', 'bad_nicks',   $conf{bad_nicks}   // '([[:punct:]](afk|brb|bbl|away|sleep|z+|work|gone|study|out|home|busy|off)[[:punct:]]*$|.+\[.*\]$)');
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'antiaway', 'bad_actions', $conf{bad_actions} // '^/me (is (away|gone)|.*auto.?away)');
 | 
			
		||||
@ -52,7 +51,6 @@ sub on_nickchange {
 | 
			
		||||
      $self->{pbot}->{chanops}->gain_ops($chan);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -70,7 +68,6 @@ sub on_action {
 | 
			
		||||
    $self->{pbot}->{chanops}->add_op_command($channel, "kick $channel $nick $kick_msg");
 | 
			
		||||
    $self->{pbot}->{chanops}->gain_ops($channel);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}    = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('array', 'antikickautorejoin', 'punishment', '300,900,1800,3600,28800');
 | 
			
		||||
@ -36,7 +35,6 @@ sub initialize {
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
 | 
			
		||||
 | 
			
		||||
  $self->{kicks} = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -54,7 +52,6 @@ sub on_kick {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{kicks}->{$channel}->{$target}->{last_kick} = gettimeofday;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -82,7 +79,6 @@ sub on_join {
 | 
			
		||||
      $self->{kicks}->{$channel}->{$nick}->{rejoins}++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,10 +30,8 @@ sub new {
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.public',  sub { $self->on_public(@_) });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_action(@_) });
 | 
			
		||||
 | 
			
		||||
  $self->{nicks} = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -98,7 +96,6 @@ sub clear_old_nicks {
 | 
			
		||||
      last;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  delete $self->{nicks}->{$channel} if not @{$self->{nicks}->{$channel}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'antiflood', 'antirepeat',           $conf{antirepeat}           // 1);
 | 
			
		||||
@ -40,17 +39,11 @@ sub initialize {
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_public(@_) });
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{timer}->register(sub { $self->adjust_offenses }, 60 * 60 * 1, 'antirepeat');
 | 
			
		||||
 | 
			
		||||
  $self->{offenses} = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub unload {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  # perform plugin clean-up here
 | 
			
		||||
  # normally we'd unregister the 'irc.public' event handler; however, the
 | 
			
		||||
  # event dispatcher will do this automatically for us when it sees there
 | 
			
		||||
  # is no longer an existing sub.
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{timer}->unregister('antirepeat');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -161,7 +154,6 @@ sub on_public {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -28,20 +28,17 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}    = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('array', 'autorejoin', 'rejoin_delay', '900,1800,3600');
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.kick',   sub { $self->on_kick(@_)   });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.part',   sub { $self->on_part(@_)   });
 | 
			
		||||
 | 
			
		||||
  $self->{rejoins} = {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub rejoin_channel {
 | 
			
		||||
  my ($self, $channel) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{rejoins}->{$channel}->{rejoins} = 0 if not exists $self->{rejoins}->{$channel};
 | 
			
		||||
 | 
			
		||||
  my $delay = $self->{pbot}->{registry}->get_array_value($channel, 'rejoin_delay', $self->{rejoins}->{$channel}->{rejoins});
 | 
			
		||||
@ -51,8 +48,6 @@ sub rejoin_channel {
 | 
			
		||||
 | 
			
		||||
  $delay = duration $delay;
 | 
			
		||||
  $self->{pbot}->{logger}->log("Rejoining $channel in $delay.\n");
 | 
			
		||||
 | 
			
		||||
  #$self->{rejoins}->{$channel}->{rejoins}++;
 | 
			
		||||
  $self->{rejoins}->{$channel}->{last_rejoin} = gettimeofday;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -66,7 +61,6 @@ sub on_kick {
 | 
			
		||||
  if ($target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) {
 | 
			
		||||
    $self->rejoin_channel($channel);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -80,7 +74,6 @@ sub on_part {
 | 
			
		||||
  if ($nick eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) {
 | 
			
		||||
    $self->rejoin_channel($channel);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->battleship_cmd(@_) }, 'battleship', 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot};
 | 
			
		||||
  $self->{pbot} = $conf{pbot};
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->connect4_cmd(@_) }, 'connect4', 0);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,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 { $self->counteradd(@_)      }, 'counteradd',      0);
 | 
			
		||||
@ -40,7 +39,6 @@ sub initialize {
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) });
 | 
			
		||||
 | 
			
		||||
  $self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/counters.sqlite3';
 | 
			
		||||
 | 
			
		||||
  $self->create_database;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -130,7 +128,6 @@ sub add_counter {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Add counter failed: $@");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -155,7 +152,6 @@ sub reset_counter {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Reset counter failed: $@");
 | 
			
		||||
    return (undef, undef);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ($description, $timestamp);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -179,7 +175,6 @@ sub delete_counter {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Delete counter failed: $@");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -196,7 +191,6 @@ sub list_counters {
 | 
			
		||||
  if ($@) {
 | 
			
		||||
    $self->{pbot}->{logger}->log("List counters failed: $@");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return map { $_->[0] } @$counters;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -216,7 +210,6 @@ sub get_counter {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Get counter failed: $@");
 | 
			
		||||
    return undef;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ($description, $time, $counter, $created_on, $created_by);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -240,7 +233,6 @@ sub add_trigger {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Add trigger failed: $@");
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -256,7 +248,6 @@ sub delete_trigger {
 | 
			
		||||
  $sth->bind_param(1, lc $channel);
 | 
			
		||||
  $sth->bind_param(2, lc $trigger);
 | 
			
		||||
  $sth->execute();
 | 
			
		||||
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -273,7 +264,6 @@ sub list_triggers {
 | 
			
		||||
  if ($@) {
 | 
			
		||||
    $self->{pbot}->{logger}->log("List triggers failed: $@");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return @$triggers;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -293,17 +283,12 @@ sub get_trigger {
 | 
			
		||||
    $self->{pbot}->{logger}->log("Get trigger failed: $@");
 | 
			
		||||
    return undef;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub counteradd {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my ($channel, $name, $description);
 | 
			
		||||
 | 
			
		||||
  if ($from !~ m/^#/) {
 | 
			
		||||
@ -325,18 +310,13 @@ sub counteradd {
 | 
			
		||||
  } else {
 | 
			
		||||
    $result = "Counter '$name' already exists.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->dbi_end;
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub counterdel {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my ($channel, $name);
 | 
			
		||||
 | 
			
		||||
  if ($from !~ m/^#/) {
 | 
			
		||||
@ -358,18 +338,13 @@ sub counterdel {
 | 
			
		||||
  } else {
 | 
			
		||||
    $result = "No such counter.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->dbi_end;
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub counterreset {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my ($channel, $name);
 | 
			
		||||
 | 
			
		||||
  if ($from !~ m/^#/) {
 | 
			
		||||
@ -400,11 +375,7 @@ sub counterreset {
 | 
			
		||||
 | 
			
		||||
sub countershow {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my ($channel, $name);
 | 
			
		||||
 | 
			
		||||
  if ($from !~ m/^#/) {
 | 
			
		||||
@ -436,11 +407,7 @@ sub countershow {
 | 
			
		||||
 | 
			
		||||
sub counterlist {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my $channel;
 | 
			
		||||
 | 
			
		||||
  if ($from !~ m/^#/) {
 | 
			
		||||
@ -472,11 +439,7 @@ sub counterlist {
 | 
			
		||||
 | 
			
		||||
sub countertrigger {
 | 
			
		||||
  my ($self, $from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  if (not $self->dbi_begin) {
 | 
			
		||||
    return "Internal error.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "Internal error." if not $self->dbi_begin;
 | 
			
		||||
  my $command;
 | 
			
		||||
  ($command, $arguments) = split / /, $arguments, 2;
 | 
			
		||||
 | 
			
		||||
@ -642,9 +605,7 @@ sub on_public {
 | 
			
		||||
      $self->{pbot}->{logger}->log("Skipping bad trigger $trigger->{trigger}: $@");
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->dbi_end;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,10 +21,8 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.public',    sub { $self->on_public(@_) });
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub unload {
 | 
			
		||||
@ -45,7 +43,6 @@ sub on_public {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{logger}->log("Example plugin: got message from $nick!$user\@$host: $msg\n");
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text', 'googlesearch', 'api_key', '');
 | 
			
		||||
@ -107,7 +106,6 @@ sub googlesearch {
 | 
			
		||||
    $comma = " -- ";
 | 
			
		||||
    last if --$matches <= 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $output;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,6 @@ use warnings;
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
use feature 'unicode_strings';
 | 
			
		||||
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
@ -26,7 +25,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->magic(@_)}, "mc", 90);
 | 
			
		||||
}
 | 
			
		||||
@ -39,7 +37,6 @@ sub unload {
 | 
			
		||||
sub magic {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
  my ($from, $nick, $user, $host, $arguments) = @_;
 | 
			
		||||
 | 
			
		||||
  # do something magical!
 | 
			
		||||
  return "Did something magical.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,6 @@ use warnings;
 | 
			
		||||
use strict;
 | 
			
		||||
 | 
			
		||||
use feature 'unicode_strings';
 | 
			
		||||
 | 
			
		||||
use Carp ();
 | 
			
		||||
 | 
			
		||||
use Time::Duration qw/duration/;
 | 
			
		||||
 | 
			
		||||
@ -26,12 +26,8 @@ use PBot::Utils::ValidateString;
 | 
			
		||||
use POSIX qw(strftime);
 | 
			
		||||
 | 
			
		||||
sub new {
 | 
			
		||||
  if (ref($_[1]) eq 'HASH') {
 | 
			
		||||
    Carp::croak("Options to Quotegrabs should be key/value pairs, not hash reference");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Carp::croak("Options to Quotegrabs 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;
 | 
			
		||||
@ -39,7 +35,6 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}        = $conf{pbot} // Carp::croak("Missing pbot reference in Quotegrabs");
 | 
			
		||||
  $self->{filename}    = $conf{quotegrabs_file} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/quotegrabs.sqlite3';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,31 +30,23 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->register(sub { $self->remindme(@_) }, 'remindme', 0);
 | 
			
		||||
 | 
			
		||||
  $self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/reminders.sqlite3';
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{timer}->register(sub { $self->check_reminders(@_) }, 1, 'RemindMe');
 | 
			
		||||
 | 
			
		||||
  $self->dbi_begin;
 | 
			
		||||
  $self->create_database;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub unload {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
 | 
			
		||||
  $self->dbi_end;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{commands}->unregister('remindme');
 | 
			
		||||
  $self->{pbot}->{timer}->unregister('RemindMe');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub create_database {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
 | 
			
		||||
  return if not $self->{dbh};
 | 
			
		||||
 | 
			
		||||
  eval {
 | 
			
		||||
@ -154,7 +146,6 @@ sub get_reminder {
 | 
			
		||||
    $self->{pbot}->{logger}->log("List reminders failed: $@");
 | 
			
		||||
    return undef;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $reminder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,7 @@ sub new {
 | 
			
		||||
 | 
			
		||||
sub initialize {
 | 
			
		||||
  my ($self, %conf) = @_;
 | 
			
		||||
 | 
			
		||||
  $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
  $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
 | 
			
		||||
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('text',  'general', 'show_url_titles',                 $conf{show_url_titles}                 // 1);
 | 
			
		||||
  $self->{pbot}->{registry}->add_default('array', 'general', 'show_url_titles_channels',        $conf{show_url_titles_channels}        // '.*');
 | 
			
		||||
@ -37,9 +36,7 @@ sub initialize {
 | 
			
		||||
  $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->show_url_titles(@_) });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub unload {
 | 
			
		||||
  my $self = shift;
 | 
			
		||||
}
 | 
			
		||||
sub unload {}
 | 
			
		||||
 | 
			
		||||
sub show_url_titles {
 | 
			
		||||
  my ($self, $event_type, $event) = @_;
 | 
			
		||||
@ -88,7 +85,6 @@ sub show_url_titles {
 | 
			
		||||
      $self->{pbot}->{factoids}->{factoidmodulelauncher}->execute_module($stuff);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,6 @@ sub weathercmd {
 | 
			
		||||
  if (not length $arguments) {
 | 
			
		||||
    return $usage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $self->get_weather($arguments);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -125,7 +124,6 @@ sub get_weather {
 | 
			
		||||
      last;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return $result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										230
									
								
								data/commands
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										230
									
								
								data/commands
									
									
									
									
										vendored
									
									
								
							@ -2,532 +2,546 @@
 | 
			
		||||
   "actiontrigger" : {
 | 
			
		||||
      "_name" : "actiontrigger",
 | 
			
		||||
      "help" : "Adds a new actiontrigger to PBot. See https://github.com/pragma-/pbot/blob/master/doc/ActionTrigger.md",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "aka" : {
 | 
			
		||||
      "_name" : "aka",
 | 
			
		||||
      "help" : "Lists known aliases for a given nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#aka",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "akalink" : {
 | 
			
		||||
      "_name" : "akalink",
 | 
			
		||||
      "help" : "Manually link a known alias to a nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#akalink",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "akaunlink" : {
 | 
			
		||||
      "_name" : "akaunlink",
 | 
			
		||||
      "help" : "Manually unlink a known alias from a nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#akaunlink",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "antispam" : {
 | 
			
		||||
      "_name" : "antispam",
 | 
			
		||||
      "help" : "See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#antispam",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "ban" : {
 | 
			
		||||
      "_name" : "ban",
 | 
			
		||||
      "help" : "Bans a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#banmute",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "battleship" : {
 | 
			
		||||
      "_name" : "battleship",
 | 
			
		||||
      "help" : "The classic Battleship board game, modified for IRC. See https://github.com/pragma-/pbot/blob/master/doc/Battleship.md",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "blacklist" : {
 | 
			
		||||
      "_name" : "blacklist",
 | 
			
		||||
      "help" : "Blacklists a hostmask from joining a channel. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#blacklist",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "cap" : {
 | 
			
		||||
      "_name" : "cap",
 | 
			
		||||
      "help" : "",
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "chanadd" : {
 | 
			
		||||
      "_name" : "chanadd",
 | 
			
		||||
      "help" : "Permanently adds a channel to PBot's list of channels to auto-join and manage. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanadd",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "chanlist" : {
 | 
			
		||||
      "_name" : "chanlist",
 | 
			
		||||
      "help" : "Lists all added channels and their metadata keys and values. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanlist",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "chanrem" : {
 | 
			
		||||
      "_name" : "chanrem",
 | 
			
		||||
      "help" : "Removes a channel from PBot's list of channels to auto-join and manage. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanrem",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "chanset" : {
 | 
			
		||||
      "_name" : "chanset",
 | 
			
		||||
      "help" : "Sets a channel's metadata. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanset and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#channel-metadata-list",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "chanunset" : {
 | 
			
		||||
      "_name" : "chanunset",
 | 
			
		||||
      "help" : "Deletes a channel's metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanunset",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "checkban" : {
 | 
			
		||||
      "_name" : "checkban",
 | 
			
		||||
      "help" : "Shows the reason a mask was banned and how long the ban lasts. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#checkban",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "checkmute" : {
 | 
			
		||||
      "_name" : "checkmute",
 | 
			
		||||
      "help" : "Shows the reason a mask was muted and how long the mute lasts. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#checkmute",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "cmdset" : {
 | 
			
		||||
      "_name" : "cmdset",
 | 
			
		||||
      "help" : "Sets or shows command metadata. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#cmdset",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "cmdunset" : {
 | 
			
		||||
      "_name" : "cmdunset",
 | 
			
		||||
      "help" : "Deletes a channel metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#cmdunset",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "connect4" : {
 | 
			
		||||
      "_name" : "connect4",
 | 
			
		||||
      "help" : "The classic Connect-4 board game. See https://github.com/pragma-/pbot/blob/master/doc/Connect4.md",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "count" : {
 | 
			
		||||
      "_name" : "count",
 | 
			
		||||
      "help" : "Shows how many factoids and what percentage of the database <nick> has submitted. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#count",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "counteradd" : {
 | 
			
		||||
      "_name" : "counteradd",
 | 
			
		||||
      "help" : "Adds a new counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#add",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "counterdel" : {
 | 
			
		||||
      "_name" : "counterdel",
 | 
			
		||||
      "help" : "Deletes a counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#del",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "counterlist" : {
 | 
			
		||||
      "_name" : "counterlist",
 | 
			
		||||
      "help" : "Lists counters. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#list",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "counterreset" : {
 | 
			
		||||
      "_name" : "counterreset",
 | 
			
		||||
      "help" : "Resets a counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#reset",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "countershow" : {
 | 
			
		||||
      "_name" : "countershow",
 | 
			
		||||
      "help" : "Shows a counter's data. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#show",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "countertrigger" : {
 | 
			
		||||
      "_name" : "countertrigger",
 | 
			
		||||
      "help" : "Manages counter triggers. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#trigger",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "date" : {
 | 
			
		||||
      "_name" : "date",
 | 
			
		||||
      "help" : "Shows date and time for a timezone. Accepts Linux timezone locations. You can set `!my timezone ...` to remember your timezone.",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "delq" : {
 | 
			
		||||
      "_name" : "delq",
 | 
			
		||||
      "help" : "Deletes a quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#delq",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "deop" : {
 | 
			
		||||
      "_name" : "deop",
 | 
			
		||||
      "help" : "Removes OP status from users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#deop",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "devoice" : {
 | 
			
		||||
      "_name" : "devoice",
 | 
			
		||||
      "help" : "Removes VOICE status from users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#devoice",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "die" : {
 | 
			
		||||
      "_name" : "die",
 | 
			
		||||
      "help" : "Tells PBot to disconnect and exit. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#die",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "dumpbans" : {
 | 
			
		||||
      "_name" : "dumpbans",
 | 
			
		||||
      "help" : "Displays PBot's internal banlist data structure. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#dumpbans",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "eval" : {
 | 
			
		||||
      "_name" : "eval",
 | 
			
		||||
      "help" : "Evaluates Perl code within PBot's context. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#eval",
 | 
			
		||||
      "level" : 99
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "export" : {
 | 
			
		||||
      "_name" : "export",
 | 
			
		||||
      "help" : "Exports specified list to HTML file. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#export",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "fact" : {
 | 
			
		||||
      "_name" : "fact",
 | 
			
		||||
      "help" : "Displays or invokes a factoid belonging to a specific channel. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#fact",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factadd" : {
 | 
			
		||||
      "_name" : "factadd",
 | 
			
		||||
      "help" : "Creates a new factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factadd",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factalias" : {
 | 
			
		||||
      "_name" : "factalias",
 | 
			
		||||
      "help" : "Creates a factoid that acts as an alias for a command. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factalias",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factchange" : {
 | 
			
		||||
      "_name" : "factchange",
 | 
			
		||||
      "help" : "Changes a factoid using a regular expression. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factchange",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factfind" : {
 | 
			
		||||
      "_name" : "factfind",
 | 
			
		||||
      "help" : "Searches the database for a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factfind",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factinfo" : {
 | 
			
		||||
      "_name" : "factinfo",
 | 
			
		||||
      "help" : "Displays information about a factoid, such as who submitted it and when. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factinfo",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factlog" : {
 | 
			
		||||
      "_name" : "factlog",
 | 
			
		||||
      "help" : "Displays a factoid's changelog history. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factlog",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factmove" : {
 | 
			
		||||
      "_name" : "factmove",
 | 
			
		||||
      "help" : "Renames a factoid or moves a factoid to a different channel. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factmove",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factredo" : {
 | 
			
		||||
      "_name" : "factredo",
 | 
			
		||||
      "help" : "Reverts a factoid to a newer revision. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factredo",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factrem" : {
 | 
			
		||||
      "_name" : "factrem",
 | 
			
		||||
      "help" : "Deletes a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factrem",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factset" : {
 | 
			
		||||
      "_name" : "factset",
 | 
			
		||||
      "help" : "Displays or sets factoid metadata, such as owner, rate-limit, etc. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factset and https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factoid-metadata-list",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factshow" : {
 | 
			
		||||
      "_name" : "factshow",
 | 
			
		||||
      "help" : "Displays a factoid's literal value without invoking the factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factshow",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factundo" : {
 | 
			
		||||
      "_name" : "factundo",
 | 
			
		||||
      "help" : "Reverts a factoid to an older revision. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factundo",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "factunset" : {
 | 
			
		||||
      "_name" : "factunset",
 | 
			
		||||
      "help" : "Unsets a factoid metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factunset and https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factoid-metadata-list",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "forget" : {
 | 
			
		||||
      "_name" : "forget",
 | 
			
		||||
      "help" : "Deletes a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#forget",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "func" : {
 | 
			
		||||
      "_name" : "func",
 | 
			
		||||
      "help" : "Invokes built-in functions. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#func",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "getq" : {
 | 
			
		||||
      "_name" : "getq",
 | 
			
		||||
      "help" : "Retrieves and displays a specific quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#getq",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "google" : {
 | 
			
		||||
      "_name" : "google",
 | 
			
		||||
      "help" : "Displays Google search results for a query. See https://github.com/pragma-/pbot/blob/master/doc/Modules.md#google",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "grab" : {
 | 
			
		||||
      "_name" : "grab",
 | 
			
		||||
      "help" : "Grabs a message someone says, and adds it to the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#grab",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "help" : {
 | 
			
		||||
      "_name" : "help",
 | 
			
		||||
      "help" : "Displays the `help` metadata for commands and factoids.",
 | 
			
		||||
      "level" : "0"
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "histogram" : {
 | 
			
		||||
      "_name" : "histogram",
 | 
			
		||||
      "help" : "Displays a histogram of the top factoid submitters. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#histogram",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "ignore" : {
 | 
			
		||||
      "_name" : "ignore",
 | 
			
		||||
      "help" : "Ignores a user. If you omit [channel] PBot will ignore the user in all channels, including private messages. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#ignore",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "in" : {
 | 
			
		||||
      "_name" : "in",
 | 
			
		||||
      "help" : "Performs a command in a specific channel. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#in",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "invite" : {
 | 
			
		||||
      "_name" : "invite",
 | 
			
		||||
      "help" : "Invites a user to a channel. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#invite",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "join" : {
 | 
			
		||||
      "_name" : "join",
 | 
			
		||||
      "help" : "Temporarily joins a channel without adding it to PBot's list of channels to manage/auto-join. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#join",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "kick" : {
 | 
			
		||||
      "_name" : "kick",
 | 
			
		||||
      "help" : "Removes a user from the channel. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#kick",
 | 
			
		||||
      "level" : "10"
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "lagcheck" : {
 | 
			
		||||
      "_name" : "lagcheck",
 | 
			
		||||
      "help" : "Displays history of PING times. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#lagcheck",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "learn" : {
 | 
			
		||||
      "_name" : "learn",
 | 
			
		||||
      "help" : "Creates a new factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factadd",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "list" : {
 | 
			
		||||
      "_name" : "list",
 | 
			
		||||
      "help" : "Lists various collections, such as channels or admins. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#list",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "help" : "Lists various collections, such as channels or admins. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#list"
 | 
			
		||||
   },
 | 
			
		||||
   "load" : {
 | 
			
		||||
      "_name" : "load",
 | 
			
		||||
      "help" : "This command loads a module as a PBot command. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#load",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "login" : {
 | 
			
		||||
      "_name" : "login",
 | 
			
		||||
      "help" : "Logs into a PBot admin account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#login",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "logout" : {
 | 
			
		||||
      "_name" : "logout",
 | 
			
		||||
      "help" : "Logs out of a PBot admin account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#logout",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "mod" : {
 | 
			
		||||
      "_name" : "mod",
 | 
			
		||||
      "help" : "Provides restricted moderation abilities to voiced users. They can kick/ban/etc only users that are not admins, whitelisted, voiced or opped.",
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "mode" : {
 | 
			
		||||
      "_name" : "mode",
 | 
			
		||||
      "help" : "Sets or unsets channel or user modes. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#mode",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "mute" : {
 | 
			
		||||
      "_name" : "mute",
 | 
			
		||||
      "help" : "Mutes a user. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#banmute",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "my" : {
 | 
			
		||||
      "_name" : "my",
 | 
			
		||||
      "help" : "Lets users view and manipulate their own user metadata. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#my and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#user-metadata-list",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "nicklist" : {
 | 
			
		||||
      "_name" : "nicklist",
 | 
			
		||||
      "help" : "Dumps the internal nicklist structure. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#nicklist",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "op" : {
 | 
			
		||||
      "_name" : "op",
 | 
			
		||||
      "help" : "Gives channel operator status to users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#op",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : "1"
 | 
			
		||||
   },
 | 
			
		||||
   "part" : {
 | 
			
		||||
      "_name" : "part",
 | 
			
		||||
      "help" : "Departs a channel, without removing it from PBot's list of channels to manage/auto-join. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#part",
 | 
			
		||||
      "level" : 40
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "plug" : {
 | 
			
		||||
      "_name" : "plug",
 | 
			
		||||
      "help" : "Loads a plugin into PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#plug",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "pluglist" : {
 | 
			
		||||
      "_name" : "pluglist",
 | 
			
		||||
      "help" : "Lists all currently loaded plugins. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#pluglist",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "rebuildaliases" : {
 | 
			
		||||
      "_name" : "rebuildaliases",
 | 
			
		||||
      "help" : "Rebuilds the aka link table. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#rebuildaliases",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "recall" : {
 | 
			
		||||
      "_name" : "recall",
 | 
			
		||||
      "help" : "Recalls previous chat history for a channel. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#recall",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "refresh" : {
 | 
			
		||||
      "_name" : "refresh",
 | 
			
		||||
      "help" : "Refreshes and reloads PBot core modules and plugins. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#refresh",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "regchange" : {
 | 
			
		||||
      "_name" : "regchange",
 | 
			
		||||
      "help" : "Changes the value of a registry item using a regular expression. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regchange",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "regex" : {
 | 
			
		||||
      "_name" : "regex",
 | 
			
		||||
      "help" : "Manages regular expression commands. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#regex",
 | 
			
		||||
      "level" : 999
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "regfind" : {
 | 
			
		||||
      "_name" : "regfind",
 | 
			
		||||
      "help" : "Searches the registry for keywords or values. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regfind",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "regset" : {
 | 
			
		||||
      "_name" : "regset",
 | 
			
		||||
      "help" : "Creates a new registry item or updates an existing item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regset",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "regsetmeta" : {
 | 
			
		||||
      "_name" : "regsetmeta",
 | 
			
		||||
      "help" : "Sets or displays the metadata for a specific registry key. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regsetmeta and https://github.com/pragma-/pbot/blob/master/doc/Registry.md#metadata-list",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "regshow" : {
 | 
			
		||||
      "_name" : "regshow",
 | 
			
		||||
      "help" : "Displays the type and value of a registry item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regshow",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "regunset" : {
 | 
			
		||||
      "_name" : "regunset",
 | 
			
		||||
      "help" : "Deletes a registry item from a specific section/channel. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regunset",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "regunsetmeta" : {
 | 
			
		||||
      "_name" : "regunsetmeta",
 | 
			
		||||
      "help" : "Deletes a metadata key from a registry item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regsetmeta and https://github.com/pragma-/pbot/blob/master/doc/Registry.md#metadata",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "reload" : {
 | 
			
		||||
      "_name" : "reload",
 | 
			
		||||
      "help" : "Reloads a data or configuration file. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#reload",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "remindme" : {
 | 
			
		||||
      "_name" : "remindme",
 | 
			
		||||
      "help" : "Manages user reminders. See https://github.com/pragma-/pbot/blob/master/doc/Remindme.md",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "replug" : {
 | 
			
		||||
      "_name" : "replug",
 | 
			
		||||
      "help" : "Reloads a plugin into PBot. The plugin is first unloaded and then it is loaded again. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#replug",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "rq" : {
 | 
			
		||||
      "_name" : "rq",
 | 
			
		||||
      "help" : "Retrieves and displays a random quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#rq",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "sl" : {
 | 
			
		||||
      "_name" : "sl",
 | 
			
		||||
      "help" : "Sends a raw IRC command to the server. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#sl",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "spinach" : {
 | 
			
		||||
      "_name" : "spinach",
 | 
			
		||||
      "help" : "Manages and interacts with the Spinach trivia game. See https://github.com/pragma-/pbot/blob/master/doc/Spinach.md",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "top20" : {
 | 
			
		||||
      "_name" : "top20",
 | 
			
		||||
      "help" : "Displays the top 20 most popular factoids. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#top20",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "unban" : {
 | 
			
		||||
      "_name" : "unban",
 | 
			
		||||
      "help" : "Unbans a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unbanunmute",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "unbanme" : {
 | 
			
		||||
      "_name" : "unbanme",
 | 
			
		||||
      "help" : "Removes a join-flood ban. See https://github.com/pragma-/pbot/blob/master/doc/Abuse.md#unbanme",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "unignore" : {
 | 
			
		||||
      "_name" : "unignore",
 | 
			
		||||
      "help" : "Unignores a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unignore",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "unload" : {
 | 
			
		||||
      "_name" : "unload",
 | 
			
		||||
      "help" : "Unloads a module and removes its associated command. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unload",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "unmute" : {
 | 
			
		||||
      "_name" : "unmute",
 | 
			
		||||
      "help" : "Unmutes a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unbanunmute",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "unplug" : {
 | 
			
		||||
      "_name" : "unplug",
 | 
			
		||||
      "help" : "Unloads a plugin from PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unplug",
 | 
			
		||||
      "level" : 90
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "uptime" : {
 | 
			
		||||
      "_name" : "uptime",
 | 
			
		||||
      "help" : "",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "help" : "Displays the date and time this instance of PBot was started and how long it has been running.",
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "useradd" : {
 | 
			
		||||
      "_name" : "useradd",
 | 
			
		||||
      "help" : "Adds a new user to PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#useradd",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "userdel" : {
 | 
			
		||||
      "_name" : "userdel",
 | 
			
		||||
      "help" : "Removes an user from PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userdel",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "userset" : {
 | 
			
		||||
      "_name" : "userset",
 | 
			
		||||
      "help" : "Sets metadata for an user account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userset and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#user-metadata-list",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "userunset" : {
 | 
			
		||||
      "_name" : "userunset",
 | 
			
		||||
      "help" : "Deletes a metadata key from an user account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userunset",
 | 
			
		||||
      "level" : 60
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "version" : {
 | 
			
		||||
      "_name" : "version",
 | 
			
		||||
      "help" : "",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "voice" : {
 | 
			
		||||
      "_name" : "voice",
 | 
			
		||||
      "help" : "Sets mode +v on users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#voice",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "weather" : {
 | 
			
		||||
      "_name" : "weather",
 | 
			
		||||
      "help" : "Fetches and displays weather data. You may set `!my location ...` to remember your location.",
 | 
			
		||||
      "level" : 0
 | 
			
		||||
      "requires_cap" : 0
 | 
			
		||||
   },
 | 
			
		||||
   "whitelist" : {
 | 
			
		||||
      "_name" : "whitelist",
 | 
			
		||||
      "help" : "Whitelists a hostmask to be exempted from ban evasions or anti-flood enforcement. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#whitelist",
 | 
			
		||||
      "level" : 10
 | 
			
		||||
      "requires_cap" : 1
 | 
			
		||||
   },
 | 
			
		||||
   "wttr" : {
 | 
			
		||||
      "_name" : "wttr",
 | 
			
		||||
      "help" : "",
 | 
			
		||||
      "requires_cap" : "0"
 | 
			
		||||
   }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								data/factoids
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								data/factoids
									
									
									
									
										vendored
									
									
								
							@ -419,7 +419,7 @@
 | 
			
		||||
         "created_on" : "1519931258.36575",
 | 
			
		||||
         "edited_by" : "iamgarp!~chaos@unaffiliated/pragmatic-chaos",
 | 
			
		||||
         "edited_on" : "1555805847.25419",
 | 
			
		||||
         "effective-level" : "100",
 | 
			
		||||
         "cap-override" : "botowner",
 | 
			
		||||
         "enabled" : "1",
 | 
			
		||||
         "last_referenced_in" : "##spinach",
 | 
			
		||||
         "last_referenced_on" : 1578910468.98602,
 | 
			
		||||
@ -10231,10 +10231,10 @@
 | 
			
		||||
      },
 | 
			
		||||
      "kickme" : {
 | 
			
		||||
         "_name" : "kickme",
 | 
			
		||||
         "action" : "/kick $nick",
 | 
			
		||||
         "action_with_args" : "/kick $nick $args",
 | 
			
		||||
         "action" : "/call kick $nick",
 | 
			
		||||
         "action_with_args" : "/call kick $nick $args",
 | 
			
		||||
         "created_on" : "1524347954.63458",
 | 
			
		||||
         "effective-level" : "10",
 | 
			
		||||
         "cap-override" : "can-kick",
 | 
			
		||||
         "enabled" : "1",
 | 
			
		||||
         "last_referenced_in" : "##c-offtopic",
 | 
			
		||||
         "last_referenced_on" : 1572992043.13307,
 | 
			
		||||
@ -14908,7 +14908,7 @@
 | 
			
		||||
         "_name" : "removeme",
 | 
			
		||||
         "action" : "/call sl remove $channel $nick :Bye!",
 | 
			
		||||
         "created_on" : 1567373781.37961,
 | 
			
		||||
         "effective-level" : "90",
 | 
			
		||||
         "cap-override" : "botowner",
 | 
			
		||||
         "enabled" : "1",
 | 
			
		||||
         "last_referenced_in" : "##c-offtopic",
 | 
			
		||||
         "last_referenced_on" : 1567721282.13253,
 | 
			
		||||
@ -15257,7 +15257,7 @@
 | 
			
		||||
         "created_on" : "1254874530",
 | 
			
		||||
         "edited_by" : "pragma-!~chaos@unaffiliated/pragmatic-chaos",
 | 
			
		||||
         "edited_on" : "1504601046.70364",
 | 
			
		||||
         "effective-level" : "20",
 | 
			
		||||
         "cap-override" : "can-kick",
 | 
			
		||||
         "enabled" : "1",
 | 
			
		||||
         "last_referenced_in" : "##c-offtopic",
 | 
			
		||||
         "last_referenced_on" : 1579041706.76328,
 | 
			
		||||
@ -15270,7 +15270,7 @@
 | 
			
		||||
      },
 | 
			
		||||
      "roulette_outcome" : {
 | 
			
		||||
         "_name" : "roulette_outcome",
 | 
			
		||||
         "action" : "\"/kick $nick *BANG!*\" \"/say $args: *click*\" \"/say $args: *click*\"",
 | 
			
		||||
         "action" : "\"/call kick $nick *BANG!*\" \"/say $args: *click*\" \"/say $args: *click*\"",
 | 
			
		||||
         "created_on" : "1254874748",
 | 
			
		||||
         "edited_by" : "pragma-!~chaos@unaffiliated/pragmatic-chaos",
 | 
			
		||||
         "edited_on" : "1492056512.90395",
 | 
			
		||||
@ -21511,21 +21511,6 @@
 | 
			
		||||
         "ref_user" : "k!~krok@unaffiliated/krok",
 | 
			
		||||
         "type" : "text"
 | 
			
		||||
      },
 | 
			
		||||
      "wang" : {
 | 
			
		||||
         "_name" : "wang",
 | 
			
		||||
         "action" : "/msg mnrmnaugh wtf",
 | 
			
		||||
         "created_on" : "1519278998.50208",
 | 
			
		||||
         "effective-level" : "60",
 | 
			
		||||
         "enabled" : "1",
 | 
			
		||||
         "last_referenced_in" : "#pbot2",
 | 
			
		||||
         "last_referenced_on" : "1519979002.921",
 | 
			
		||||
         "locked" : "1",
 | 
			
		||||
         "owner" : "pragma-!~chaos@unaffiliated/pragmatic-chaos",
 | 
			
		||||
         "rate_limit" : "15",
 | 
			
		||||
         "ref_count" : "8",
 | 
			
		||||
         "ref_user" : "mnrmnaugh!~mnrmnaugh@unaffiliated/mnrmnaugh",
 | 
			
		||||
         "type" : "text"
 | 
			
		||||
      },
 | 
			
		||||
      "warning-labels" : {
 | 
			
		||||
         "_name" : "warning-labels",
 | 
			
		||||
         "action" : "http://www.myconfinedspace.com/2007/03/23/internet-warning-labels/",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user