From 20c1ffd2fc90ac1210f940ac287923737745b231 Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 1 Nov 2015 18:03:11 -0800 Subject: [PATCH] Convert tabs to spaces --- bin/pyircbot | 52 +- pyircbot/__init__.py | 2 +- pyircbot/modulebase.py | 122 ++--- pyircbot/modules/AttributeStorage.py | 326 ++++++------ pyircbot/modules/AttributeStorageLite.py | 314 ++++++------ pyircbot/modules/BitcoinPrice.py | 60 +-- pyircbot/modules/Calc.py | 510 +++++++++---------- pyircbot/modules/CryptoWallet.py | 424 ++++++++-------- pyircbot/modules/CryptoWalletRPC.py | 230 ++++----- pyircbot/modules/DogeDice.py | 602 +++++++++++------------ pyircbot/modules/DogeWallet.py | 318 ++++++------ pyircbot/modules/Error.py | 32 +- pyircbot/modules/GameBase.py | 106 ++-- pyircbot/modules/Inventory.py | 190 +++---- pyircbot/modules/LMGTFY.py | 44 +- pyircbot/modules/MySQL.py | 202 ++++---- pyircbot/modules/NFLLive.py | 422 ++++++++-------- pyircbot/modules/NickUser.py | 156 +++--- pyircbot/modules/PingResponder.py | 18 +- pyircbot/modules/RandQuote.py | 110 ++--- pyircbot/modules/Remind.py | 518 +++++++++---------- pyircbot/modules/SQLite.py | 146 +++--- pyircbot/modules/Seen.py | 118 ++--- pyircbot/modules/Services.py | 102 ++-- pyircbot/modules/Tell.py | 368 +++++++------- pyircbot/modules/TextCDC.py | 184 +++---- pyircbot/modules/Urban.py | 44 +- pyircbot/modules/Weather.py | 400 +++++++-------- pyircbot/modules/__init__.py | 2 +- setup.py | 16 +- 30 files changed, 3069 insertions(+), 3069 deletions(-) diff --git a/bin/pyircbot b/bin/pyircbot index 27120ff..4be32ea 100755 --- a/bin/pyircbot +++ b/bin/pyircbot @@ -7,29 +7,29 @@ from pyircbot import PyIRCBot if __name__ == "__main__": - " logging level and facility " - logging.basicConfig(level=logging.DEBUG, format="%(asctime)-15s %(levelname)-8s %(message)s") - log = logging.getLogger('main') - - " parse command line args " - parser = OptionParser() - parser.add_option("-c", "--config", action="store", type="string", dest="config", help="Path to config file") - - (options, args) = parser.parse_args() - - log.debug(options) - - if not options.config: - log.critical("No bot config file specified (-c). Exiting.") - sys.exit(0) - - botconfig = PyIRCBot.load(options.config) - - log.debug(botconfig) - - bot = PyIRCBot(botconfig) - try: - bot.loop() - except KeyboardInterrupt: - bot.kill() - + " logging level and facility " + logging.basicConfig(level=logging.DEBUG, format="%(asctime)-15s %(levelname)-8s %(message)s") + log = logging.getLogger('main') + + " parse command line args " + parser = OptionParser() + parser.add_option("-c", "--config", action="store", type="string", dest="config", help="Path to config file") + + (options, args) = parser.parse_args() + + log.debug(options) + + if not options.config: + log.critical("No bot config file specified (-c). Exiting.") + sys.exit(0) + + botconfig = PyIRCBot.load(options.config) + + log.debug(botconfig) + + bot = PyIRCBot(botconfig) + try: + bot.loop() + except KeyboardInterrupt: + bot.kill() + diff --git a/pyircbot/__init__.py b/pyircbot/__init__.py index 5266c3a..47c3bee 100644 --- a/pyircbot/__init__.py +++ b/pyircbot/__init__.py @@ -1,6 +1,6 @@ """ .. module:: Core - :synopsis: Core module of the bot + :synopsis: Core module of the bot .. moduleauthor:: Dave Pedu diff --git a/pyircbot/modulebase.py b/pyircbot/modulebase.py index 29c608a..8182ca3 100644 --- a/pyircbot/modulebase.py +++ b/pyircbot/modulebase.py @@ -1,6 +1,6 @@ """ .. module:: ModuleBase - :synopsis: Base class that modules will extend + :synopsis: Base class that modules will extend .. moduleauthor:: Dave Pedu @@ -11,65 +11,65 @@ import os from .pyircbot import PyIRCBot class ModuleBase: - """All modules will extend this class - - :param bot: A reference to the main bot passed when this module is created - :type bot: PyIRCBot - :param moduleName: The name assigned to this module - :type moduleName: str - """ - - def __init__(self, bot, moduleName): - self.moduleName=moduleName - """Assigned name of this module""" - - self.bot = bot - """Reference to the master PyIRCBot object""" - - self.hooks=[] - """Hooks (aka listeners) this module has""" - - self.services=[] - """If this module provides services usable by another module, they're listed - here""" - - self.config={} - """Configuration dictionary. Autoloaded from `%(datadir)s/%(modulename)s.json`""" - - self.log = logging.getLogger("Module.%s" % self.moduleName) - """Logger object for this module""" - - # Autoload config if available - self.loadConfig() - - self.log.info("Loaded module %s" % self.moduleName) - - def loadConfig(self): - """Loads this module's config into self.config""" - configPath = self.bot.getConfigPath(self.moduleName) - if not configPath == None: - self.config = PyIRCBot.load(configPath) - - def ondisable(self): - """Called when the module should be disabled. Your module should do any sort - of clean-up operations here like ending child threads or saving data files. - """ - pass - - def getConfigPath(self): - """Returns the absolute path of this module's json config file""" - return self.bot.getConfigPath(self.moduleName) - - def getFilePath(self, f=None): - """Returns the absolute path to a file in this Module's data dir - - :param f: The file name included in the path - :type channel: str - :Warning: .. Warning:: this does no error checking if the file exists or is\ - writable. The bot's data dir *should* always be writable""" - return self.bot.getDataPath(self.moduleName) + (f if f else '') + """All modules will extend this class + + :param bot: A reference to the main bot passed when this module is created + :type bot: PyIRCBot + :param moduleName: The name assigned to this module + :type moduleName: str + """ + + def __init__(self, bot, moduleName): + self.moduleName=moduleName + """Assigned name of this module""" + + self.bot = bot + """Reference to the master PyIRCBot object""" + + self.hooks=[] + """Hooks (aka listeners) this module has""" + + self.services=[] + """If this module provides services usable by another module, they're listed + here""" + + self.config={} + """Configuration dictionary. Autoloaded from `%(datadir)s/%(modulename)s.json`""" + + self.log = logging.getLogger("Module.%s" % self.moduleName) + """Logger object for this module""" + + # Autoload config if available + self.loadConfig() + + self.log.info("Loaded module %s" % self.moduleName) + + def loadConfig(self): + """Loads this module's config into self.config""" + configPath = self.bot.getConfigPath(self.moduleName) + if not configPath == None: + self.config = PyIRCBot.load(configPath) + + def ondisable(self): + """Called when the module should be disabled. Your module should do any sort + of clean-up operations here like ending child threads or saving data files. + """ + pass + + def getConfigPath(self): + """Returns the absolute path of this module's json config file""" + return self.bot.getConfigPath(self.moduleName) + + def getFilePath(self, f=None): + """Returns the absolute path to a file in this Module's data dir + + :param f: The file name included in the path + :type channel: str + :Warning: .. Warning:: this does no error checking if the file exists or is\ + writable. The bot's data dir *should* always be writable""" + return self.bot.getDataPath(self.moduleName) + (f if f else '') class ModuleHook: - def __init__(self, hook, method): - self.hook=hook - self.method=method + def __init__(self, hook, method): + self.hook=hook + self.method=method diff --git a/pyircbot/modules/AttributeStorage.py b/pyircbot/modules/AttributeStorage.py index 84d5c77..bb7ced2 100755 --- a/pyircbot/modules/AttributeStorage.py +++ b/pyircbot/modules/AttributeStorage.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: AttributeStorage - :synopsis: An item key->value storage engine based on mysql + :synopsis: An item key->value storage engine based on mysql .. moduleauthor:: Dave Pedu @@ -10,165 +10,165 @@ from pyircbot.modulebase import ModuleBase,ModuleHook class AttributeStorage(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["attributes"] - self.db = None - serviceProviders = self.bot.getmodulesbyservice("mysql") - if len(serviceProviders)==0: - self.log.error("AttributeStorage: Could not find a valid mysql service provider") - else: - self.log.info("AttributeStorage: Selecting mysql service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0] - - if not self.db.connection.tableExists("attribute"): - self.log.info("AttributeStorage: Creating table: attribute") - c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `attribute` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `attribute` varchar(128) NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `attribute` (`attribute`) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") - c.close() - - if not self.db.connection.tableExists("items"): - self.log.info("AttributeStorage: Creating table: items") - c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `items` ( - `id` int(11) NOT NULL AUTO_INCREMENT, - `item` varchar(512) CHARACTER SET utf8 NOT NULL, - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") - c.close() - - if not self.db.connection.tableExists("values"): - self.log.info("AttributeStorage: Creating table: values") - c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `values` ( - `itemid` int(11) NOT NULL, - `attributeid` int(11) NOT NULL, - `value` varchar(512) CHARACTER SET utf8 NOT NULL, - PRIMARY KEY (`itemid`,`attributeid`) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") - c.close() - - # self.getItem('xMopxShell', 'name') - # self.getKey('xMopxShell', 'name') - # self.setKey('xMopxShell', 'name', 'dave') - - # SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, `v`.`value` FROM `items` `i` INNER JOIN `values` `v` on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` ORDER BY `i`.`id` ASC, `a`.`id` ASC LIMIT 1000 ; - - def getItem(self, name): - """Get all values for a item - - :param name: the item - :type name: str - :returns: dict -- the item's values expressed as a dict""" - c = self.db.connection.query("""SELECT - `i`.`id`, - `i`.`item`, - `a`.`attribute`, - `v`.`value` - FROM - `items` `i` - INNER JOIN `values` `v` - on `v`.`itemid`=`i`.`id` - INNER JOIN `attribute` `a` - on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=%s;""", - (name,) - ) - item = {} - while True: - row = c.fetchone() - if row == None: - break - item[row["attribute"]]=row["value"] - c.close() - - if len(item)==0: - return {} - return item - - def get(self, item, key): - return self.getKey(item, key) - - def getKey(self, item, key): - """Get the value of an key on an item - - :param item: the item to fetch a key from - :type item: str - :param key: they key who's value to return - :type key: str - :returns: str -- the item from the database or **None**""" - c = self.db.connection.query("""SELECT - `i`.`id`, - `i`.`item`, - `a`.`attribute`, - `v`.`value` - FROM - `items` `i` - INNER JOIN `values` `v` - on `v`.`itemid`=`i`.`id` - INNER JOIN `attribute` `a` - on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=%s - AND - `a`.`attribute`=%s;""", - (item,key) - ) - row = c.fetchone() - c.close() - if row == None: - return None - return row["value"] - - def set(self, item, key, value): - return self.setKey(item, key, value) - - def setKey(self, item, key, value): - """Set the key on an item - - :param item: the item name to set the key on - :type item: str - :param key: the key to set - :type key: tuple - :param value: the value to set - :type value: str""" - item = item.lower() - attribute = key.lower() - - # Check attribute exists - c = self.db.connection.query("SELECT `id` FROM `attribute` WHERE `attribute`=%s;", (attribute)) - row = c.fetchone() - attributeId = -1 - if row == None: - c = self.db.connection.query("INSERT INTO `attribute` (`attribute`) VALUES (%s);", (attribute)) - attributeId = c.lastrowid - else: - attributeId = row["id"] - c.close() - - # check item exists - c = self.db.connection.query("SELECT `id` FROM `items` WHERE `item`=%s;", (item)) - row = c.fetchone() - itemId = -1 - if row == None: - c = self.db.connection.query("INSERT INTO `items` (`item`) VALUES (%s);", (item)) - itemId = c.lastrowid - else: - itemId = row["id"] - c.close() - - if value == None: - # delete it - c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId)) - self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) - else: - # add attribute - c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (%s, %s, %s);", (itemId, attributeId, value)) - self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) - c.close() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["attributes"] + self.db = None + serviceProviders = self.bot.getmodulesbyservice("mysql") + if len(serviceProviders)==0: + self.log.error("AttributeStorage: Could not find a valid mysql service provider") + else: + self.log.info("AttributeStorage: Selecting mysql service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0] + + if not self.db.connection.tableExists("attribute"): + self.log.info("AttributeStorage: Creating table: attribute") + c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `attribute` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `attribute` varchar(128) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `attribute` (`attribute`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") + c.close() + + if not self.db.connection.tableExists("items"): + self.log.info("AttributeStorage: Creating table: items") + c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `items` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `item` varchar(512) CHARACTER SET utf8 NOT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") + c.close() + + if not self.db.connection.tableExists("values"): + self.log.info("AttributeStorage: Creating table: values") + c = self.db.connection.query("""CREATE TABLE IF NOT EXISTS `values` ( + `itemid` int(11) NOT NULL, + `attributeid` int(11) NOT NULL, + `value` varchar(512) CHARACTER SET utf8 NOT NULL, + PRIMARY KEY (`itemid`,`attributeid`) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;""") + c.close() + + # self.getItem('xMopxShell', 'name') + # self.getKey('xMopxShell', 'name') + # self.setKey('xMopxShell', 'name', 'dave') + + # SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, `v`.`value` FROM `items` `i` INNER JOIN `values` `v` on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` ORDER BY `i`.`id` ASC, `a`.`id` ASC LIMIT 1000 ; + + def getItem(self, name): + """Get all values for a item + + :param name: the item + :type name: str + :returns: dict -- the item's values expressed as a dict""" + c = self.db.connection.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=%s;""", + (name,) + ) + item = {} + while True: + row = c.fetchone() + if row == None: + break + item[row["attribute"]]=row["value"] + c.close() + + if len(item)==0: + return {} + return item + + def get(self, item, key): + return self.getKey(item, key) + + def getKey(self, item, key): + """Get the value of an key on an item + + :param item: the item to fetch a key from + :type item: str + :param key: they key who's value to return + :type key: str + :returns: str -- the item from the database or **None**""" + c = self.db.connection.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=%s + AND + `a`.`attribute`=%s;""", + (item,key) + ) + row = c.fetchone() + c.close() + if row == None: + return None + return row["value"] + + def set(self, item, key, value): + return self.setKey(item, key, value) + + def setKey(self, item, key, value): + """Set the key on an item + + :param item: the item name to set the key on + :type item: str + :param key: the key to set + :type key: tuple + :param value: the value to set + :type value: str""" + item = item.lower() + attribute = key.lower() + + # Check attribute exists + c = self.db.connection.query("SELECT `id` FROM `attribute` WHERE `attribute`=%s;", (attribute)) + row = c.fetchone() + attributeId = -1 + if row == None: + c = self.db.connection.query("INSERT INTO `attribute` (`attribute`) VALUES (%s);", (attribute)) + attributeId = c.lastrowid + else: + attributeId = row["id"] + c.close() + + # check item exists + c = self.db.connection.query("SELECT `id` FROM `items` WHERE `item`=%s;", (item)) + row = c.fetchone() + itemId = -1 + if row == None: + c = self.db.connection.query("INSERT INTO `items` (`item`) VALUES (%s);", (item)) + itemId = c.lastrowid + else: + itemId = row["id"] + c.close() + + if value == None: + # delete it + c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) + else: + # add attribute + c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (%s, %s, %s);", (itemId, attributeId, value)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) + c.close() diff --git a/pyircbot/modules/AttributeStorageLite.py b/pyircbot/modules/AttributeStorageLite.py index 8840672..0669047 100755 --- a/pyircbot/modules/AttributeStorageLite.py +++ b/pyircbot/modules/AttributeStorageLite.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: AttributeStorageLite - :synopsis: An item key->value storage engine based on Sqlite + :synopsis: An item key->value storage engine based on Sqlite .. moduleauthor:: Dave Pedu @@ -10,159 +10,159 @@ from pyircbot.modulebase import ModuleBase,ModuleHook class AttributeStorageLite(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["attributes"] - self.db = None - serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: - self.log.error("AttributeStorage: Could not find a valid sqlite service provider") - else: - self.log.info("AttributeStorage: Selecting sqlite service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0].opendb("attributes.db") - - if not self.db.tableExists("attribute"): - self.log.info("AttributeStorage: Creating table: attribute") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `attribute` ( - `id` INTEGER PRIMARY KEY, - `attribute` varchar(128) UNIQUE - ) ;""") - c.close() - - if not self.db.tableExists("items"): - self.log.info("AttributeStorage: Creating table: items") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `items` ( - `id` INTEGER PRIMARY KEY, - `item` varchar(512) - ) ;""") - c.close() - - if not self.db.tableExists("values"): - self.log.info("AttributeStorage: Creating table: values") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `values` ( - `itemid` INTEGER NOT NULL, - `attributeid` INTEGER NOT NULL, - `value` TEXT, - PRIMARY KEY (`itemid`, `attributeid`) - ) ;""") - c.close() - - #print(self.setKey('xmopxshell', 'name', 'dave')) - #print(self.getKey('xmopxshell', 'name')) - #print(self.getItem('xMopxShell')) - - def getItem(self, name): - """Get all values for a item - - :param name: the item - :type name: str - :returns: dict -- the item's values expressed as a dict""" - c = self.db.query("""SELECT - `i`.`id`, - `i`.`item`, - `a`.`attribute`, - `v`.`value` - FROM - `items` `i` - INNER JOIN `values` `v` - on `v`.`itemid`=`i`.`id` - INNER JOIN `attribute` `a` - on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=?;""", - (name.lower(),) - ) - item = {} - while True: - row = c.fetchone() - if row == None: - break - item[row["attribute"]]=row["value"] - c.close() - - if len(item)==0: - return {} - return item - - def get(self, item, key): - return self.getKey(item, key) - def getKey(self, item, key): - """Get the value of an key on an item - - :param item: the item to fetch a key from - :type item: str - :param key: they key who's value to return - :type key: str - :returns: str -- the item from the database or **None**""" - c = self.db.query("""SELECT - `i`.`id`, - `i`.`item`, - `a`.`attribute`, - `v`.`value` - FROM - `items` `i` - INNER JOIN `values` `v` - on `v`.`itemid`=`i`.`id` - INNER JOIN `attribute` `a` - on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=? - AND - `a`.`attribute`=?;""", - (item.lower(),key.lower()) - ) - row = c.fetchone() - - c.close() - if row == None: - return None - return row["value"] - - def set(self, item, key, value): - return self.setKey(item, key, value) - def setKey(self, item, key, value): - """Set the key on an item - - :param item: the item name to set the key on - :type item: str - :param key: the key to set - :type key: tuple - :param value: the value to set - :type value: str""" - item = item.lower() - attribute = key.lower() - - # Check attribute exists - c = self.db.query("SELECT `id` FROM `attribute` WHERE `attribute`=?;", (attribute,)) - row = c.fetchone() - attributeId = -1 - if row == None: - c = self.db.query("INSERT INTO `attribute` (`attribute`) VALUES (?);", (attribute,)) - attributeId = c.lastrowid - else: - attributeId = row["id"] - c.close() - - # check item exists - c = self.db.query("SELECT `id` FROM `items` WHERE `item`=?;", (item,)) - row = c.fetchone() - itemId = -1 - if row == None: - c = self.db.query("INSERT INTO `items` (`item`) VALUES (?);", (item,)) - itemId = c.lastrowid - else: - itemId = row["id"] - c.close() - - if value == None: - # delete it - c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId)) - self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) - else: - # add attribute - c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", (itemId, attributeId, value)) - self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) - c.close() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["attributes"] + self.db = None + serviceProviders = self.bot.getmodulesbyservice("sqlite") + if len(serviceProviders)==0: + self.log.error("AttributeStorage: Could not find a valid sqlite service provider") + else: + self.log.info("AttributeStorage: Selecting sqlite service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0].opendb("attributes.db") + + if not self.db.tableExists("attribute"): + self.log.info("AttributeStorage: Creating table: attribute") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `attribute` ( + `id` INTEGER PRIMARY KEY, + `attribute` varchar(128) UNIQUE + ) ;""") + c.close() + + if not self.db.tableExists("items"): + self.log.info("AttributeStorage: Creating table: items") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `items` ( + `id` INTEGER PRIMARY KEY, + `item` varchar(512) + ) ;""") + c.close() + + if not self.db.tableExists("values"): + self.log.info("AttributeStorage: Creating table: values") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `values` ( + `itemid` INTEGER NOT NULL, + `attributeid` INTEGER NOT NULL, + `value` TEXT, + PRIMARY KEY (`itemid`, `attributeid`) + ) ;""") + c.close() + + #print(self.setKey('xmopxshell', 'name', 'dave')) + #print(self.getKey('xmopxshell', 'name')) + #print(self.getItem('xMopxShell')) + + def getItem(self, name): + """Get all values for a item + + :param name: the item + :type name: str + :returns: dict -- the item's values expressed as a dict""" + c = self.db.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=?;""", + (name.lower(),) + ) + item = {} + while True: + row = c.fetchone() + if row == None: + break + item[row["attribute"]]=row["value"] + c.close() + + if len(item)==0: + return {} + return item + + def get(self, item, key): + return self.getKey(item, key) + def getKey(self, item, key): + """Get the value of an key on an item + + :param item: the item to fetch a key from + :type item: str + :param key: they key who's value to return + :type key: str + :returns: str -- the item from the database or **None**""" + c = self.db.query("""SELECT + `i`.`id`, + `i`.`item`, + `a`.`attribute`, + `v`.`value` + FROM + `items` `i` + INNER JOIN `values` `v` + on `v`.`itemid`=`i`.`id` + INNER JOIN `attribute` `a` + on `a`.`id`=`v`.`attributeid` + + WHERE + `i`.`item`=? + AND + `a`.`attribute`=?;""", + (item.lower(),key.lower()) + ) + row = c.fetchone() + + c.close() + if row == None: + return None + return row["value"] + + def set(self, item, key, value): + return self.setKey(item, key, value) + def setKey(self, item, key, value): + """Set the key on an item + + :param item: the item name to set the key on + :type item: str + :param key: the key to set + :type key: tuple + :param value: the value to set + :type value: str""" + item = item.lower() + attribute = key.lower() + + # Check attribute exists + c = self.db.query("SELECT `id` FROM `attribute` WHERE `attribute`=?;", (attribute,)) + row = c.fetchone() + attributeId = -1 + if row == None: + c = self.db.query("INSERT INTO `attribute` (`attribute`) VALUES (?);", (attribute,)) + attributeId = c.lastrowid + else: + attributeId = row["id"] + c.close() + + # check item exists + c = self.db.query("SELECT `id` FROM `items` WHERE `item`=?;", (item,)) + row = c.fetchone() + itemId = -1 + if row == None: + c = self.db.query("INSERT INTO `items` (`item`) VALUES (?);", (item,)) + itemId = c.lastrowid + else: + itemId = row["id"] + c.close() + + if value == None: + # delete it + c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) + else: + # add attribute + c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", (itemId, attributeId, value)) + self.log.debug("AttributeStorage: Stored item %s attribute %s value: %s" % (itemId, attributeId, value)) + c.close() diff --git a/pyircbot/modules/BitcoinPrice.py b/pyircbot/modules/BitcoinPrice.py index 4dc2f00..d5239d5 100644 --- a/pyircbot/modules/BitcoinPrice.py +++ b/pyircbot/modules/BitcoinPrice.py @@ -1,6 +1,6 @@ """ .. module:: BitcoinPrice - :synopsis: Provides .bitcoin to show price indexes + :synopsis: Provides .bitcoin to show price indexes .. moduleauthor:: Dave Pedu @@ -11,32 +11,32 @@ from requests import get from time import time class BitcoinPrice(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - - self.cache = None - self.cacheAge = 0 - - self.hooks=[ - ModuleHook(["PRIVMSG"], self.btc) - ] - - def btc(self, args, prefix, trailing): - prefix = self.bot.decodePrefix(prefix) - replyTo = prefix.nick if not "#" in args[0] else args[0] - - cmd = self.bot.messageHasCommand([".btc", ".bitcoin"], trailing) - if cmd: - data = self.getApi() - self.bot.act_PRIVMSG(replyTo, "%s: %s" % ( - prefix.nick, - "\x02\x0307Bitcoin:\x03\x02 \x0307{buy:.0f}\x0f$ - High: \x0307{high:.0f}\x0f$ - Low: \x0307{low:.0f}\x0f$ - Volume: \x0307{vol_cur:.0f}\x03฿".format(**data['ticker']) - )) - - def getApi(self): - if self.cache == None or time()-self.cacheAge>self.config["cache"]: - self.cache = get("https://btc-e.com/api/2/btc_usd/ticker").json() - self.cacheAge = time() - return self.cache - - \ No newline at end of file + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + + self.cache = None + self.cacheAge = 0 + + self.hooks=[ + ModuleHook(["PRIVMSG"], self.btc) + ] + + def btc(self, args, prefix, trailing): + prefix = self.bot.decodePrefix(prefix) + replyTo = prefix.nick if not "#" in args[0] else args[0] + + cmd = self.bot.messageHasCommand([".btc", ".bitcoin"], trailing) + if cmd: + data = self.getApi() + self.bot.act_PRIVMSG(replyTo, "%s: %s" % ( + prefix.nick, + "\x02\x0307Bitcoin:\x03\x02 \x0307{buy:.0f}\x0f$ - High: \x0307{high:.0f}\x0f$ - Low: \x0307{low:.0f}\x0f$ - Volume: \x0307{vol_cur:.0f}\x03฿".format(**data['ticker']) + )) + + def getApi(self): + if self.cache == None or time()-self.cacheAge>self.config["cache"]: + self.cache = get("https://btc-e.com/api/2/btc_usd/ticker").json() + self.cacheAge = time() + return self.cache + + \ No newline at end of file diff --git a/pyircbot/modules/Calc.py b/pyircbot/modules/Calc.py index 3081b80..a4a4ebb 100644 --- a/pyircbot/modules/Calc.py +++ b/pyircbot/modules/Calc.py @@ -4,258 +4,258 @@ import time import math class Calc(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - - self.hooks=[ModuleHook("PRIVMSG", self.calc)] - self.timers={} - - self.sqlite = self.bot.getBestModuleForService("sqlite") - if self.sqlite==None: - self.log.error("Calc: SQLIite service is required.") - return - - self.sql = self.sqlite.opendb("calc.db") - - if not self.sql.tableExists("calc_addedby"): - c = self.sql.getCursor() - c.execute(""" - CREATE TABLE `calc_addedby` ( - `id` INTEGER PRIMARY KEY, - `username` varchar(32), - `userhost` varchar(128) - ) ; - """) - c.close() - if not self.sql.tableExists("calc_channels"): - c = self.sql.getCursor() - c.execute(""" - CREATE TABLE `calc_channels` ( - `id` INTEGER PRIMARY KEY, - `channel` varchar(32) - ) ; - """) - if not self.sql.tableExists("calc_definitions"): - c = self.sql.getCursor() - c.execute(""" - CREATE TABLE `calc_definitions` ( - `id` INTEGER PRIMARY KEY, - `word` INTEGET, - `definition` varchar(512), - `addedby` INTEGER, - `date` timestamp, - `status` varchar(16) - ) ; - """) - if not self.sql.tableExists("calc_words"): - c = self.sql.getCursor() - c.execute(""" - CREATE TABLE `calc_words` ( - `id` INTEGER PRIMARY KEY, - `channel` INTEGER, - `word` varchar(512), - `status` varchar(32), - unique(`channel`,`word`) - ); - """) - c.close() - - def timeSince(self, channel, timetype): - if not channel in self.timers: - self.createDefaultTimers(channel) - return time.time()-self.timers[channel][timetype] - - def updateTimeSince(self, channel, timetype): - if not channel in self.timers: - self.createDefaultTimers(channel) - self.timers[channel][timetype] = time.time() - - def createDefaultTimers(self, channel): - self.timers[channel]={"add":0, "calc":0, "calcspec":0, "match":0} - - def remainingToStr(self, total, elasped): - remaining = total-elasped - minutes = int(math.floor(remaining/60)) - seconds = int(remaining - (minutes*60)) - return "Please wait %s minute(s) and %s second(s)." % (minutes, seconds) - - def calc(self, args, prefix, trailing): - # Channel only - if not args[0][0]=="#": - return - sender = self.bot.decodePrefix(prefix) - - foundCalc = False - commandFound = "" - for cmd in self.config["cmd_calc"]: - if trailing[0:len(cmd)] == cmd and ( len(trailing) == len(cmd) or (trailing[len(cmd):len(cmd)+1] in [" ", "="])): - commandFound=cmd - foundCalc=True - - if foundCalc: - calcCmd = trailing[len(cmd)-1:].strip() - if "=" in calcCmd[1:]: - " Add a new calc " - calcWord, calcDefinition = calcCmd.split("=", 1) - calcWord = calcWord.strip() - calcDefinition = calcDefinition.strip() - if self.config["allowDelete"] and calcDefinition == "": - result = self.deleteCalc(args[0], calcWord) - if result: - self.bot.act_PRIVMSG(args[0], "Calc deleted, %s." % sender.nick) - else: - self.bot.act_PRIVMSG(args[0], "Sorry %s, I don't know what '%s' is." % (sender.nick, calcWord)) - else: - if self.config["delaySubmit"]>0 and self.timeSince(args[0], "add") < self.config["delaySubmit"]: - self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delaySubmit"], self.timeSince(args[0], "add"))) - else: - self.addNewCalc(args[0], calcWord, calcDefinition, prefix) - self.bot.act_PRIVMSG(args[0], "Thanks for the info, %s." % sender.nick) - self.updateTimeSince(args[0], "add") - elif len(calcCmd)>0: - " Lookup the word in calcCmd " - - if self.config["delayCalcSpecific"]>0 and self.timeSince(args[0], "calcspec") < self.config["delayCalcSpecific"]: - self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayCalcSpecific"], self.timeSince(args[0], "calcspec"))) - else: - randCalc = self.getSpecificCalc(args[0], calcCmd) - if randCalc==None: - self.bot.act_PRIVMSG(args[0], "Sorry %s, I don't know what '%s' is." % (sender.nick, calcCmd)) - else: - self.bot.act_PRIVMSG(args[0], "%s \x03= %s \x0314[added by: %s]" % (randCalc["word"], randCalc["definition"], randCalc["by"])) - self.updateTimeSince(args[0], "calcspec") - else: - if self.config["delayCalc"]>0 and self.timeSince(args[0], "calc") < self.config["delayCalc"]: - self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayCalc"], self.timeSince(args[0], "calc"))) - else: - randCalc = self.getRandomCalc(args[0]) - if randCalc == None: - self.bot.act_PRIVMSG(args[0], "This channel has no calcs, %s :(" % (sender.nick,)) - else: - self.bot.act_PRIVMSG(args[0], "%s \x03= %s \x0314[added by: %s]" % (randCalc["word"], randCalc["definition"], randCalc["by"])) - self.updateTimeSince(args[0], "calc") - return - - cmd = self.bot.messageHasCommand(self.config["cmd_match"], trailing, True) - if cmd: - if self.config["delayMatch"]>0 and self.timeSince(args[0], "match") < self.config["delayMatch"]: - self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayMatch"], self.timeSince(args[0], "match"))) - else: - term = cmd.args_str - if term.strip()=='': - return - c = self.sql.getCursor() - channelId = self.getChannelId(args[0]) - c.execute("SELECT * FROM `calc_words` WHERE `word` LIKE ? AND `channel`=? ORDER BY `word` ASC ;", ("%%"+term+"%%", channelId)) - rows = c.fetchall() - if len(rows)==0: - self.bot.act_PRIVMSG(args[0], "%s: Sorry, no matches" % sender.nick) - else: - matches = [] - for row in rows[0:10]: - if row == None: - break - matches.append(row["word"]) - self.bot.act_PRIVMSG(args[0], "%s: %s match%s (%s\x03)" % (sender.nick, len(matches), "es" if len(matches)>1 else "", ", \x03".join(matches) )) - self.updateTimeSince(args[0], "match") - - def addNewCalc(self, channel, word, definition, prefix): - sender = self.bot.decodePrefix(prefix) - - " Find the channel ID" - channelId = self.getChannelId(channel) - - " Check if we need to add a user" - c = self.sql.getCursor() - name = sender.nick - host = sender.hostname - c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host)) - rows = c.fetchall() - if len(rows)==0: - c.execute("INSERT INTO `calc_addedby` (`username`, `userhost`) VALUES (?, ?) ;", (name, host,)) - c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host)) - rows = c.fetchall() - addedId = rows[0]["id"] - - " Check if the word exists" - c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word)) - rows = c.fetchall() - if len(rows)==0: - c.execute("INSERT INTO `calc_words` (`channel`, `word`, `status`) VALUES (?, ?, ?) ;", (channelId, word, 'approved')) - c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word)) - rows = c.fetchall() - wordId = rows[0]["id"] - " Add definition " - c.execute("INSERT INTO `calc_definitions` (`word`, `definition`, `addedby`, `date`, `status`) VALUES (?, ?, ?, ?, ?) ;", (wordId, definition, addedId, datetime.datetime.now(), 'approved',)) - c.close() - pass - - def getSpecificCalc(self, channel, word): - c = self.sql.getCursor() - channelId = self.getChannelId(channel) - c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE `cdq`.`word`=`cw`.`id` AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as `definitionId` FROM `calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' AND `cw`.`word`=? COLLATE NOCASE ORDER BY RANDOM() LIMIT 1 ;", (channelId, word.lower())) - word = c.fetchone() - - if word == None: - return None - - c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` ON `ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], )) - - who = c.fetchone() - - if who == None: - return None - - c.close() - return {"word":word["word"], "definition":who["definition"], "by":who["username"]} - - - def getRandomCalc(self, channel): - c = self.sql.getCursor() - channelId = self.getChannelId(channel) - - for i in range(0, 5): - c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE `cdq`.`word`=`cw`.`id` AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as `definitionId` FROM `calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' ORDER BY RANDOM() LIMIT 1 ;", (channelId,)) - word = c.fetchone() - if word == None: - return None - c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` ON `ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], )) - - who = c.fetchone() - - if who == None: - continue - - c.close() - return {"word":word["word"], "definition":who["definition"], "by":who["username"]} - - def deleteCalc(self, channel, word): - " Return true if deleted something, false if it doesnt exist" - c = self.sql.getCursor() - channelId = self.getChannelId(channel) - c.execute("SELECT * FROM `calc_words` WHERE `channel`=? and `word`=? ;", (channelId, word)) - rows = c.fetchall() - if len(rows)==0: - c.close() - return False - - wordId = rows[0]["id"] - - #c.execute("DELETE FROM `calc_words` WHERE `id`=? ;", (wordId,)) - #c.execute("DELETE FROM `calc_definitions` WHERE `word`=? ;", (wordId,)) - c.execute("UPDATE `calc_definitions` SET `status`='deleted' WHERE `word`=? ;", (wordId,)) - - c.close() - return True - - def getChannelId(self, channel): - c = self.sql.getCursor() - c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,)) - rows = c.fetchall() - if len(rows)==0: - c.execute("INSERT INTO `calc_channels` (`channel`) VALUES (?);", (channel,)) - c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,)) - rows = c.fetchall() - chId = rows[0]["id"] - c.close() - return chId + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + + self.hooks=[ModuleHook("PRIVMSG", self.calc)] + self.timers={} + + self.sqlite = self.bot.getBestModuleForService("sqlite") + if self.sqlite==None: + self.log.error("Calc: SQLIite service is required.") + return + + self.sql = self.sqlite.opendb("calc.db") + + if not self.sql.tableExists("calc_addedby"): + c = self.sql.getCursor() + c.execute(""" + CREATE TABLE `calc_addedby` ( + `id` INTEGER PRIMARY KEY, + `username` varchar(32), + `userhost` varchar(128) + ) ; + """) + c.close() + if not self.sql.tableExists("calc_channels"): + c = self.sql.getCursor() + c.execute(""" + CREATE TABLE `calc_channels` ( + `id` INTEGER PRIMARY KEY, + `channel` varchar(32) + ) ; + """) + if not self.sql.tableExists("calc_definitions"): + c = self.sql.getCursor() + c.execute(""" + CREATE TABLE `calc_definitions` ( + `id` INTEGER PRIMARY KEY, + `word` INTEGET, + `definition` varchar(512), + `addedby` INTEGER, + `date` timestamp, + `status` varchar(16) + ) ; + """) + if not self.sql.tableExists("calc_words"): + c = self.sql.getCursor() + c.execute(""" + CREATE TABLE `calc_words` ( + `id` INTEGER PRIMARY KEY, + `channel` INTEGER, + `word` varchar(512), + `status` varchar(32), + unique(`channel`,`word`) + ); + """) + c.close() + + def timeSince(self, channel, timetype): + if not channel in self.timers: + self.createDefaultTimers(channel) + return time.time()-self.timers[channel][timetype] + + def updateTimeSince(self, channel, timetype): + if not channel in self.timers: + self.createDefaultTimers(channel) + self.timers[channel][timetype] = time.time() + + def createDefaultTimers(self, channel): + self.timers[channel]={"add":0, "calc":0, "calcspec":0, "match":0} + + def remainingToStr(self, total, elasped): + remaining = total-elasped + minutes = int(math.floor(remaining/60)) + seconds = int(remaining - (minutes*60)) + return "Please wait %s minute(s) and %s second(s)." % (minutes, seconds) + + def calc(self, args, prefix, trailing): + # Channel only + if not args[0][0]=="#": + return + sender = self.bot.decodePrefix(prefix) + + foundCalc = False + commandFound = "" + for cmd in self.config["cmd_calc"]: + if trailing[0:len(cmd)] == cmd and ( len(trailing) == len(cmd) or (trailing[len(cmd):len(cmd)+1] in [" ", "="])): + commandFound=cmd + foundCalc=True + + if foundCalc: + calcCmd = trailing[len(cmd)-1:].strip() + if "=" in calcCmd[1:]: + " Add a new calc " + calcWord, calcDefinition = calcCmd.split("=", 1) + calcWord = calcWord.strip() + calcDefinition = calcDefinition.strip() + if self.config["allowDelete"] and calcDefinition == "": + result = self.deleteCalc(args[0], calcWord) + if result: + self.bot.act_PRIVMSG(args[0], "Calc deleted, %s." % sender.nick) + else: + self.bot.act_PRIVMSG(args[0], "Sorry %s, I don't know what '%s' is." % (sender.nick, calcWord)) + else: + if self.config["delaySubmit"]>0 and self.timeSince(args[0], "add") < self.config["delaySubmit"]: + self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delaySubmit"], self.timeSince(args[0], "add"))) + else: + self.addNewCalc(args[0], calcWord, calcDefinition, prefix) + self.bot.act_PRIVMSG(args[0], "Thanks for the info, %s." % sender.nick) + self.updateTimeSince(args[0], "add") + elif len(calcCmd)>0: + " Lookup the word in calcCmd " + + if self.config["delayCalcSpecific"]>0 and self.timeSince(args[0], "calcspec") < self.config["delayCalcSpecific"]: + self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayCalcSpecific"], self.timeSince(args[0], "calcspec"))) + else: + randCalc = self.getSpecificCalc(args[0], calcCmd) + if randCalc==None: + self.bot.act_PRIVMSG(args[0], "Sorry %s, I don't know what '%s' is." % (sender.nick, calcCmd)) + else: + self.bot.act_PRIVMSG(args[0], "%s \x03= %s \x0314[added by: %s]" % (randCalc["word"], randCalc["definition"], randCalc["by"])) + self.updateTimeSince(args[0], "calcspec") + else: + if self.config["delayCalc"]>0 and self.timeSince(args[0], "calc") < self.config["delayCalc"]: + self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayCalc"], self.timeSince(args[0], "calc"))) + else: + randCalc = self.getRandomCalc(args[0]) + if randCalc == None: + self.bot.act_PRIVMSG(args[0], "This channel has no calcs, %s :(" % (sender.nick,)) + else: + self.bot.act_PRIVMSG(args[0], "%s \x03= %s \x0314[added by: %s]" % (randCalc["word"], randCalc["definition"], randCalc["by"])) + self.updateTimeSince(args[0], "calc") + return + + cmd = self.bot.messageHasCommand(self.config["cmd_match"], trailing, True) + if cmd: + if self.config["delayMatch"]>0 and self.timeSince(args[0], "match") < self.config["delayMatch"]: + self.bot.act_PRIVMSG(sender.nick, self.remainingToStr(self.config["delayMatch"], self.timeSince(args[0], "match"))) + else: + term = cmd.args_str + if term.strip()=='': + return + c = self.sql.getCursor() + channelId = self.getChannelId(args[0]) + c.execute("SELECT * FROM `calc_words` WHERE `word` LIKE ? AND `channel`=? ORDER BY `word` ASC ;", ("%%"+term+"%%", channelId)) + rows = c.fetchall() + if len(rows)==0: + self.bot.act_PRIVMSG(args[0], "%s: Sorry, no matches" % sender.nick) + else: + matches = [] + for row in rows[0:10]: + if row == None: + break + matches.append(row["word"]) + self.bot.act_PRIVMSG(args[0], "%s: %s match%s (%s\x03)" % (sender.nick, len(matches), "es" if len(matches)>1 else "", ", \x03".join(matches) )) + self.updateTimeSince(args[0], "match") + + def addNewCalc(self, channel, word, definition, prefix): + sender = self.bot.decodePrefix(prefix) + + " Find the channel ID" + channelId = self.getChannelId(channel) + + " Check if we need to add a user" + c = self.sql.getCursor() + name = sender.nick + host = sender.hostname + c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host)) + rows = c.fetchall() + if len(rows)==0: + c.execute("INSERT INTO `calc_addedby` (`username`, `userhost`) VALUES (?, ?) ;", (name, host,)) + c.execute("SELECT * FROM `calc_addedby` WHERE `username`=? AND `userhost`=? ;", (name, host)) + rows = c.fetchall() + addedId = rows[0]["id"] + + " Check if the word exists" + c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word)) + rows = c.fetchall() + if len(rows)==0: + c.execute("INSERT INTO `calc_words` (`channel`, `word`, `status`) VALUES (?, ?, ?) ;", (channelId, word, 'approved')) + c.execute("SELECT * FROM `calc_words` WHERE `channel`=? AND `word`=? ;", (channelId, word)) + rows = c.fetchall() + wordId = rows[0]["id"] + " Add definition " + c.execute("INSERT INTO `calc_definitions` (`word`, `definition`, `addedby`, `date`, `status`) VALUES (?, ?, ?, ?, ?) ;", (wordId, definition, addedId, datetime.datetime.now(), 'approved',)) + c.close() + pass + + def getSpecificCalc(self, channel, word): + c = self.sql.getCursor() + channelId = self.getChannelId(channel) + c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE `cdq`.`word`=`cw`.`id` AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as `definitionId` FROM `calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' AND `cw`.`word`=? COLLATE NOCASE ORDER BY RANDOM() LIMIT 1 ;", (channelId, word.lower())) + word = c.fetchone() + + if word == None: + return None + + c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` ON `ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], )) + + who = c.fetchone() + + if who == None: + return None + + c.close() + return {"word":word["word"], "definition":who["definition"], "by":who["username"]} + + + def getRandomCalc(self, channel): + c = self.sql.getCursor() + channelId = self.getChannelId(channel) + + for i in range(0, 5): + c.execute("SELECT `cw`.`word`, (SELECT `cdq`.`id` FROM `calc_definitions` `cdq` WHERE `cdq`.`word`=`cw`.`id` AND `cdq`.`status`='approved' ORDER BY `cdq`.`date` DESC LIMIT 1) as `definitionId` FROM `calc_words` `cw` WHERE `cw`.`channel`=? AND `cw`.`status`='approved' ORDER BY RANDOM() LIMIT 1 ;", (channelId,)) + word = c.fetchone() + if word == None: + return None + c.execute("SELECT `ca`.`username`, `cd`.`definition` FROM `calc_definitions` `cd` JOIN `calc_addedby` `ca` ON `ca`.`id` = `cd`.`addedby` WHERE `cd`.`id`=? LIMIT 1 ;", (word["definitionId"], )) + + who = c.fetchone() + + if who == None: + continue + + c.close() + return {"word":word["word"], "definition":who["definition"], "by":who["username"]} + + def deleteCalc(self, channel, word): + " Return true if deleted something, false if it doesnt exist" + c = self.sql.getCursor() + channelId = self.getChannelId(channel) + c.execute("SELECT * FROM `calc_words` WHERE `channel`=? and `word`=? ;", (channelId, word)) + rows = c.fetchall() + if len(rows)==0: + c.close() + return False + + wordId = rows[0]["id"] + + #c.execute("DELETE FROM `calc_words` WHERE `id`=? ;", (wordId,)) + #c.execute("DELETE FROM `calc_definitions` WHERE `word`=? ;", (wordId,)) + c.execute("UPDATE `calc_definitions` SET `status`='deleted' WHERE `word`=? ;", (wordId,)) + + c.close() + return True + + def getChannelId(self, channel): + c = self.sql.getCursor() + c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,)) + rows = c.fetchall() + if len(rows)==0: + c.execute("INSERT INTO `calc_channels` (`channel`) VALUES (?);", (channel,)) + c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,)) + rows = c.fetchall() + chId = rows[0]["id"] + c.close() + return chId diff --git a/pyircbot/modules/CryptoWallet.py b/pyircbot/modules/CryptoWallet.py index 0ace884..b301d84 100755 --- a/pyircbot/modules/CryptoWallet.py +++ b/pyircbot/modules/CryptoWallet.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Error - :synopsis: Module to provide a multi-type cryptocurrency wallet + :synopsis: Module to provide a multi-type cryptocurrency wallet .. moduleauthor:: Dave Pedu @@ -12,214 +12,214 @@ import time import hashlib class CryptoWallet(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] - - def gotmsg(self, args, prefix, trailing): - channel = args[0] - if channel[0] == "#": - # Ignore channel messages - pass - else: - self.handlePm(args, prefix, trailing) - - def getMods(self): - return (self.bot.getBestModuleForService("attributes"), self.bot.getBestModuleForService("login"), self.bot.getBestModuleForService("bitcoinrpc")) - - def handle_setaddr(self, args, prefix, trailing, cmd): - usage = ".setaddr
" - attr,login,rpc = self.getMods() - # Check login - if not login.check(prefix.nick, prefix.hostname): - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: Please .login to use this command.") - return - # Check for args - if not len(cmd.args)==2: - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: usage: %s" % usage) - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: usage: .setaddr BTC 1xyWx6X5EABprhe3s9XduNxLn5NCtpSNB") - return - # Check if currency is known - if not rpc.isSupported(cmd.args[0]): - supportedStr = ', '.join(rpc.getSupported()) - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) - return - if len(cmd.args[1])<16 or len(cmd.args[1])>42: - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: '%s' appears to be an invalid address." % (cmd.args[1])) - return - - # Just make sure they have a wallet - self.checkUserHasWallet(prefix.nick, cmd.args[0]) - - # Set their address - attr.setKey(prefix.nick, "cryptowallet-%s-address"%cmd.args[0].lower(), cmd.args[1]) - self.bot.act_PRIVMSG(prefix.nick, ".setaddr: Your address has been saved as: %s. Please verify that this is correct or your coins could be lost." % (cmd.args[1])) - - def handle_getbal(self, args, prefix, trailing, cmd): - usage = ".getbal " - attr,login,rpc = self.getMods() - # Check login - if not login.check(prefix.nick, prefix.hostname): - self.bot.act_PRIVMSG(prefix.nick, ".getbal: Please .login to use this command.") - return - # Check for args - if not len(cmd.args)==1: - self.bot.act_PRIVMSG(prefix.nick, ".getbal: usage: %s" % usage) - self.bot.act_PRIVMSG(prefix.nick, ".getbal: usage: .getbal BTC") - return - # Check if currency is known - if not rpc.isSupported(cmd.args[0]): - supportedStr = ', '.join(rpc.getSupported()) - self.bot.act_PRIVMSG(prefix.nick, ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) - return - - # Just make sure they have a wallet - self.checkUserHasWallet(prefix.nick, cmd.args[0]) - - # fetch RPC and tell them the balance - walletname = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0].lower()) - amount = 0.0 - if walletname: - client = rpc.getRpc(cmd.args[0].lower()) - amount = client.getBal(walletname) - self.bot.act_PRIVMSG(prefix.nick, "Your balance is: %s %s" % (amount, cmd.args[0].upper())) - - def handle_withdraw(self, args, prefix, trailing, cmd): - usage = ".withdraw " - attr,login,rpc = self.getMods() - # Check login - if not login.check(prefix.nick, prefix.hostname): - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Please .login to use this command.") - return - # Check for args - if not len(cmd.args)==2: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: usage: %s" % usage) - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: usage: .getbal BTC 0.035") - return - # Check if currency is known - if not rpc.isSupported(cmd.args[0]): - supportedStr = ', '.join(rpc.getSupported()) - self.bot.act_PRIVMSG(prefix.nick, ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) - return - - # Just make sure they have a wallet - self.checkUserHasWallet(prefix.nick, cmd.args[0]) - - # check that they have a withdraw addr - withdrawaddr = attr.getKey(prefix.nick, "cryptowallet-%s-address"%cmd.args[0].lower()) - if withdrawaddr == None: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: You need to set a withdraw address before withdrawing. Try .setaddr") - return - - # fetch RPC and check balance - walletname = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0].lower()) - balance = 0.0 - - client = rpc.getRpc(cmd.args[0].lower()) - balance = client.getBal(walletname) - withdrawamount = float(cmd.args[1]) - - if balance < withdrawamount or withdrawamount<0: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: You don't have enough %s to withdraw %s" % (cmd.args[0].upper(), withdrawamount)) - return - - if not client.reserve == 0 and balance - client.reserve < withdrawamount: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Withdrawing that much would put you below the reserve (%s %s)." % (client.reserve, cmd.args[0].upper())) - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: The reserve is to cover network transaction fees. To recover it you must close your account. (Talk to an admin)") - return - - # Check if the precision is wrong - if not client.checkPrecision(withdrawamount): - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: %s has maximum %s decimal places" % (cmd.args[0].upper(), client.precision)) - return - - # Create a transaction - txn = client.send(walletname, withdrawaddr, withdrawamount) - if txn: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: %s %s sent to %s. Transaction ID: %s"% (withdrawamount, client.name, withdrawaddr, txn)) - else: - self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Transaction create failed. Maybe the transaction was too large for the network? Try a smaller increment.") - - def handle_getaddr(self, args, prefix, trailing, cmd): - attr,login,rpc = self.getMods() - usage = ".getaddr " - attr,login,rpc = self.getMods() - # Check login - if not login.check(prefix.nick, prefix.hostname): - self.bot.act_PRIVMSG(prefix.nick, ".getaddr: Please .login to use this command.") - return - # Check for args - if not len(cmd.args)==1: - self.bot.act_PRIVMSG(prefix.nick, ".getaddr: usage: %s" % usage) - self.bot.act_PRIVMSG(prefix.nick, ".getaddr: usage: .getaddr BTC") - return - # Check if currency is known - if not rpc.isSupported(cmd.args[0]): - supportedStr = ', '.join(rpc.getSupported()) - self.bot.act_PRIVMSG(prefix.nick, ".getaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) - return - - # Just make sure they have a wallet - self.checkUserHasWallet(prefix.nick, cmd.args[0]) - - walletaddr = attr.getKey(prefix.nick, "cryptowallet-depoaddr-%s"%cmd.args[0].lower()) - self.bot.act_PRIVMSG(prefix.nick, "Your %s deposit address is: %s" % (cmd.args[0].upper(), walletaddr)) - - def handle_curinfo(self, args, prefix, trailing, cmd): - attr,login,rpc = self.getMods() - usage = ".curinfo []" - attr,login,rpc = self.getMods() - - # Check for args - if len(cmd.args)==0: - self.bot.act_PRIVMSG(prefix.nick, ".curinfo: supported currencies: %s. Use '.curinfo BTC' to see details. " % ', '.join([x.upper() for x in rpc.getSupported()])) - return - else: - if not rpc.isSupported(cmd.args[0]): - self.bot.act_PRIVMSG(prefix.nick, ".curinfo: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], ', '.join([x.upper() for x in rpc.getSupported()]))) - return - else: - info = rpc.getInfo(cmd.args[0]) - self.bot.act_PRIVMSG(prefix.nick, ".curinfo: %s - %s. More info: %s" % (args[0], info["name"], info["link"])) - - def checkUserHasWallet(self, username, currency): - # Ensure the user has a wallet in the client - attr,login,rpc = self.getMods() - currency = currency.lower() - if attr.getKey(username, "cryptowallet-account-%s"%currency)==None: - randName = self.md5(str(time.time()))[0:16] - attr.setKey(username, "cryptowallet-account-%s"%currency, randName) - # Generate a deposit addr to nudge the wallet - wallet = rpc.getRpc(currency.lower()) - address = wallet.getAcctAddr(randName) - attr.setKey(username, "cryptowallet-depoaddr-%s"%currency, address) - elif attr.getKey(username, "cryptowallet-depoaddr-%s"%currency)==None: - walletName = attr.getKey(username, "cryptowallet-account-%s"%currency) - wallet = rpc.getRpc(currency.lower()) - address = wallet.getAcctAddr(walletName) - attr.setKey(username, "cryptowallet-depoaddr-%s"%currency, address) - - - def handlePm(self, args, prefix, trailing): - prefix = self.bot.decodePrefix(prefix) - - cmd = self.bot.messageHasCommand(".setaddr", trailing) - if cmd: - self.handle_setaddr(args, prefix, trailing, cmd) - cmd = self.bot.messageHasCommand(".getbal", trailing) - if cmd: - self.handle_getbal(args, prefix, trailing, cmd) - cmd = self.bot.messageHasCommand(".withdraw", trailing) - if cmd: - self.handle_withdraw(args, prefix, trailing, cmd) - cmd = self.bot.messageHasCommand(".getaddr", trailing) - if cmd: - self.handle_getaddr(args, prefix, trailing, cmd) - cmd = self.bot.messageHasCommand(".curinfo", trailing) - if cmd: - self.handle_curinfo(args, prefix, trailing, cmd) - - def md5(self, data): - m = hashlib.md5() - m.update(data.encode("ascii")) - return m.hexdigest() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] + + def gotmsg(self, args, prefix, trailing): + channel = args[0] + if channel[0] == "#": + # Ignore channel messages + pass + else: + self.handlePm(args, prefix, trailing) + + def getMods(self): + return (self.bot.getBestModuleForService("attributes"), self.bot.getBestModuleForService("login"), self.bot.getBestModuleForService("bitcoinrpc")) + + def handle_setaddr(self, args, prefix, trailing, cmd): + usage = ".setaddr
" + attr,login,rpc = self.getMods() + # Check login + if not login.check(prefix.nick, prefix.hostname): + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: Please .login to use this command.") + return + # Check for args + if not len(cmd.args)==2: + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: usage: %s" % usage) + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: usage: .setaddr BTC 1xyWx6X5EABprhe3s9XduNxLn5NCtpSNB") + return + # Check if currency is known + if not rpc.isSupported(cmd.args[0]): + supportedStr = ', '.join(rpc.getSupported()) + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + return + if len(cmd.args[1])<16 or len(cmd.args[1])>42: + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: '%s' appears to be an invalid address." % (cmd.args[1])) + return + + # Just make sure they have a wallet + self.checkUserHasWallet(prefix.nick, cmd.args[0]) + + # Set their address + attr.setKey(prefix.nick, "cryptowallet-%s-address"%cmd.args[0].lower(), cmd.args[1]) + self.bot.act_PRIVMSG(prefix.nick, ".setaddr: Your address has been saved as: %s. Please verify that this is correct or your coins could be lost." % (cmd.args[1])) + + def handle_getbal(self, args, prefix, trailing, cmd): + usage = ".getbal " + attr,login,rpc = self.getMods() + # Check login + if not login.check(prefix.nick, prefix.hostname): + self.bot.act_PRIVMSG(prefix.nick, ".getbal: Please .login to use this command.") + return + # Check for args + if not len(cmd.args)==1: + self.bot.act_PRIVMSG(prefix.nick, ".getbal: usage: %s" % usage) + self.bot.act_PRIVMSG(prefix.nick, ".getbal: usage: .getbal BTC") + return + # Check if currency is known + if not rpc.isSupported(cmd.args[0]): + supportedStr = ', '.join(rpc.getSupported()) + self.bot.act_PRIVMSG(prefix.nick, ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + return + + # Just make sure they have a wallet + self.checkUserHasWallet(prefix.nick, cmd.args[0]) + + # fetch RPC and tell them the balance + walletname = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0].lower()) + amount = 0.0 + if walletname: + client = rpc.getRpc(cmd.args[0].lower()) + amount = client.getBal(walletname) + self.bot.act_PRIVMSG(prefix.nick, "Your balance is: %s %s" % (amount, cmd.args[0].upper())) + + def handle_withdraw(self, args, prefix, trailing, cmd): + usage = ".withdraw " + attr,login,rpc = self.getMods() + # Check login + if not login.check(prefix.nick, prefix.hostname): + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Please .login to use this command.") + return + # Check for args + if not len(cmd.args)==2: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: usage: %s" % usage) + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: usage: .getbal BTC 0.035") + return + # Check if currency is known + if not rpc.isSupported(cmd.args[0]): + supportedStr = ', '.join(rpc.getSupported()) + self.bot.act_PRIVMSG(prefix.nick, ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + return + + # Just make sure they have a wallet + self.checkUserHasWallet(prefix.nick, cmd.args[0]) + + # check that they have a withdraw addr + withdrawaddr = attr.getKey(prefix.nick, "cryptowallet-%s-address"%cmd.args[0].lower()) + if withdrawaddr == None: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: You need to set a withdraw address before withdrawing. Try .setaddr") + return + + # fetch RPC and check balance + walletname = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0].lower()) + balance = 0.0 + + client = rpc.getRpc(cmd.args[0].lower()) + balance = client.getBal(walletname) + withdrawamount = float(cmd.args[1]) + + if balance < withdrawamount or withdrawamount<0: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: You don't have enough %s to withdraw %s" % (cmd.args[0].upper(), withdrawamount)) + return + + if not client.reserve == 0 and balance - client.reserve < withdrawamount: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Withdrawing that much would put you below the reserve (%s %s)." % (client.reserve, cmd.args[0].upper())) + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: The reserve is to cover network transaction fees. To recover it you must close your account. (Talk to an admin)") + return + + # Check if the precision is wrong + if not client.checkPrecision(withdrawamount): + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: %s has maximum %s decimal places" % (cmd.args[0].upper(), client.precision)) + return + + # Create a transaction + txn = client.send(walletname, withdrawaddr, withdrawamount) + if txn: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: %s %s sent to %s. Transaction ID: %s"% (withdrawamount, client.name, withdrawaddr, txn)) + else: + self.bot.act_PRIVMSG(prefix.nick, ".withdraw: Transaction create failed. Maybe the transaction was too large for the network? Try a smaller increment.") + + def handle_getaddr(self, args, prefix, trailing, cmd): + attr,login,rpc = self.getMods() + usage = ".getaddr " + attr,login,rpc = self.getMods() + # Check login + if not login.check(prefix.nick, prefix.hostname): + self.bot.act_PRIVMSG(prefix.nick, ".getaddr: Please .login to use this command.") + return + # Check for args + if not len(cmd.args)==1: + self.bot.act_PRIVMSG(prefix.nick, ".getaddr: usage: %s" % usage) + self.bot.act_PRIVMSG(prefix.nick, ".getaddr: usage: .getaddr BTC") + return + # Check if currency is known + if not rpc.isSupported(cmd.args[0]): + supportedStr = ', '.join(rpc.getSupported()) + self.bot.act_PRIVMSG(prefix.nick, ".getaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + return + + # Just make sure they have a wallet + self.checkUserHasWallet(prefix.nick, cmd.args[0]) + + walletaddr = attr.getKey(prefix.nick, "cryptowallet-depoaddr-%s"%cmd.args[0].lower()) + self.bot.act_PRIVMSG(prefix.nick, "Your %s deposit address is: %s" % (cmd.args[0].upper(), walletaddr)) + + def handle_curinfo(self, args, prefix, trailing, cmd): + attr,login,rpc = self.getMods() + usage = ".curinfo []" + attr,login,rpc = self.getMods() + + # Check for args + if len(cmd.args)==0: + self.bot.act_PRIVMSG(prefix.nick, ".curinfo: supported currencies: %s. Use '.curinfo BTC' to see details. " % ', '.join([x.upper() for x in rpc.getSupported()])) + return + else: + if not rpc.isSupported(cmd.args[0]): + self.bot.act_PRIVMSG(prefix.nick, ".curinfo: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], ', '.join([x.upper() for x in rpc.getSupported()]))) + return + else: + info = rpc.getInfo(cmd.args[0]) + self.bot.act_PRIVMSG(prefix.nick, ".curinfo: %s - %s. More info: %s" % (args[0], info["name"], info["link"])) + + def checkUserHasWallet(self, username, currency): + # Ensure the user has a wallet in the client + attr,login,rpc = self.getMods() + currency = currency.lower() + if attr.getKey(username, "cryptowallet-account-%s"%currency)==None: + randName = self.md5(str(time.time()))[0:16] + attr.setKey(username, "cryptowallet-account-%s"%currency, randName) + # Generate a deposit addr to nudge the wallet + wallet = rpc.getRpc(currency.lower()) + address = wallet.getAcctAddr(randName) + attr.setKey(username, "cryptowallet-depoaddr-%s"%currency, address) + elif attr.getKey(username, "cryptowallet-depoaddr-%s"%currency)==None: + walletName = attr.getKey(username, "cryptowallet-account-%s"%currency) + wallet = rpc.getRpc(currency.lower()) + address = wallet.getAcctAddr(walletName) + attr.setKey(username, "cryptowallet-depoaddr-%s"%currency, address) + + + def handlePm(self, args, prefix, trailing): + prefix = self.bot.decodePrefix(prefix) + + cmd = self.bot.messageHasCommand(".setaddr", trailing) + if cmd: + self.handle_setaddr(args, prefix, trailing, cmd) + cmd = self.bot.messageHasCommand(".getbal", trailing) + if cmd: + self.handle_getbal(args, prefix, trailing, cmd) + cmd = self.bot.messageHasCommand(".withdraw", trailing) + if cmd: + self.handle_withdraw(args, prefix, trailing, cmd) + cmd = self.bot.messageHasCommand(".getaddr", trailing) + if cmd: + self.handle_getaddr(args, prefix, trailing, cmd) + cmd = self.bot.messageHasCommand(".curinfo", trailing) + if cmd: + self.handle_curinfo(args, prefix, trailing, cmd) + + def md5(self, data): + m = hashlib.md5() + m.update(data.encode("ascii")) + return m.hexdigest() diff --git a/pyircbot/modules/CryptoWalletRPC.py b/pyircbot/modules/CryptoWalletRPC.py index 63fe504..bdba03e 100755 --- a/pyircbot/modules/CryptoWalletRPC.py +++ b/pyircbot/modules/CryptoWalletRPC.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: CryptoWalletRPC - :synopsis: Module capable of operating bitcoind-style RPC. Provided as a service. + :synopsis: Module capable of operating bitcoind-style RPC. Provided as a service. .. moduleauthor:: Dave Pedu @@ -14,120 +14,120 @@ from threading import Thread from time import sleep class CryptoWalletRPC(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["bitcoinrpc"] - self.loadConfig() - self.rpcservices={} - - self.loadrpcservices() - - def loadrpcservices(self): - # Create a dict of abbreviation=>BitcoinRPC objcet relation - self.log.info("CryptoWalletRPC: loadrpcservices: connecting to RPCs") - count = len(list(self.config["types"].keys())) - num = 0 - for key in self.config["types"]: - self.rpcservices[key.lower()]=BitcoinRPC(self, key, self.config["types"][key]["host"], self.config["types"][key]["port"], self.config["types"][key]["username"], self.config["types"][key]["password"], self.config["types"][key]["precision"], self.config["types"][key]["reserve"]) - num+=1 - - def getRpc(self, currencyAbbr): - # Return the rpc for the currency requested - # self.getRpc("LTC") -> returns a litecoin rpc instance - currencyAbbr = currencyAbbr.lower() - if currencyAbbr in self.rpcservices: - return self.rpcservices[currencyAbbr] - return None - - def getSupported(self): - # return a list of (appreviatons of) supported currencies - return list(self.rpcservices.keys()) - - def isSupported(self, abbr): - # true/false if currency is supported - supported = self.getSupported() - return abbr.lower() in supported - - def getInfo(self, abbr): - # return the coin's info from config - if self.isSupported(abbr): - return self.config["types"][abbr.upper()] - + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["bitcoinrpc"] + self.loadConfig() + self.rpcservices={} + + self.loadrpcservices() + + def loadrpcservices(self): + # Create a dict of abbreviation=>BitcoinRPC objcet relation + self.log.info("CryptoWalletRPC: loadrpcservices: connecting to RPCs") + count = len(list(self.config["types"].keys())) + num = 0 + for key in self.config["types"]: + self.rpcservices[key.lower()]=BitcoinRPC(self, key, self.config["types"][key]["host"], self.config["types"][key]["port"], self.config["types"][key]["username"], self.config["types"][key]["password"], self.config["types"][key]["precision"], self.config["types"][key]["reserve"]) + num+=1 + + def getRpc(self, currencyAbbr): + # Return the rpc for the currency requested + # self.getRpc("LTC") -> returns a litecoin rpc instance + currencyAbbr = currencyAbbr.lower() + if currencyAbbr in self.rpcservices: + return self.rpcservices[currencyAbbr] + return None + + def getSupported(self): + # return a list of (appreviatons of) supported currencies + return list(self.rpcservices.keys()) + + def isSupported(self, abbr): + # true/false if currency is supported + supported = self.getSupported() + return abbr.lower() in supported + + def getInfo(self, abbr): + # return the coin's info from config + if self.isSupported(abbr): + return self.config["types"][abbr.upper()] + class BitcoinRPC: - def __init__(self, parent, name, host, port, username, password, precision, reserve): - # Store info and connect - self.master = parent - self.name = name - self.host = host - self.port = port - self.username = username - self.password = password - self.precision = precision - self.reserve = reserve - self.log = self.master.log - - # AuthServiceProxy (json client) stored here - self.con = None - # Connect - Thread(target=self.ping).start() - - def getBal(self, acct): - # get a balance of an address or an account - return self.getAcctBal(acct) - - def getAcctAddr(self, acct): - # returns the address for an account. creates if necessary - self.ping() - addrs = self.con.getaddressesbyaccount(acct) - if len(addrs)==0: - return self.con.getnewaddress(acct) - return addrs[0] - - def getAcctBal(self, acct): - # returns an account's balance - self.ping() - return float(self.con.getbalance(acct)) - - def canMove(self, fromAcct, toAcct, amount): - # true or false if fromAcct can afford to give toAcct an amount of coins - balfrom = self.getAcctBal(fromAcct) - return balfrom >= amount - - def move(self, fromAcct, toAcct, amount): - # move coins from one account to another - self.ping() - if self.canMove(fromAcct, toAcct, amount): - return self.con.move(fromAcct, toAcct, amount) - return False - - def send(self, fromAcct, toAddr, amount): - # send coins to an external addr - self.ping() - if self.canMove(fromAcct, toAddr, amount): - return self.con.sendfrom(fromAcct, toAddr, amount) - return False - - def checkPrecision(self, amount): - return amount == round(amount, self.precision) - - def ping(self): - # internal. test connection and connect if necessary - try: - self.con.getinfo() - except: - self.connect() - - def connect(self): - # internal. connect to the service - self.log.debug("CryptoWalletRPC: %s: Connecting to %s:%s" % (self.name, self.host,self.port)) - try: - self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.username, self.password, self.host, self.port)) - except Exception as e: - self.log.debug("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % (self.name, self.host, self.port, str(e))) - return - - self.log.debug("CryptoWalletRPC: %s: Connected to %s:%s" % (self.name, self.host, self.port)) - + def __init__(self, parent, name, host, port, username, password, precision, reserve): + # Store info and connect + self.master = parent + self.name = name + self.host = host + self.port = port + self.username = username + self.password = password + self.precision = precision + self.reserve = reserve + self.log = self.master.log + + # AuthServiceProxy (json client) stored here + self.con = None + # Connect + Thread(target=self.ping).start() + + def getBal(self, acct): + # get a balance of an address or an account + return self.getAcctBal(acct) + + def getAcctAddr(self, acct): + # returns the address for an account. creates if necessary + self.ping() + addrs = self.con.getaddressesbyaccount(acct) + if len(addrs)==0: + return self.con.getnewaddress(acct) + return addrs[0] + + def getAcctBal(self, acct): + # returns an account's balance + self.ping() + return float(self.con.getbalance(acct)) + + def canMove(self, fromAcct, toAcct, amount): + # true or false if fromAcct can afford to give toAcct an amount of coins + balfrom = self.getAcctBal(fromAcct) + return balfrom >= amount + + def move(self, fromAcct, toAcct, amount): + # move coins from one account to another + self.ping() + if self.canMove(fromAcct, toAcct, amount): + return self.con.move(fromAcct, toAcct, amount) + return False + + def send(self, fromAcct, toAddr, amount): + # send coins to an external addr + self.ping() + if self.canMove(fromAcct, toAddr, amount): + return self.con.sendfrom(fromAcct, toAddr, amount) + return False + + def checkPrecision(self, amount): + return amount == round(amount, self.precision) + + def ping(self): + # internal. test connection and connect if necessary + try: + self.con.getinfo() + except: + self.connect() + + def connect(self): + # internal. connect to the service + self.log.debug("CryptoWalletRPC: %s: Connecting to %s:%s" % (self.name, self.host,self.port)) + try: + self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.username, self.password, self.host, self.port)) + except Exception as e: + self.log.debug("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % (self.name, self.host, self.port, str(e))) + return + + self.log.debug("CryptoWalletRPC: %s: Connected to %s:%s" % (self.name, self.host, self.port)) + diff --git a/pyircbot/modules/DogeDice.py b/pyircbot/modules/DogeDice.py index 5661902..d3da0b1 100755 --- a/pyircbot/modules/DogeDice.py +++ b/pyircbot/modules/DogeDice.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: DogeDice - :synopsis: Module to provide a game for gambling Dogecoin + :synopsis: Module to provide a game for gambling Dogecoin .. moduleauthor:: Dave Pedu @@ -16,307 +16,307 @@ import hashlib from threading import Timer class DogeDice(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] - self.loadConfig() - # Load attribute storage - self.attr = self.bot.getBestModuleForService("attributes") - # Load doge RPC - self.doge = self.bot.getBestModuleForService("dogerpc") - # Dict of #channel -> game object - self.games = {} - - def gotMsg(self, args, prefix, trailing): - prefixObj = self.bot.decodePrefix(prefix) - # Ignore messages from users not logged in - loggedinfrom = self.attr.getKey(prefixObj.nick, "loggedinfrom") - if loggedinfrom==None: - # Send them a hint? - return - elif prefixObj.hostname == loggedinfrom: - if args[0][0] == "#": - # create a blank game obj if there isn't one (and whitelisted ? ) - if not args[0] in self.games and (not self.config["channelWhitelistOn"] or (self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"]) ): - self.games[args[0]]=gameObj(self, args[0]) - # Channel message - self.games[args[0]].gotMsg(args, prefix, trailing) - else: - # Private message - #self.games[args[0].gotPrivMsg(args, prefix, trailing) - pass - else: - # Ignore potential spoofing - pass - - def removeGame(self, channel): - del self.games[channel] - - def ondisable(self): - self.log.info("DogeDice: Unload requested, ending games...") - while len(self.games)>0: - first = list(self.games.keys())[0] - self.games[first].gameover() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] + self.loadConfig() + # Load attribute storage + self.attr = self.bot.getBestModuleForService("attributes") + # Load doge RPC + self.doge = self.bot.getBestModuleForService("dogerpc") + # Dict of #channel -> game object + self.games = {} + + def gotMsg(self, args, prefix, trailing): + prefixObj = self.bot.decodePrefix(prefix) + # Ignore messages from users not logged in + loggedinfrom = self.attr.getKey(prefixObj.nick, "loggedinfrom") + if loggedinfrom==None: + # Send them a hint? + return + elif prefixObj.hostname == loggedinfrom: + if args[0][0] == "#": + # create a blank game obj if there isn't one (and whitelisted ? ) + if not args[0] in self.games and (not self.config["channelWhitelistOn"] or (self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"]) ): + self.games[args[0]]=gameObj(self, args[0]) + # Channel message + self.games[args[0]].gotMsg(args, prefix, trailing) + else: + # Private message + #self.games[args[0].gotPrivMsg(args, prefix, trailing) + pass + else: + # Ignore potential spoofing + pass + + def removeGame(self, channel): + del self.games[channel] + + def ondisable(self): + self.log.info("DogeDice: Unload requested, ending games...") + while len(self.games)>0: + first = list(self.games.keys())[0] + self.games[first].gameover() class gameObj: - def __init__(self, master, channel): - self.master = master - self.channel = channel - # Game state - # 0 = waiting for players - # - advertise self? - # - players must be registered and have enough doge for current bet - # 1 = enough players, countdown - # - Last warning to pull out - # 2 = locked in / game setup - # - Move doge from player's wallets to table wallet. kick players if they can't afford - # 3 = start of a round - # - Each player's turn to roll - # 4 = determine winner, move doge - # - if > 10 doge, house fee? - self.step = 0 - - # Bet amount - self.bet = 0.0 - # players list - self.players = [] - # min players - self.minPlayers = 2 - # max players - self.maxPlayers = 4 - # Lobby countdown timer - self.startCountdownTimer = None - # pre-result timer - self.endgameResultTimer = None - # in-game timeout - self.playTimeout = None - # Wallet for this game - self.walletName = None - - def getPlayer(self, nick): - for player in self.players: - if player.nick == nick: - return player - return None - - def gotPrivMsg(self, args, prefix, trailing): - prefix = self.master.bot.decodePrefix(prefix) - pass - - def gotMsg(self, args, prefix, trailing): - prefix = self.master.bot.decodePrefix(prefix) - if self.step == 0 or self.step == 1: - # Join game - cmd = self.master.bot.messageHasCommand(".join", trailing) - if cmd: - if len(self.players)-1 < self.maxPlayers: - if self.getPlayer(prefix.nick)==None: - userWallet = self.master.attr.getKey(prefix.nick, "dogeaccountname") - if userWallet == None: - self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) - return - balance = self.master.doge.getBal(userWallet) - - # check if the room is 'opened' already: - if len(self.players)==0: - # require an amount - if len(cmd.args)==1: - # Check if they have enough coins - try: - bet = float(cmd.args[0]) - except: - return - - if bet < self.master.config["minBet"]: - self.master.bot.act_PRIVMSG(self.channel, "%s: Minimum bet is %s DOGE!" % (prefix.nick, self.master.config["minBet"])) - return - - if balance>=bet: - newPlayer = playerObj(self, prefix.nick) - newPlayer.dogeWalletName = userWallet - self.players.append(newPlayer) - self.bet = bet - self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) - else: - self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) - else: - self.master.bot.act_PRIVMSG(self.channel, "%s: You need to specify a bet amount: .join 10" % (prefix.nick)) - else: - # no amount required - if balance>=self.bet: - newPlayer = playerObj(self, prefix.nick) - newPlayer.dogeWalletName = userWallet - self.players.append(newPlayer) - self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) - if self.canStart() and self.startCountdownTimer == None: - self.initStartCountdown() - self.master.bot.act_PRIVMSG(self.channel, "The game will start in %s seconds! Bet is %s DOGE each!" % (self.master.config["lobbyIdleSeconds"], self.bet)) - else: - self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) - - else: - self.master.bot.act_PRIVMSG(self.channel, "%s: you're already in the game. Quit with .leave" % (prefix.nick)) - else: - self.master.bot.act_PRIVMSG(self.channel, "%s: the game is full (%s/%)! Cannot join." % (prefix.nick, len(self.players), self.maxPlayers)) - # Leave game - cmd = self.master.bot.messageHasCommand(".leave", trailing) - if cmd: - if self.getPlayer(prefix.nick)==None: - self.master.bot.act_PRIVMSG(self.channel, "%s: You're not in the game." % (prefix.nick)) - else: - self.removePlayer(prefix.nick) - self.master.bot.act_PRIVMSG(self.channel, "%s: You have left the game!" % (prefix.nick)) - if not self.canStart() and self.startCountdownTimer: - self.clearTimer(self.startCountdownTimer) - self.startCountdownTimer = None - self.master.bot.act_PRIVMSG(self.channel, "Game start aborted." ) - self.step = 0 - elif self.step == 2: - pass - elif self.step == 3: - # Ignore cmds from people outside the game - player = self.getPlayer(prefix.nick) - if not player: - return - - # handle a .roll - cmd = self.master.bot.messageHasCommand(".roll", trailing) - if cmd and not player.hasRolled: - roll1 = random.randint(1,6) - roll2 = random.randint(1,6) - self.master.bot.act_PRIVMSG(self.channel, "%s rolls %s and %s!" % (prefix.nick, roll1, roll2)) - player.hasRolled = True - player.rollValue = roll1+roll2 - - # Check if all players have rolled - for player in self.players: - if not player.hasRolled: - return - - # start endgame timer - self.step = 4 - self.endgameResultTimer = Timer(2, self.endgameResults) - self.endgameResultTimer.start() - - elif self.step == 4: - pass - - #senderIsOp = self.master.attr.getKey(prefix.nick, "op")=="yes" - def clearTimer(self, timer): - if timer: - timer.cancel() - timer = None - - def removePlayer(self, playerNick): - pos = -1 - for i in range(0, len(self.players)): - if self.players[i].nick == playerNick: - pos = i - break - if pos >= 0: - self.players.pop(pos) - - def canStart(self): - # Return true if the step is 'lobby' mode and player count is OK - return self.step == 0 and len(self.players)>=self.minPlayers - def initStartCountdown(self): - # Start the game-start countdown - self.startCountdownTimer = Timer(self.master.config["lobbyIdleSeconds"], self.lobbyCountdownDone) - self.startCountdownTimer.start() - self.step = 1 - - def lobbyCountdownDone(self): - self.step = 2 - self.master.bot.act_PRIVMSG(self.channel, "Collecting DOGE and starting game.. Type .roll !") - # Make a wallet for this game - self.walletName = "DogeDice-"+self.channel - # Generate an address to 'create' a wallet - self.master.doge.getAcctAddr(self.walletName) - - # Verify and move funds from each player - for player in self.players: - playerBalance = self.master.doge.getAcctBal(player.dogeWalletName) - if playerBalance < self.bet: - self.master.bot.act_PRIVMSG(self.channel, "%s was dropped from the game!") - self.removePlayer(player.nick) - - if len(self.players) <= 1: - self.master.bot.act_PRIVMSG(self.channel, "1 or players left - game over!") - self.resetGame() - return - - # Take doges - for player in self.players: - self.master.doge.move(player.dogeWalletName, self.walletName, self.bet) - - # Pre-game setup (nothing !) - - # Accept game commands - self.step = 3 - - # Start play timeout - self.playTimeout = Timer(30, self.gamePlayTimeoutExpired) - self.playTimeout.start() - - def gamePlayTimeoutExpired(self): - # Time out - return doges - self.master.bot.act_PRIVMSG(self.channel, "Time expired! Returning all doges.") - if self.step == 3: - # In game step. Refund doges - for player in self.players: - self.master.doge.move(self.walletName, player.dogeWalletName, self.bet) - self.resetGame() - - def endgameResults(self): - maxRollNames = [] - maxRollValue = 0 - for player in self.players: - if player.rollValue > maxRollValue: - maxRollNames = [] - maxRollNames.append(player.nick) - maxRollValue = player.rollValue - if player.rollValue == maxRollValue: - if not player.nick in maxRollNames: - maxRollNames.append(player.nick) - - pot = self.master.doge.getAcctBal(self.walletName) - DOGEeachDec = pot/len(maxRollNames) - DOGEeach = math.floor(DOGEeachDec*100000000) / 100000000 - - if len(maxRollNames)==1: - self.master.bot.act_PRIVMSG(self.channel, "We have a winner - %s! Winnings are: %s DOGE" % (maxRollNames[0], DOGEeach)) - else: - self.master.bot.act_PRIVMSG(self.channel, "We have a tie between %s - The take is %s DOGE each" % (' and '.join(maxRollNames), DOGEeach)) - - # Pay out - for nick in maxRollNames: - player = self.getPlayer(nick) - self.master.doge.move(self.walletName, player.dogeWalletName, DOGEeach) - - # the end! - self.resetGame() - - def resetGame(self): - self.clearTimer(self.startCountdownTimer) - self.startCountdownTimer = None - self.clearTimer(self.endgameResultTimer) - self.endgameResultTimer = None - self.clearTimer(self.playTimeout) - self.playTimeout = None - self.master.removeGame(self.channel) - - def gameover(self): - self.gamePlayTimeoutExpired() - + def __init__(self, master, channel): + self.master = master + self.channel = channel + # Game state + # 0 = waiting for players + # - advertise self? + # - players must be registered and have enough doge for current bet + # 1 = enough players, countdown + # - Last warning to pull out + # 2 = locked in / game setup + # - Move doge from player's wallets to table wallet. kick players if they can't afford + # 3 = start of a round + # - Each player's turn to roll + # 4 = determine winner, move doge + # - if > 10 doge, house fee? + self.step = 0 + + # Bet amount + self.bet = 0.0 + # players list + self.players = [] + # min players + self.minPlayers = 2 + # max players + self.maxPlayers = 4 + # Lobby countdown timer + self.startCountdownTimer = None + # pre-result timer + self.endgameResultTimer = None + # in-game timeout + self.playTimeout = None + # Wallet for this game + self.walletName = None + + def getPlayer(self, nick): + for player in self.players: + if player.nick == nick: + return player + return None + + def gotPrivMsg(self, args, prefix, trailing): + prefix = self.master.bot.decodePrefix(prefix) + pass + + def gotMsg(self, args, prefix, trailing): + prefix = self.master.bot.decodePrefix(prefix) + if self.step == 0 or self.step == 1: + # Join game + cmd = self.master.bot.messageHasCommand(".join", trailing) + if cmd: + if len(self.players)-1 < self.maxPlayers: + if self.getPlayer(prefix.nick)==None: + userWallet = self.master.attr.getKey(prefix.nick, "dogeaccountname") + if userWallet == None: + self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) + return + balance = self.master.doge.getBal(userWallet) + + # check if the room is 'opened' already: + if len(self.players)==0: + # require an amount + if len(cmd.args)==1: + # Check if they have enough coins + try: + bet = float(cmd.args[0]) + except: + return + + if bet < self.master.config["minBet"]: + self.master.bot.act_PRIVMSG(self.channel, "%s: Minimum bet is %s DOGE!" % (prefix.nick, self.master.config["minBet"])) + return + + if balance>=bet: + newPlayer = playerObj(self, prefix.nick) + newPlayer.dogeWalletName = userWallet + self.players.append(newPlayer) + self.bet = bet + self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) + else: + self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) + else: + self.master.bot.act_PRIVMSG(self.channel, "%s: You need to specify a bet amount: .join 10" % (prefix.nick)) + else: + # no amount required + if balance>=self.bet: + newPlayer = playerObj(self, prefix.nick) + newPlayer.dogeWalletName = userWallet + self.players.append(newPlayer) + self.master.bot.act_PRIVMSG(self.channel, "%s: You have joined!" % (prefix.nick)) + if self.canStart() and self.startCountdownTimer == None: + self.initStartCountdown() + self.master.bot.act_PRIVMSG(self.channel, "The game will start in %s seconds! Bet is %s DOGE each!" % (self.master.config["lobbyIdleSeconds"], self.bet)) + else: + self.master.bot.act_PRIVMSG(self.channel, "%s: You don't have enough DOGE!" % (prefix.nick)) + + else: + self.master.bot.act_PRIVMSG(self.channel, "%s: you're already in the game. Quit with .leave" % (prefix.nick)) + else: + self.master.bot.act_PRIVMSG(self.channel, "%s: the game is full (%s/%)! Cannot join." % (prefix.nick, len(self.players), self.maxPlayers)) + # Leave game + cmd = self.master.bot.messageHasCommand(".leave", trailing) + if cmd: + if self.getPlayer(prefix.nick)==None: + self.master.bot.act_PRIVMSG(self.channel, "%s: You're not in the game." % (prefix.nick)) + else: + self.removePlayer(prefix.nick) + self.master.bot.act_PRIVMSG(self.channel, "%s: You have left the game!" % (prefix.nick)) + if not self.canStart() and self.startCountdownTimer: + self.clearTimer(self.startCountdownTimer) + self.startCountdownTimer = None + self.master.bot.act_PRIVMSG(self.channel, "Game start aborted." ) + self.step = 0 + elif self.step == 2: + pass + elif self.step == 3: + # Ignore cmds from people outside the game + player = self.getPlayer(prefix.nick) + if not player: + return + + # handle a .roll + cmd = self.master.bot.messageHasCommand(".roll", trailing) + if cmd and not player.hasRolled: + roll1 = random.randint(1,6) + roll2 = random.randint(1,6) + self.master.bot.act_PRIVMSG(self.channel, "%s rolls %s and %s!" % (prefix.nick, roll1, roll2)) + player.hasRolled = True + player.rollValue = roll1+roll2 + + # Check if all players have rolled + for player in self.players: + if not player.hasRolled: + return + + # start endgame timer + self.step = 4 + self.endgameResultTimer = Timer(2, self.endgameResults) + self.endgameResultTimer.start() + + elif self.step == 4: + pass + + #senderIsOp = self.master.attr.getKey(prefix.nick, "op")=="yes" + def clearTimer(self, timer): + if timer: + timer.cancel() + timer = None + + def removePlayer(self, playerNick): + pos = -1 + for i in range(0, len(self.players)): + if self.players[i].nick == playerNick: + pos = i + break + if pos >= 0: + self.players.pop(pos) + + def canStart(self): + # Return true if the step is 'lobby' mode and player count is OK + return self.step == 0 and len(self.players)>=self.minPlayers + def initStartCountdown(self): + # Start the game-start countdown + self.startCountdownTimer = Timer(self.master.config["lobbyIdleSeconds"], self.lobbyCountdownDone) + self.startCountdownTimer.start() + self.step = 1 + + def lobbyCountdownDone(self): + self.step = 2 + self.master.bot.act_PRIVMSG(self.channel, "Collecting DOGE and starting game.. Type .roll !") + # Make a wallet for this game + self.walletName = "DogeDice-"+self.channel + # Generate an address to 'create' a wallet + self.master.doge.getAcctAddr(self.walletName) + + # Verify and move funds from each player + for player in self.players: + playerBalance = self.master.doge.getAcctBal(player.dogeWalletName) + if playerBalance < self.bet: + self.master.bot.act_PRIVMSG(self.channel, "%s was dropped from the game!") + self.removePlayer(player.nick) + + if len(self.players) <= 1: + self.master.bot.act_PRIVMSG(self.channel, "1 or players left - game over!") + self.resetGame() + return + + # Take doges + for player in self.players: + self.master.doge.move(player.dogeWalletName, self.walletName, self.bet) + + # Pre-game setup (nothing !) + + # Accept game commands + self.step = 3 + + # Start play timeout + self.playTimeout = Timer(30, self.gamePlayTimeoutExpired) + self.playTimeout.start() + + def gamePlayTimeoutExpired(self): + # Time out - return doges + self.master.bot.act_PRIVMSG(self.channel, "Time expired! Returning all doges.") + if self.step == 3: + # In game step. Refund doges + for player in self.players: + self.master.doge.move(self.walletName, player.dogeWalletName, self.bet) + self.resetGame() + + def endgameResults(self): + maxRollNames = [] + maxRollValue = 0 + for player in self.players: + if player.rollValue > maxRollValue: + maxRollNames = [] + maxRollNames.append(player.nick) + maxRollValue = player.rollValue + if player.rollValue == maxRollValue: + if not player.nick in maxRollNames: + maxRollNames.append(player.nick) + + pot = self.master.doge.getAcctBal(self.walletName) + DOGEeachDec = pot/len(maxRollNames) + DOGEeach = math.floor(DOGEeachDec*100000000) / 100000000 + + if len(maxRollNames)==1: + self.master.bot.act_PRIVMSG(self.channel, "We have a winner - %s! Winnings are: %s DOGE" % (maxRollNames[0], DOGEeach)) + else: + self.master.bot.act_PRIVMSG(self.channel, "We have a tie between %s - The take is %s DOGE each" % (' and '.join(maxRollNames), DOGEeach)) + + # Pay out + for nick in maxRollNames: + player = self.getPlayer(nick) + self.master.doge.move(self.walletName, player.dogeWalletName, DOGEeach) + + # the end! + self.resetGame() + + def resetGame(self): + self.clearTimer(self.startCountdownTimer) + self.startCountdownTimer = None + self.clearTimer(self.endgameResultTimer) + self.endgameResultTimer = None + self.clearTimer(self.playTimeout) + self.playTimeout = None + self.master.removeGame(self.channel) + + def gameover(self): + self.gamePlayTimeoutExpired() + class playerObj: - def __init__(self, game, nick): - self.game = game - self.nick = nick - # Save the player's wallet name - self.dogeWalletName = None - # Set to true after they roll - self.hasRolled = False - # Sum of their dice - self.rollValue = None - + def __init__(self, game, nick): + self.game = game + self.nick = nick + # Save the player's wallet name + self.dogeWalletName = None + # Set to true after they roll + self.hasRolled = False + # Sum of their dice + self.rollValue = None + diff --git a/pyircbot/modules/DogeWallet.py b/pyircbot/modules/DogeWallet.py index 221afaf..a69b72a 100755 --- a/pyircbot/modules/DogeWallet.py +++ b/pyircbot/modules/DogeWallet.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: DogeWallet - :synopsis: Module to provide a Dogecoin wallet + :synopsis: Module to provide a Dogecoin wallet .. moduleauthor:: Dave Pedu @@ -12,161 +12,161 @@ import time import hashlib class DogeWallet(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] - # Load attribute storage - self.attr = self.bot.getBestModuleForService("attributes") - # Load doge RPC - self.doge = self.bot.getBestModuleForService("dogerpc") - - def gotmsg(self, args, prefix, trailing): - channel = args[0] - if channel[0] == "#": - # Ignore channel messages - pass - else: - self.handlePm(args, prefix, trailing) - - def handlePm(self, args, prefix, trailing): - prefix = self.bot.decodePrefix(prefix) - cmd = self.bot.messageHasCommand(".setpass", trailing) - if cmd: - if len(cmd.args)==0: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or \".setpass oldpass newpass\"") - else: - oldpass = self.attr.getKey(prefix.nick, "password") - if oldpass == None: - self.attr.setKey(prefix.nick, "password", cmd.args[0]) - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[0]) - else: - if len(cmd.args)==2: - if cmd.args[0] == oldpass: - self.attr.setKey(prefix.nick, "password", cmd.args[1]) - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[1]) - else: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Old password incorrect.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: You must provide the old password when setting a new one.") - cmd = self.bot.messageHasCommand(".setdogeaddr", trailing) - if cmd: - userpw = self.attr.getKey(prefix.nick, "password") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: You must first set a password with .setpass") - else: - if len(cmd.args)==2: - if userpw == cmd.args[0]: - self.attr.setKey(prefix.nick, "dogeaddr", cmd.args[1]) - self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: Your doge address has been set to \"%s\"." % cmd.args[1]) - # if they don't have a wallet name, we'll make one now - if self.attr.getKey(prefix.nick, "dogeaccountname")==None: - randName = self.md5(str(time.time()))[0:10] - self.attr.setKey(prefix.nick, "dogeaccountname", randName) - - else: - self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: usage: \".setdogeaddr password address\" or \".setdogeaddr mypassword D8VNy3zkMGspffcFSWWqsxx7GrtVsmF2up\"") - - cmd = self.bot.messageHasCommand(".getdogebal", trailing) - if cmd: - userpw = self.attr.getKey(prefix.nick, "password") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: You must first set a password with .setpass") - else: - if len(cmd.args)==1: - if userpw == cmd.args[0]: - ################# - walletname = self.attr.getKey(prefix.nick, "dogeaccountname") - amount = 0.0 - if walletname: - amount = self.doge.getBal(walletname) - - self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: Your balance is: %s DOGE" % amount) - - ################# - else: - self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: usage: \".getdogebal password\"") - - cmd = self.bot.messageHasCommand(".withdrawdoge", trailing) - if cmd: - userpw = self.attr.getKey(prefix.nick, "password") - useraddr = self.attr.getKey(prefix.nick, "dogeaddr") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a password with .setpass") - elif useraddr==None: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a withdraw address .setdogeaddr") - else: - if len(cmd.args)==2: - if userpw == cmd.args[0]: - ################# - walletname = self.attr.getKey(prefix.nick, "dogeaccountname") - walletbal = self.doge.getBal(walletname) - - desiredAmount = float(cmd.args[1]) - - if walletbal >= desiredAmount: - txn = self.doge.send(walletname, useraddr, desiredAmount) - if txn: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: %s DOGE sent to %s. Transaction ID: %s"% (desiredAmount, useraddr, txn)) - else: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: Unable to create transaction. Please contact an Operator.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You only have %s DOGE. You cannot withdraw %s DOGE." % (walletbal, desiredAmount)) - ################# - else: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: usage: \".withdrawdoge password amount\" - \".withdrawdoge mypassword 5.0\" - ") - - cmd = self.bot.messageHasCommand(".getdepositaddr", trailing) - if cmd: - userpw = self.attr.getKey(prefix.nick, "password") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: You must first set a password with .setpass") - else: - if len(cmd.args)==1: - if userpw == cmd.args[0]: - ################# - walletname = self.attr.getKey(prefix.nick, "dogeaccountname") - addr = self.doge.getAcctAddr(walletname) - self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: Your deposit address is: %s" % addr) - ################# - else: - self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: usage: \".getdepositaddr password\"") - - - - cmd = self.bot.messageHasCommand(".login", trailing) - if cmd: - userpw = self.attr.getKey(prefix.nick, "password") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") - else: - if len(cmd.args)==1: - if userpw == cmd.args[0]: - ################# - self.attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) - self.bot.act_PRIVMSG(prefix.nick, ".login: You have been logged in from: %s" % prefix.hostname) - ################# - else: - self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"") - cmd = self.bot.messageHasCommand(".logout", trailing) - if cmd: - loggedin = self.attr.getKey(prefix.nick, "loggedinfrom") - if loggedin == None: - self.bot.act_PRIVMSG(prefix.nick, ".logout: You must first be logged in") - else: - self.attr.setKey(prefix.nick, "loggedinfrom", None) - self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.") - - def md5(self, data): - m = hashlib.md5() - m.update(data.encode("ascii")) - return m.hexdigest() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] + # Load attribute storage + self.attr = self.bot.getBestModuleForService("attributes") + # Load doge RPC + self.doge = self.bot.getBestModuleForService("dogerpc") + + def gotmsg(self, args, prefix, trailing): + channel = args[0] + if channel[0] == "#": + # Ignore channel messages + pass + else: + self.handlePm(args, prefix, trailing) + + def handlePm(self, args, prefix, trailing): + prefix = self.bot.decodePrefix(prefix) + cmd = self.bot.messageHasCommand(".setpass", trailing) + if cmd: + if len(cmd.args)==0: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or \".setpass oldpass newpass\"") + else: + oldpass = self.attr.getKey(prefix.nick, "password") + if oldpass == None: + self.attr.setKey(prefix.nick, "password", cmd.args[0]) + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[0]) + else: + if len(cmd.args)==2: + if cmd.args[0] == oldpass: + self.attr.setKey(prefix.nick, "password", cmd.args[1]) + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[1]) + else: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Old password incorrect.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: You must provide the old password when setting a new one.") + cmd = self.bot.messageHasCommand(".setdogeaddr", trailing) + if cmd: + userpw = self.attr.getKey(prefix.nick, "password") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: You must first set a password with .setpass") + else: + if len(cmd.args)==2: + if userpw == cmd.args[0]: + self.attr.setKey(prefix.nick, "dogeaddr", cmd.args[1]) + self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: Your doge address has been set to \"%s\"." % cmd.args[1]) + # if they don't have a wallet name, we'll make one now + if self.attr.getKey(prefix.nick, "dogeaccountname")==None: + randName = self.md5(str(time.time()))[0:10] + self.attr.setKey(prefix.nick, "dogeaccountname", randName) + + else: + self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: usage: \".setdogeaddr password address\" or \".setdogeaddr mypassword D8VNy3zkMGspffcFSWWqsxx7GrtVsmF2up\"") + + cmd = self.bot.messageHasCommand(".getdogebal", trailing) + if cmd: + userpw = self.attr.getKey(prefix.nick, "password") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: You must first set a password with .setpass") + else: + if len(cmd.args)==1: + if userpw == cmd.args[0]: + ################# + walletname = self.attr.getKey(prefix.nick, "dogeaccountname") + amount = 0.0 + if walletname: + amount = self.doge.getBal(walletname) + + self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: Your balance is: %s DOGE" % amount) + + ################# + else: + self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: usage: \".getdogebal password\"") + + cmd = self.bot.messageHasCommand(".withdrawdoge", trailing) + if cmd: + userpw = self.attr.getKey(prefix.nick, "password") + useraddr = self.attr.getKey(prefix.nick, "dogeaddr") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a password with .setpass") + elif useraddr==None: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a withdraw address .setdogeaddr") + else: + if len(cmd.args)==2: + if userpw == cmd.args[0]: + ################# + walletname = self.attr.getKey(prefix.nick, "dogeaccountname") + walletbal = self.doge.getBal(walletname) + + desiredAmount = float(cmd.args[1]) + + if walletbal >= desiredAmount: + txn = self.doge.send(walletname, useraddr, desiredAmount) + if txn: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: %s DOGE sent to %s. Transaction ID: %s"% (desiredAmount, useraddr, txn)) + else: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: Unable to create transaction. Please contact an Operator.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You only have %s DOGE. You cannot withdraw %s DOGE." % (walletbal, desiredAmount)) + ################# + else: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: usage: \".withdrawdoge password amount\" - \".withdrawdoge mypassword 5.0\" - ") + + cmd = self.bot.messageHasCommand(".getdepositaddr", trailing) + if cmd: + userpw = self.attr.getKey(prefix.nick, "password") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: You must first set a password with .setpass") + else: + if len(cmd.args)==1: + if userpw == cmd.args[0]: + ################# + walletname = self.attr.getKey(prefix.nick, "dogeaccountname") + addr = self.doge.getAcctAddr(walletname) + self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: Your deposit address is: %s" % addr) + ################# + else: + self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: usage: \".getdepositaddr password\"") + + + + cmd = self.bot.messageHasCommand(".login", trailing) + if cmd: + userpw = self.attr.getKey(prefix.nick, "password") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") + else: + if len(cmd.args)==1: + if userpw == cmd.args[0]: + ################# + self.attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) + self.bot.act_PRIVMSG(prefix.nick, ".login: You have been logged in from: %s" % prefix.hostname) + ################# + else: + self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"") + cmd = self.bot.messageHasCommand(".logout", trailing) + if cmd: + loggedin = self.attr.getKey(prefix.nick, "loggedinfrom") + if loggedin == None: + self.bot.act_PRIVMSG(prefix.nick, ".logout: You must first be logged in") + else: + self.attr.setKey(prefix.nick, "loggedinfrom", None) + self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.") + + def md5(self, data): + m = hashlib.md5() + m.update(data.encode("ascii")) + return m.hexdigest() diff --git a/pyircbot/modules/Error.py b/pyircbot/modules/Error.py index 5fbea22..9bffb89 100644 --- a/pyircbot/modules/Error.py +++ b/pyircbot/modules/Error.py @@ -1,6 +1,6 @@ """ .. module:: Error - :synopsis: Module to deliberately cause an error for testing handling. + :synopsis: Module to deliberately cause an error for testing handling. .. moduleauthor:: Dave Pedu @@ -10,19 +10,19 @@ from pyircbot.modulebase import ModuleBase,ModuleHook class Error(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName) - self.hooks=[ModuleHook("PRIVMSG", self.error)] - - def error(self, args, prefix, trailing): - """If the message recieved from IRC has the string "error" in it, cause a ZeroDivisionError - - :param args: IRC args received - :type args: list - :param prefix: IRC prefix of sender - :type prefix: str - :param trailing: IRC message body - :type trailing: str""" - if "error" in trailing: - print(10/0) + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName) + self.hooks=[ModuleHook("PRIVMSG", self.error)] + + def error(self, args, prefix, trailing): + """If the message recieved from IRC has the string "error" in it, cause a ZeroDivisionError + + :param args: IRC args received + :type args: list + :param prefix: IRC prefix of sender + :type prefix: str + :param trailing: IRC message body + :type trailing: str""" + if "error" in trailing: + print(10/0) diff --git a/pyircbot/modules/GameBase.py b/pyircbot/modules/GameBase.py index d026186..20dcf2f 100755 --- a/pyircbot/modules/GameBase.py +++ b/pyircbot/modules/GameBase.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: GameBase - :synopsis: A codebase for making IRC games + :synopsis: A codebase for making IRC games .. moduleauthor:: Dave Pedu @@ -14,59 +14,59 @@ import time from threading import Timer class GameBase(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] - self.loadConfig() - # Load attribute storage - self.attr = self.bot.getBestModuleForService("attributes") - # Load doge RPC - self.doge = self.bot.getBestModuleForService("dogerpc") - # Dict of #channel -> game object - self.games = {} - - def gotMsg(self, args, prefix, trailing): - prefixObj = self.bot.decodePrefix(prefix) - # Ignore messages from users not logged in - if self.attr.getKey(prefixObj.nick, "loggedinfrom")==None: - # Send them a hint? - return - else: - if args[0][0] == "#": - # create a blank game obj if there isn't one (and whitelisted ? ) - if not args[0] in self.games and (not self.config["channelWhitelistOn"] or (self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"]) ): - self.games[args[0]]=gameObj(self, args[0]) - # Channel message - self.games[args[0]].gotMsg(args, prefix, trailing) - else: - # Private message - #self.games[args[0]].gotPrivMsg(args, prefix, trailing) - pass - - def ondisable(self): - self.log.info("GameBase: Unload requested, ending games...") - for game in self.games: - self.games[game].gameover() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] + self.loadConfig() + # Load attribute storage + self.attr = self.bot.getBestModuleForService("attributes") + # Load doge RPC + self.doge = self.bot.getBestModuleForService("dogerpc") + # Dict of #channel -> game object + self.games = {} + + def gotMsg(self, args, prefix, trailing): + prefixObj = self.bot.decodePrefix(prefix) + # Ignore messages from users not logged in + if self.attr.getKey(prefixObj.nick, "loggedinfrom")==None: + # Send them a hint? + return + else: + if args[0][0] == "#": + # create a blank game obj if there isn't one (and whitelisted ? ) + if not args[0] in self.games and (not self.config["channelWhitelistOn"] or (self.config["channelWhitelistOn"] and args[0][1:] in self.config["channelWhitelist"]) ): + self.games[args[0]]=gameObj(self, args[0]) + # Channel message + self.games[args[0]].gotMsg(args, prefix, trailing) + else: + # Private message + #self.games[args[0]].gotPrivMsg(args, prefix, trailing) + pass + + def ondisable(self): + self.log.info("GameBase: Unload requested, ending games...") + for game in self.games: + self.games[game].gameover() class gameObj: - def __init__(self, master, channel): - self.master = master - self.channel = channel - - def gotPrivMsg(self, args, prefix, trailing): - prefix = self.master.bot.decodePrefix(prefix) - pass - - def gotMsg(self, args, prefix, trailing): - prefix = self.master.bot.decodePrefix(prefix) - pass - - #senderIsOp = self.master.attr.getKey(prefix.nick, "op")=="yes" - def gameover(self): - pass + def __init__(self, master, channel): + self.master = master + self.channel = channel + + def gotPrivMsg(self, args, prefix, trailing): + prefix = self.master.bot.decodePrefix(prefix) + pass + + def gotMsg(self, args, prefix, trailing): + prefix = self.master.bot.decodePrefix(prefix) + pass + + #senderIsOp = self.master.attr.getKey(prefix.nick, "op")=="yes" + def gameover(self): + pass class playerObj: - def __init__(self, game, nick): - self.game = game - self.nick = nick - + def __init__(self, game, nick): + self.game = game + self.nick = nick + diff --git a/pyircbot/modules/Inventory.py b/pyircbot/modules/Inventory.py index 9240c4c..1d03c88 100755 --- a/pyircbot/modules/Inventory.py +++ b/pyircbot/modules/Inventory.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Inventory - :synopsis: Lets the bot hold random items + :synopsis: Lets the bot hold random items .. moduleauthor:: Dave Pedu @@ -11,97 +11,97 @@ from pyircbot.modulebase import ModuleBase,ModuleHook from datetime import datetime class Inventory(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.db = None - serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: - self.log.error("Inventory: Could not find a valid sqlite service provider") - else: - self.log.info("Inventory: Selecting sqlite service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0].opendb("inventory.db") - - if not self.db.tableExists("inventory"): - self.log.info("Inventory: Creating table: inventory") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `inventory` ( - `id` INTEGER PRIMARY KEY, - `date` INTEGER, - `donor` varchar(64), - `item` varchar(64) - ) ;""") - c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.checkInv)] - - def checkInv(self, args, prefix, trailing): - if not args[0][0]=="#": - return - prefixObj = self.bot.decodePrefix(prefix) - cmd = self.bot.messageHasCommand([".have"], trailing, True) - if cmd: - if len(cmd.args)<1: - return - adjective = None - if cmd.args[0] in self.config["adjectives"]: - cmd.args_str = cmd.args_str[len(cmd.args[0]):].strip() - adjective = cmd.args[0] - newItem = cmd.args_str - - if self.has_item(newItem): - self.bot.act_PRIVMSG(args[0], self.config["dupe_msg"] % - {"item":newItem,}) - return - - dropped = self.add_item(prefixObj.nick, newItem) - if len(dropped) > 0: - self.bot.act_PRIVMSG(args[0], self.config["swap_msg"] % - {"adjective":(adjective+" ") if adjective else "", "recv_item":newItem, "drop_item":", ".join(dropped)}) - else: - self.bot.act_PRIVMSG(args[0], self.config["recv_msg"] % - {"item":newItem, "adjective":"these " if newItem[-1:]=="s" else "this "}) - - cmd = self.bot.messageHasCommand([".inventory", ".inv"], trailing) - if cmd: - inv = self.getinventory() - if len(inv)==0: - self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist":"nothing!"}) - else: - self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist":", ".join(inv)}) - - - def has_item(self, itemName): - c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,)) # - row = c.fetchone() - c.close() - return row["num"]>0 - - def add_item(self, donor, itemName): - dropped = [] - c = self.db.query("SELECT * FROM `inventory` ORDER BY RANDOM() LIMIT %s,1000000" % self.config["limit"]) - while True: - row = c.fetchone() - if row == None: - break - dropped.append(row["item"]) - self.db.query("DELETE FROM `inventory` WHERE `id`=?", (row["id"],)).close() - c.close() - - self.db.query("INSERT INTO `inventory` (`date`, `donor`, `item`) VALUES (?, ?, ?)", - (int(datetime.now().timestamp()), donor, itemName)).close() - return dropped - - def getinventory(self): - inv = [] - c = self.db.query("SELECT * FROM `inventory`") - while True: - row = c.fetchone() - if row == None: - break - inv.append(row["item"]) - c.close() - return inv - - def ondisable(self): - self.db.close() - \ No newline at end of file + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.db = None + serviceProviders = self.bot.getmodulesbyservice("sqlite") + if len(serviceProviders)==0: + self.log.error("Inventory: Could not find a valid sqlite service provider") + else: + self.log.info("Inventory: Selecting sqlite service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0].opendb("inventory.db") + + if not self.db.tableExists("inventory"): + self.log.info("Inventory: Creating table: inventory") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `inventory` ( + `id` INTEGER PRIMARY KEY, + `date` INTEGER, + `donor` varchar(64), + `item` varchar(64) + ) ;""") + c.close() + + self.hooks=[ModuleHook("PRIVMSG", self.checkInv)] + + def checkInv(self, args, prefix, trailing): + if not args[0][0]=="#": + return + prefixObj = self.bot.decodePrefix(prefix) + cmd = self.bot.messageHasCommand([".have"], trailing, True) + if cmd: + if len(cmd.args)<1: + return + adjective = None + if cmd.args[0] in self.config["adjectives"]: + cmd.args_str = cmd.args_str[len(cmd.args[0]):].strip() + adjective = cmd.args[0] + newItem = cmd.args_str + + if self.has_item(newItem): + self.bot.act_PRIVMSG(args[0], self.config["dupe_msg"] % + {"item":newItem,}) + return + + dropped = self.add_item(prefixObj.nick, newItem) + if len(dropped) > 0: + self.bot.act_PRIVMSG(args[0], self.config["swap_msg"] % + {"adjective":(adjective+" ") if adjective else "", "recv_item":newItem, "drop_item":", ".join(dropped)}) + else: + self.bot.act_PRIVMSG(args[0], self.config["recv_msg"] % + {"item":newItem, "adjective":"these " if newItem[-1:]=="s" else "this "}) + + cmd = self.bot.messageHasCommand([".inventory", ".inv"], trailing) + if cmd: + inv = self.getinventory() + if len(inv)==0: + self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist":"nothing!"}) + else: + self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist":", ".join(inv)}) + + + def has_item(self, itemName): + c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,)) # + row = c.fetchone() + c.close() + return row["num"]>0 + + def add_item(self, donor, itemName): + dropped = [] + c = self.db.query("SELECT * FROM `inventory` ORDER BY RANDOM() LIMIT %s,1000000" % self.config["limit"]) + while True: + row = c.fetchone() + if row == None: + break + dropped.append(row["item"]) + self.db.query("DELETE FROM `inventory` WHERE `id`=?", (row["id"],)).close() + c.close() + + self.db.query("INSERT INTO `inventory` (`date`, `donor`, `item`) VALUES (?, ?, ?)", + (int(datetime.now().timestamp()), donor, itemName)).close() + return dropped + + def getinventory(self): + inv = [] + c = self.db.query("SELECT * FROM `inventory`") + while True: + row = c.fetchone() + if row == None: + break + inv.append(row["item"]) + c.close() + return inv + + def ondisable(self): + self.db.close() + \ No newline at end of file diff --git a/pyircbot/modules/LMGTFY.py b/pyircbot/modules/LMGTFY.py index 4c4930d..63b6f50 100644 --- a/pyircbot/modules/LMGTFY.py +++ b/pyircbot/modules/LMGTFY.py @@ -2,7 +2,7 @@ """ .. module::LMGTFY - :synopsis: LMGTFY + :synopsis: LMGTFY .. moduleauthor::Nick Krichevsky """ @@ -12,27 +12,27 @@ from pyircbot.modulebase import ModuleBase, ModuleHook BASE_URL = "http://lmgtfy.com/?q=" class LMGTFY(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName) - self.hooks.append(ModuleHook("PRIVMSG", self.handleMessage)) - self.bot = bot + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName) + self.hooks.append(ModuleHook("PRIVMSG", self.handleMessage)) + self.bot = bot - def handleMessage(self, args, prefix, trailing): - channel = args[0] - prefix = self.bot.decodePrefix(prefix) - if self.bot.messageHasCommand(".lmgtfy", trailing): - message = trailing.split(" ")[1:] - link = self.createLink(message) - self.bot.act_PRIVMSG(channel, "%s: %s" % (prefix.nick, link)) - - def createLink(self, message): - finalUrl = BASE_URL - if type(message) == str: - message = message.split(" ") + def handleMessage(self, args, prefix, trailing): + channel = args[0] + prefix = self.bot.decodePrefix(prefix) + if self.bot.messageHasCommand(".lmgtfy", trailing): + message = trailing.split(" ")[1:] + link = self.createLink(message) + self.bot.act_PRIVMSG(channel, "%s: %s" % (prefix.nick, link)) + + def createLink(self, message): + finalUrl = BASE_URL + if type(message) == str: + message = message.split(" ") - for word in message: - finalUrl += urllib.parse.quote(word) - if word != message[-1]: - finalUrl+="+" + for word in message: + finalUrl += urllib.parse.quote(word) + if word != message[-1]: + finalUrl+="+" - return finalUrl + return finalUrl diff --git a/pyircbot/modules/MySQL.py b/pyircbot/modules/MySQL.py index 19e98cb..dc1bfa3 100755 --- a/pyircbot/modules/MySQL.py +++ b/pyircbot/modules/MySQL.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: MySQL - :synopsis: Module providing a mysql type service + :synopsis: Module providing a mysql type service .. moduleauthor:: Dave Pedu @@ -11,108 +11,108 @@ from pyircbot.modulebase import ModuleBase,ModuleHook import sys try: - import MySQLdb #python 2.x + import MySQLdb #python 2.x except: - import pymysql as MySQLdb #python 3.x + import pymysql as MySQLdb #python 3.x class MySQL(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["mysql"] - self.loadConfig() - self.connection = self.getConnection() - - def getConnection(self): - return Connection(self) - + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["mysql"] + self.loadConfig() + self.connection = self.getConnection() + + def getConnection(self): + return Connection(self) + class Connection: - def __init__(self, master): - self.config = master.config - self.log = master.log - self._connect() - - # Check if the table requested exists - def tableExists(self, tablename): - c = self.getCursor() - c.execute("SHOW TABLES;") - tables = c.fetchall() - if len(tables)==0: - return False; - key = list(tables[0].keys())[0] - for table in tables: - if table[key]==tablename: - return True; - return False - - def query(self, queryText, args=()): - """Execute a MySQL query and return the cursor - - :param queryText: the mysql query as a string, using '%s' for token replacement - :type queryText: str - :param args: arguments to be escaped into the query - :type args: tuple - :returns: cursor -- the sql cursor""" - c = self.getCursor() - if len(args)==0: - c.execute(queryText) - else: - c.execute(queryText, args) - return c - - # Returns a cusor object, after checking for connectivity - def getCursor(self): - self.ensureConnected() - if sys.version_info > (3,0): - c = self.connection.cursor(MySQLdb.cursors.DictCursor) - else: - c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) - c.execute("USE `%s`;" % self.config["database"]) - return c - - def escape(self, s): - """Escape a string using the mysql server - - :param s: the string to escape - :type s: str - :returns: str -- the escaped string""" - self.ensureConnected() - return self.connection.escape_string(s) - - def ensureConnected(self): - try: - self.connection.ping() - except: - try: - self.connection.close() - except: - pass - del self.connection - self._connect() - - def ondisable(self): - self.connection.close() - - # Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet) - def _connect(self): - self.log.info("MySQL: Connecting to db host at %s" % self.config["host"]) - self.connection = MySQLdb.connect(host=self.config["host"],user=self.config["username"] ,passwd=self.config["password"]) - self.log.info("MySQL: Connected.") - self.connection.autocommit(True) - c = None - if sys.version_info > (3,0): - c = self.connection.cursor(MySQLdb.cursors.DictCursor) - else: - c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) - - c.execute("SHOW DATABASES") - dblist = c.fetchall() - found = False - for row in dblist: - if row["Database"]==self.config["database"]: - found = True - if not found: - c.execute("CREATE DATABASE `%s`;" % self.config["database"]) - c.execute("USE `%s`;" % self.config["database"]) - c.close() + def __init__(self, master): + self.config = master.config + self.log = master.log + self._connect() + + # Check if the table requested exists + def tableExists(self, tablename): + c = self.getCursor() + c.execute("SHOW TABLES;") + tables = c.fetchall() + if len(tables)==0: + return False; + key = list(tables[0].keys())[0] + for table in tables: + if table[key]==tablename: + return True; + return False + + def query(self, queryText, args=()): + """Execute a MySQL query and return the cursor + + :param queryText: the mysql query as a string, using '%s' for token replacement + :type queryText: str + :param args: arguments to be escaped into the query + :type args: tuple + :returns: cursor -- the sql cursor""" + c = self.getCursor() + if len(args)==0: + c.execute(queryText) + else: + c.execute(queryText, args) + return c + + # Returns a cusor object, after checking for connectivity + def getCursor(self): + self.ensureConnected() + if sys.version_info > (3,0): + c = self.connection.cursor(MySQLdb.cursors.DictCursor) + else: + c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) + c.execute("USE `%s`;" % self.config["database"]) + return c + + def escape(self, s): + """Escape a string using the mysql server + + :param s: the string to escape + :type s: str + :returns: str -- the escaped string""" + self.ensureConnected() + return self.connection.escape_string(s) + + def ensureConnected(self): + try: + self.connection.ping() + except: + try: + self.connection.close() + except: + pass + del self.connection + self._connect() + + def ondisable(self): + self.connection.close() + + # Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet) + def _connect(self): + self.log.info("MySQL: Connecting to db host at %s" % self.config["host"]) + self.connection = MySQLdb.connect(host=self.config["host"],user=self.config["username"] ,passwd=self.config["password"]) + self.log.info("MySQL: Connected.") + self.connection.autocommit(True) + c = None + if sys.version_info > (3,0): + c = self.connection.cursor(MySQLdb.cursors.DictCursor) + else: + c = self.connection.cursor(cursorclass=MySQLdb.cursors.DictCursor) + + c.execute("SHOW DATABASES") + dblist = c.fetchall() + found = False + for row in dblist: + if row["Database"]==self.config["database"]: + found = True + if not found: + c.execute("CREATE DATABASE `%s`;" % self.config["database"]) + c.execute("USE `%s`;" % self.config["database"]) + c.close() diff --git a/pyircbot/modules/NFLLive.py b/pyircbot/modules/NFLLive.py index 9841deb..056b6ac 100644 --- a/pyircbot/modules/NFLLive.py +++ b/pyircbot/modules/NFLLive.py @@ -1,6 +1,6 @@ """ .. module:: NFLLive - :synopsis: Show upcoming NFL games and current scores. + :synopsis: Show upcoming NFL games and current scores. .. moduleauthor:: Dave Pedu @@ -13,213 +13,213 @@ from lxml import etree from datetime import datetime,timedelta class NFLLive(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.cache = None - self.cacheAge=0 - - self.hooks=[ModuleHook(["PRIVMSG"], self.nflitup)] - - def nflitup(self, args, prefix, trailing): - prefix = self.bot.decodePrefix(prefix) - replyTo = prefixObj.nick if not "#" in args[0] else args[0] - - cmd = self.bot.messageHasCommand(".nfl", trailing) - if cmd: - games = self.getNflGamesCached() - msg = [] - - liveGames = [] - gamesLaterToday = [] - gamesToday = [] - gamesUpcoming = [] - gamesEarlierWeek = [] - - # sort games - for game in games["games"]: - if not game["time"]==None: - liveGames.append(game) - elif game["quarter"]=="P" and game["startdate"].day==datetime.now().day: - gamesLaterToday.append(game) - elif game["startdate"].day==datetime.now().day: - gamesToday.append(game) - elif game["startdate"].day > datetime.now().day: - gamesUpcoming.append(game) - else: - gamesEarlierWeek.append(game) - - # create list of formatted games - liveGamesStr = [] - for game in liveGames: - liveGamesStr.append(self.formatGameLive(game)) - liveGamesStr = ", ".join(liveGamesStr) - - gamesLaterTodayStr = [] - for game in gamesLaterToday: - gamesLaterTodayStr.append(self.formatGameFuture(game)) - gamesLaterTodayStr = ", ".join(gamesLaterTodayStr) - - gamesTodayStr = [] - for game in gamesToday: - gamesTodayStr.append(self.formatGamePast(game)) - gamesTodayStr = ", ".join(gamesTodayStr) - - gamesUpcomingStr = [] - for game in gamesUpcoming: - gamesUpcomingStr.append(self.formatGameFuture(game)) - gamesUpcomingStr = ", ".join(gamesUpcomingStr) - - gamesEarlierWeekStr = [] - for game in gamesEarlierWeek: - gamesEarlierWeekStr.append(self.formatGamePast(game)) - gamesEarlierWeekStr = ", ".join(gamesEarlierWeekStr) - - msgPieces = [] - - msgPieces.append("\x02NFL week %s\x02:" % (games["season"]["week"])) - - # Depending on args build the respon pieces - if len(cmd.args)>0 and cmd.args[0]=="today": - if not liveGamesStr == "": - msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) - if not gamesLaterTodayStr == "": - msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr) - if not gamesTodayStr == "": - msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) - elif len(cmd.args)>0 and cmd.args[0]=="live": - if not liveGamesStr == "": - msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) - elif len(cmd.args)>0 and cmd.args[0]=="scores": - if not liveGamesStr == "": - msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) - if not gamesTodayStr == "": - msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) - if not gamesEarlierWeekStr == "": - msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr) - else: - if not liveGamesStr == "": - msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) - if not gamesLaterTodayStr == "": - msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr) - if not gamesTodayStr == "": - msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) - if not gamesEarlierWeekStr == "": - msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr) - if not gamesUpcomingStr == "": - msgPieces.append("\x02Upcoming:\x02 %s" % gamesUpcomingStr) - - # Collaspe the list into a repsonse string. Fix grammar - msg = ", ".join(msgPieces).replace(":, ", ": ") - - # Nothing means there were probably no games - if len(msgPieces)==1: - msg = "No games!" - - if len(msg)>0: - # The message can be long so chunk it into pieces splitting at commas - while len(msg)>0: - piece = msg[0:330] - msg = msg[330:] - while not piece[-1:]=="," and len(msg)>0: - piece+=msg[0:1] - msg = msg[1:] - self.bot.act_PRIVMSG(replyTo, "%s: %s" % (prefix.nick, piece.strip())) - - def formatGameLive(self, game): - c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4 - c_home = 4 if int(game["visitor_score"]) > int(game["home_score"]) else 3 - - return "\x03%s%s(%s)\x03 @ \x03%s%s(%s)\x03 Q%s %s" % ( - c_vis, - game["visitor"], - game["visitor_score"], - c_home, - game["home"], - game["home_score"], - game["quarter"], - game["time"] - ) - - def formatGameFuture(self, game): - return "\x02%s\x02@\x02%s\x02" % ( - game["visitor"], - game["home"] - ) - - def formatGamePast(self, game): - c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4 - c_home = 4 if int(game["visitor_score"]) > int(game["home_score"]) else 3 - - return "\x03%s%s(%s)\x03@\x03%s%s(%s)\x03" % ( - c_vis, - game["visitor"], - game["visitor_score"], - c_home, - game["home"], - game["home_score"] - ) - - def getNflGamesCached(self): - if time()-self.cacheAge > self.config["cache"]: - self.cache = NFLLive.getNflGames() - self.cacheAge = time() - return self.cache - - @staticmethod - def getNflGames(): - result = {} - - # Fetch NFL information as XML - nflxml = get("http://www.nfl.com/liveupdate/scorestrip/ss.xml?random=1413140448433") - doc = etree.fromstring(nflxml.content) - games = doc.xpath("/ss/gms")[0] - - result["season"]={ - "week":games.attrib["w"], - "year":games.attrib["y"], - "type":NFLLive.translateSeasonType(games.attrib["t"]), # R for regular season, probably P for pre (?) - "gameday":int(games.attrib["gd"]), # 1 or 0 for gameday or not (?) - "bph":games.attrib["bph"] # not sure - } - result["games"]=[] - - for game in games.getchildren(): - gameblob = { - "home":game.attrib["h"], - "home_name":game.attrib["hnn"], - "home_score":game.attrib["hs"], - - "visitor":game.attrib["v"], - "visitor_name":game.attrib["vnn"], - "visitor_score":game.attrib["vs"], - - "gametype":game.attrib["gt"], # REGular season, probably P for preseason (?) - "quarter":game.attrib["q"], # P if not started, 1-4, F is finished - "time":game.attrib["k"] if "k" in game.attrib else None, - "id":game.attrib["eid"], - "gamenum":game.attrib["gsis"], - - "starttime":game.attrib["t"], - "startdate":datetime.strptime(game.attrib["eid"][0:-2]+" "+game.attrib["t"], "%Y%m%d %I:%M")+timedelta(hours=12) # NHL provides a 12 hour EST clock with all times PM. Add 12 hours so the datetime obj is PM instead of AM. - } - - # Add 4 more hours to make it GMT - gameblob["startdate_gmt"]=gameblob["startdate"]+timedelta(hours=4) - gameblob["nfl_link"]="http://www.nfl.com/gamecenter/%s/%s/%s%s/%s@%s" % ( - gameblob["id"], - result["season"]["year"], - gameblob["gametype"], - result["season"]["week"], - gameblob["visitor_name"], - gameblob["home_name"]) - - result["games"].append(gameblob) - return result - - @staticmethod - def translateSeasonType(season): - if season=="R": - return "Regular" - if season=="P": - return "Pre" - return season + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.cache = None + self.cacheAge=0 + + self.hooks=[ModuleHook(["PRIVMSG"], self.nflitup)] + + def nflitup(self, args, prefix, trailing): + prefix = self.bot.decodePrefix(prefix) + replyTo = prefixObj.nick if not "#" in args[0] else args[0] + + cmd = self.bot.messageHasCommand(".nfl", trailing) + if cmd: + games = self.getNflGamesCached() + msg = [] + + liveGames = [] + gamesLaterToday = [] + gamesToday = [] + gamesUpcoming = [] + gamesEarlierWeek = [] + + # sort games + for game in games["games"]: + if not game["time"]==None: + liveGames.append(game) + elif game["quarter"]=="P" and game["startdate"].day==datetime.now().day: + gamesLaterToday.append(game) + elif game["startdate"].day==datetime.now().day: + gamesToday.append(game) + elif game["startdate"].day > datetime.now().day: + gamesUpcoming.append(game) + else: + gamesEarlierWeek.append(game) + + # create list of formatted games + liveGamesStr = [] + for game in liveGames: + liveGamesStr.append(self.formatGameLive(game)) + liveGamesStr = ", ".join(liveGamesStr) + + gamesLaterTodayStr = [] + for game in gamesLaterToday: + gamesLaterTodayStr.append(self.formatGameFuture(game)) + gamesLaterTodayStr = ", ".join(gamesLaterTodayStr) + + gamesTodayStr = [] + for game in gamesToday: + gamesTodayStr.append(self.formatGamePast(game)) + gamesTodayStr = ", ".join(gamesTodayStr) + + gamesUpcomingStr = [] + for game in gamesUpcoming: + gamesUpcomingStr.append(self.formatGameFuture(game)) + gamesUpcomingStr = ", ".join(gamesUpcomingStr) + + gamesEarlierWeekStr = [] + for game in gamesEarlierWeek: + gamesEarlierWeekStr.append(self.formatGamePast(game)) + gamesEarlierWeekStr = ", ".join(gamesEarlierWeekStr) + + msgPieces = [] + + msgPieces.append("\x02NFL week %s\x02:" % (games["season"]["week"])) + + # Depending on args build the respon pieces + if len(cmd.args)>0 and cmd.args[0]=="today": + if not liveGamesStr == "": + msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) + if not gamesLaterTodayStr == "": + msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr) + if not gamesTodayStr == "": + msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) + elif len(cmd.args)>0 and cmd.args[0]=="live": + if not liveGamesStr == "": + msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) + elif len(cmd.args)>0 and cmd.args[0]=="scores": + if not liveGamesStr == "": + msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) + if not gamesTodayStr == "": + msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) + if not gamesEarlierWeekStr == "": + msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr) + else: + if not liveGamesStr == "": + msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) + if not gamesLaterTodayStr == "": + msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr) + if not gamesTodayStr == "": + msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr) + if not gamesEarlierWeekStr == "": + msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr) + if not gamesUpcomingStr == "": + msgPieces.append("\x02Upcoming:\x02 %s" % gamesUpcomingStr) + + # Collaspe the list into a repsonse string. Fix grammar + msg = ", ".join(msgPieces).replace(":, ", ": ") + + # Nothing means there were probably no games + if len(msgPieces)==1: + msg = "No games!" + + if len(msg)>0: + # The message can be long so chunk it into pieces splitting at commas + while len(msg)>0: + piece = msg[0:330] + msg = msg[330:] + while not piece[-1:]=="," and len(msg)>0: + piece+=msg[0:1] + msg = msg[1:] + self.bot.act_PRIVMSG(replyTo, "%s: %s" % (prefix.nick, piece.strip())) + + def formatGameLive(self, game): + c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4 + c_home = 4 if int(game["visitor_score"]) > int(game["home_score"]) else 3 + + return "\x03%s%s(%s)\x03 @ \x03%s%s(%s)\x03 Q%s %s" % ( + c_vis, + game["visitor"], + game["visitor_score"], + c_home, + game["home"], + game["home_score"], + game["quarter"], + game["time"] + ) + + def formatGameFuture(self, game): + return "\x02%s\x02@\x02%s\x02" % ( + game["visitor"], + game["home"] + ) + + def formatGamePast(self, game): + c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4 + c_home = 4 if int(game["visitor_score"]) > int(game["home_score"]) else 3 + + return "\x03%s%s(%s)\x03@\x03%s%s(%s)\x03" % ( + c_vis, + game["visitor"], + game["visitor_score"], + c_home, + game["home"], + game["home_score"] + ) + + def getNflGamesCached(self): + if time()-self.cacheAge > self.config["cache"]: + self.cache = NFLLive.getNflGames() + self.cacheAge = time() + return self.cache + + @staticmethod + def getNflGames(): + result = {} + + # Fetch NFL information as XML + nflxml = get("http://www.nfl.com/liveupdate/scorestrip/ss.xml?random=1413140448433") + doc = etree.fromstring(nflxml.content) + games = doc.xpath("/ss/gms")[0] + + result["season"]={ + "week":games.attrib["w"], + "year":games.attrib["y"], + "type":NFLLive.translateSeasonType(games.attrib["t"]), # R for regular season, probably P for pre (?) + "gameday":int(games.attrib["gd"]), # 1 or 0 for gameday or not (?) + "bph":games.attrib["bph"] # not sure + } + result["games"]=[] + + for game in games.getchildren(): + gameblob = { + "home":game.attrib["h"], + "home_name":game.attrib["hnn"], + "home_score":game.attrib["hs"], + + "visitor":game.attrib["v"], + "visitor_name":game.attrib["vnn"], + "visitor_score":game.attrib["vs"], + + "gametype":game.attrib["gt"], # REGular season, probably P for preseason (?) + "quarter":game.attrib["q"], # P if not started, 1-4, F is finished + "time":game.attrib["k"] if "k" in game.attrib else None, + "id":game.attrib["eid"], + "gamenum":game.attrib["gsis"], + + "starttime":game.attrib["t"], + "startdate":datetime.strptime(game.attrib["eid"][0:-2]+" "+game.attrib["t"], "%Y%m%d %I:%M")+timedelta(hours=12) # NHL provides a 12 hour EST clock with all times PM. Add 12 hours so the datetime obj is PM instead of AM. + } + + # Add 4 more hours to make it GMT + gameblob["startdate_gmt"]=gameblob["startdate"]+timedelta(hours=4) + gameblob["nfl_link"]="http://www.nfl.com/gamecenter/%s/%s/%s%s/%s@%s" % ( + gameblob["id"], + result["season"]["year"], + gameblob["gametype"], + result["season"]["week"], + gameblob["visitor_name"], + gameblob["home_name"]) + + result["games"].append(gameblob) + return result + + @staticmethod + def translateSeasonType(season): + if season=="R": + return "Regular" + if season=="P": + return "Pre" + return season diff --git a/pyircbot/modules/NickUser.py b/pyircbot/modules/NickUser.py index ee98551..61c3fc1 100755 --- a/pyircbot/modules/NickUser.py +++ b/pyircbot/modules/NickUser.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: NickUser - :synopsis: A module providing a simple login/logout account service + :synopsis: A module providing a simple login/logout account service .. moduleauthor:: Dave Pedu @@ -12,80 +12,80 @@ import time import hashlib class NickUser(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] - self.services=["login"] - - def check(self, nick, hostname): - attr = self.bot.getBestModuleForService("attributes") - loggedin = attr.getKey(nick, "loggedinfrom") - if hostname==loggedin: - return True - return False - - def ondisable(self): - pass - # TODO: log out all users - - def gotmsg(self, args, prefix, trailing): - channel = args[0] - if channel[0] == "#": - # Ignore channel messages - pass - else: - self.handlePm(args, prefix, trailing) - - def handlePm(self, args, prefix, trailing): - prefix = self.bot.decodePrefix(prefix) - cmd = self.bot.messageHasCommand(".setpass", trailing) - if cmd: - if len(cmd.args)==0: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or \".setpass oldpass newpass\"") - else: - attr = self.bot.getBestModuleForService("attributes") - oldpass = attr.getKey(prefix.nick, "password") - if oldpass == None: - attr.setKey(prefix.nick, "password", cmd.args[0]) - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[0]) - else: - if len(cmd.args)==2: - if cmd.args[0] == oldpass: - attr.setKey(prefix.nick, "password", cmd.args[1]) - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[1]) - else: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: Old password incorrect.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".setpass: You must provide the old password when setting a new one.") - - cmd = self.bot.messageHasCommand(".login", trailing) - if cmd: - attr = self.bot.getBestModuleForService("attributes") - userpw = attr.getKey(prefix.nick, "password") - if userpw==None: - self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") - else: - if len(cmd.args)==1: - if userpw == cmd.args[0]: - ################# - attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) - self.bot.act_PRIVMSG(prefix.nick, ".login: You have been logged in from: %s" % prefix.hostname) - ################# - else: - self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.") - else: - self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"") - cmd = self.bot.messageHasCommand(".logout", trailing) - if cmd: - attr = self.bot.getBestModuleForService("attributes") - loggedin = attr.getKey(prefix.nick, "loggedinfrom") - if loggedin == None: - self.bot.act_PRIVMSG(prefix.nick, ".logout: You must first be logged in") - else: - attr.setKey(prefix.nick, "loggedinfrom", None) - self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.") - - def md5(self, data): - m = hashlib.md5() - m.update(data.encode("ascii")) - return m.hexdigest() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] + self.services=["login"] + + def check(self, nick, hostname): + attr = self.bot.getBestModuleForService("attributes") + loggedin = attr.getKey(nick, "loggedinfrom") + if hostname==loggedin: + return True + return False + + def ondisable(self): + pass + # TODO: log out all users + + def gotmsg(self, args, prefix, trailing): + channel = args[0] + if channel[0] == "#": + # Ignore channel messages + pass + else: + self.handlePm(args, prefix, trailing) + + def handlePm(self, args, prefix, trailing): + prefix = self.bot.decodePrefix(prefix) + cmd = self.bot.messageHasCommand(".setpass", trailing) + if cmd: + if len(cmd.args)==0: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or \".setpass oldpass newpass\"") + else: + attr = self.bot.getBestModuleForService("attributes") + oldpass = attr.getKey(prefix.nick, "password") + if oldpass == None: + attr.setKey(prefix.nick, "password", cmd.args[0]) + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[0]) + else: + if len(cmd.args)==2: + if cmd.args[0] == oldpass: + attr.setKey(prefix.nick, "password", cmd.args[1]) + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Your password has been set to \"%s\"." % cmd.args[1]) + else: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: Old password incorrect.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".setpass: You must provide the old password when setting a new one.") + + cmd = self.bot.messageHasCommand(".login", trailing) + if cmd: + attr = self.bot.getBestModuleForService("attributes") + userpw = attr.getKey(prefix.nick, "password") + if userpw==None: + self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") + else: + if len(cmd.args)==1: + if userpw == cmd.args[0]: + ################# + attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) + self.bot.act_PRIVMSG(prefix.nick, ".login: You have been logged in from: %s" % prefix.hostname) + ################# + else: + self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.") + else: + self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"") + cmd = self.bot.messageHasCommand(".logout", trailing) + if cmd: + attr = self.bot.getBestModuleForService("attributes") + loggedin = attr.getKey(prefix.nick, "loggedinfrom") + if loggedin == None: + self.bot.act_PRIVMSG(prefix.nick, ".logout: You must first be logged in") + else: + attr.setKey(prefix.nick, "loggedinfrom", None) + self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.") + + def md5(self, data): + m = hashlib.md5() + m.update(data.encode("ascii")) + return m.hexdigest() diff --git a/pyircbot/modules/PingResponder.py b/pyircbot/modules/PingResponder.py index dd04377..8e22a0b 100755 --- a/pyircbot/modules/PingResponder.py +++ b/pyircbot/modules/PingResponder.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: PingResponder - :synopsis: Module to repsond to irc server PING requests + :synopsis: Module to repsond to irc server PING requests .. moduleauthor:: Dave Pedu @@ -10,11 +10,11 @@ from pyircbot.modulebase import ModuleBase,ModuleHook class PingResponder(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PING", self.pingrespond)] - def pingrespond(self, args, prefix, trailing): - """Respond to the PING command""" - # got a ping? send it right back - self.bot.act_PONG(trailing) - self.log.info("%s Responded to a ping: %s" % (self.bot.get_nick(), trailing)) + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PING", self.pingrespond)] + def pingrespond(self, args, prefix, trailing): + """Respond to the PING command""" + # got a ping? send it right back + self.bot.act_PONG(trailing) + self.log.info("%s Responded to a ping: %s" % (self.bot.get_nick(), trailing)) diff --git a/pyircbot/modules/RandQuote.py b/pyircbot/modules/RandQuote.py index 600d1ff..fb1f8f4 100755 --- a/pyircbot/modules/RandQuote.py +++ b/pyircbot/modules/RandQuote.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: RandQuote - :synopsis: Log a configurable number of messages and pull up random ones on command + :synopsis: Log a configurable number of messages and pull up random ones on command .. moduleauthor:: Dave Pedu @@ -11,57 +11,57 @@ from pyircbot.modulebase import ModuleBase,ModuleHook from datetime import datetime class RandQuote(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.db = None - serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: - self.log.error("RandQuote: Could not find a valid sqlite service provider") - else: - self.log.info("RandQuote: Selecting sqlite service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0].opendb("randquote.db") - - if not self.db.tableExists("chat"): - self.log.info("RandQuote: Creating table: chat") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `chat` ( - `id` INTEGER PRIMARY KEY, - `date` INTEGER, - `sender` varchar(64), - `message` varchar(2048) - ) ;""") - c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.logquote),ModuleHook("PRIVMSG", self.fetchquotes)] - - def fetchquotes(self, args, prefix, trailing): - if not args[0][0]=="#": - return - prefixObj = self.bot.decodePrefix(prefix) - cmd = self.bot.messageHasCommand([".randomquote", ".randquote", ".rq"], trailing) - if cmd: - c = self.db.query("SELECT * FROM `chat` ORDER BY RANDOM() LIMIT 1;") - row = c.fetchone() - c.close() - if row: - self.bot.act_PRIVMSG(args[0], "<%s> %s" % (row["sender"], row["message"],)) - - def logquote(self, args, prefix, trailing): - if not args[0][0]=="#": - return - prefixObj = self.bot.decodePrefix(prefix) - self.db.query("INSERT INTO `chat` (`date`, `sender`, `message`) VALUES (?, ?, ?)", - (int(datetime.now().timestamp()), prefixObj.nick, trailing)).close() - # Trim quotes - deleteIds = [] - c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"]) - while True: - row = c.fetchone() - if not row: - break - self.db.query("DELETE FROM `chat` WHERE id=?", (row["id"],)).close() - c.close() - - def ondisable(self): - self.db.close() - \ No newline at end of file + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.db = None + serviceProviders = self.bot.getmodulesbyservice("sqlite") + if len(serviceProviders)==0: + self.log.error("RandQuote: Could not find a valid sqlite service provider") + else: + self.log.info("RandQuote: Selecting sqlite service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0].opendb("randquote.db") + + if not self.db.tableExists("chat"): + self.log.info("RandQuote: Creating table: chat") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `chat` ( + `id` INTEGER PRIMARY KEY, + `date` INTEGER, + `sender` varchar(64), + `message` varchar(2048) + ) ;""") + c.close() + + self.hooks=[ModuleHook("PRIVMSG", self.logquote),ModuleHook("PRIVMSG", self.fetchquotes)] + + def fetchquotes(self, args, prefix, trailing): + if not args[0][0]=="#": + return + prefixObj = self.bot.decodePrefix(prefix) + cmd = self.bot.messageHasCommand([".randomquote", ".randquote", ".rq"], trailing) + if cmd: + c = self.db.query("SELECT * FROM `chat` ORDER BY RANDOM() LIMIT 1;") + row = c.fetchone() + c.close() + if row: + self.bot.act_PRIVMSG(args[0], "<%s> %s" % (row["sender"], row["message"],)) + + def logquote(self, args, prefix, trailing): + if not args[0][0]=="#": + return + prefixObj = self.bot.decodePrefix(prefix) + self.db.query("INSERT INTO `chat` (`date`, `sender`, `message`) VALUES (?, ?, ?)", + (int(datetime.now().timestamp()), prefixObj.nick, trailing)).close() + # Trim quotes + deleteIds = [] + c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"]) + while True: + row = c.fetchone() + if not row: + break + self.db.query("DELETE FROM `chat` WHERE id=?", (row["id"],)).close() + c.close() + + def ondisable(self): + self.db.close() + \ No newline at end of file diff --git a/pyircbot/modules/Remind.py b/pyircbot/modules/Remind.py index c547458..be0b6c0 100644 --- a/pyircbot/modules/Remind.py +++ b/pyircbot/modules/Remind.py @@ -1,6 +1,6 @@ """ .. module:: Remind - :synopsis: A module to support reminders + :synopsis: A module to support reminders .. moduleauthor:: Dave Pedu @@ -14,262 +14,262 @@ import re import pytz class Remind(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - - self.db = None - serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: - self.log.error("Remind: Could not find a valid sqlite service provider") - else: - self.log.info("Remind: Selecting sqlite service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0].opendb("remind.db") - - if not self.db.tableExists("reminders"): - self.log.info("Remind: Creating table: reminders") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `reminders` ( - `id` INTEGER PRIMARY KEY, - `sender` varchar(64), - `senderch` varchar(64), - `when` timestamp, - `message` varchar(2048) - ) ;""") - c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.remindin),ModuleHook("PRIVMSG", self.remindat)] - - self.disabled = False - - # Start monitor thread - self.t = Thread(target=self.monitor_thread) - self.t.daemon=True - self.t.start() - - - def monitor_thread(self): - while True: - sleep(self.config["precision"]) - if self.disabled: - break - self.monitor() - - def monitor(self): - remindPeople = self.db.query("SELECT * FROM `reminders` WHERE `when` < ?", (datetime.now(),)) - reminders = remindPeople.fetchall() - remindPeople.close() - - byrecip = {} - - for reminder in reminders: - if not reminder["sender"] in byrecip: - byrecip[reminder["sender"]]=[] - - byrecip[reminder["sender"]].append(reminder) - - reminders_bych = {} - - for recip in byrecip: - reminders_pm = [] - - for reminder in byrecip[recip]: - if reminder["senderch"]=="": - reminders_pm.append(reminder) - else: - if not reminder["senderch"] in reminders_bych: - reminders_bych[reminder["senderch"]] = [] - reminders_bych[reminder["senderch"]].append(reminder) - self.sendReminders(reminders_pm, recip, recip) - - for channel in reminders_bych: - channelpms_bysender = {} - - for chreminder in reminders_bych[channel]: - if not chreminder["sender"] in channelpms_bysender: - channelpms_bysender[chreminder["sender"]]=[] - channelpms_bysender[chreminder["sender"]].append(chreminder) - - for recip in channelpms_bysender: - self.sendReminders(channelpms_bysender[recip], channel, recip) - - # Delete now that it's sent - for item in reminders: - self.db.query("DELETE FROM `reminders` WHERE `id`=?", (item["id"],)).close() - - def sendReminders(self, reminders, target, nick): - " Send a set of reminders of the same recipient, to them. Collapse down into one message." - reminder_str = [] - for reminder in reminders: - reminder_str.append(reminder["message"]) - reminder_str = ", ".join(reminder_str) - if len(reminder_str)>0: - self.bot.act_PRIVMSG(target, "%s: Reminder: %s" % (nick, reminder_str)) - - def ondisable(self): - self.disabled = True - - def remindat(self, args, prefix, trailing): - prefixObj = self.bot.decodePrefix(prefix) - - replyTo = prefixObj.nick if not "#" in args[0] else args[0] - - # Lots of code borrowed from https://github.com/embolalia/willie/blob/master/willie/modules/remind.py - cmd = self.bot.messageHasCommand([".at", ".remind"], trailing, True) - if cmd: - regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)') - match = regex.match(cmd.args_str) - - try: - hour, minute, second, tz, message = match.groups() - message = message.strip() - - assert not message == "" - - hour = int(hour) - minute = int(minute) - if not second == None: - second = int(second) - - except: - self.bot.act_PRIVMSG(replyTo, "%s: .at - Remind at a time. Example: .at 20:45EST Do your homework!" % prefixObj.nick) - return - - now = datetime.now() - remindAt = datetime.now() - - # if there was timezone, make sure the time we're reminding them at is relative to their timezone - if not tz == None: - try: - theirzone = pytz.timezone(Remind.translateZoneStr(tz)) - except: - self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz)) - return - remindAt = theirzone.localize(remindAt, is_dst=Remind.is_dst(theirzone)) - - # Set the hour and minute we'll remind them at today. If the ends up being in the past, we'll add a day alter - remindAt = remindAt.replace(hour=hour).replace(minute=minute).replace(microsecond=0) - - # Set seconds - if second == None: - remindAt = remindAt.replace(second=0) - else: - remindAt = remindAt.replace(second=second) - - # if there was timezone, convert remindAt to our zone - if not tz == None: - try: - theirzone = pytz.timezone(Remind.translateZoneStr(tz)) - except: - self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz)) - return - remindAt = remindAt.astimezone(pytz.timezone(self.config["mytimezone"])).replace(tzinfo=None) - - # Advance it a day if the time would have been earlier today - while remindAt0: + self.bot.act_PRIVMSG(target, "%s: Reminder: %s" % (nick, reminder_str)) + + def ondisable(self): + self.disabled = True + + def remindat(self, args, prefix, trailing): + prefixObj = self.bot.decodePrefix(prefix) + + replyTo = prefixObj.nick if not "#" in args[0] else args[0] + + # Lots of code borrowed from https://github.com/embolalia/willie/blob/master/willie/modules/remind.py + cmd = self.bot.messageHasCommand([".at", ".remind"], trailing, True) + if cmd: + regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)') + match = regex.match(cmd.args_str) + + try: + hour, minute, second, tz, message = match.groups() + message = message.strip() + + assert not message == "" + + hour = int(hour) + minute = int(minute) + if not second == None: + second = int(second) + + except: + self.bot.act_PRIVMSG(replyTo, "%s: .at - Remind at a time. Example: .at 20:45EST Do your homework!" % prefixObj.nick) + return + + now = datetime.now() + remindAt = datetime.now() + + # if there was timezone, make sure the time we're reminding them at is relative to their timezone + if not tz == None: + try: + theirzone = pytz.timezone(Remind.translateZoneStr(tz)) + except: + self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz)) + return + remindAt = theirzone.localize(remindAt, is_dst=Remind.is_dst(theirzone)) + + # Set the hour and minute we'll remind them at today. If the ends up being in the past, we'll add a day alter + remindAt = remindAt.replace(hour=hour).replace(minute=minute).replace(microsecond=0) + + # Set seconds + if second == None: + remindAt = remindAt.replace(second=0) + else: + remindAt = remindAt.replace(second=second) + + # if there was timezone, convert remindAt to our zone + if not tz == None: + try: + theirzone = pytz.timezone(Remind.translateZoneStr(tz)) + except: + self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz)) + return + remindAt = remindAt.astimezone(pytz.timezone(self.config["mytimezone"])).replace(tzinfo=None) + + # Advance it a day if the time would have been earlier today + while remindAt @@ -12,78 +12,78 @@ import sys import sqlite3 class SQLite(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["sqlite"] - self.loadConfig() - - def opendb(self, dbname): - return Connection(self, dbname) - + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[] + self.services=["sqlite"] + self.loadConfig() + + def opendb(self, dbname): + return Connection(self, dbname) + class Connection: - def __init__(self, master, dbname): - self.master = master - self.log = master.log - self.dbname = dbname - self.connection = None - self._connect() - - # Check if the table requested exists - def tableExists(self, tablename): - c = self.getCursor() - c.execute("SELECT * FROM SQLITE_MASTER WHERE `type`='table' AND `name`=?", (tablename,)) - tables = c.fetchall() - if len(tables)==0: - return False; - return True - - @staticmethod - def dict_factory(cursor, row): - d = {} - for idx, col in enumerate(cursor.description): - d[col[0]] = row[idx] - return d + def __init__(self, master, dbname): + self.master = master + self.log = master.log + self.dbname = dbname + self.connection = None + self._connect() + + # Check if the table requested exists + def tableExists(self, tablename): + c = self.getCursor() + c.execute("SELECT * FROM SQLITE_MASTER WHERE `type`='table' AND `name`=?", (tablename,)) + tables = c.fetchall() + if len(tables)==0: + return False; + return True + + @staticmethod + def dict_factory(cursor, row): + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d - def query(self, queryText, args=()): - """Execute a Sqlite query and return the cursor - - :param queryText: the sqlite query as a string, using '%s' for token replacement - :type queryText: str - :param args: arguments to be escaped into the query - :type args: tuple - :returns: cursor -- the sql cursor""" - c = self.getCursor() - if len(args)==0: - c.execute(queryText) - else: - c.execute(queryText, args) - return c - - # Returns a cusor object, after checking for connectivity - def getCursor(self): - c=self.connection.cursor() - return c - - def escape(self, s): - raise NotImplementedError - - def ondisable(self): - self.connection.close() - - # Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet) - def _connect(self): - self.log.info("Sqlite: opening database %s" % self.master.getFilePath(self.dbname)) - self.connection = sqlite3.connect(self.master.getFilePath(self.dbname), check_same_thread=False) - self.connection.row_factory = Connection.dict_factory - self.connection.isolation_level = None - self.log.info("Sqlite: Connected.") - - # Test the connection - c = self.connection.cursor() - derp=c.execute("SELECT * FROM SQLITE_MASTER") - c.close() - - def close(self): - self.connection.close() + def query(self, queryText, args=()): + """Execute a Sqlite query and return the cursor + + :param queryText: the sqlite query as a string, using '%s' for token replacement + :type queryText: str + :param args: arguments to be escaped into the query + :type args: tuple + :returns: cursor -- the sql cursor""" + c = self.getCursor() + if len(args)==0: + c.execute(queryText) + else: + c.execute(queryText, args) + return c + + # Returns a cusor object, after checking for connectivity + def getCursor(self): + c=self.connection.cursor() + return c + + def escape(self, s): + raise NotImplementedError + + def ondisable(self): + self.connection.close() + + # Connects to the database server, and selects a database (Or attempts to create it if it doesn't exist yet) + def _connect(self): + self.log.info("Sqlite: opening database %s" % self.master.getFilePath(self.dbname)) + self.connection = sqlite3.connect(self.master.getFilePath(self.dbname), check_same_thread=False) + self.connection.row_factory = Connection.dict_factory + self.connection.isolation_level = None + self.log.info("Sqlite: Connected.") + + # Test the connection + c = self.connection.cursor() + derp=c.execute("SELECT * FROM SQLITE_MASTER") + c.close() + + def close(self): + self.connection.close() diff --git a/pyircbot/modules/Seen.py b/pyircbot/modules/Seen.py index 0a78212..6676aad 100755 --- a/pyircbot/modules/Seen.py +++ b/pyircbot/modules/Seen.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Seen - :synopsis: Provides !seen + :synopsis: Provides !seen .. moduleauthor:: Dave Pedu @@ -12,61 +12,61 @@ import sqlite3 import time class Seen(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName) - self.hooks=[ModuleHook("PRIVMSG", self.lastSeen)] - self.loadConfig() - # if the database doesnt exist, it will be created - sql = self.getSql() - c=sql.cursor() - # check if our table exists - c.execute("SELECT * FROM SQLITE_MASTER WHERE `type`='table' AND `name`='seen'") - if len(c.fetchall())==0: - self.log.info("Seen: Creating database") - # if no, create it. - c.execute("CREATE TABLE `seen` (`nick` VARCHAR(32), `date` INTEGER, PRIMARY KEY(`nick`))"); - self.x = "asdf" - - def lastSeen(self, args, prefix, trailing): - # using a message to update last seen, also, the .seen query - prefixObj = self.bot.decodePrefix(prefix) - nick = prefixObj.nick - sql=self.getSql() - c = sql.cursor() - # update or add the user's row - datest=str( time.time()+(int(self.config["add_hours"])*60*60)) - c.execute("REPLACE INTO `seen` (`nick`, `date`) VALUES (?, ?)", (nick.lower(), datest )) - #self.log.info("Seen: %s on %s" % (nick.lower(), datest)) - sql.commit() - if trailing.startswith(".seen"): - cmdargs = trailing.split(" "); - # query the DB for the user - if len(cmdargs)>=2: - searchnic = cmdargs[1].lower(); - c.execute("SELECT * FROM `seen` WHERE `nick`= ? ", [searchnic]) - rows = c.fetchall() - if len(rows)==1: - self.bot.act_PRIVMSG(args[0], "I last saw %s on %s (%s)."% (cmdargs[1], time.strftime("%m/%d/%y at %I:%M %p", time.localtime(rows[0]['date'])), self.config["timezone"])); - else: - self.bot.act_PRIVMSG(args[0], "Sorry, I haven't seen %s!" % cmdargs[1]) - c.close() - - def getSql(self): - # return a SQL reference to the database - path = self.getFilePath('database.sql3') - sql = sqlite3.connect(path); - sql.row_factory = self.dict_factory - return sql - - def dict_factory(self, cursor, row): - # because Lists suck for database results - d = {} - for idx, col in enumerate(cursor.description): - d[col[0]] = row[idx] - return d - - def test(self, arg): - print("TEST: %s" % arg) - print("self.x = %s" % self.x) - return arg - + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName) + self.hooks=[ModuleHook("PRIVMSG", self.lastSeen)] + self.loadConfig() + # if the database doesnt exist, it will be created + sql = self.getSql() + c=sql.cursor() + # check if our table exists + c.execute("SELECT * FROM SQLITE_MASTER WHERE `type`='table' AND `name`='seen'") + if len(c.fetchall())==0: + self.log.info("Seen: Creating database") + # if no, create it. + c.execute("CREATE TABLE `seen` (`nick` VARCHAR(32), `date` INTEGER, PRIMARY KEY(`nick`))"); + self.x = "asdf" + + def lastSeen(self, args, prefix, trailing): + # using a message to update last seen, also, the .seen query + prefixObj = self.bot.decodePrefix(prefix) + nick = prefixObj.nick + sql=self.getSql() + c = sql.cursor() + # update or add the user's row + datest=str( time.time()+(int(self.config["add_hours"])*60*60)) + c.execute("REPLACE INTO `seen` (`nick`, `date`) VALUES (?, ?)", (nick.lower(), datest )) + #self.log.info("Seen: %s on %s" % (nick.lower(), datest)) + sql.commit() + if trailing.startswith(".seen"): + cmdargs = trailing.split(" "); + # query the DB for the user + if len(cmdargs)>=2: + searchnic = cmdargs[1].lower(); + c.execute("SELECT * FROM `seen` WHERE `nick`= ? ", [searchnic]) + rows = c.fetchall() + if len(rows)==1: + self.bot.act_PRIVMSG(args[0], "I last saw %s on %s (%s)."% (cmdargs[1], time.strftime("%m/%d/%y at %I:%M %p", time.localtime(rows[0]['date'])), self.config["timezone"])); + else: + self.bot.act_PRIVMSG(args[0], "Sorry, I haven't seen %s!" % cmdargs[1]) + c.close() + + def getSql(self): + # return a SQL reference to the database + path = self.getFilePath('database.sql3') + sql = sqlite3.connect(path); + sql.row_factory = self.dict_factory + return sql + + def dict_factory(self, cursor, row): + # because Lists suck for database results + d = {} + for idx, col in enumerate(cursor.description): + d[col[0]] = row[idx] + return d + + def test(self, arg): + print("TEST: %s" % arg) + print("self.x = %s" % self.x) + return arg + diff --git a/pyircbot/modules/Services.py b/pyircbot/modules/Services.py index fbac7c2..06b70cf 100755 --- a/pyircbot/modules/Services.py +++ b/pyircbot/modules/Services.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Services - :synopsis: Provides the ability to configure a nickname, password, channel auto-join + :synopsis: Provides the ability to configure a nickname, password, channel auto-join .. moduleauthor:: Dave Pedu @@ -11,54 +11,54 @@ from pyircbot.modulebase import ModuleBase,ModuleHook from time import sleep class Services(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName) - self.hooks=[ModuleHook("_CONNECT", self.doConnect), ModuleHook("433", self.nickTaken), ModuleHook("001", self.initservices), ModuleHook("INVITE", self.invited), ] - self.loadConfig() - self.current_nick = 0 - self.do_ghost = False - def doConnect(self, args, prefix, trailing): - """Hook for when the IRC conneciton is opened""" - self.bot.act_NICK(self.config["user"]["nick"][0]) - self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"], self.config["user"]["realname"]) - - def nickTaken(self, args, prefix, trailing): - """Hook that responds to 433, meaning our nick is taken""" - if self.config["ident"]["ghost"]: - self.do_ghost = True - self.current_nick+=1 - if self.current_nick >= len(self.config["user"]["nick"]): - self.log.critical("Ran out of usernames while selecting backup username!") - return - self.bot.act_NICK(self.config["user"]["nick"][self.current_nick]) + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName) + self.hooks=[ModuleHook("_CONNECT", self.doConnect), ModuleHook("433", self.nickTaken), ModuleHook("001", self.initservices), ModuleHook("INVITE", self.invited), ] + self.loadConfig() + self.current_nick = 0 + self.do_ghost = False + def doConnect(self, args, prefix, trailing): + """Hook for when the IRC conneciton is opened""" + self.bot.act_NICK(self.config["user"]["nick"][0]) + self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"], self.config["user"]["realname"]) + + def nickTaken(self, args, prefix, trailing): + """Hook that responds to 433, meaning our nick is taken""" + if self.config["ident"]["ghost"]: + self.do_ghost = True + self.current_nick+=1 + if self.current_nick >= len(self.config["user"]["nick"]): + self.log.critical("Ran out of usernames while selecting backup username!") + return + self.bot.act_NICK(self.config["user"]["nick"][self.current_nick]) - def initservices(self, args, prefix, trailing): - """Hook that sets our initial nickname""" - if self.do_ghost: - self.bot.act_PRIVMSG(self.config["ident"]["ghost_to"], self.config["ident"]["ghost_cmd"] % {"nick":self.config["user"]["nick"][0], "password":self.config["user"]["password"]}) - sleep(2) - self.bot.act_NICK(self.config["user"]["nick"][0]) - self.do_initservices() - - def invited(self, args, prefix, trailing): - """Hook responding to INVITE channel invitations""" - if trailing.lower() in self.config["privatechannels"]["list"]: - self.log.info("Invited to %s, joining" % trailing) - self.bot.act_JOIN(trailing) - - def do_initservices(self): - """Identify with nickserv and join startup channels""" - " id to nickserv " - if self.config["ident"]["enable"]: - self.bot.act_PRIVMSG(self.config["ident"]["to"], self.config["ident"]["command"] % {"password":self.config["user"]["password"]}) - - " join plain channels " - for channel in self.config["channels"]: - self.log.info("Joining %s" % channel) - self.bot.act_JOIN(channel) - - " request invite for private message channels " - for channel in self.config["privatechannels"]["list"]: - self.log.info("Requesting invite to %s" % channel) - self.bot.act_PRIVMSG(self.config["privatechannels"]["to"], self.config["privatechannels"]["command"] % {"channel":channel}) - + def initservices(self, args, prefix, trailing): + """Hook that sets our initial nickname""" + if self.do_ghost: + self.bot.act_PRIVMSG(self.config["ident"]["ghost_to"], self.config["ident"]["ghost_cmd"] % {"nick":self.config["user"]["nick"][0], "password":self.config["user"]["password"]}) + sleep(2) + self.bot.act_NICK(self.config["user"]["nick"][0]) + self.do_initservices() + + def invited(self, args, prefix, trailing): + """Hook responding to INVITE channel invitations""" + if trailing.lower() in self.config["privatechannels"]["list"]: + self.log.info("Invited to %s, joining" % trailing) + self.bot.act_JOIN(trailing) + + def do_initservices(self): + """Identify with nickserv and join startup channels""" + " id to nickserv " + if self.config["ident"]["enable"]: + self.bot.act_PRIVMSG(self.config["ident"]["to"], self.config["ident"]["command"] % {"password":self.config["user"]["password"]}) + + " join plain channels " + for channel in self.config["channels"]: + self.log.info("Joining %s" % channel) + self.bot.act_JOIN(channel) + + " request invite for private message channels " + for channel in self.config["privatechannels"]["list"]: + self.log.info("Requesting invite to %s" % channel) + self.bot.act_PRIVMSG(self.config["privatechannels"]["to"], self.config["privatechannels"]["command"] % {"channel":channel}) + diff --git a/pyircbot/modules/Tell.py b/pyircbot/modules/Tell.py index 8eac078..f994550 100644 --- a/pyircbot/modules/Tell.py +++ b/pyircbot/modules/Tell.py @@ -1,6 +1,6 @@ """ .. module:: Tell - :synopsis: Deliver a message to a user when they're next seen + :synopsis: Deliver a message to a user when they're next seen .. moduleauthor:: Dave Pedu @@ -11,186 +11,186 @@ import datetime from time import mktime class Tell(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - - self.db = None - serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: - self.log.error("Tell: Could not find a valid sqlite service provider") - else: - self.log.info("Tell: Selecting sqlite service provider: %s" % serviceProviders[0]) - self.db = serviceProviders[0].opendb("tell.db") - - if not self.db.tableExists("tells"): - self.log.info("Remind: Creating table: tells") - c = self.db.query("""CREATE TABLE IF NOT EXISTS `tells` ( - `id` INTEGER PRIMARY KEY, - `sender` varchar(64), - `channel` varchar(64), - `when` INTEGER, - `recip` varchar(64), - `message` varchar(2048) - ) ;""").close() - - # Purge expired tells - self.db.query("DELETE FROM `tells` WHERE `when` - Tell someone something the next time they're seen. Example: .tell antiroach Do your homework!" % prefixObj.nick) - return - - recip = cmd.args[0] - message = ' '.join(cmd.args[1:]).strip() - - if message=="": - self.bot.act_PRIVMSG(replyTo, "%s: .tell - Tell someone something the next time they're seen. Example: .tell antiroach Do your homework!" % prefixObj.nick) - return - - self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES (?, ?, ?, ?, ?);", - ( - prefixObj.nick, - args[0] if "#" in args[0] else "", - int(mktime(datetime.datetime.now().timetuple())), - recip, - message - ) - ).close() - - self.bot.act_PRIVMSG(replyTo, "%s: I'll pass that along." % prefixObj.nick) - - # Copyright (c) Django Software Foundation and individual contributors. - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions are met: - # - # 1. Redistributions of source code must retain the above copyright notice, - # this list of conditions and the following disclaimer. - # - # 2. 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. - # - # 3. Neither the name of Django nor the names of its contributors may be used - # to endorse or promote products derived from this software without - # specific prior written permission. - # - #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. - - @staticmethod - def timesince(d, now=None): - """ - Takes two datetime objects and returns the time between d and now - as a nicely formatted string, e.g. "10 minutes". If d occurs after now, - then "0 minutes" is returned. - - Units used are years, months, weeks, days, hours, and minutes. - Seconds and microseconds are ignored. Up to two adjacent units will be - displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are - possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. - - Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since - """ - chunks = ( - (60 * 60 * 24 * 365, ('year', 'years')), - (60 * 60 * 24 * 30, ('month', 'months')), - (60 * 60 * 24 * 7, ('week', 'weeks')), - (60 * 60 * 24, ('day', 'days')), - (60 * 60, ('hour', 'hours')), - (60, ('minute', 'minutes')) - ) - - # Convert int or float (unix epoch) to datetime.datetime for comparison - if isinstance(d, int) or isinstance(d, float): - d = datetime.datetime.fromtimestamp(d) - - # Convert datetime.date to datetime.datetime for comparison. - if not isinstance(d, datetime.datetime): - d = datetime.datetime(d.year, d.month, d.day) - if now and not isinstance(now, datetime.datetime): - now = datetime.datetime(now.year, now.month, now.day) - - if not now: - now = datetime.datetime.now() - - # ignore microsecond part of 'd' since we removed it from 'now' - delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) - since = delta.days * 24 * 60 * 60 + delta.seconds - if since <= 0: - # d is in the future compared to now, stop processing. - return u'0 ' + 'minutes' - for i, (seconds, name) in enumerate(chunks): - count = since // seconds - if count != 0: - break - - if count == 1: - s = '%(number)d %(type)s' % {'number': count, 'type': name[0]} - else: - s = '%(number)d %(type)s' % {'number': count, 'type': name[1]} - - if i + 1 < len(chunks): - # Now get the second item - seconds2, name2 = chunks[i + 1] - count2 = (since - (seconds * count)) // seconds2 - if count2 != 0: - if count2 == 1: - s += ', %d %s' % (count2, name2[0]) - else: - s += ', %d %s' % (count2, name2[1]) - return s - - @staticmethod - def timeuntil(d, now=None): - """ - Like timesince, but returns a string measuring the time until - the given time. - """ - if not now: - now = datetime.datetime.now() - return timesince(now, d) + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + + self.db = None + serviceProviders = self.bot.getmodulesbyservice("sqlite") + if len(serviceProviders)==0: + self.log.error("Tell: Could not find a valid sqlite service provider") + else: + self.log.info("Tell: Selecting sqlite service provider: %s" % serviceProviders[0]) + self.db = serviceProviders[0].opendb("tell.db") + + if not self.db.tableExists("tells"): + self.log.info("Remind: Creating table: tells") + c = self.db.query("""CREATE TABLE IF NOT EXISTS `tells` ( + `id` INTEGER PRIMARY KEY, + `sender` varchar(64), + `channel` varchar(64), + `when` INTEGER, + `recip` varchar(64), + `message` varchar(2048) + ) ;""").close() + + # Purge expired tells + self.db.query("DELETE FROM `tells` WHERE `when` - Tell someone something the next time they're seen. Example: .tell antiroach Do your homework!" % prefixObj.nick) + return + + recip = cmd.args[0] + message = ' '.join(cmd.args[1:]).strip() + + if message=="": + self.bot.act_PRIVMSG(replyTo, "%s: .tell - Tell someone something the next time they're seen. Example: .tell antiroach Do your homework!" % prefixObj.nick) + return + + self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES (?, ?, ?, ?, ?);", + ( + prefixObj.nick, + args[0] if "#" in args[0] else "", + int(mktime(datetime.datetime.now().timetuple())), + recip, + message + ) + ).close() + + self.bot.act_PRIVMSG(replyTo, "%s: I'll pass that along." % prefixObj.nick) + + # Copyright (c) Django Software Foundation and individual contributors. + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are met: + # + # 1. Redistributions of source code must retain the above copyright notice, + # this list of conditions and the following disclaimer. + # + # 2. 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. + # + # 3. Neither the name of Django nor the names of its contributors may be used + # to endorse or promote products derived from this software without + # specific prior written permission. + # + #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. + + @staticmethod + def timesince(d, now=None): + """ + Takes two datetime objects and returns the time between d and now + as a nicely formatted string, e.g. "10 minutes". If d occurs after now, + then "0 minutes" is returned. + + Units used are years, months, weeks, days, hours, and minutes. + Seconds and microseconds are ignored. Up to two adjacent units will be + displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are + possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. + + Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since + """ + chunks = ( + (60 * 60 * 24 * 365, ('year', 'years')), + (60 * 60 * 24 * 30, ('month', 'months')), + (60 * 60 * 24 * 7, ('week', 'weeks')), + (60 * 60 * 24, ('day', 'days')), + (60 * 60, ('hour', 'hours')), + (60, ('minute', 'minutes')) + ) + + # Convert int or float (unix epoch) to datetime.datetime for comparison + if isinstance(d, int) or isinstance(d, float): + d = datetime.datetime.fromtimestamp(d) + + # Convert datetime.date to datetime.datetime for comparison. + if not isinstance(d, datetime.datetime): + d = datetime.datetime(d.year, d.month, d.day) + if now and not isinstance(now, datetime.datetime): + now = datetime.datetime(now.year, now.month, now.day) + + if not now: + now = datetime.datetime.now() + + # ignore microsecond part of 'd' since we removed it from 'now' + delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) + since = delta.days * 24 * 60 * 60 + delta.seconds + if since <= 0: + # d is in the future compared to now, stop processing. + return u'0 ' + 'minutes' + for i, (seconds, name) in enumerate(chunks): + count = since // seconds + if count != 0: + break + + if count == 1: + s = '%(number)d %(type)s' % {'number': count, 'type': name[0]} + else: + s = '%(number)d %(type)s' % {'number': count, 'type': name[1]} + + if i + 1 < len(chunks): + # Now get the second item + seconds2, name2 = chunks[i + 1] + count2 = (since - (seconds * count)) // seconds2 + if count2 != 0: + if count2 == 1: + s += ', %d %s' % (count2, name2[0]) + else: + s += ', %d %s' % (count2, name2[1]) + return s + + @staticmethod + def timeuntil(d, now=None): + """ + Like timesince, but returns a string measuring the time until + the given time. + """ + if not now: + now = datetime.datetime.now() + return timesince(now, d) diff --git a/pyircbot/modules/TextCDC.py b/pyircbot/modules/TextCDC.py index 6735acd..fc36621 100644 --- a/pyircbot/modules/TextCDC.py +++ b/pyircbot/modules/TextCDC.py @@ -2,7 +2,7 @@ """ .. module::TextCDC - :synopsis: Text Chrisdotcode, right now. + :synopsis: Text Chrisdotcode, right now. .. moduleauthor::Nick Krichevsky """ @@ -14,98 +14,98 @@ from pyircbot.modulebase import ModuleBase, ModuleHook COMMAND_PREFIX = ".text-" class TextCDC(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName) - self.hooks.append(ModuleHook("PRIVMSG",self.handleMessage)) - self.loadConfig() - self.prefixes = [person for person in self.config["people"]] - self.bot = bot - self.timer = None - self.setupTimer() + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName) + self.hooks.append(ModuleHook("PRIVMSG",self.handleMessage)) + self.loadConfig() + self.prefixes = [person for person in self.config["people"]] + self.bot = bot + self.timer = None + self.setupTimer() - def ondisable(self): - if self.timer != None: - self.timer.cancel() + def ondisable(self): + if self.timer != None: + self.timer.cancel() - def handleMessage(self, args, prefix, trailing): - channel = args[0] - p = self.bot.decodePrefix(prefix) - if self.bot.messageHasCommand(".textstatus", trailing): - #self.bot.act_PRIVMSG(channel, "POP: %s" % "Good" if setupPop() != None else "Failed.") - self.bot.act_PRIVMSG(channel, "SMTP: %s" % "Good" if setupSMTP() != None else "Failed.") - for prefix in self.prefixes: - if self.bot.messageHasCommand(COMMAND_PREFIX + prefix, trailing): - email = self.config["people"][prefix]["email-addr"] - message = ' '.join(trailing.split(" ")[1:]) - smtp = self.setupSMTP() - try: - smtp.sendmail(self.config["account"]["auth"]["username"], email, "Subject:\n\n%s -%s" % (message, p.nick)) - smtp.quit() - self.bot.act_PRIVMSG(channel, "Message sent.") - except Exception as e: - self.bot.log.error(str(e)) - self.bot.act_PRIVMSG(channel, "An SMTP Error has Occured") + def handleMessage(self, args, prefix, trailing): + channel = args[0] + p = self.bot.decodePrefix(prefix) + if self.bot.messageHasCommand(".textstatus", trailing): + #self.bot.act_PRIVMSG(channel, "POP: %s" % "Good" if setupPop() != None else "Failed.") + self.bot.act_PRIVMSG(channel, "SMTP: %s" % "Good" if setupSMTP() != None else "Failed.") + for prefix in self.prefixes: + if self.bot.messageHasCommand(COMMAND_PREFIX + prefix, trailing): + email = self.config["people"][prefix]["email-addr"] + message = ' '.join(trailing.split(" ")[1:]) + smtp = self.setupSMTP() + try: + smtp.sendmail(self.config["account"]["auth"]["username"], email, "Subject:\n\n%s -%s" % (message, p.nick)) + smtp.quit() + self.bot.act_PRIVMSG(channel, "Message sent.") + except Exception as e: + self.bot.log.error(str(e)) + self.bot.act_PRIVMSG(channel, "An SMTP Error has Occured") - def setupIMAP(self): - imapObj = None - if self.config["account"]["imap"]["ssl"]: - imapObj = imaplib.IMAP4_SSL(self.config["account"]["imap"]["host"], self.config["account"]["imap"]["port"]) - else: - imapObj = imaplib.IMAP4(self.config["account"]["imap"]["host"], self.config["account"]["imap"]["port"]) - imapObj.login(self.config["account"]["auth"]["username"], self.config["account"]["auth"]["password"]) - resp = imapObj.select("INBOX") - if resp[0] == "OK": - return imapObj - else: - return None + def setupIMAP(self): + imapObj = None + if self.config["account"]["imap"]["ssl"]: + imapObj = imaplib.IMAP4_SSL(self.config["account"]["imap"]["host"], self.config["account"]["imap"]["port"]) + else: + imapObj = imaplib.IMAP4(self.config["account"]["imap"]["host"], self.config["account"]["imap"]["port"]) + imapObj.login(self.config["account"]["auth"]["username"], self.config["account"]["auth"]["password"]) + resp = imapObj.select("INBOX") + if resp[0] == "OK": + return imapObj + else: + return None - def setupSMTP(self): - smtpObj = None - if self.config["account"]["smtp"]["ssl"]: - smtpObj = smtplib.SMTP_SSL(self.config["account"]["smtp"]["host"], self.config["account"]["smtp"]["port"]) - else: - smtpObj = smtplib.SMTP_SSL(self.config["account"]["smtp"]["host"], self.config["account"]["smtp"]["port"]) - if self.config["account"]["smtp"]["authentication"]: - resp = smtpObj.login(self.config["account"]["auth"]["username"], self.config["account"]["auth"]["password"]) - if resp[0] == 235: - return smtpObj - else: - return None - else: - resp = smtpObj.connect() - if resp[0] == 220: - return smtpObj - else: - return None - - def setupTimer(self): - self.timer = Timer(self.config["interval"], self.checkMail, [self.bot, self.config["people"], self.config["output-channels"]],{}) - self.timer.start() - - def checkMail(self, bot, people, channels, imapObj = None): - try: - if imapObj == None: - imapObj = self.setupIMAP() - for person in people: - emailAddr = people[person]["email-addr"] - result = imapObj.search(None, "(FROM \"%s\")" % emailAddr) - if (result[0] == "OK"): - messageIds = result[1][0].decode("utf-8") - if len(messageIds) > 0: - messageIds = messageIds.split(" ") - for messageId in messageIds: - message = imapObj.fetch(messageId, "BODY[TEXT]") - if (message[0] == "OK"): - messageText = message[1][0][1].decode("utf-8").split("-----Original Message-----")[0].rstrip() - for channel in channels: - bot.act_PRIVMSG(channel, "Message from %s: %s" % (person, messageText)) - imapObj.store(messageId, "+FLAGS", "\\Deleted") - else: - raise Exception("SMTP Error. Status was %s, expected OK" % message[0]) - imapObj.logout() - self.setupTimer() - except Exception as e: - if imapObj != None: - imapObj.logout() - self.setupTimer() - raise e + def setupSMTP(self): + smtpObj = None + if self.config["account"]["smtp"]["ssl"]: + smtpObj = smtplib.SMTP_SSL(self.config["account"]["smtp"]["host"], self.config["account"]["smtp"]["port"]) + else: + smtpObj = smtplib.SMTP_SSL(self.config["account"]["smtp"]["host"], self.config["account"]["smtp"]["port"]) + if self.config["account"]["smtp"]["authentication"]: + resp = smtpObj.login(self.config["account"]["auth"]["username"], self.config["account"]["auth"]["password"]) + if resp[0] == 235: + return smtpObj + else: + return None + else: + resp = smtpObj.connect() + if resp[0] == 220: + return smtpObj + else: + return None + + def setupTimer(self): + self.timer = Timer(self.config["interval"], self.checkMail, [self.bot, self.config["people"], self.config["output-channels"]],{}) + self.timer.start() + + def checkMail(self, bot, people, channels, imapObj = None): + try: + if imapObj == None: + imapObj = self.setupIMAP() + for person in people: + emailAddr = people[person]["email-addr"] + result = imapObj.search(None, "(FROM \"%s\")" % emailAddr) + if (result[0] == "OK"): + messageIds = result[1][0].decode("utf-8") + if len(messageIds) > 0: + messageIds = messageIds.split(" ") + for messageId in messageIds: + message = imapObj.fetch(messageId, "BODY[TEXT]") + if (message[0] == "OK"): + messageText = message[1][0][1].decode("utf-8").split("-----Original Message-----")[0].rstrip() + for channel in channels: + bot.act_PRIVMSG(channel, "Message from %s: %s" % (person, messageText)) + imapObj.store(messageId, "+FLAGS", "\\Deleted") + else: + raise Exception("SMTP Error. Status was %s, expected OK" % message[0]) + imapObj.logout() + self.setupTimer() + except Exception as e: + if imapObj != None: + imapObj.logout() + self.setupTimer() + raise e diff --git a/pyircbot/modules/Urban.py b/pyircbot/modules/Urban.py index 2b15190..1d1727d 100755 --- a/pyircbot/modules/Urban.py +++ b/pyircbot/modules/Urban.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Urban - :synopsis: Lookup from urban dictionary + :synopsis: Lookup from urban dictionary .. moduleauthor:: Dave Pedu @@ -12,24 +12,24 @@ import json from requests import get class Urban(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.urban)] - - def urban(self, args, prefix, trailing): - cmd = self.bot.messageHasCommand(".urban", trailing) - if not cmd: - cmd = self.bot.messageHasCommand(".u", trailing) - if cmd and args[0][0:1]=="#": - if cmd.args_str.strip() =="": - self.bot.act_PRIVMSG(args[0], ".u/.urban -- looks up on urbandictionary.com") - return - definitions = get("http://www.urbandictionary.com/iphone/search/define", params={"term":cmd.args_str}).json()["list"] - if len(definitions) == 0: - self.bot.act_PRIVMSG(args[0], "Urban definition: no results!") - else: - defstr = definitions[0]['definition'].replace('\n', ' ').replace('\r', '') - if len(defstr)>360: - defstr = defstr[0:360]+"..." - self.bot.act_PRIVMSG(args[0], "Urban definition: %s - http://urbanup.com/%s" % (defstr, definitions[0]['defid'])) - \ No newline at end of file + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + self.hooks=[ModuleHook("PRIVMSG", self.urban)] + + def urban(self, args, prefix, trailing): + cmd = self.bot.messageHasCommand(".urban", trailing) + if not cmd: + cmd = self.bot.messageHasCommand(".u", trailing) + if cmd and args[0][0:1]=="#": + if cmd.args_str.strip() =="": + self.bot.act_PRIVMSG(args[0], ".u/.urban -- looks up on urbandictionary.com") + return + definitions = get("http://www.urbandictionary.com/iphone/search/define", params={"term":cmd.args_str}).json()["list"] + if len(definitions) == 0: + self.bot.act_PRIVMSG(args[0], "Urban definition: no results!") + else: + defstr = definitions[0]['definition'].replace('\n', ' ').replace('\r', '') + if len(defstr)>360: + defstr = defstr[0:360]+"..." + self.bot.act_PRIVMSG(args[0], "Urban definition: %s - http://urbanup.com/%s" % (defstr, definitions[0]['defid'])) + \ No newline at end of file diff --git a/pyircbot/modules/Weather.py b/pyircbot/modules/Weather.py index d422162..f478326 100755 --- a/pyircbot/modules/Weather.py +++ b/pyircbot/modules/Weather.py @@ -1,7 +1,7 @@ #!/usr/bin/env python """ .. module:: Weather - :synopsis: Fetch weather by location string + :synopsis: Fetch weather by location string .. moduleauthor:: Dave Pedu @@ -12,206 +12,206 @@ from requests import get from urllib.parse import urlencode class Weather(ModuleBase): - def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - - assert not "get an API key" in self.config["apikey"] - - self.login = self.bot.getBestModuleForService("login") - try: - assert not self.login == None - except AssertionError as _ae: - self.log.error("Weather: A 'login' service is required") - return - - self.attr = self.bot.getBestModuleForService("attributes") - try: - assert not self.attr == None - except AssertionError as _ae: - self.log.error("Weather: An 'attributes' service is required") - return - - self.hooks=[ModuleHook("PRIVMSG", self.weather)] - - def weather(self, args, prefix, trailing): - prefixObj = self.bot.decodePrefix(prefix) - fromWho = prefixObj.nick - - replyTo = args[0] if "#" in args[0] else fromWho - - hasUnit = self.attr.get(fromWho, "weather-unit") - if hasUnit: - hasUnit = hasUnit.upper() - - cmd = self.bot.messageHasCommand([".w", ".weather"], trailing) - if cmd: - if len(cmd.args_str)>0: - self.send_weather(replyTo, fromWho, cmd.args_str, hasUnit) - return - - weatherZip = self.attr.get(fromWho, "weather-zip") - if weatherZip == None: - self.bot.act_PRIVMSG(replyTo, "%s: you must set a location with .setloc" % (fromWho,)) - return - - self.bot.act_PRIVMSG(replyTo, "%s: %s" % (fromWho, self.getWeather(weatherZip, hasUnit))) - - cmd = self.bot.messageHasCommand(".setloc", trailing) - if cmd and not args[0]=="#": - - if len(cmd.args)==0: - self.bot.act_PRIVMSG(fromWho, ".setloc: set your location for weather lookup. Example: .setloc Rochester, NY") - return - - weatherLoc = cmd.args_str - - try: - result = self.getWeather(weatherLoc) - except LocationNotSpecificException as lnse: - self.bot.act_PRIVMSG(fromWho, "'%s': location not specific enough. Did you mean: %s" % (weatherLoc, self.alternates_to_str(lnse.alternates))) - return - except LocationException as le: - self.bot.act_PRIVMSG(fromWho, "'%s': location not found" % weatherLoc) - return - - if not self.login.check(prefixObj.nick, prefixObj.hostname): - self.bot.act_PRIVMSG(fromWho, ".setloc: you need to be logged in to do that (try .login)") - return - - self.attr.set(fromWho, "weather-zip", weatherLoc) - self.bot.act_PRIVMSG(fromWho, "Saved your location as %s" % self.attr.get(fromWho, "weather-zip")) - if self.attr.get(fromWho, "weather-zip")==None: - self.bot.act_PRIVMSG(fromWho, "Tip: choose C or F with .wunit ") - - cmd = self.bot.messageHasCommand(".wunit", trailing) - if cmd and not args[0]=="#": - unit = None - try: - assert cmd.args[0].lower() in ['c', 'f'] - unit = cmd.args[0] - except: - pass - - if unit == None: - self.bot.act_PRIVMSG(fromWho, ".wunit: set your preferred temperature unit to C or F") - return - - if not self.login.check(prefixObj.nick, prefixObj.hostname): - self.bot.act_PRIVMSG(fromWho, ".wunit: you need to be logged in to do that (try .login)") - return - - self.attr.set(fromWho, "weather-unit", unit.lower()) - self.bot.act_PRIVMSG(fromWho, "Saved your preferred unit as %s" % self.attr.get(fromWho, "weather-unit").upper()) - - def send_weather(self, target, hilight, location, units=None): - try: - self.bot.act_PRIVMSG(target, "%s: %s" % (hilight, self.getWeather(location, units))) - except LocationNotSpecificException as lnse: - self.bot.act_PRIVMSG(target, "'%s': location not specific enough. Did you mean: %s" % (location, self.alternates_to_str(lnse.alternates))) - except LocationException as le: - self.bot.act_PRIVMSG(target, "'%s': location not found" % location) - - def alternates_to_str(self, alternates): - pieces = [] - for item in alternates: - item_pieces = [] - for key in ["name", "state", "country_name"]: - if key in item and len(item[key].strip()): - item_pieces.append(item[key]) - pieces.append(', '.join(item_pieces)) - return ' -- '.join(pieces) - - def getWeather(self, zipcode, unit=None): - if unit==None: - unit = self.config["defaultUnit"] - unit = unit.lower() - # Get data - data = get("http://api.wunderground.com/api/%s/geolookup/conditions/forecast10day/q/%s.json" % (self.config["apikey"], zipcode)).json() - - if "results" in data["response"]: - raise LocationNotSpecificException(data["response"]["results"]) - if "error" in data["response"] and data["response"]["error"]["type"]=="querynotfound": - raise LocationException - - # Build 5day - fiveday = "" - for item in data["forecast"]["simpleforecast"]["forecastday"][1:6]: - fiveday += "%(day)s %(icon)s %(low)s-%(high)s°%(unit)s • " % { - "unit":unit.upper(), - "high":item["high"]["fahrenheit" if unit=="f" else "celsius"], - "low":item["low"]["fahrenheit" if unit=="f" else "celsius"], - "icon":self.icon2emoji(item["icon"]), - "day":item["date"]["weekday_short"] - } - fiveday=fiveday[0:-3] - - # build wind speed - wind_speed = data["current_observation"]["wind_mph"] if unit=="f" else data["current_observation"]["wind_kph"] - wind_speed_gust = data["current_observation"]["wind_gust_mph"] if unit=="f" else data["current_observation"]["wind_gust_mph"] - if not wind_speed==wind_speed_gust and float(wind_speed_gust)>0: - wind_speed = "%s-%s" % (wind_speed, wind_speed_gust) - else: - wind_speed = "%s" % (wind_speed,) - # return message - return "\x02%(city)s, %(state)s:\x02 %(sky)s, \x02%(temp)s°%(unit)s\x02. %(wind_str)s %(wind_speed)smph (%(wind_dir)s). \x02Next 5 days:\x02 %(fiveday)s" % { - "city": data["current_observation"]["display_location"]["city"], - "state": data["current_observation"]["display_location"]["state"], - "sky": data["forecast"]["simpleforecast"]["forecastday"][0]["conditions"], - "temp": int(data["current_observation"]["temp_f"]) if unit=="f" else int(data["current_observation"]["temp_c"]), - "unit": unit.upper(), - "wind_str": self.shorten_windstr(data["current_observation"]["wind_string"].lower()), - "wind_speed": wind_speed, - "wind_dir": self.deg_to_arrow(int(data["current_observation"]["wind_degrees"])), - "fiveday":fiveday - } - - def shorten_windstr(self, windstr): - if "gusting" in windstr: - return "Gusting" - if "calm" in windstr: - return "Calm" - if "from the" in windstr.lower(): - return "Varying" - return windstr[0:12] - - - - def icon2emoji(self,icon): - if "partlycloudy" in icon or "mostlycloudy" in icon: - return "⛅️" - elif "cloudy" in icon: - return "☁️" - elif "rain" in icon: - return "💧" - elif "clear" in icon: - return "☀️" - elif "snow" in icon: - return "❄️" - else: - return "(%s)" % icon - - def deg_to_arrow(self, deg): - if deg > 335 or deg < 0: - return "↑" - elif deg > 292: - return "⇖" - elif deg > 247: - return "←" - elif deg > 202: - return "⇙" - elif deg > 157: - return "↓" - elif deg > 112: - return "⇘" - elif deg > 67: - return "→" - elif deg > 22: - return "⇗" + def __init__(self, bot, moduleName): + ModuleBase.__init__(self, bot, moduleName); + + assert not "get an API key" in self.config["apikey"] + + self.login = self.bot.getBestModuleForService("login") + try: + assert not self.login == None + except AssertionError as _ae: + self.log.error("Weather: A 'login' service is required") + return + + self.attr = self.bot.getBestModuleForService("attributes") + try: + assert not self.attr == None + except AssertionError as _ae: + self.log.error("Weather: An 'attributes' service is required") + return + + self.hooks=[ModuleHook("PRIVMSG", self.weather)] + + def weather(self, args, prefix, trailing): + prefixObj = self.bot.decodePrefix(prefix) + fromWho = prefixObj.nick + + replyTo = args[0] if "#" in args[0] else fromWho + + hasUnit = self.attr.get(fromWho, "weather-unit") + if hasUnit: + hasUnit = hasUnit.upper() + + cmd = self.bot.messageHasCommand([".w", ".weather"], trailing) + if cmd: + if len(cmd.args_str)>0: + self.send_weather(replyTo, fromWho, cmd.args_str, hasUnit) + return + + weatherZip = self.attr.get(fromWho, "weather-zip") + if weatherZip == None: + self.bot.act_PRIVMSG(replyTo, "%s: you must set a location with .setloc" % (fromWho,)) + return + + self.bot.act_PRIVMSG(replyTo, "%s: %s" % (fromWho, self.getWeather(weatherZip, hasUnit))) + + cmd = self.bot.messageHasCommand(".setloc", trailing) + if cmd and not args[0]=="#": + + if len(cmd.args)==0: + self.bot.act_PRIVMSG(fromWho, ".setloc: set your location for weather lookup. Example: .setloc Rochester, NY") + return + + weatherLoc = cmd.args_str + + try: + result = self.getWeather(weatherLoc) + except LocationNotSpecificException as lnse: + self.bot.act_PRIVMSG(fromWho, "'%s': location not specific enough. Did you mean: %s" % (weatherLoc, self.alternates_to_str(lnse.alternates))) + return + except LocationException as le: + self.bot.act_PRIVMSG(fromWho, "'%s': location not found" % weatherLoc) + return + + if not self.login.check(prefixObj.nick, prefixObj.hostname): + self.bot.act_PRIVMSG(fromWho, ".setloc: you need to be logged in to do that (try .login)") + return + + self.attr.set(fromWho, "weather-zip", weatherLoc) + self.bot.act_PRIVMSG(fromWho, "Saved your location as %s" % self.attr.get(fromWho, "weather-zip")) + if self.attr.get(fromWho, "weather-zip")==None: + self.bot.act_PRIVMSG(fromWho, "Tip: choose C or F with .wunit ") + + cmd = self.bot.messageHasCommand(".wunit", trailing) + if cmd and not args[0]=="#": + unit = None + try: + assert cmd.args[0].lower() in ['c', 'f'] + unit = cmd.args[0] + except: + pass + + if unit == None: + self.bot.act_PRIVMSG(fromWho, ".wunit: set your preferred temperature unit to C or F") + return + + if not self.login.check(prefixObj.nick, prefixObj.hostname): + self.bot.act_PRIVMSG(fromWho, ".wunit: you need to be logged in to do that (try .login)") + return + + self.attr.set(fromWho, "weather-unit", unit.lower()) + self.bot.act_PRIVMSG(fromWho, "Saved your preferred unit as %s" % self.attr.get(fromWho, "weather-unit").upper()) + + def send_weather(self, target, hilight, location, units=None): + try: + self.bot.act_PRIVMSG(target, "%s: %s" % (hilight, self.getWeather(location, units))) + except LocationNotSpecificException as lnse: + self.bot.act_PRIVMSG(target, "'%s': location not specific enough. Did you mean: %s" % (location, self.alternates_to_str(lnse.alternates))) + except LocationException as le: + self.bot.act_PRIVMSG(target, "'%s': location not found" % location) + + def alternates_to_str(self, alternates): + pieces = [] + for item in alternates: + item_pieces = [] + for key in ["name", "state", "country_name"]: + if key in item and len(item[key].strip()): + item_pieces.append(item[key]) + pieces.append(', '.join(item_pieces)) + return ' -- '.join(pieces) + + def getWeather(self, zipcode, unit=None): + if unit==None: + unit = self.config["defaultUnit"] + unit = unit.lower() + # Get data + data = get("http://api.wunderground.com/api/%s/geolookup/conditions/forecast10day/q/%s.json" % (self.config["apikey"], zipcode)).json() + + if "results" in data["response"]: + raise LocationNotSpecificException(data["response"]["results"]) + if "error" in data["response"] and data["response"]["error"]["type"]=="querynotfound": + raise LocationException + + # Build 5day + fiveday = "" + for item in data["forecast"]["simpleforecast"]["forecastday"][1:6]: + fiveday += "%(day)s %(icon)s %(low)s-%(high)s°%(unit)s • " % { + "unit":unit.upper(), + "high":item["high"]["fahrenheit" if unit=="f" else "celsius"], + "low":item["low"]["fahrenheit" if unit=="f" else "celsius"], + "icon":self.icon2emoji(item["icon"]), + "day":item["date"]["weekday_short"] + } + fiveday=fiveday[0:-3] + + # build wind speed + wind_speed = data["current_observation"]["wind_mph"] if unit=="f" else data["current_observation"]["wind_kph"] + wind_speed_gust = data["current_observation"]["wind_gust_mph"] if unit=="f" else data["current_observation"]["wind_gust_mph"] + if not wind_speed==wind_speed_gust and float(wind_speed_gust)>0: + wind_speed = "%s-%s" % (wind_speed, wind_speed_gust) + else: + wind_speed = "%s" % (wind_speed,) + # return message + return "\x02%(city)s, %(state)s:\x02 %(sky)s, \x02%(temp)s°%(unit)s\x02. %(wind_str)s %(wind_speed)smph (%(wind_dir)s). \x02Next 5 days:\x02 %(fiveday)s" % { + "city": data["current_observation"]["display_location"]["city"], + "state": data["current_observation"]["display_location"]["state"], + "sky": data["forecast"]["simpleforecast"]["forecastday"][0]["conditions"], + "temp": int(data["current_observation"]["temp_f"]) if unit=="f" else int(data["current_observation"]["temp_c"]), + "unit": unit.upper(), + "wind_str": self.shorten_windstr(data["current_observation"]["wind_string"].lower()), + "wind_speed": wind_speed, + "wind_dir": self.deg_to_arrow(int(data["current_observation"]["wind_degrees"])), + "fiveday":fiveday + } + + def shorten_windstr(self, windstr): + if "gusting" in windstr: + return "Gusting" + if "calm" in windstr: + return "Calm" + if "from the" in windstr.lower(): + return "Varying" + return windstr[0:12] + + + + def icon2emoji(self,icon): + if "partlycloudy" in icon or "mostlycloudy" in icon: + return "⛅️" + elif "cloudy" in icon: + return "☁️" + elif "rain" in icon: + return "💧" + elif "clear" in icon: + return "☀️" + elif "snow" in icon: + return "❄️" + else: + return "(%s)" % icon + + def deg_to_arrow(self, deg): + if deg > 335 or deg < 0: + return "↑" + elif deg > 292: + return "⇖" + elif deg > 247: + return "←" + elif deg > 202: + return "⇙" + elif deg > 157: + return "↓" + elif deg > 112: + return "⇘" + elif deg > 67: + return "→" + elif deg > 22: + return "⇗" class LocationException(Exception): - pass + pass class LocationNotSpecificException(LocationException): - def __init__(self, alternates): - self.alternates = alternates - pass \ No newline at end of file + def __init__(self, alternates): + self.alternates = alternates + pass \ No newline at end of file diff --git a/pyircbot/modules/__init__.py b/pyircbot/modules/__init__.py index 189b536..6dc88e2 100644 --- a/pyircbot/modules/__init__.py +++ b/pyircbot/modules/__init__.py @@ -1,6 +1,6 @@ """ .. module:: Modules - :synopsis: Module containing the bot's modules + :synopsis: Module containing the bot's modules .. moduleauthor:: Dave Pedu diff --git a/setup.py b/setup.py index 3e1057d..56e4ab9 100755 --- a/setup.py +++ b/setup.py @@ -4,11 +4,11 @@ from setuptools import setup __version__ = "4.0.0-r02" setup(name='pyircbot', - version='4.0.0-r02', - description='A modular python irc bot', - url='http://gitlab.xmopx.net/dave/pyircbot3/tree/master', - author='dpedu', - author_email='dave@davepedu.com', - packages=['pyircbot', 'pyircbot.modules'], - scripts=['bin/pyircbot'], - zip_safe=False) + version='4.0.0-r02', + description='A modular python irc bot', + url='http://gitlab.xmopx.net/dave/pyircbot3/tree/master', + author='dpedu', + author_email='dave@davepedu.com', + packages=['pyircbot', 'pyircbot.modules'], + scripts=['bin/pyircbot'], + zip_safe=False)