diff --git a/Eta/README.md b/Eta/README.md new file mode 100644 index 0000000..fb580ac --- /dev/null +++ b/Eta/README.md @@ -0,0 +1 @@ +ETA until ETA diff --git a/Eta/__init__.py b/Eta/__init__.py new file mode 100644 index 0000000..8baa281 --- /dev/null +++ b/Eta/__init__.py @@ -0,0 +1,72 @@ +### +# Copyright (c) 2019, Pedro de Oliveira +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +""" +Eta: ETA until ETA +""" + +import sys +import supybot +from supybot import world + +# Use this for the version of this plugin. You may wish to put a CVS keyword +# in here if you're keeping the plugin in CVS or some similar system. +__version__ = "" + +# XXX Replace this with an appropriate author or supybot.Author instance. +__author__ = supybot.authors.unknown + +# This is a dictionary mapping supybot.Author instances to lists of +# contributions. +__contributors__ = {} + +# This is a url where the most recent plugin package can be downloaded. +__url__ = '' + +from . import config +from . import plugin +if sys.version_info >= (3, 4): + from importlib import reload +else: + from imp import reload +# In case we're being reloaded. +reload(config) +reload(plugin) +# Add more reloads here if you add third-party modules and want them to be +# reloaded when this plugin is reloaded. Don't forget to import them as well! + +if world.testing: + from . import test + +Class = plugin.Class +configure = config.configure + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/Eta/config.py b/Eta/config.py new file mode 100644 index 0000000..f0819e5 --- /dev/null +++ b/Eta/config.py @@ -0,0 +1,56 @@ +### +# Copyright (c) 2019, Pedro de Oliveira +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot import conf, registry +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('Eta') +except: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x + + +def configure(advanced): + # This will be called by supybot to configure this module. advanced is + # a bool that specifies whether the user identified themself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. + from supybot.questions import expect, anything, something, yn + conf.registerPlugin('Eta', True) + + +Eta = conf.registerPlugin('Eta') +# This is where your configuration variables (if any) should go. For example: +# conf.registerGlobalValue(Eta, 'someConfigVariableName', +# registry.Boolean(False, _("""Help for someConfigVariableName."""))) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: diff --git a/Eta/local/__init__.py b/Eta/local/__init__.py new file mode 100644 index 0000000..e86e97b --- /dev/null +++ b/Eta/local/__init__.py @@ -0,0 +1 @@ +# Stub so local is a module, used for third-party modules diff --git a/Eta/plugin.py b/Eta/plugin.py new file mode 100644 index 0000000..7d39e13 --- /dev/null +++ b/Eta/plugin.py @@ -0,0 +1,199 @@ +### +# Copyright (c) 2019, Pedro de Oliveira +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot import utils, plugins, ircutils, callbacks +from supybot.commands import * +try: + from supybot.i18n import PluginInternationalization + _ = PluginInternationalization('Eta') +except ImportError: + # Placeholder that allows to run the plugin on a bot + # without the i18n module + _ = lambda x: x +import supybot.utils.minisix as minisix +import os +import sqlite3 +import re +import timeago +import datetime +#from datetime import datetime + + +class SqliteEtaDB(object): + def __init__(self, filename): + self.dbs = ircutils.IrcDict() + self.filename = filename + + def close(self): + for db in self.dbs.values(): + db.close() + + def _getDb(self, channel): + filename = plugins.makeChannelFilename(self.filename, channel) + if filename in self.dbs: + return self.dbs[filename] + if os.path.exists(filename): + db = sqlite3.connect(filename, check_same_thread=False) + if minisix.PY2: + db.text_factory = str + self.dbs[filename] = db + return db + db = sqlite3.connect(filename, check_same_thread=False) + if minisix.PY2: + db.text_factory = str + self.dbs[filename] = db + cursor = db.cursor() + cursor.execute("""CREATE TABLE etatbl ( + id INTEGER PRIMARY KEY, + nick TEXT, + eta TEXT + )""") + db.commit() + return db + + def get_eta(self, channel, nick): + db = self._getDb(channel) + cursor = db.cursor() + cursor.execute("""SELECT eta FROM etatbl + WHERE nick=?""", (nick,)) + result = cursor.fetchone() + if result: + return result[0] + else: + return None + + def set_eta(self, channel, nick, eta): + db = self._getDb(channel) + cursor = db.cursor() + cursor.execute("""DELETE FROM etatbl + WHERE nick=?""", (nick,)) + cursor.execute("""INSERT INTO etatbl VALUES (NULL, ?, ?)""", + (nick, eta,)) + db.commit() + +EtaDB = plugins.DB('Eta', + {'sqlite3': SqliteEtaDB}) + +class Eta(callbacks.Plugin): + """ETA until ETA""" + threaded = True + + def __init__(self, irc): + self.__parent = super(Eta, self) + self.__parent.__init__(irc) + self.db = EtaDB() + self.prepend = "4,8ETA" + + def set(self, irc, msg, args, channel, eta): + """[] + + Assigns the to the current nick, in [channel]. + """ + eta = eta.group(0) + nick = msg.nick.lower() + self.db.set_eta(channel, nick, eta) + irc.reply("{} Set for {} for {}".format( + self.prepend, + eta, + nick + ), prefixNick=False) + _time = re.compile(r'^$|^(([01][0-9])|(2[0-3])):[0-5][0-9]$') + set = wrap(set, [ + 'channel', + ('matches', _time, _('Time must be in the 24h time format.'))]) + + def get(self, irc, msg, args, channel, user): + """[] [] + + Show the ETA until the specified time, by [nick], in [channel]. + """ + # Se ja passou da hora, e é antes da meia noite -> DONE! + # Se faltar menos de 60min -> ALMOST DONE! + + eta = None + if user: + nick = user.lower() + else: + nick = msg.nick.lower() + eta = self.db.get_eta(channel, nick) + + if eta is not None: + etas = eta.split(':') + eta_s = int(etas[0]) * 60 * 60 + int(etas[1]) * 60 + + # data e hora actual + now = datetime.datetime.now() + + # apenas a data, para facilitar depois + now = datetime.datetime(year=now.year, month=now.month, day=now.day, hour=now.hour, minute=now.minute) + + date = datetime.datetime(year=now.year, month=now.month, day=now.day) + + # segundos do dia actual + today_s = (now - date).total_seconds() + + # se ja passamos do eta por hoje, aponta para amanha + eta = date + datetime.timedelta(0, 24 * 60 * 60 + eta_s) if today_s > eta_s else date + datetime.timedelta(0, eta_s) + + # finalmente, subtrair para saber qt tempo falta. vou usar um Time com a + # data anterior so para facilitar sacar as horas e minutos! + r = date+(eta-now) + h = r.hour + m = r.minute + # como nao vamos considerar segundos, falta sempre pelo menos 1 minuto + + # ai meu deus tantos ifs + message = "" + if now == eta or h >= 12: + message = "DONE FOR THE DAY!" + else: + message = "in " + if h > 0: + if m == 0: + message += "exactly " + message += "{} hour{}".format(h, 's' if h > 1 else '') + if m > 0: + if h != 0: + message += " and " + message += "{} minute{}".format(m, 's' if m > 1 else '') + + irc.reply("{} {}".format( + self.prepend, + message), prefixNick=False) + else: + irc.reply("{} No ETA for {}".format( + self.prepend, + nick), prefixNick=False) + get = wrap(get, ['channel', optional('anything')]) + +Class = Eta + + +# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: diff --git a/Eta/test.py b/Eta/test.py new file mode 100644 index 0000000..e382b33 --- /dev/null +++ b/Eta/test.py @@ -0,0 +1,38 @@ +### +# Copyright (c) 2019, Pedro de Oliveira +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions, and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions, and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author of this software nor the name of +# contributors to this software may be used to endorse or promote products +# derived from this software without specific prior written consent. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +### + +from supybot.test import * + + +class EtaTestCase(PluginTestCase): + plugins = ('Eta',) + + +# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: