Now running pgsql, added rollbacks for failed insert/updates/deletes

This commit is contained in:
mrhanky 2017-06-30 15:03:01 +02:00
parent 45c4b55cf2
commit 8821161f71
No known key found for this signature in database
GPG Key ID: 67D772C481CB41B8
7 changed files with 257 additions and 148 deletions

View File

@ -4,46 +4,69 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import parse_int from ..utils import parse_int
# TODO: fix sql shit
@irc3.plugin
class McManiac(DatabasePlugin): class McManiac(DatabasePlugin):
requires = ['irc3.plugins.command', requires = ['irc3.plugins.command',
'nxy.plugins.database'] 'nxy.plugins.storage']
table = 'mcmaniacs'
@command(options_first=True) @command(options_first=True)
def mcmaniac(self, mask: IrcString, channel: IrcString, args: DocOptDict): def mcmaniac(self, mask: IrcString, target: IrcString, args: DocOptDict):
"""Get a random McManiaC or by index. """Get a random McManiaC or by index.
%%mcmaniac [<index>] %%mcmaniac [<index>]
""" """
index = args.get('<index>') index = args.get('<index>')
if index: if index:
index = parse_int(index) index = parse_int(index)
if not index: if not index:
return return
index, order, _ = index index, order, _ = index
order = 'id {order}'.format(order=order) order = 'id {order}'.format(order=order)
extra = 'offset ?' offset = 'offset %s'
binds = [index]
else: else:
order = 'random()' order = 'random()'
extra = '' offset = ''
binds = []
self.cur.execute('''select item,
(select count(id) from mcmaniacs b where a.id >= b.id) as idx,
(select count(id) from mcmaniacs) as len
from mcmaniacs a order by {order} limit 1 {extra}
'''.format(order=order, extra=extra), binds)
result = self.cur.fetchone()
if result:
return '[{idx}/{len}] {item}'.format(**result)
@irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG \S+ :.*(?P<item>Mc\S+iaC).*') # Fetch result from database
self.cur.execute('''
select
item,
rank() over (order by id),
count(*) over (rows between unbounded preceding
and unbounded following) as total
from
mcmaniacs
order by
{order}
limit
1
{offset}
'''.format(order=order, offset=offset), [index])
result = self.cur.fetchone()
if result:
return '[{rank}/{total}] {item}'.format(**result)
@irc3.event(r'^:(?P<mask>\S+) PRIVMSG \S+ :.*(?P<item>Mc\S+iaC).*')
def save(self, mask: str, item: str): def save(self, mask: str, item: str):
if IrcString(mask).nick != self.bot.nick: if IrcString(mask).nick != self.bot.nick:
self.cur.execute('insert into mcmaniacs (item) values (?)', [item]) try:
self.con.commit() # Insert into database
self.cur.execute('''
insert into
mcmaniacs (item)
values
(%s)
''', [item])
self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -4,18 +4,85 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import NICK_REGEX, parse_int from ..utils import NICK_REGEX, parse_int
"""
delete from
quotes
where
id = (
select
id
from
quotes a
where
nick like %s and %s = (
select
count(id)
from
quotes b where a.id {op} b.id
)
order by
id {order}
)
"""
@irc3.plugin @irc3.plugin
class Quotes(DatabasePlugin): class Quotes(DatabasePlugin):
requires = ['irc3.plugins.command', requires = ['irc3.plugins.command',
'nxy.plugins.database'] 'nxy.plugins.storage']
def add_quote(self, mask, nick, quote):
# Parse nick from "<@foobar>" like strings
nick = NICK_REGEX.match(nick).group(1)
if not nick:
self.bot.notice(mask.nick, '[Quotes] Error parsing nick')
else:
# Insert quote into database
self.cur.execute('''
insert into
quotes (nick, item)
values
(%s, %s)
''', [nick, quote])
def delete_quote(self, nick, quote):
index, order, _ = parse_int(quote, select=False)
if index:
# Delete from database
self.cur.execute('''
with ranked_quotes as (
select
id,
rank() over (partition by nick order by id {order})
from
quotes
where
nick = %s
)
delete from
quotes
where
id = (
select
id
from
ranked_quotes
where
rank = %s
)
'''.format(order=order), [nick, index])
@command(options_first=True) @command(options_first=True)
def q(self, mask: IrcString, channel: IrcString, args: DocOptDict): def q(self, mask: IrcString, target: IrcString, args: DocOptDict):
"""Get, add or delete quots for a user """Get, add or delete quots for a user
%%q <cmd> <nick> <quote>... %%q <cmd> <nick> <quote>...
@ -23,48 +90,62 @@ class Quotes(DatabasePlugin):
""" """
cmd = args.get('<cmd>') cmd = args.get('<cmd>')
nick = args['<nick>'] nick = args['<nick>']
item = args.get('<quote>') quote = ''.join(args.get('<quote>'))
if cmd and item:
if self.guard.has_permission(mask, 'admin'): if cmd and quote:
try:
# Anybody can add
if cmd == 'add': if cmd == 'add':
nick = NICK_REGEX.match(nick).group(1) self.add_quote(mask, nick, quote)
if not nick: # But only admins can delete
return '[Quotes] Error parsing nick' elif cmd == 'del' and self.guard.has_permission(mask, 'admin'):
self.cur.execute('insert into quotes (nick, item) values ' self.delete_quote(nick, quote)
'(?, ?', [nick, ' '.join(item)])
self.con.commit()
if cmd == 'del':
index, order, op = parse_int(''.join(item), select=False)
if not index:
return
self.cur.execute('''delete from quotes where id =
(select id from quotes a where nick like ? and ? =
(select count(id) from quotes b where a.id {op} b.id)
order by id {order})
'''.format(op=op, order=order), [nick, index])
self.con.commit() self.con.commit()
else: except Error:
self.bot.notice(mask.nick, 'Permission denied') # Rollback transaction on error
self.con.rollback()
else: else:
index = args.get('<index>') index = args.get('<index>')
binds = [nick, nick, nick] values = [nick]
if index: if index:
index = parse_int(index) index = parse_int(index)
if not index: if not index:
return return
index, order, _ = index index, order, _ = index
order = 'id {order}'.format(order=order) order = 'rank {order}'.format(order=order)
extra = 'offset ?' offset = 'offset %s'
binds.append(index) values.append(index)
else: else:
order = 'random()' order = 'random()'
extra = '' offset = ''
self.cur.execute('''select nick, item,
(select count(id) from quotes b where b.nick like ? and # Fetch quote from database
a.id >= b.id) as idx, self.cur.execute("""
(select count(id) from quotes where nick like ?) as len with ranked_quotes as (
from quotes a where nick like ? order by {order} limit 1 {extra} select
'''.format(order=order, extra=extra), binds) nick,
item,
rank() over (partition by nick order by id),
count(*) over (partition by nick) as total
from
quotes
)
select
*
from
ranked_quotes
where
nick like %s
order by
{order}
limit
1
{offset}
""".format(order=order, offset=offset), values)
result = self.cur.fetchone() result = self.cur.fetchone()
if result: if result:
return '[{idx}/{len}] <{nick}> {item}'.format(**result) return '[{rank}/{total}] <{nick}> {item}'.format(**result)

View File

@ -5,6 +5,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -56,27 +57,31 @@ class Rape(DatabasePlugin):
else: else:
fine = random.randint(1, 500) fine = random.randint(1, 500)
# Insert or add fine to database and return total owe try:
self.cur.execute(''' # Insert or add fine to database and return total owe
insert into self.cur.execute('''
owes (nick, amount) insert into
values owes (nick, amount)
(%s, %s) values
on conflict (nick) do update set (%s, %s)
amount = owes.amount + excluded.amount on conflict (nick) do update set
returning amount = owes.amount + excluded.amount
amount returning
''', [nick, fine]) amount
self.con.commit() ''', [nick, fine])
self.con.commit()
# Get reason based on rand value # Get reason based on rand value
reason = ('raping', 'being too lewd and getting raped')[rand] reason = ('raping', 'being too lewd and getting raped')[rand]
# Print fine and total owe # Print fine and total owe
self.bot.action(target, self.bot.action(target,
'fines {nick} \x02${fine}\x02 for {reason}. ' 'fines {nick} \x02${fine}\x02 for {reason}. '
'You owe: \x0304${total}\x03' 'You owe: \x0304${total}\x03'
.format(nick=nick, .format(nick=nick,
fine=fine, fine=fine,
reason=reason, reason=reason,
total=self.cur.fetchone()['amount'])) total=self.cur.fetchone()['amount']))
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -6,6 +6,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -52,15 +53,19 @@ class Seen(DatabasePlugin):
@irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG (?P<target>\S+) :(?P<msg>.*)') @irc3.event(r'(?i)^:(?P<mask>\S+) PRIVMSG (?P<target>\S+) :(?P<msg>.*)')
def save(self, mask: str, target: str, msg: str): def save(self, mask: str, target: str, msg: str):
# Insert or update if user writes a message try:
self.cur.execute(''' # Insert or update if user writes a message
insert into self.cur.execute('''
seens (nick, channel, message, seen_at) insert into
values seens (nick, channel, message, seen_at)
(%s, %s, %s, %s) values
on conflict (nick) do update set (%s, %s, %s, %s)
channel = excluded.channel, on conflict (nick) do update set
seen_at = excluded.seen_at, channel = excluded.channel,
message = excluded.message seen_at = excluded.seen_at,
''', [IrcString(mask).nick, target, msg, datetime.now()]) message = excluded.message
self.con.commit() ''', [IrcString(mask).nick, target, msg, datetime.now()])
self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -3,6 +3,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
@ -49,14 +50,18 @@ class Tell(DatabasePlugin):
self.tell_queue[nick] = [] self.tell_queue[nick] = []
self.tell_queue[nick].append(tell) self.tell_queue[nick].append(tell)
# Insert tell into database try:
self.cur.execute(''' # Insert tell into database
insert into self.cur.execute('''
tells (from_nick, to_nick, message) insert into
values tells (from_nick, to_nick, message)
(%s, %s, %s) values
''', tell) (%s, %s, %s)
self.con.commit() ''', tell)
self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()
@irc3.event(r'(?i)^:(?P<mask>.*) PRIVMSG .* :.*') @irc3.event(r'(?i)^:(?P<mask>.*) PRIVMSG .* :.*')
def check(self, mask: str): def check(self, mask: str):
@ -72,11 +77,15 @@ class Tell(DatabasePlugin):
# Remove nick from queue # Remove nick from queue
del self.tell_queue[nick] del self.tell_queue[nick]
# Remove tells from database try:
self.cur.execute(''' # Remove tells from database
delete from self.cur.execute('''
tells delete from
where tells
to_nick = %s where
''', [nick]) to_nick = %s
self.con.commit() ''', [nick])
self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()

View File

@ -6,6 +6,7 @@ import irc3
from docopt import Dict as DocOptDict from docopt import Dict as DocOptDict
from irc3.plugins.command import command from irc3.plugins.command import command
from irc3.utils import IrcString from irc3.utils import IrcString
from psycopg2 import Error
from . import DatabasePlugin from . import DatabasePlugin
from ..utils import time_delta from ..utils import time_delta
@ -51,23 +52,27 @@ class Timer(DatabasePlugin):
message = ' '.join(args['<message>']) message = ' '.join(args['<message>'])
values = [mask, target, message, delay] values = [mask, target, message, delay]
# Insert into database (add now + delta) try:
self.cur.execute(''' # Insert into database (add now + delta)
insert into self.cur.execute('''
timers (mask, target, message, delay, ends_at) insert into
values timers (mask, target, message, delay, ends_at)
(%s, %s, %s, %s, %s) values
returning id (%s, %s, %s, %s, %s)
''', values + [datetime.now() + delta]) returning id
self.con.commit() ''', values + [datetime.now() + delta])
self.con.commit()
# Add delta and id from inserted and start timer # Add delta and id from inserted and start timer
values.extend([delta, self.cur.fetchone()['id']]) values.extend([delta, self.cur.fetchone()['id']])
self.start_timer(*values) self.start_timer(*values)
# Send notice to user that timer has been set # Send notice to user that timer has been set
self.bot.notice(mask.nick, 'Timer in {delay} set: {message}' self.bot.notice(mask.nick, 'Timer in {delay} set: {message}'
.format(delay=delay, message=message)) .format(delay=delay, message=message))
except Error:
# Rollback transaction on error
self.con.rollback()
def start_timer(self, mask: IrcString, target: IrcString, message: str, def start_timer(self, mask: IrcString, target: IrcString, message: str,
delay: str, delta: timedelta, row_id: int): delay: str, delta: timedelta, row_id: int):
@ -85,13 +90,17 @@ class Timer(DatabasePlugin):
nick=mask.nick, nick=mask.nick,
delay=delay)) delay=delay))
# Delete timer from database try:
self.cur.execute(''' # Delete timer from database
delete from self.cur.execute('''
timers delete from
where timers
id = %s where
''', [row_id]) id = %s
self.con.commit() ''', [row_id])
self.con.commit()
except Error:
# Rollback transaction on error
self.con.rollback()
asyncio.ensure_future(callback()) asyncio.ensure_future(callback())

View File

@ -45,26 +45,3 @@ create table if not exists owes (
amount integer not null, amount integer not null,
unique (nick) unique (nick)
); );
with ranked_quotes as (
select
id,
rank() over (partition by nick order by id desc)
from
quotes
where
nick = 'mrhanky'
)
delete from
quotes
where
id = (
select
id
from
ranked_quotes
where
rank = 1
)