pbot/lib/PBot/Plugin/Quotegrabs/Storage/SQLite.pm

206 lines
5.5 KiB
Perl

# File: SQLite.pm
#
# Purpose: SQLite backend for storing and retreiving quotegrabs.
# SPDX-FileCopyrightText: 2021-2023 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
package PBot::Plugin::Quotegrabs::Storage::SQLite;
use PBot::Imports;
use DBI;
use Carp;
sub new($class, %conf) {
my $self = bless {}, $class;
$self->initialize(%conf);
return $self;
}
sub initialize($self, %conf) {
$self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__);
$self->{filename} = delete $conf{filename};
}
sub begin($self) {
$self->{pbot}->{logger}->log("Opening quotegrabs SQLite database: $self->{filename}\n");
$self->{dbh} = DBI->connect("dbi:SQLite:dbname=$self->{filename}", "", "", {RaiseError => 1, PrintError => 0, sqlite_unicode => 1}) or die $DBI::errstr;
eval {
$self->{dbh}->do(<< 'SQL');
CREATE TABLE IF NOT EXISTS Quotegrabs (
id INTEGER PRIMARY KEY,
nick TEXT,
channel TEXT,
grabbed_by TEXT,
text TEXT,
timestamp NUMERIC
)
SQL
$self->{dbh}->do('CREATE TABLE IF NOT EXISTS Seen (id INTEGER UNIQUE)');
};
$self->{pbot}->{logger}->log($@) if $@;
}
sub end($self) {
if (exists $self->{dbh} and defined $self->{dbh}) {
$self->{pbot}->{logger}->log("Closing quotegrabs SQLite database\n");
$self->{dbh}->disconnect();
delete $self->{dbh};
}
}
sub add_quotegrab($self, $quotegrab) {
my $id = eval {
my $sth = $self->{dbh}->prepare('INSERT INTO Quotegrabs VALUES (?, ?, ?, ?, ?, ?)');
$sth->execute(
undef,
$quotegrab->{nick},
$quotegrab->{channel},
$quotegrab->{grabbed_by},
$quotegrab->{text},
$quotegrab->{timestamp},
);
return $self->{dbh}->sqlite_last_insert_rowid();
};
$self->{pbot}->{logger}->log($@) if $@;
return $id;
}
sub get_quotegrab($self, $id) {
my $quotegrab = eval {
my $sth = $self->{dbh}->prepare('SELECT * FROM Quotegrabs WHERE id == ?');
$sth->bind_param(1, $id);
$sth->execute();
return $sth->fetchrow_hashref();
};
$self->{pbot}->{logger}->log($@) if $@;
return $quotegrab;
}
sub get_random_quotegrab($self, $nick, $channel, $text) {
# convert from regex metachars to SQL LIKE metachars
if (defined $nick) {
$nick =~ s/\.?\*\??/%/g;
$nick =~ s/\./_/g;
}
if (defined $channel) {
$channel =~ s/\.?\*\??/%/g;
$channel =~ s/\./_/g;
}
if (defined $text) {
$text =~ s/\.?\*\??/%/g;
$text =~ s/\./_/g;
}
my $quotegrab = eval {
my $sql = 'SELECT * FROM Quotegrabs';
my @params;
my $joiner = ' WHERE';
# multi-grabs have the nick separated by +'s so we must test for
# nick, nick+*, *+nick, and *+nick+* to match each of these cases
if (defined $nick) {
$sql .= "$joiner (nick LIKE ? OR nick LIKE ? OR nick LIKE ? OR nick LIKE ?)";
push @params, $nick;
push @params, $nick . '+%';
push @params, '%+' . $nick;
push @params, '%+' . $nick . '+%';
$joiner = ' AND';
}
if (defined $channel) {
$sql .= "$joiner channel LIKE ?";
push @params, $channel;
$joiner = ' AND';
}
if (defined $text) {
$sql .= "$joiner text LIKE ?";
push @params, '%' . $text . '%';
$joiner = ' AND';
}
# search for a random unseen quotegrab
my $sth = $self->{dbh}->prepare("$sql $joiner id NOT IN Seen ORDER BY RANDOM() LIMIT 1");
$sth->execute(@params);
my $quotegrab = $sth->fetchrow_hashref();
# no unseen quote found
if (not defined $quotegrab) {
# remove queried quotes from Seen table
if (not $self->remove_seen($sql, \@params)) {
# no matching quotes in Seen table ergo no quote found
return undef;
}
# try again to search for random unseen quotegrab
$sth->execute(@params);
$quotegrab = $sth->fetchrow_hashref();
}
# mark quote as seen if found
if (defined $quotegrab) {
$self->add_seen($quotegrab->{id});
}
return $quotegrab;
};
$self->{pbot}->{logger}->log($@) if $@;
return $quotegrab;
}
sub remove_seen($self, $sql, $params) {
$sql =~ s/^SELECT \*/SELECT id/;
my $count = eval {
my $sth = $self->{dbh}->prepare("DELETE FROM Seen WHERE id IN ($sql)");
$sth->execute(@$params);
return $sth->rows;
};
$self->{pbot}->{logger}->log($@) if $@;
return $count;
}
sub add_seen($self, $id) {
eval {
my $sth = $self->{dbh}->prepare('INSERT INTO Seen VALUES (?)');
$sth->execute($id);
};
$self->{pbot}->{logger}->log($@) if $@;
}
sub get_all_quotegrabs($self) {
my $quotegrabs = eval {
my $sth = $self->{dbh}->prepare('SELECT * from Quotegrabs');
$sth->execute();
return $sth->fetchall_arrayref({});
};
$self->{pbot}->{logger}->log($@) if $@;
return $quotegrabs;
}
sub delete_quotegrab($self, $id) {
eval {
my $sth = $self->{dbh}->prepare('DELETE FROM Quotegrabs WHERE id == ?');
$sth->execute($id);
$sth = $self->{dbh}->prepare('DELETE FROM Seen WHERE id == ?');
$sth->execute($id);
};
$self->{pbot}->{logger}->log($@) if $@;
}
1;