Bot-Cobalt
view release on metacpan or search on metacpan
lib/Bot/Cobalt/Plugin/RDB.pm view on Meta::CPAN
}
sub DBmgr {
my ($self) = @_;
unless ($self->{DBMGR}) {
my $cfg = core->get_plugin_cfg($self);
my $cachekeys = $cfg->{Opts}->{CacheItems} // 30;
my $rdbdir = File::Spec->catdir(
core()->var,
$cfg->{Opts}->{RDBDir} ? $cfg->{Opts}->{RDBDir} : ('db', 'rdb')
);
$self->{DBMGR} = Bot::Cobalt::Plugin::RDB::Database->new(
CacheKeys => $cachekeys,
RDBDir => $rdbdir,
);
}
$self->{DBMGR}
}
sub rand_delay {
my ($self, $delay) = @_;
return $self->{RANDDELAY} = $delay if defined $delay;
$self->{RANDDELAY}
}
sub SessionID {
my ($self, $id) = @_;
return $self->{SESSID} = $id if defined $id;
$self->{SESSID}
}
sub AsyncSessionID {
my ($self, $id) = @_;
return $self->{ASYNCID} = $id if defined $id;
$self->{ASYNCID}
}
sub Cobalt_register {
my ($self, $core) = splice @_, 0, 2;
register($self, 'SERVER',
[
'public_msg',
'rdb_broadcast',
'rdb_triggered',
],
);
## if the rdbdir doesn't exist, ::Database will try to create it
## (it'll also handle creating 'main' for us)
my $dbmgr = $self->DBmgr;
## we'll die out here if there's a problem with 'main' :
my $keys_c = $dbmgr->get_keys('main');
core->Provided->{randstuff_items} = $keys_c;
## kickstart a randstuff timer (named timer for rdb_broadcast)
## delay is in Opts->RandDelay as a timestr
## (0 turns off timer)
my $cfg = core->get_plugin_cfg( $self );
my $randdelay = $cfg->{Opts}->{RandDelay} // '30m';
logger->debug("randdelay: $randdelay");
$randdelay = timestr_to_secs($randdelay) unless $randdelay =~ /^\d+$/;
$self->rand_delay( $randdelay );
if ($randdelay) {
core->timer_set( $randdelay,
{
Event => 'rdb_broadcast',
Alias => core->get_plugin_alias($self)
},
'RANDSTUFF'
);
}
if ($cfg->{Opts}->{AsyncSearch}) {
logger->debug("spawning Session to handle AsyncSearch");
POE::Session->create(
object_states => [
$self => [
'_start',
'poe_post_search',
'poe_got_result',
'poe_got_error',
],
],
);
}
logger->info("Registered, $keys_c items in main RDB");
return PLUGIN_EAT_NONE
}
sub Cobalt_unregister {
my ($self, $core) = splice @_, 0, 2;
logger->info("Unregistering RDB");
$poe_kernel->alias_remove('sess_'. core->get_plugin_alias($self) );
if ( $self->AsyncSessionID ) {
$poe_kernel->post( $self->AsyncSessionID, 'shutdown' );
}
delete core->Provided->{randstuff_items};
core->timer_del('RANDSTUFF');
return PLUGIN_EAT_NONE
}
sub Bot_public_msg {
my ($self, $core) = splice @_, 0, 2;
my $msg = ${$_[0]};
my $context = $msg->context;
my @handled = qw/
randstuff
randq
rdb
/;
## would be better in a public_cmd_, but eh, darkbot legacy syntax..
return PLUGIN_EAT_NONE unless $msg->highlight;
## uses message_array_sp, ie spaces are preserved
## (so don't include them prior to rdb names, for example)
my $msg_arr = $msg->message_array_sp;
## since this is a highlighted message, bot's nickname is first
my ($cmd, @message) = @$msg_arr[1 .. (scalar @$msg_arr - 1)];
$cmd = lc($cmd||'');
## ..if it's not @handled we don't care:
return PLUGIN_EAT_NONE unless $cmd and first {; $_ eq $cmd } @handled;
logger->debug("dispatching $cmd");
## dispatcher:
my ($id, $resp);
CMD: {
if ($cmd eq "randstuff") {
$resp = $self->_cmd_randstuff(\@message, $msg);
last CMD
}
if ($cmd eq "randq") {
$resp = $self->_cmd_randq(\@message, $msg, 'randq');
last CMD
}
if ($cmd eq "rdb") {
$resp = $self->_cmd_rdb(\@message, $msg);
last CMD
}
}
my $channel = $msg->channel;
if (defined $resp) {
logger->debug("dispatching msg -> $channel");
broadcast( 'message', $context, $channel, $resp );
}
PLUGIN_EAT_NONE
lib/Bot/Cobalt/Plugin/RDB.pm view on Meta::CPAN
my (@returned, $prefix);
if ($count > 30) {
@returned = @$indices[0 .. 29];
$prefix = "Matches (30 of $count): ";
} else {
@returned = @$indices;
$prefix = "Matches: ";
}
return $prefix.join(' ', @returned);
}
### self-events ###
sub Bot_rdb_triggered {
## Bot_rdb_triggered $context, $channel, $nick, $rdb
my ($self, $core) = splice @_, 0, 2;
my $context = ${$_[0]};
my $channel = ${$_[1]};
my $nick = ${$_[2]};
my $rdb = ${$_[3]};
my $orig = ${$_[4]};
my $questionstr = ${$_[5]};
## event normally triggered by Info3 when a topic references a ~rdb
## grab a random response and throw it back at the pipeline
## info3 plugin can pick it up and do variable replacement on it
logger->debug("received rdb_triggered");
my $dbmgr = $self->DBmgr;
## if referenced rdb doesn't exist, send orig string
my $send_orig;
unless ( $dbmgr->dbexists($rdb) ) {
++$send_orig;
}
## construct fake msg obj for _select_random
my $new_msg = Bot::Cobalt::IRC::Message::Public->new(
context => $context,
src => $nick . '!fake@host',
targets => [ $channel ],
message => '',
);
my $random = $send_orig ? $orig
: $self->_select_random($new_msg, $rdb, 'quietfail') ;
if (exists core()->Provided->{info_topics}) {
broadcast( 'info3_relay_string',
$context, $channel, $nick, $random, $questionstr
);
} else {
logger->warn("RDB plugin cannot trigger, Info3 is missing");
}
return PLUGIN_EAT_ALL
}
sub Bot_rdb_broadcast {
my ($self, $core) = splice @_, 0, 2;
## our timer self-event
## reset timer unless randdelay is 0
if ($self->rand_delay) {
$core->timer_set( $self->rand_delay,
{
Event => 'rdb_broadcast',
Alias => $core->get_plugin_alias($self)
},
'RANDSTUFF'
);
logger->debug("rdb_broadcast; timer reset; ".$self->rand_delay);
}
my $mock_msg = Bot::Cobalt::IRC::Message::Public->new(
context => '',
src => '',
targets => [],
message => '',
);
my $random = $self->_select_random($mock_msg, 'main', 'quietfail')
// return PLUGIN_EAT_ALL;
## iterate channels cfg
## throw randstuffs at configured channels unless told not to
my $servers = $core->Servers;
SERVER: for my $context (keys %$servers) {
my $c_obj = $core->get_irc_context($context);
next SERVER unless $c_obj->connected;
my $irc = $core->get_irc_obj($context) || next SERVER;
my $chcfg = $core->get_channels_cfg($context) || next SERVER;
logger->debug("rdb_broadcast to $context");
my $on_channels = $irc->channels || {};
my $casemap = $core->get_irc_casemap($context) || 'rfc1459';
my @channels = map { lc_irc($_, $casemap) } keys %$on_channels;
my $evtype;
if ( index($random, '+') == 0 ) {
## action
$random = substr($random, 1);
$evtype = 'action';
} else {
$evtype = 'message';
}
logger->debug("rdb_broadcast; type is $evtype");
@channels = grep {
$chcfg->{ lc_irc($_, $casemap) }->{rdb_randstuffs} // 1
} @channels;
if ($evtype eq 'message') {
my $maxtargets = $c_obj->maxtargets;
while (my @targets = splice @channels, 0, $maxtargets) {
my $tcount = @targets;
my $targetstr = join ',', @targets;
logger->debug(
"rdb_broadcast (MSG) to $tcount targets (max $maxtargets)",
"($context -> $targetstr)"
);
broadcast($evtype, $context, $targetstr, $random);
}
} else {
## FIXME
lib/Bot/Cobalt/Plugin/RDB.pm view on Meta::CPAN
my (@returned, $prefix);
if ($count > 30) {
@returned = (shuffle @$resultarr)[0 .. 29];
$prefix = "$nickname: matches (30 / $count): ";
} else {
@returned = @$resultarr;
$prefix = "$nickname: matches ($count): ";
}
$resp = $prefix . join(' ', @returned);
}
last RESPTYPE
}
if ($type eq 'count') {
$dbmgr->cache_push($rdb, $glob, $resultarr)
if @$resultarr;
my $count = @$resultarr;
$resp = "$nickname: Found $count matches for $glob";
last RESPTYPE
}
}
broadcast( 'message', $context, $channel, $resp )
if defined $resp;
}
sub poe_got_error {
my ($self, $kernel, $heap) = @_[OBJECT, KERNEL, HEAP];
my ($error, $hints) = @_[ARG0, ARG1];
my $glob = $hints->{Glob};
my $rdb = $hints->{RDB};
logger->warn("Received error from AsyncSearch: $rdb ($glob): $error");
my $context = $hints->{Context};
my $channel = $hints->{Channel};
my $nickname = $hints->{Nickname};
broadcast( 'message', $context, $channel,
"$nickname: asyncsearch error: $error ($rdb)"
);
}
1;
=pod
=head1 NAME
Bot::Cobalt::Plugin::RDB - Bot::Cobalt "random" DB plugin
=head1 DESCRIPTION
Jason Hamilton's B<darkbot> came with the concept of "randstuffs,"
randomized responses broadcast to channels via a timer.
Later versions included a search interface and "RDBs" -- discrete
'randstuff' databases that could be accessed via 'info' topic triggers
to return a random response.
B<cobalt1> used essentially the same interface.
This B<RDB> plugin attempts to expand on that concept.
This functionality is often useful to simulate humanoid responses to
conversation (by writing 'conversational' RDB replies triggered by
L<Bot::Cobalt::Plugin::Info3> topics), to implement IRC quotebots, or just
to fill your channel with random chatter.
The "randstuff" db is labelled "main" -- all other RDB names must be
in the [a-z0-9] set.
Requires L<Bot::Cobalt::Plugin::Info3>.
=head1 COMMANDS
Commands are prefixed with the bot's nickname, rather than CmdChar.
This is a holdover from darkbot legacy syntax.
<JoeUser> botnick: randq some*glob
=head2 randq
Search for a specified glob in RDB 'main' (randstuffs):
<JoeUser> bot: randq some+string*
See L<Bot::Cobalt::Utils/glob_to_re_str> for details regarding glob syntax.
=head2 randstuff
Add a new "randstuff" to the 'main' RDB
<JoeUser> bot: randstuff new randstuff string
A randstuff can also be an action; simply prefix the string with B<+> :
<JoeUser> bot: randstuff +dances around
Legacy darkbot-style syntax is supported; you can add items to RDBs
by prefixing the RDB name with B<~>, like so:
randstuff ~myrdb some new string
The RDB must already exist; see L</"rdb dbadd">
=head2 rdb
=head3 rdb get
rdb get <rdb> <itemID>
Retrieves the specified item from the specified RDB.
=head3 rdb info
lib/Bot/Cobalt/Plugin/RDB.pm view on Meta::CPAN
rdb del <rdb> <itemID> [itemID ...]
Deletes items from the specified RDB.
=head3 rdb dbadd
rdb dbadd <rdb>
Creates a new, empty RDB.
=head3 rdb dbdel
rdb dbdel <rdb>
Deletes the specified RDB entirely.
Deletion may be disabled in the plugin's configuration file via the
B<< Opts->AllowDelete >> directive.
=head3 rdb search
rdb search <rdb> <glob>
Search within a specific RDB. Returns a single random response from the
result set. Also see L</randq> and L<Bot::Cobalt::Utils/glob_to_re_str>
for more details on search syntax.
=head3 rdb searchidx
rdb searchidx <rdb> <glob>
Returns all RDB item IDs matching the specified glob.
=head3 rdb count
rdb count <rdb> <glob>
Returns just the total number of matches for the specified glob.
=head2 random
'random' is not actually a built-in command; however, since you must have
L<Bot::Cobalt::Plugin::Info3>, a handy trick is to add a topic named 'random'
that triggers RDB 'main':
<JoeUser> bot: add random ~main
That will allow use of 'random' to pull a randomly-selected entry from the
'randstuffs' database.
=head1 EVENTS
=head2 Received events
=head3 rdb_broadcast
Self-triggered event.
Called on a timer to broadcast randstuffs from RDB "main."
Takes no arguments.
=head3 rdb_triggered
Triggered (usually by L<Bot::Cobalt::Plugin::Info3>) when a RDB is polled
for a random response.
Arguments are:
$context, $channel, $nick, $rdb, $topic_value, $original_str
Broadcasts an L</info3_relay_string> in response, which is picked up by
B<Info3> to perform variable replacement before relaying back to the
calling channel.
=head2 Emitted events
=head3 info3_relay_string
Broadcast by L</rdb_triggered> to be picked up by L<Bot::Cobalt::Plugin::Info3>.
Arguments are:
$context, $channel, $nick, $string, $original
=head1 AUTHOR
Jon Portnoy <avenj@cobaltirc.org>
=cut
( run in 1.092 second using v1.01-cache-2.11-cpan-437f7b0c052 )