diff --git a/config.json b/config.json index ae7bd01..185a3c7 100644 --- a/config.json +++ b/config.json @@ -18,9 +18,9 @@ "nxy.plugins.bitcoin", "nxy.plugins.ctcp", "nxy.plugins.database", + "nxy.plugins.futures", "nxy.plugins.mcmaniac", "nxy.plugins.quotes", - "nxy.plugins.reminder", "nxy.plugins.useless" ], "irc3.plugins.command": { diff --git a/nxy/plugins/__init__.py b/nxy/plugins/__init__.py index 4fcb329..188a505 100644 --- a/nxy/plugins/__init__.py +++ b/nxy/plugins/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from irc3.plugins.command import Commands from irc3 import IrcBot +from irc3.plugins.command import Commands MODULE = __name__ diff --git a/nxy/plugins/admin.py b/nxy/plugins/admin.py index 681a49e..069bc65 100644 --- a/nxy/plugins/admin.py +++ b/nxy/plugins/admin.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- +import irc3 + from docopt import Dict as DocoptDict from irc3 import IrcBot -from irc3.utils import IrcString from irc3.plugins.command import command -import irc3 +from irc3.utils import IrcString from . import MODULE, Plugin diff --git a/nxy/plugins/bitcoin.py b/nxy/plugins/bitcoin.py index 3d6bcb1..07495ba 100644 --- a/nxy/plugins/bitcoin.py +++ b/nxy/plugins/bitcoin.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- +import irc3 + from docopt import Dict as DocOptDict from irc3.plugins.command import command from irc3.utils import IrcString -import irc3 from . import Plugin from ..utils import fmt, req diff --git a/nxy/plugins/ctcp.py b/nxy/plugins/ctcp.py index c668687..f2bff31 100644 --- a/nxy/plugins/ctcp.py +++ b/nxy/plugins/ctcp.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- +import irc3 +import time + from docopt import Dict as DocOptDict from irc3.plugins.command import command from irc3.utils import IrcString -import time -import irc3 from . import Plugin from ..utils import fmt diff --git a/nxy/plugins/database.py b/nxy/plugins/database.py index 20c8e4e..caf6db0 100644 --- a/nxy/plugins/database.py +++ b/nxy/plugins/database.py @@ -2,17 +2,22 @@ import sqlite3 import irc3 +from irc3 import IrcBot + from . import Plugin @irc3.plugin class Database(Plugin): - def __init__(self, bot): + def __init__(self, bot: IrcBot): super().__init__(bot) file = bot.config.storage.split('sqlite://', 1)[1] if not file: raise ValueError('Invalid database: {}'.format(bot.config.storage)) - self.db = sqlite3.connect(file) + # @formatter:off + self.db = sqlite3.connect(file, detect_types=sqlite3.PARSE_DECLTYPES + | sqlite3.PARSE_COLNAMES) + # @formatter:on self.db.row_factory = sqlite3.Row self.bot = bot self.bot.con = self diff --git a/nxy/plugins/futures.py b/nxy/plugins/futures.py new file mode 100644 index 0000000..0221f0d --- /dev/null +++ b/nxy/plugins/futures.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from datetime import datetime, timedelta +import asyncio + +from docopt import Dict as DocOptDict +from irc3.plugins.command import command +from irc3.utils import IrcString +from irc3 import IrcBot +import irc3 + +from . import DatabasePlugin +from ..utils import fmt, time_delta + + +@irc3.plugin +class Futures(DatabasePlugin): + requires = [ + 'irc3.plugins.command', + 'nxy.plugins.database', + ] + + def __init__(self, bot: IrcBot): + super().__init__(bot) + self.restore() + + def restore(self): + self.cur.execute('select * from timers') + for res in self.cur.fetchall(): + delta = res['until'] - datetime.utcnow() + args = (IrcString(res['mask']), res['channel'], delta, + res['message'], res['delay'], res['id']) + asyncio.ensure_future(self._timer(*args)) + + @command + def timer(self, mask: IrcString, channel: IrcString, args: DocOptDict): + """Timer command. + %%timer ... + """ + delay = args[''] + delta = time_delta(delay) + if delta: + date = datetime.utcnow() + delta + message = ' '.join(args['']) + self.cur.execute('''insert into timers (mask, channel, message, + delay, until) values (?, ?, ?, ?, ?)''', + [mask.nick, channel, message, delay, date]) + self.con.commit() + asyncio.ensure_future(self._timer( + mask, channel, delta, message, delay, self.cur.lastrowid)) + else: + self.bot.notice(mask.nick, 'Invalid delay "{delay}" for "timer"' + .format(delay=args[''])) + + async def _timer(self, mask: IrcString, channel: IrcString, + delta: timedelta, message: str, delay: str, row_id: int): + """ + Actually the reminder function. Sleeps `delay` seconds and then sends + message to `channel` after that. + """ + seconds = delta.total_seconds() + if seconds > 0: + await asyncio.sleep(seconds) + text = fmt('{bold}[Reminder]{reset} {nick}: {message} ({delay})', + message=message, + delay=delay, + nick=mask.nick) + self.bot.privmsg(channel, text) + self.cur.execute('delete from timers where id = ?', [row_id]) + self.con.commit() diff --git a/nxy/plugins/mcmaniac.py b/nxy/plugins/mcmaniac.py index 1039bad..fc0f329 100644 --- a/nxy/plugins/mcmaniac.py +++ b/nxy/plugins/mcmaniac.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from docopt import Dict as DocOptDict -from irc3.utils import IrcString from irc3.plugins.command import command +from irc3.utils import IrcString from . import DatabasePlugin from ..utils import parse_int @@ -25,15 +25,15 @@ class McManiac(DatabasePlugin): if cmd and item: if self.guard.has_permission(mask, 'admin'): if cmd == 'add': - self.cur.execute('insert into mcmaniac (item) values (?)', + self.cur.execute('insert into mcmaniacs (item) values (?)', [item]) if cmd == 'del': index, order, op = parse_int(item, select=False) if not index: return - self.cur.execute('''delete from mcmaniac where id = - (select id from mcmaniac a where ? = (select count(id) - from mcmaniac b where a.id {op} b.id) order by id {order}) + self.cur.execute('''delete from mcmaniacs where id = + (select id from mcmaniacs a where ? = (select count(id) + from mcmaniacs b where a.id {op} b.id) order by id {order}) '''.format(op=op, order=order), [index]) self.con.commit() else: @@ -53,9 +53,9 @@ class McManiac(DatabasePlugin): extra = '' binds = [] self.cur.execute('''select item, - (select count(id) from mcmaniac b where a.id >= b.id) as idx, - (select count(id) from mcmaniac) as len - from mcmaniac a order by {order} limit 1 {extra} + (select count(id) from mcmaniacs b where a.id >= b.id) as idx, + (select count(id) from mcmaniasc) as len + from mcmaniacs a order by {order} limit 1 {extra} '''.format(order=order, extra=extra), binds) result = self.cur.fetchone() if result: diff --git a/nxy/plugins/quotes.py b/nxy/plugins/quotes.py index 97bc2ce..b9d6518 100644 --- a/nxy/plugins/quotes.py +++ b/nxy/plugins/quotes.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -from docopt import Dict as DocOptDict -from irc3.utils import IrcString -from irc3.plugins.command import command import irc3 import re +from docopt import Dict as DocOptDict +from irc3.plugins.command import command +from irc3.utils import IrcString + from . import DatabasePlugin from ..utils import parse_int diff --git a/nxy/plugins/reminder.py b/nxy/plugins/reminder.py deleted file mode 100644 index ec83748..0000000 --- a/nxy/plugins/reminder.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -from docopt import Dict as DocOptDict -from irc3.plugins.command import command -from irc3.utils import IrcString -import asyncio -import irc3 - -from . import Plugin -from ..utils import fmt, time_to_sec - - -@irc3.plugin -class Reminder(Plugin): - requires = [ - 'irc3.plugins.command', - ] - - @command - def remind(self, mask: IrcString, channel: IrcString, args: DocOptDict): - """Remind command. - %%remind ... - """ - delay = time_to_sec(args['']) - if delay: - asyncio.ensure_future(self.timer(delay, mask, channel, args)) - else: - self.bot.notice(mask.nick, 'Invalid delay "{delay}" for "remind"' - .format(delay=args[''])) - - async def timer(self, delay, mask: IrcString, channel: IrcString, - args: DocOptDict): - """ - Actually the reminder function. Sleeps `delay` seconds and then sends - message to `channel` after that. - """ - await asyncio.sleep(delay) - text = fmt('{bold}[Reminder]{reset} {nick}: {message} ({delay})', - message=' '.join(args['']), - delay=args[''], - nick=mask.nick) - self.bot.privmsg(channel, text) diff --git a/nxy/plugins/useless.py b/nxy/plugins/useless.py index 3f27978..fadbf24 100644 --- a/nxy/plugins/useless.py +++ b/nxy/plugins/useless.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- +import irc3 +import random + from docopt import Dict as DocOptDict +from irc3.dec import event from irc3.plugins.command import command from irc3.utils import IrcString -from irc3.dec import event -import random -import irc3 from . import Plugin from ..utils import fmt diff --git a/nxy/utils.py b/nxy/utils.py index 07bd0eb..c1d507f 100644 --- a/nxy/utils.py +++ b/nxy/utils.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -from requests import request, Response +import re + from datetime import timedelta from pprint import pprint -import re +from requests import request, Response # @formatter:off COLORS = dict( @@ -62,14 +63,15 @@ async def req(method: str, url: str, **kwargs) -> Response: def time_to_sec(text: str) -> int: match = TIME_REGEX.match(text) if match: - unit, num = match - return num * TIME_DICT[unit][1] + num, unit = match.groups() + return int(num) * TIME_DICT[unit][1] def time_delta(text: str) -> timedelta: match = TIME_REGEX.match(text) if match: - unit, num = match + num, unit = match.groups() + num = int(num) if unit == 'years': num *= 52 return timedelta(**{TIME_DICT[unit][0]: num}) @@ -80,31 +82,12 @@ def parse_int(val: str, select: bool = True) -> tuple: val = int(val) if val is not 0: if val < 1: - order = 'desc' + order, op = 'desc', '<=' val *= -1 - op = '<=' else: - order = 'asc' - op = '>=' + order, op = 'asc', '>=' if select: val -= 1 return val, order, op except ValueError: pass - - -def _parse_int(val: list) -> int: - """ - Parses an int from list, decremts by -1 if positiv. - Only returns if value from list is not 0. - """ - try: - val = int(''.join(val)) - # Ignore val == 0 - if val is not 0: - # Decrement to match list index - if val > 0: - val -= 1 - return val - except ValueError: - pass diff --git a/schema.sql b/schema.sql index 1ef38a5..d99c81a 100644 --- a/schema.sql +++ b/schema.sql @@ -5,8 +5,18 @@ create table if not exists quotes ( unique (nick, item collate nocase) ); -create table if not exists mcmaniac ( +create table if not exists mcmaniacs ( id integer primary key autoincrement, item text not null, unique (item collate nocase) on conflict replace ); + +create table if not exists timers ( + id integer primary key autoincrement, + mask text not null, + channel text not null, + message text not null, + delay text not null, + until timestamp not null, + created timestamp not null default current_timestamp +);