diff --git a/bin/pyircbot b/bin/pyircbot index f181c6d..4f3aab4 100755 --- a/bin/pyircbot +++ b/bin/pyircbot @@ -6,7 +6,8 @@ from pyircbot import PyIRCBot if __name__ == "__main__": " logging level and facility " - logging.basicConfig(level=logging.INFO, format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s") + logging.basicConfig(level=logging.INFO, + format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s") log = logging.getLogger('main') " parse command line args " diff --git a/pyircbot/irccore.py b/pyircbot/irccore.py index 3a351e3..3672345 100644 --- a/pyircbot/irccore.py +++ b/pyircbot/irccore.py @@ -24,6 +24,7 @@ try: except: from io import BytesIO as StringIO + IRCEvent = namedtuple("IRCEvent", "args prefix trailing") UserPrefix = namedtuple("UserPrefix", "nick username hostname") ServerPrefix = namedtuple("ServerPrefix", "hostname") @@ -34,7 +35,7 @@ class IRCCore(asynchat.async_chat): def __init__(self): asynchat.async_chat.__init__(self) - self.connected=False + self.connected = False """If we're connected or not""" self.log = logging.getLogger('IRCCore') @@ -74,7 +75,8 @@ class IRCCore(asynchat.async_chat): while self.alive: try: asyncore.loop(map=self.asynmap, timeout=1) - except Exception as e: + except Exception: + self.log.error("Loop error: ") self.log.error(IRCCore.trace()) @@ -86,7 +88,7 @@ class IRCCore(asynchat.async_chat): logging.info("Loop: reconnecting") try: self._connect() - except Exception as e2: + except Exception: self.log.error("Error reconnecting: ") self.log.error(IRCCore.trace()) @@ -103,10 +105,10 @@ class IRCCore(asynchat.async_chat): # Clear any pending messages self.outputQueueRunner.clear() # Send quit message and flush queue - self.act_QUIT(message) # TODO will this hang if the socket is having issues? + self.act_QUIT(message) # TODO will this hang if the socket is having issues? self.outputQueueRunner.flush() # Signal disconnection - self.alive=alive + self.alive = alive # Close socket self.socket.shutdown(SHUT_RDWR) self.close() @@ -126,7 +128,7 @@ class IRCCore(asynchat.async_chat): :param data: the data that was recieved :type data: str""" - #self.log.info("<< %(message)s", {"message":repr(data)}) + self.buffer.write(data) def found_terminator(self): @@ -147,7 +149,7 @@ class IRCCore(asynchat.async_chat): def handle_close(self): """Called when the socket is disconnected. Triggers the _DISCONNECT hook""" self.log.info("handle_close") - self.connected=False + self.connected = False self.close() self.fire_hook("_DISCONNECT") @@ -156,15 +158,15 @@ class IRCCore(asynchat.async_chat): self.log.error("Connection failed (handle_error)") self.log.error(str(args)) self.log.error(str(kwargs)) - self.log.error(IRCCore.trace()); + self.log.error(IRCCore.trace()) def _connect(self): """Connect to IRC""" - self.server+=1 + self.server += 1 if self.server >= len(self.servers): - self.server=0 + self.server = 0 serverHostname = self.servers[self.server] - self.log.info("Connecting to %(server)s:%(port)i", {"server":serverHostname, "port":self.port}) + self.log.info("Connecting to %(server)s:%(port)i", {"server": serverHostname, "port": self.port}) socket_type = socket.AF_INET if self.ipv6: self.log.info("IPv6 is enabled.") @@ -175,12 +177,13 @@ class IRCCore(asynchat.async_chat): self.connect(socketInfo[0][4]) self.log.info("Connection established") self._fileno = self.socket.fileno() - self.asynmap[self._fileno] = self # http://willpython.blogspot.com/2010/08/multiple-event-loops-with-asyncore-and.html + # See http://willpython.blogspot.com/2010/08/multiple-event-loops-with-asyncore-and.html + self.asynmap[self._fileno] = self self.log.info("_connect: Socket map: %s" % str(self.asynmap)) def handle_connect(self): """When asynchat indicates our socket is connected, fire the _CONNECT hook""" - self.connected=True + self.connected = True self.log.info("handle_connect: connected") self.fire_hook("_CONNECT") self.log.info("handle_connect: complete") @@ -190,7 +193,7 @@ class IRCCore(asynchat.async_chat): :param text: the string to send :type text: str""" - text = (text+"\r\n").encode("UTF-8").decode().encode("UTF-8") + text = (text + "\r\n").encode("UTF-8").decode().encode("UTF-8") self.outputQueue.put((prio, text), block=False) def process_data(self, data): @@ -203,71 +206,71 @@ class IRCCore(asynchat.async_chat): prefix = None command = None - args=[] - trailing=None + args = [] + trailing = None - if data[0]==":": - prefix=data.split(" ")[0][1:] - data=data[data.find(" ")+1:] + if data[0] == ":": + prefix = data.split(" ")[0][1:] + data = data[data.find(" ") + 1:] command = data.split(" ")[0] - data=data[data.find(" ")+1:] - if(data[0]==":"): + data = data[data.find(" ") + 1:] + if(data[0] == ":"): # no args trailing = data[1:].strip() else: - trailing = data[data.find(" :")+2:].strip() + trailing = data[data.find(" :") + 2:].strip() data = data[:data.find(" :")] args = data.split(" ") - for index,arg in enumerate(args): - args[index]=arg.strip() + for index, arg in enumerate(args): + args[index] = arg.strip() self.fire_hook("_RECV", args=args, prefix=prefix, trailing=trailing) - if not command in self.hookcalls: - self.log.warning("Unknown command: cmd='%s' prefix='%s' args='%s' trailing='%s'" % (command, prefix, args, trailing)) + if command not in self.hookcalls: + self.log.warning("Unknown command: cmd='%s' prefix='%s' args='%s' trailing='%s'" % (command, prefix, args, + trailing)) else: self.fire_hook(command, args=args, prefix=prefix, trailing=trailing) - " Module related code " def initHooks(self): """Defines hooks that modules can listen for events of""" self.hooks = [ - '_CONNECT', # Called when the bot connects to IRC on the socket level - '_DISCONNECT', # Called when the irc socket is forcibly closed - '_RECV', # Called on network activity - 'NOTICE', # :irc.129irc.com NOTICE AUTH :*** Looking up your hostname... - 'MODE', # :CloneABCD MODE CloneABCD :+iwx - 'PING', # PING :irc.129irc.com - 'JOIN', # :CloneA!dave@hidden-B4F6B1AA.rit.edu JOIN :#clonea - 'QUIT', # :HCSMPBot!~HCSMPBot@108.170.48.18 QUIT :Quit: Disconnecting! - 'NICK', # :foxiAway!foxi@irc.hcsmp.com NICK :foxi - 'PART', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PART #clonea - 'PRIVMSG', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PRIVMSG #clonea :aaa - 'KICK', # :xMopxShell!~rduser@host KICK #xMopx2 xBotxShellTest :xBotxShellTest - 'INVITE', # :gmx!~gmxgeek@irc.hcsmp.com INVITE Tyrone :#hcsmp' - '001', # :irc.129irc.com 001 CloneABCD :Welcome to the 129irc IRC Network CloneABCD!CloneABCD@djptwc-laptop1.rit.edu - '002', # :irc.129irc.com 002 CloneABCD :Your host is irc.129irc.com, running version Unreal3.2.8.1 - '003', # :irc.129irc.com 003 CloneABCD :This server was created Mon Jul 19 2010 at 03:12:01 EDT - '004', # :irc.129irc.com 004 CloneABCD irc.129irc.com Unreal3.2.8.1 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj - '005', # :irc.129irc.com 005 CloneABCD CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server - '250', # :chaos.esper.net 250 xBotxShellTest :Highest connection count: 1633 (1632 clients) (186588 connections received) - '251', # :irc.129irc.com 251 CloneABCD :There are 1 users and 48 invisible on 2 servers - '252', # :irc.129irc.com 252 CloneABCD 9 :operator(s) online - '254', # :irc.129irc.com 254 CloneABCD 6 :channels formed - '255', # :irc.129irc.com 255 CloneABCD :I have 42 clients and 1 servers - '265', # :irc.129irc.com 265 CloneABCD :Current Local Users: 42 Max: 47 - '266', # :irc.129irc.com 266 CloneABCD :Current Global Users: 49 Max: 53 - '332', # :chaos.esper.net 332 xBotxShellTest #xMopx2 :/ #XMOPX2 / https://code.google.com/p/pyircbot/ (Channel Topic) - '333', # :chaos.esper.net 333 xBotxShellTest #xMopx2 xMopxShell!~rduser@108.170.60.242 1344370109 - '353', # :irc.129irc.com 353 CloneABCD = #clonea :CloneABCD CloneABC - '366', # :irc.129irc.com 366 CloneABCD #clonea :End of /NAMES list. - '372', # :chaos.esper.net 372 xBotxShell :motd text here - '375', # :chaos.esper.net 375 xBotxShellTest :- chaos.esper.net Message of the Day - - '376', # :chaos.esper.net 376 xBotxShell :End of /MOTD command. - '422', # :irc.129irc.com 422 CloneABCD :MOTD File is missing - '433', # :nova.esper.net 433 * pyircbot3 :Nickname is already in use. + '_CONNECT', # Called when the bot connects to IRC on the socket level + '_DISCONNECT', # Called when the irc socket is forcibly closed + '_RECV', # Called on network activity + 'NOTICE', # :irc.129irc.com NOTICE AUTH :*** Looking up your hostname... + 'MODE', # :CloneABCD MODE CloneABCD :+iwx + 'PING', # PING :irc.129irc.com + 'JOIN', # :CloneA!dave@hidden-B4F6B1AA.rit.edu JOIN :#clonea + 'QUIT', # :HCSMPBot!~HCSMPBot@108.170.48.18 QUIT :Quit: Disconnecting! + 'NICK', # :foxiAway!foxi@irc.hcsmp.com NICK :foxi + 'PART', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PART #clonea + 'PRIVMSG', # :CloneA!dave@hidden-B4F6B1AA.rit.edu PRIVMSG #clonea :aaa + 'KICK', # :xMopxShell!~rduser@host KICK #xMopx2 xBotxShellTest :xBotxShellTest + 'INVITE', # :gmx!~gmxgeek@irc.hcsmp.com INVITE Tyrone :#hcsmp' + '001', # :irc.129irc.com 001 CloneABCD :Welcome to the 129irc IRC Network CloneABCD!CloneABCD@djptwc-laptop1.rit.edu + '002', # :irc.129irc.com 002 CloneABCD :Your host is irc.129irc.com, running version Unreal3.2.8.1 + '003', # :irc.129irc.com 003 CloneABCD :This server was created Mon Jul 19 2010 at 03:12:01 EDT + '004', # :irc.129irc.com 004 CloneABCD irc.129irc.com Unreal3.2.8.1 iowghraAsORTVSxNCWqBzvdHtGp lvhopsmntikrRcaqOALQbSeIKVfMCuzNTGj + '005', # :irc.129irc.com 005 CloneABCD CMDS=KNOCK,MAP,DCCALLOW,USERIP UHNAMES NAMESX SAFELIST HCN MAXCHANNELS=10 CHANLIMIT=#:10 MAXLIST=b:60,e:60,I:60 NICKLEN=30 CHANNELLEN=32 TOPICLEN=307 KICKLEN=307 AWAYLEN=307 :are supported by this server + '250', # :chaos.esper.net 250 xBotxShellTest :Highest connection count: 1633 (1632 clients) (186588 connections received) + '251', # :irc.129irc.com 251 CloneABCD :There are 1 users and 48 invisible on 2 servers + '252', # :irc.129irc.com 252 CloneABCD 9 :operator(s) online + '254', # :irc.129irc.com 254 CloneABCD 6 :channels formed + '255', # :irc.129irc.com 255 CloneABCD :I have 42 clients and 1 servers + '265', # :irc.129irc.com 265 CloneABCD :Current Local Users: 42 Max: 47 + '266', # :irc.129irc.com 266 CloneABCD :Current Global Users: 49 Max: 53 + '332', # :chaos.esper.net 332 xBotxShellTest #xMopx2 :/ #XMOPX2 / https://code.google.com/p/pyircbot/ (Channel Topic) + '333', # :chaos.esper.net 333 xBotxShellTest #xMopx2 xMopxShell!~rduser@108.170.60.242 1344370109 + '353', # :irc.129irc.com 353 CloneABCD = #clonea :CloneABCD CloneABC + '366', # :irc.129irc.com 366 CloneABCD #clonea :End of /NAMES list. + '372', # :chaos.esper.net 372 xBotxShell :motd text here + '375', # :chaos.esper.net 375 xBotxShellTest :- chaos.esper.net Message of the Day - + '376', # :chaos.esper.net 376 xBotxShell :End of /MOTD command. + '422', # :irc.129irc.com 422 CloneABCD :MOTD File is missing + '433', # :nova.esper.net 433 * pyircbot3 :Nickname is already in use. ] " mapping of hooks to methods " - self.hookcalls = {command:[] for command in self.hooks} + self.hookcalls = {command: [] for command in self.hooks} def fire_hook(self, command, args=None, prefix=None, trailing=None): """Run any listeners for a specific hook @@ -289,7 +292,7 @@ class IRCCore(asynchat.async_chat): hook(args, prefix, trailing) except: - self.log.warning("Error processing hook: \n%s"% self.trace()) + self.log.warning("Error processing hook: \n%s" % self.trace()) def addHook(self, command, method): """**Internal.** Enable (connect) a single hook of a module @@ -343,7 +346,9 @@ class IRCCore(asynchat.async_chat): :param prefix: the prefix to disassemble :type prefix: str - :returns: object -- an UserPrefix object with the properties `nick`, `username`, `hostname` or a ServerPrefix object with the property `hostname`""" + :returns: object -- an UserPrefix object with the properties `nick`, `username`, `hostname` or a ServerPrefix + object with the property `hostname` + """ if "!" in prefix: nick, prefix = prefix.split("!") username, hostname = prefix.split("@") @@ -412,7 +417,7 @@ class IRCCore(asynchat.async_chat): :param channel: the channel to attempt to join :type channel: str""" - self.sendRaw("JOIN %s"%channel) + self.sendRaw("JOIN %s" % channel) def act_PRIVMSG(self, towho, message): """Use the `/msg` command @@ -421,7 +426,7 @@ class IRCCore(asynchat.async_chat): :type towho: str :param message: the message to send :type message: str""" - self.sendRaw("PRIVMSG %s :%s"%(towho,message)) + self.sendRaw("PRIVMSG %s :%s" % (towho, message)) def act_MODE(self, channel, mode, extra=None): """Use the `/mode` command @@ -432,10 +437,10 @@ class IRCCore(asynchat.async_chat): :type mode: str :param extra: additional argument if the mode needs it. Example: user@*!* :type extra: str""" - if extra != None: - self.sendRaw("MODE %s %s %s" % (channel,mode,extra)) + if extra is not None: + self.sendRaw("MODE %s %s %s" % (channel, mode, extra)) else: - self.sendRaw("MODE %s %s" % (channel,mode)) + self.sendRaw("MODE %s %s" % (channel, mode)) def act_ACTION(self, channel, action): """Use the `/me ` command @@ -444,7 +449,7 @@ class IRCCore(asynchat.async_chat): :type channel: str :param action: the text to send :type action: str""" - self.sendRaw("PRIVMSG %s :\x01ACTION %s"%(channel,action)) + self.sendRaw("PRIVMSG %s :\x01ACTION %s" % (channel, action)) def act_KICK(self, channel, who, comment=""): """Use the `/kick ` command @@ -464,11 +469,18 @@ class IRCCore(asynchat.async_chat): :type message: str""" self.sendRaw("QUIT :%s" % message, prio=0) + def act_PASS(self, password): + """ + Send server password, for use on connection + """ + self.sendRaw("PASS %s" % password, prio=0) + + class OutputQueueRunner(Thread): """Rate-limited output queue""" def __init__(self, bot): Thread.__init__(self, daemon=True) - self.bot = bot #reference to main bot thread + self.bot = bot # reference to main bot thread self.log = logging.getLogger('OutputQueueRunner') self.paused = False @@ -488,14 +500,14 @@ class OutputQueueRunner(Thread): self.process_queue_item() lastSend = time() except queue.Empty: - #self.log.info("Queue is empty") + # self.log.debug("Queue is empty") pass sleep(0.01) def process_queue_item(self): """Remove 1 item from queue and process it""" - prio,text = self.bot.outputQueue.get(block=True, timeout=10) - #self.log.info("%s>> %s" % (prio,text)) + prio, text = self.bot.outputQueue.get(block=True, timeout=10) + # self.log.debug("%s>> %s" % (prio,text)) self.bot.outputQueue.task_done() self.log.debug("> {}".format(text.decode('UTF-8'))) self.bot.send(text) @@ -508,7 +520,7 @@ class OutputQueueRunner(Thread): self.bot.outputQueue.get(block=False) except queue.Empty: pass - #self.log.info("output queue cleared") + # self.log.debug("output queue cleared") return length def flush(self): @@ -518,4 +530,4 @@ class OutputQueueRunner(Thread): self.process_queue_item() except: pass - #self.log.info("output queue flushed") + # self.log.debug("output queue flushed") diff --git a/pyircbot/modulebase.py b/pyircbot/modulebase.py index 3269bfc..a3bef3a 100644 --- a/pyircbot/modulebase.py +++ b/pyircbot/modulebase.py @@ -7,7 +7,6 @@ """ import logging -import os from .pyircbot import PyIRCBot @@ -21,20 +20,20 @@ class ModuleBase: """ def __init__(self, bot, moduleName): - self.moduleName=moduleName + self.moduleName = moduleName """Assigned name of this module""" self.bot = bot """Reference to the master PyIRCBot object""" - self.hooks=[] + self.hooks = [] """Hooks (aka listeners) this module has""" - self.services=[] + self.services = [] """If this module provides services usable by another module, they're listed here""" - self.config={} + self.config = {} """Configuration dictionary. Autoloaded from `%(datadir)s/%(modulename)s.json`""" self.log = logging.getLogger("Module.%s" % self.moduleName) @@ -70,7 +69,8 @@ class ModuleBase: 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 + self.hook = hook + self.method = method diff --git a/pyircbot/modules/AttributeStorage.py b/pyircbot/modules/AttributeStorage.py index 624b097..7470b8a 100755 --- a/pyircbot/modules/AttributeStorage.py +++ b/pyircbot/modules/AttributeStorage.py @@ -7,21 +7,22 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase + class AttributeStorage(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["attributes"] + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] + self.services = ["attributes"] self.db = None serviceProviders = self.bot.getmodulesbyservice("mysql") - if len(serviceProviders)==0: + 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` ( @@ -31,7 +32,7 @@ class AttributeStorage(ModuleBase): 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` ( @@ -40,7 +41,7 @@ class AttributeStorage(ModuleBase): 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` ( @@ -50,20 +51,14 @@ class AttributeStorage(ModuleBase): 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 + c = self.db.connection.query("""SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, @@ -74,35 +69,33 @@ class AttributeStorage(ModuleBase): on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=%s;""", - (name,) - ) + + WHERE + `i`.`item`=%s;""", (name,)) item = {} while True: row = c.fetchone() - if row == None: + if row is None: break - item[row["attribute"]]=row["value"] + item[row["attribute"]] = row["value"] c.close() - - if len(item)==0: + + 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 + + :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 + c = self.db.connection.query("""SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, @@ -113,25 +106,23 @@ class AttributeStorage(ModuleBase): on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` - - WHERE + + WHERE `i`.`item`=%s AND - `a`.`attribute`=%s;""", - (item,key) - ) + `a`.`attribute`=%s;""", (item, key)) row = c.fetchone() c.close() - if row == None: + if row is 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 @@ -140,35 +131,38 @@ class AttributeStorage(ModuleBase): :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: + if row is 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: + if row is 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: + + if value is None: # delete it - c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", (itemId, attributeId)) - self.log.info("AttributeStorage: Stored item %s attribute %s value: %s (Deleted)" % (itemId, attributeId, value)) + c = self.db.connection.query("DELETE FROM `values` WHERE `itemid`=%s AND `attributeid`=%s ;", + (itemId, attributeId)) + self.log.info("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)) + c = self.db.connection.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) " + "VALUES (%s, %s, %s);", (itemId, attributeId, value)) self.log.info("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 19b692a..acd5f50 100755 --- a/pyircbot/modules/AttributeStorageLite.py +++ b/pyircbot/modules/AttributeStorageLite.py @@ -7,22 +7,23 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase + class AttributeStorageLite(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["attributes"] + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] + self.services = ["attributes"] self.db = None serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: + if len(serviceProviders) == 0: self.log.error("Could not find a valid sqlite service provider") raise Exception("No sqlite provider available") else: self.log.info("Selecting sqlite service provider: %s" % serviceProviders[0]) self.db = serviceProviders[0].opendb("attributes.db") - + if not self.db.tableExists("attribute"): self.log.info("Creating table: attribute") c = self.db.query("""CREATE TABLE IF NOT EXISTS `attribute` ( @@ -30,7 +31,7 @@ class AttributeStorageLite(ModuleBase): `attribute` varchar(128) UNIQUE ) ;""") c.close() - + if not self.db.tableExists("items"): self.log.info("Creating table: items") c = self.db.query("""CREATE TABLE IF NOT EXISTS `items` ( @@ -38,7 +39,7 @@ class AttributeStorageLite(ModuleBase): `item` varchar(512) ) ;""") c.close() - + if not self.db.tableExists("values"): self.log.info("Creating table: values") c = self.db.query("""CREATE TABLE IF NOT EXISTS `values` ( @@ -48,18 +49,14 @@ class AttributeStorageLite(ModuleBase): 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 + c = self.db.query("""SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, @@ -70,34 +67,33 @@ class AttributeStorageLite(ModuleBase): on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` - - WHERE - `i`.`item`=?;""", - (name.lower(),) - ) + + WHERE + `i`.`item`=?;""", (name.lower(),)) item = {} while True: row = c.fetchone() - if row == None: + if row is None: break - item[row["attribute"]]=row["value"] + item[row["attribute"]] = row["value"] c.close() - - if len(item)==0: + + if not item: 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 + + :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 + c = self.db.query("""SELECT `i`.`id`, `i`.`item`, `a`.`attribute`, @@ -108,25 +104,24 @@ class AttributeStorageLite(ModuleBase): on `v`.`itemid`=`i`.`id` INNER JOIN `attribute` `a` on `a`.`id`=`v`.`attributeid` - - WHERE + + WHERE `i`.`item`=? AND - `a`.`attribute`=?;""", - (item.lower(),key.lower()) - ) + `a`.`attribute`=?;""", (item.lower(), key.lower())) row = c.fetchone() - + c.close() - if row == None: + if row is 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 @@ -135,35 +130,35 @@ class AttributeStorageLite(ModuleBase): :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: + if row is 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: + if row is None: c = self.db.query("INSERT INTO `items` (`item`) VALUES (?);", (item,)) itemId = c.lastrowid else: itemId = row["id"] c.close() - - if value == None: + + if value is None: # delete it c = self.db.query("DELETE FROM `values` WHERE `itemid`=? AND `attributeid`=? ;", (itemId, attributeId)) self.log.info("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)) + c = self.db.query("REPLACE INTO `values` (`itemid`, `attributeid`, `value`) VALUES (?, ?, ?);", + (itemId, attributeId, value)) self.log.info("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 d5239d5..fa0870b 100644 --- a/pyircbot/modules/BitcoinPrice.py +++ b/pyircbot/modules/BitcoinPrice.py @@ -6,37 +6,37 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook from requests import get from time import time + class BitcoinPrice(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - + ModuleBase.__init__(self, bot, moduleName) + self.cache = None self.cacheAge = 0 - - self.hooks=[ + + 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] - + 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']) + "\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"]: + if self.cache is 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 a4a4ebb..78cbcb2 100644 --- a/pyircbot/modules/Calc.py +++ b/pyircbot/modules/Calc.py @@ -1,22 +1,24 @@ -from pyircbot.modulebase import ModuleBase,ModuleHook + +from pyircbot.modulebase import ModuleBase, ModuleHook import datetime 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={} - + ModuleBase.__init__(self, bot, moduleName) + + self.hooks = [ModuleHook("PRIVMSG", self.calc)] + self.timers = {} + self.sqlite = self.bot.getBestModuleForService("sqlite") - if self.sqlite==None: + if self.sqlite is 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(""" @@ -59,41 +61,40 @@ class Calc(ModuleBase): ); """) c.close() - + def timeSince(self, channel, timetype): - if not channel in self.timers: + if channel not in self.timers: self.createDefaultTimers(channel) - return time.time()-self.timers[channel][timetype] - + return time.time() - self.timers[channel][timetype] + def updateTimeSince(self, channel, timetype): - if not channel in self.timers: + if channel not 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} - + 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)) + 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]=="#": + 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 trailing[0:len(cmd)] == cmd and (len(trailing) == len(cmd) or + (trailing[len(cmd):len(cmd) + 1] in [" ", "="])): + foundCalc = True + if foundCalc: - calcCmd = trailing[len(cmd)-1:].strip() + calcCmd = trailing[len(cmd) - 1:].strip() if "=" in calcCmd[1:]: " Add a new calc " calcWord, calcDefinition = calcCmd.split("=", 1) @@ -106,153 +107,172 @@ class Calc(ModuleBase): 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"))) + 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: + 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"))) + + 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: + if randCalc is 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.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"))) + 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: + if randCalc is 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.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"))) + 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()=='': + if not 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)) + c.execute("SELECT * FROM `calc_words` WHERE `word` LIKE ? AND `channel`=? ORDER BY `word` ASC ;", + ("%%" + term + "%%", channelId)) rows = c.fetchall() - if len(rows)==0: + if not rows: self.bot.act_PRIVMSG(args[0], "%s: Sorry, no matches" % sender.nick) else: matches = [] for row in rows[0:10]: - if row == None: + if not row: 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.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: + if not rows: 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')) + if not rows: + 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.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())) + 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: + + if word is 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"], )) - + + 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: + + if who is None: return None - + c.close() - return {"word":word["word"], "definition":who["definition"], "by":who["username"]} - - + 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,)) + 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: + if word is 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"], )) - + 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: + + if who is None: continue - + c.close() - return {"word":word["word"], "definition":who["definition"], "by":who["username"]} - + 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: + if not rows: 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("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: + if not rows: c.execute("INSERT INTO `calc_channels` (`channel`) VALUES (?);", (channel,)) c.execute("SELECT * FROM `calc_channels` WHERE `channel` = ?", (channel,)) rows = c.fetchall() diff --git a/pyircbot/modules/CardsAgainstHumanity.py b/pyircbot/modules/CardsAgainstHumanity.py index fb44cdd..435f2d3 100755 --- a/pyircbot/modules/CardsAgainstHumanity.py +++ b/pyircbot/modules/CardsAgainstHumanity.py @@ -5,47 +5,44 @@ .. moduleauthor:: Nick Krichevsky """ -from pyircbot.modulebase import ModuleBase,ModuleHook -import os -import time +from pyircbot.modulebase import ModuleBase, ModuleHook from threading import Timer -from operator import itemgetter from random import choice + class CardsAgainstHumanity(ModuleBase): def __init__(self, bot, moduleName): # init the base module - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.scramble)] - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [ModuleHook("PRIVMSG", self.scramble)] + # Dictionary - self.whitesFile = open(self.getFilePath("answers.txt"),'r') - self.blacksFile = open(self.getFilePath("questions.txt"),'r') + self.whitesFile = open(self.getFilePath("answers.txt"), 'r') + self.blacksFile = open(self.getFilePath("questions.txt"), 'r') self.whites = [line.rstrip() for line in self.whitesFile] self.blacks = [line.rstrip() for line in self.blacksFile] self.currentBlack = "" self.whitesFile.close() self.blacksFile.close() - self.log.info("CAH: Loaded."+str(len(self.whites))+" White Cards "+str(len(self.blacks))+" Black Cards") + self.log.info("CAH: Loaded." + str(len(self.whites)) + " White Cards " + str(len(self.blacks)) + " Black Cards") # Per channel games self.games = {} - - + def scramble(self, args, prefix, trailing): channel = args[0] if channel[0] == "#": - if not channel in self.games: - self.games[channel]=cardsGame(self, channel,self.whites,self.blacks) + if channel not in self.games: + self.games[channel] = cardsGame(self, channel, self.whites, self.blacks) self.games[channel].stuff(args, prefix, trailing) - - + def ondisable(self): self.log.info("CAH: Unload requested, ending games...") # for game in self.games: # self.games[game].gameover() + class cardsGame: - def __init__(self, master, channel,whites,blacks): + def __init__(self, master, channel, whites, blacks): self.master = master self.channel = channel # Running? @@ -63,33 +60,34 @@ class cardsGame: self.allowPick = 0 self.choices = {} self.czarTimer = None + def stuff(self, args, prefix, trailing): prefix = self.master.bot.decodePrefix(prefix) sender = prefix.nick if self.master.bot.messageHasCommand(".joinGame", trailing): self.join(sender) - elif self.master.bot.messageHasCommand(".ready",trailing): + elif self.master.bot.messageHasCommand(".ready", trailing): result = self.markReady(sender) if result: self.started = True - self.master.bot.act_PRIVMSG(self.channel,"All players are ready!") + self.master.bot.act_PRIVMSG(self.channel, "All players are ready!") for player in self.players: - self.master.bot.act_PRIVMSG(player,"ITS TIME TO D-D-D-D-D-DUEL!") - self.players[player]=[] + self.master.bot.act_PRIVMSG(player, "ITS TIME TO D-D-D-D-D-DUEL!") + self.players[player] = [] for player in self.players: self.deal(player) self.sendCards(player) self.active = True self.makeTurn() - elif self.master.bot.messageHasCommand(".pick",trailing): + elif self.master.bot.messageHasCommand(".pick", trailing): if self.active: if sender != self.czar: - print(sender,self.czar) - print(sender != self.czar) + print(sender, self.czar) + print(sender != self.czar) if self.allowPick > 0: if sender in self.players: cards = trailing.split(' ')[1:] - if len(cards)==self.allowPick: + if len(cards) == self.allowPick: if self.checkBounds(cards): if sender not in self.choices: cardChoices = [self.players[sender][int(index)] for index in cards] @@ -98,104 +96,123 @@ class cardsGame: self.removeAndReplenishCards(sender, cardChoices) self.sendCards(sender) del self.choices[sender] - if sender in timers: + if sender in self.timers: self.timers[sender].cancel() if self.allDrawn(): self.readChoices() - self.master.bot.act_PRIVMSG(self.channel,self.czar+"! Please choose the winner!") - czarTimer = Timer(180,self.kick,(self.czar,"taking too long to pick a choice. The next turn iwll be made.")) + self.master.bot.act_PRIVMSG(self.channel, + self.czar + "! Please choose the winner!") + self.czarTimer = Timer(180, self.kick, (self.czar, "taking too long to pick a " + "choice. The next turn iwll be made.")) self.makeTurn() - + else: - self.master.bot.act_PRIVMSG(self.channel,sender+", you picked a card that was out of the range. Please don't do that.") + self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked a card that was " + "out of the range. Please don't do that.") else: - self.master.bot.act_PRIVMSG(self.channel,sender+", you picked "+str(len(cards))+" cards. You were supposed to pick "+str(self.allowPick)) - elif self.master.bot.messageHasCommand(".choose",trailing): - if sender==self.czar: + self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked " + str(len(cards)) + + " cards. You were supposed to pick " + str(self.allowPick)) + elif self.master.bot.messageHasCommand(".choose", trailing): + if sender == self.czar: choice = trailing.split()[1:] - if len(choice)==1: + if len(choice) == 1: if self.checkChoiceBounds(int(choice[0])): - self.master.bot.act_PRIVMSG(self.channel,list(self.choices.keys())[int(choice[0])]+", you won the round!") - if self.czarTimer!=None: + self.master.bot.act_PRIVMSG(self.channel, list(self.choices.keys())[int(choice[0])] + ", you " + "won the round!") + if self.czarTimer is not None: self.czarTimer.cancel() self.makeTurn() else: - self.master.bot.act_PRIVMSG(self.channel,sender+", your choice was out of the range. Please don't do that.") + self.master.bot.act_PRIVMSG(self.channel, sender + ", your choice was out of the range. Please " + "don't do that.") else: - self.master.bot.act_PRIVMSG(self.channel,sender+", you picked "+str(len(choice))+" "+" winners. You were only supposed to pick 1.") - elif self.master.bot.messageHasCommand('.leave',trailing): + self.master.bot.act_PRIVMSG(self.channel, sender + ", you picked " + str(len(choice)) + " winners." + " You were only supposed to pick 1.") + elif self.master.bot.messageHasCommand('.leave', trailing): if sender in self.players: - self.kick(sender,'choosing to leave the game you dolt') + self.kick(sender, 'choosing to leave the game you dolt') if sender is self.czar: self.makeTurn() - - def join(self,nick): + + def join(self, nick): if not self.started: if nick not in self.players: - self.players[nick]=False - self.master.bot.act_PRIVMSG(self.channel, nick+" has joined the game! | The players currently are "+str(self.players)) + self.players[nick] = False + self.master.bot.act_PRIVMSG(self.channel, nick + " has joined the game! | The players currently are " + + str(self.players)) else: print("the game has already started!") - self.master.bot.act_PRIVMSG(self.channel,"The game has already started!") - def markReady(self,nick): + self.master.bot.act_PRIVMSG(self.channel, "The game has already started!") + + def markReady(self, nick): if not self.started: if nick in self.players: - self.players[nick]=True + self.players[nick] = True for player in self.players: print(player) if not self.players[player]: - print (player+" not ready") + print (player + " not ready") return False return True else: self.master.bot.act_PRIVMSG(self.channel, "You are not in the game! Type .joinGame!") else: print("game has already started!") - self.master.bot.act_PRIVMSG(self.channel,"The game has already started!") - def deal(self,nick): + self.master.bot.act_PRIVMSG(self.channel, "The game has already started!") + + def deal(self, nick): self.players[nick] = [self.pickWhite() for i in range (7)] + def pickWhite(self): card = choice(self.whites) self.whites.remove(card) return card + def pickBlack(self): card = choice(self.blacks) self.blacks.remove(card) return card - def sendCards(self,nick): + + def sendCards(self, nick): cards = "" for card in self.players[nick]: - cards+=str(self.players[nick].index(card))+". " - cards+=card+" " - self.master.bot.act_PRIVMSG(nick,"Your cards are "+cards) - def readCard(self,card): + cards += str(self.players[nick].index(card)) + ". " + cards += card + " " + self.master.bot.act_PRIVMSG(nick, "Your cards are " + cards) + + def readCard(self, card): count = card.count('_') if count == 0: if 'haiku' in card: count = 3 else: count = 1 - self.master.bot.act_PRIVMSG(self.channel,"The black card is \""+card+"\" Pick "+str(count)) + self.master.bot.act_PRIVMSG(self.channel, "The black card is \"" + card + "\" Pick " + str(count)) return count + def pickCzar(self): - index = self.lastCzar+1 + index = self.lastCzar + 1 if index < len(self.players): self.lastCzar = index return index else: self.lastCzar = 0 return 0 + def announceCzar(self): - self.master.bot.act_PRIVMSG(self.channel,"The Czar is "+self.czar+"!") - def checkBounds(self,cards): + self.master.bot.act_PRIVMSG(self.channel, "The Czar is " + self.czar + "!") + + def checkBounds(self, cards): for item in cards: - if int(item)>6 or int(item)<0: + if int(item) > 6 or int(item) < 0: return False return True - def checkChoiceBounds(self,choice): - if choice<0 or choice>len(self.choices)-1: + + def checkChoiceBounds(self, choice): + if choice < 0 or choice > len(self.choices) - 1: return False return True + def makeTurn(self): self.choices.clear() card = self.pickBlack() @@ -204,47 +221,51 @@ class cardsGame: self.allowPick = self.readCard(card) self.lastCzar = self.pickCzar() self.czar = list(self.players.keys())[self.lastCzar] - print (self.lastCzar,self.czar) + print (self.lastCzar, self.czar) for player in self.players: - if player!=self.czar: - self.timers[player] = Timer(180,self.kick,(player,"taking more than 180 seconds for their turn.")) + if player != self.czar: + self.timers[player] = Timer(180, self.kick, (player, "taking more than 180 seconds for their turn.")) self.timers[player].start() self.announceCzar() - def kick(self,nick,reason): + + def kick(self, nick, reason): del self.players[nick] if nick in self.timers: self.timers[nick].cancel() del self.timers[nick] - self.master.bot.act_PRIVMSG(self.channel,nick+" has been kicked due to "+reason) - if len(self.players)<=1: - self.master.bot.act_PRIVMSG(self.channel,"The game is being shut down due to having <=1 players") + self.master.bot.act_PRIVMSG(self.channel, nick + " has been kicked due to " + reason) + + if len(self.players) <= 1: + self.master.bot.act_PRIVMSG(self.channel, "The game is being shut down due to having <=1 players") self.started = False self.active = False for timer in self.timers: timer.cancel() self.timers.clear() self.players.clear() - def removeAndReplenishCards(self,nick,cards): + + def removeAndReplenishCards(self, nick, cards): for card in cards: self.players[nick].remove(card) self.players[nick].append(self.pickWhite()) + def readChoices(self): if '_' in self.currentBlack: for player in list(self.choices.keys()): - cardInstance = str(list(self.choices.keys()).index(player))+". "+self.currentBlack - cardInstance = list(cardInstance) #do this as opposed to space to preserve spaces - for choice in self.choices[player]: + cardInstance = str(list(self.choices.keys()).index(player)) + ". " + self.currentBlack + cardInstance = list(cardInstance) # do this as opposed to space to preserve spaces + for plr_choice in self.choices[player]: for char in cardInstance: - if char=='_': + if char == '_': print(char) - choice = choice.replace('.','') - cardInstance[cardInstance.index(char)] = choice + plr_choice = plr_choice.replace('.', '') + cardInstance[cardInstance.index(char)] = plr_choice break - self.master.bot.act_PRIVMSG(self.channel,''.join(cardInstance)) + self.master.bot.act_PRIVMSG(self.channel, ''.join(cardInstance)) else: for player in self.choices: - self.master.bot.act_PRIVMSG(self.channel,self.currentBlack+' '+' '.join(self.choices[player])) - + self.master.bot.act_PRIVMSG(self.channel, self.currentBlack + ' ' + ' '.join(self.choices[player])) + def allDrawn(self): for player in self.players: if player not in self.choices: diff --git a/pyircbot/modules/CryptoWallet.py b/pyircbot/modules/CryptoWallet.py index 9b09a53..ba16be9 100755 --- a/pyircbot/modules/CryptoWallet.py +++ b/pyircbot/modules/CryptoWallet.py @@ -7,185 +7,206 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook import time import hashlib + class CryptoWallet(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.handle_message)] - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [ModuleHook("PRIVMSG", self.handle_message)] + def getMods(self): return (self.bot.getBestModuleForService("attributes"), self.bot.getBestModuleForService("bitcoinrpc")) - + def handle_setaddr(self, args, prefix, trailing, cmd): usage = ".setaddr
" - attr,rpc = self.getMods() - + attr, rpc = self.getMods() + # Check for args - if not len(cmd.args)==2: + if not len(cmd.args) == 2: self.bot.act_PRIVMSG(args[0], ".setaddr: usage: %s" % usage) - #self.bot.act_PRIVMSG(args[0], ".setaddr: usage: .setaddr BTC 1xyWx6X5EABprhe3s9XduNxLn5NCtpSNB") + # self.bot.act_PRIVMSG(args[0], ".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(args[0], ".setaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + self.bot.act_PRIVMSG(args[0], ".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: + if len(cmd.args[1]) < 16 or len(cmd.args[1]) > 42: self.bot.act_PRIVMSG(args[0], ".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(args[0], ".setaddr: Your address has been saved as: %s. Please verify that this is correct or your coins could be lost." % (cmd.args[1])) - + attr.setKey(prefix.nick, "cryptowallet-%s-address" % cmd.args[0].lower(), cmd.args[1]) + self.bot.act_PRIVMSG(args[0], ".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,rpc = self.getMods() + attr, rpc = self.getMods() # Check for args - if not len(cmd.args)==1: + if not len(cmd.args) == 1: self.bot.act_PRIVMSG(args[0], ".getbal: usage: %s" % usage) self.bot.act_PRIVMSG(args[0], ".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(args[0], ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + self.bot.act_PRIVMSG(args[0], ".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()) + 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(args[0], "%s: your balance is: %s %s" % (prefix.nick, amount, cmd.args[0].upper())) - + def handle_withdraw(self, args, prefix, trailing, cmd): usage = ".withdraw " - attr,rpc = self.getMods() + attr, rpc = self.getMods() # Check for args - if not len(cmd.args)==2: + if not len(cmd.args) == 2: self.bot.act_PRIVMSG(args[0], ".withdraw: usage: %s" % usage) self.bot.act_PRIVMSG(args[0], ".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(args[0], ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + self.bot.act_PRIVMSG(args[0], ".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(args[0], ".withdraw: You need to set a withdraw address before withdrawing. Try .setaddr") + withdrawaddr = attr.getKey(prefix.nick, "cryptowallet-%s-address" % cmd.args[0].lower()) + if withdrawaddr is None: + self.bot.act_PRIVMSG(args[0], ".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()) + 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(args[0], ".withdraw: You don't have enough %s to withdraw %s" % (cmd.args[0].upper(), withdrawamount)) + + if balance < withdrawamount or withdrawamount < 0: + self.bot.act_PRIVMSG(args[0], ".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(args[0], ".withdraw: Withdrawing that much would put you below the reserve (%s %s)." % (client.reserve, cmd.args[0].upper())) - self.bot.act_PRIVMSG(args[0], ".withdraw: The reserve is to cover network transaction fees. To recover it you must close your account. (Talk to an admin)") + self.bot.act_PRIVMSG(args[0], ".withdraw: Withdrawing that much would put you below the reserve (%s %s)." % + (client.reserve, cmd.args[0].upper())) + self.bot.act_PRIVMSG(args[0], ".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(args[0], ".withdraw: %s has maximum %s decimal places" % (cmd.args[0].upper(), client.precision)) + self.bot.act_PRIVMSG(args[0], ".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(args[0], "%s: .withdraw: %s %s sent to %s. "% (prefix.nick, withdrawamount, client.name, withdrawaddr)) - self.bot.act_PRIVMSG(prefix.nick, "Withdrawal: (You)->%s: Transaction ID: %s" % (prefix.nick, withdrawaddr, txn)) + self.bot.act_PRIVMSG(args[0], "%s: .withdraw: %s %s sent to %s. " % + (prefix.nick, withdrawamount, client.name, withdrawaddr)) + self.bot.act_PRIVMSG(prefix.nick, "Withdrawal: (You)->%s: Transaction ID: %s" % + (prefix.nick, withdrawaddr, txn)) else: - self.bot.act_PRIVMSG(args[0], "%s: .withdraw: Transaction create failed. Maybe the transaction was too large for the network? Try a smaller increment." % prefix.nick) - + self.bot.act_PRIVMSG(args[0], "%s: .withdraw: Transaction create failed. Maybe the transaction was too " + "large for the network? Try a smaller increment." % prefix.nick) + def handle_send(self, args, prefix, trailing, cmd): usage = ".send " - attr,rpc = self.getMods() + attr, rpc = self.getMods() # Check for args - if not len(cmd.args)==3: + if not len(cmd.args) == 3: self.bot.act_PRIVMSG(args[0], ".withdraw: usage: %s" % usage) self.bot.act_PRIVMSG(args[0], ".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(args[0], ".getbal: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + self.bot.act_PRIVMSG(args[0], ".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 check balance - walletname = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0].lower()) + 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(args[0], "%s: .send: You don't have enough %s to send %s" % (prefix.nick, cmd.args[0].upper(), withdrawamount)) + + if balance < withdrawamount or withdrawamount < 0: + self.bot.act_PRIVMSG(args[0], "%s: .send: You don't have enough %s to send %s" % + (prefix.nick, cmd.args[0].upper(), withdrawamount)) return - + # Check if the precision is wrong if not client.checkPrecision(withdrawamount): - self.bot.act_PRIVMSG(args[0], ".send: %s has maximum %s decimal places" % (cmd.args[0].upper(), client.precision)) + self.bot.act_PRIVMSG(args[0], ".send: %s has maximum %s decimal places" % + (cmd.args[0].upper(), client.precision)) return - + # Check if the recierver is a dogecoin address - if len(cmd.args[2]) == 34 and cmd.args[2][0:1]=="D": + if len(cmd.args[2]) == 34 and cmd.args[2][0:1] == "D": # Check if we can cover network fees if not client.reserve == 0 and balance - client.reserve < withdrawamount: - self.bot.act_PRIVMSG(args[0], ".send: Sending that much would put you below the reserve (%s %s)." % (client.reserve, cmd.args[0].upper())) - self.bot.act_PRIVMSG(args[0], ".send: The reserve is to cover network transaction fees. To recover it you must close your account. (Talk to an admin)") + self.bot.act_PRIVMSG(args[0], ".send: Sending that much would put you below the reserve (%s %s)." % + (client.reserve, cmd.args[0].upper())) + self.bot.act_PRIVMSG(args[0], ".send: The reserve is to cover network transaction fees. To recover it " + "you must close your account. (Talk to an admin)") return - + # Create a transaction txn = client.send(walletname, cmd.args[2], withdrawamount) if txn: - self.bot.act_PRIVMSG(args[0], "%s: .send: %s %s sent to %s. "% (prefix.nick, withdrawamount, client.name, cmd.args[2])) + self.bot.act_PRIVMSG(args[0], "%s: .send: %s %s sent to %s. " % + (prefix.nick, withdrawamount, client.name, cmd.args[2])) self.bot.act_PRIVMSG(prefix.nick, "Send: (You)->%s: Transaction ID: %s" % (cmd.args[2], txn)) else: - self.bot.act_PRIVMSG(args[0], "%s: .send: Transaction create failed. Maybe the address is invalid or transaction too large for the network? Try a smaller increment."%prefix.nick) - + self.bot.act_PRIVMSG(args[0], "%s: .send: Transaction create failed. Maybe the address is invalid or " + "transaction too large for the network? Try a smaller increment." % + prefix.nick) + else: # Move between local wallets # Check if dest user has a password set destUserPassword = attr.getKey(cmd.args[2], "password") - if destUserPassword == None: + if destUserPassword is None: self.bot.act_PRIVMSG(args[0], "%s .send: %s doesn't have a password set." % (prefix.nick, cmd.args[2])) return - + # Since the user has a password set, check that they have a wallet and create if not self.checkUserHasWallet(cmd.args[2], cmd.args[0]) - - srcWalletName = attr.getKey(prefix.nick, "cryptowallet-account-%s"%cmd.args[0]) - destWalletName = attr.getKey(cmd.args[2], "cryptowallet-account-%s"%cmd.args[0]) - + + srcWalletName = attr.getKey(prefix.nick, "cryptowallet-account-%s" % cmd.args[0]) + destWalletName = attr.getKey(cmd.args[2], "cryptowallet-account-%s" % cmd.args[0]) + assert srcWalletName is not None assert destWalletName is not None try: @@ -197,100 +218,105 @@ class CryptoWallet(ModuleBase): print(destWalletName) if client.canMove(srcWalletName, destWalletName, withdrawamount): if client.move(srcWalletName, destWalletName, withdrawamount): - self.bot.act_PRIVMSG(args[0], "%s .send: %s %s sent to %s. "% (prefix.nick, withdrawamount, client.name, cmd.args[2])) - else: + self.bot.act_PRIVMSG(args[0], "%s .send: %s %s sent to %s. " % + (prefix.nick, withdrawamount, client.name, cmd.args[2])) + else: self.bot.act_PRIVMSG(args[0], "%s: uh-oh, something went wrong doing that." % prefix.nick) - + def handle_getaddr(self, args, prefix, trailing, cmd): - attr,rpc = self.getMods() + attr, rpc = self.getMods() usage = ".getaddr " # Check for args - if not len(cmd.args)==1: + if not len(cmd.args) == 1: self.bot.act_PRIVMSG(args[0], ".getaddr: usage: %s" % usage) self.bot.act_PRIVMSG(args[0], ".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(args[0], ".getaddr: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], supportedStr)) + self.bot.act_PRIVMSG(args[0], ".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(args[0], "%s: your %s deposit address is: %s" % (prefix.nick, cmd.args[0].upper(), walletaddr)) - + + walletaddr = attr.getKey(prefix.nick, "cryptowallet-depoaddr-%s" % cmd.args[0].lower()) + self.bot.act_PRIVMSG(args[0], "%s: your %s deposit address is: %s" % + (prefix.nick, cmd.args[0].upper(), walletaddr)) + def handle_curinfo(self, args, prefix, trailing, cmd): - attr,rpc = self.getMods() - usage = ".curinfo []" - + attr, rpc = self.getMods() + # Check for args - if len(cmd.args)==0: - self.bot.act_PRIVMSG(args[0], ".curinfo: supported currencies: %s. Use '.curinfo BTC' to see details. " % ', '.join([x.upper() for x in rpc.getSupported()])) + if len(cmd.args) == 0: + self.bot.act_PRIVMSG(args[0], ".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(args[0], ".curinfo: '%s' is not a supported currency. Supported currencies are: %s" % (cmd.args[0], ', '.join([x.upper() for x in rpc.getSupported()]))) + self.bot.act_PRIVMSG(args[0], ".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(args[0], ".curinfo: %s - %s. More info: %s" % (args[0], info["name"], info["link"])) - + self.bot.act_PRIVMSG(args[0], ".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,rpc = self.getMods() + attr, rpc = self.getMods() currency = currency.lower() username = username.lower() - if attr.getKey(username, "cryptowallet-account-%s"%currency)==None: + if attr.getKey(username, "cryptowallet-account-%s" % currency) is None: randName = self.md5(str(time.time()))[0:16] - attr.setKey(username, "cryptowallet-account-%s"%currency, randName) + 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) + attr.setKey(username, "cryptowallet-depoaddr-%s" % currency, address) + elif attr.getKey(username, "cryptowallet-depoaddr-%s" % currency) is 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) - + attr.setKey(username, "cryptowallet-depoaddr-%s" % currency, address) + def handle_message(self, args, prefix, trailing): prefix = self.bot.decodePrefix(prefix) - + # Free commands cmd = self.bot.messageHasCommand(".curinfo", trailing) if cmd: self.handle_curinfo(args, prefix, trailing, cmd) - + # Login protected commands cmd = self.bot.messageHasCommand(".setaddr", trailing) if cmd and self.check_login(prefix, args[0]): self.handle_setaddr(args, prefix, trailing, cmd) - + cmd = self.bot.messageHasCommand(".getbal", trailing) if cmd and self.check_login(prefix, args[0]): self.handle_getbal(args, prefix, trailing, cmd) - + cmd = self.bot.messageHasCommand(".withdraw", trailing) if cmd and self.check_login(prefix, args[0]): self.handle_withdraw(args, prefix, trailing, cmd) - + cmd = self.bot.messageHasCommand(".getaddr", trailing) if cmd and self.check_login(prefix, args[0]): self.handle_getaddr(args, prefix, trailing, cmd) - + cmd = self.bot.messageHasCommand(".send", trailing) if cmd and self.check_login(prefix, args[0]): self.handle_send(args, prefix, trailing, cmd) - + def check_login(self, prefix, replyTo): login = self.bot.getBestModuleForService("login") if not login.check(prefix.nick, prefix.hostname): self.bot.act_PRIVMSG(replyTo, "%s: Please .login to use this command." % prefix.nick) return False return True - + def md5(self, data): m = hashlib.md5() m.update(data.encode("ascii")) diff --git a/pyircbot/modules/CryptoWalletRPC.py b/pyircbot/modules/CryptoWalletRPC.py index dc28db9..ac18584 100755 --- a/pyircbot/modules/CryptoWalletRPC.py +++ b/pyircbot/modules/CryptoWalletRPC.py @@ -7,31 +7,30 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase from bitcoinrpc.authproxy import AuthServiceProxy -from math import floor 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={} - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] + self.services = ["bitcoinrpc"] + 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 - + 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"]) + def getRpc(self, currencyAbbr): # Return the rpc for the currency requested # self.getRpc("LTC") -> returns a litecoin rpc instance @@ -39,21 +38,21 @@ class CryptoWalletRPC(ModuleBase): 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): @@ -67,67 +66,66 @@ class BitcoinRPC: 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 + # 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 + # returns the address for an account. creates if necessary self.ping() addrs = self.con.getaddressesbyaccount(acct) - if len(addrs)==0: + 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 + # 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 + # 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 + # 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.info("CryptoWalletRPC: %s: Connecting to %s:%s" % (self.name, self.host,self.port)) + self.log.info("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.info("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % (self.name, self.host, self.port, str(e))) + self.log.error("CryptoWalletRPC: %s: Could not connect to %s:%s: %s" % + (self.name, self.host, self.port, str(e))) return - - self.log.info("CryptoWalletRPC: %s: Connected to %s:%s" % (self.name, self.host, self.port)) - + self.log.info("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 4990a70..ba07774 100755 --- a/pyircbot/modules/DogeDice.py +++ b/pyircbot/modules/DogeDice.py @@ -7,57 +7,56 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase ModuleHook import random -import os -import time import math -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() + self.hooks = [ModuleHook("PRIVMSG", self.gotMsg)] # 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: + if loggedinfrom is 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]) + 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) + # 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: + 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 @@ -75,7 +74,7 @@ class gameObj: # 4 = determine winner, move doge # - if > 10 doge, house fee? self.step = 0 - + # Bet amount self.bet = 0.0 # players list @@ -88,80 +87,88 @@ class gameObj: self.startCountdownTimer = None # pre-result timer self.endgameResultTimer = None - # in-game timeout + # 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: + if len(self.players) - 1 < self.maxPlayers: + if self.getPlayer(prefix.nick) is None: userWallet = self.master.attr.getKey(prefix.nick, "dogeaccountname") - if userWallet == None: + if userWallet is 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: + if not self.players: # require an amount - if len(cmd.args)==1: + 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"])) + self.master.bot.act_PRIVMSG(self.channel, "%s: Minimum bet is %s DOGE!" % + (prefix.nick, self.master.config["minBet"])) return - - if balance>=bet: + + 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)) + 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)) + 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: + 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: + if self.canStart() and self.startCountdownTimer is 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)) + 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)) - + 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)) + 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)) + 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: + if self.getPlayer(prefix.nick) is None: self.master.bot.act_PRIVMSG(self.channel, "%s: You're not in the game." % (prefix.nick)) else: self.removePlayer(prefix.nick) @@ -169,7 +176,7 @@ class gameObj: 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.master.bot.act_PRIVMSG(self.channel, "Game canceled.") self.step = 0 elif self.step == 2: pass @@ -178,35 +185,34 @@ class gameObj: 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) + 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 - + 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)): @@ -215,49 +221,50 @@ class gameObj: 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 + 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 + 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.") @@ -267,7 +274,7 @@ class gameObj: for player in self.players: self.master.doge.move(self.walletName, player.dogeWalletName, self.bet) self.resetGame() - + def endgameResults(self): maxRollNames = [] maxRollValue = 0 @@ -277,26 +284,28 @@ class gameObj: maxRollNames.append(player.nick) maxRollValue = player.rollValue if player.rollValue == maxRollValue: - if not player.nick in maxRollNames: + if player.nick not 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)) + 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)) - + 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 @@ -305,10 +314,10 @@ class gameObj: self.clearTimer(self.playTimeout) self.playTimeout = None self.master.removeGame(self.channel) - + def gameover(self): self.gamePlayTimeoutExpired() - + class playerObj: def __init__(self, game, nick): @@ -320,4 +329,4 @@ class playerObj: self.hasRolled = False # Sum of their dice self.rollValue = None - + diff --git a/pyircbot/modules/DogeRPC.py b/pyircbot/modules/DogeRPC.py index 9912fa6..ff11fda 100755 --- a/pyircbot/modules/DogeRPC.py +++ b/pyircbot/modules/DogeRPC.py @@ -7,46 +7,46 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase from bitcoinrpc.authproxy import AuthServiceProxy + class DogeRPC(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[] - self.services=["dogerpc"] - self.loadConfig() + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] + self.services = ["dogerpc"] self.rpc = DogeController(self) - + def getBal(self, acct): "Get a balance of a local address or an account " return self.getAcctBal(acct) - + def getAcctAddr(self, acct): "Returns the address for an account. creates if necessary " self.rpc.ping() addrs = self.rpc.con.getaddressesbyaccount(acct) - if len(addrs)==0: + if len(addrs) == 0: return self.rpc.con.getnewaddress(acct) return addrs[0] - + def getAcctBal(self, acct): "Returns an account's balance" self.rpc.ping() return float(self.rpc.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.rpc.ping() if self.canMove(fromAcct, toAcct, amount): return self.rpc.con.move(fromAcct, toAcct, amount) return False - + def send(self, fromAcct, toAddr, amount): "Send coins to an external addr " self.rpc.ping() @@ -54,6 +54,7 @@ class DogeRPC(ModuleBase): return self.rpc.con.sendfrom(fromAcct, toAddr, amount) return False + class DogeController: "RPC instance control class" def __init__(self, master): @@ -61,7 +62,7 @@ class DogeController: self.log = master.log self.con = None self.ping() - + def ping(self): "Test connection and re-establish if necessary" if not self.con: @@ -70,10 +71,11 @@ class DogeController: self.con.getinfo() except: self.connect() - + def connect(self): "Connect to RPC endpoint" self.log.info("DogeRPC: Connecting to dogecoind") - self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.config["username"], self.config["password"], self.config["host"], self.config["port"])) + self.con = AuthServiceProxy("http://%s:%s@%s:%s" % (self.config["username"], self.config["password"], + self.config["host"], self.config["port"])) self.con.getinfo() self.log.info("DogeRPC: Connected to %s:%s" % (self.config["host"], self.config["port"])) diff --git a/pyircbot/modules/DogeScramble.py b/pyircbot/modules/DogeScramble.py index b395150..47d5599 100755 --- a/pyircbot/modules/DogeScramble.py +++ b/pyircbot/modules/DogeScramble.py @@ -7,48 +7,48 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook import random import os -import time from threading import Timer + class DogeScramble(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.scramble)] - self.loadConfig() - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [ModuleHook("PRIVMSG", self.scramble)] # Load attribute storage self.attr = None serviceProviders = self.bot.getmodulesbyservice("attributes") - if len(serviceProviders)==0: + if len(serviceProviders) == 0: self.log.error("DogeScramble: Could not find a valid attributes service provider") else: self.log.info("DogeScramble: Selecting attributes service provider: %s" % serviceProviders[0]) self.attr = serviceProviders[0] - + # Load doge RPC self.doge = self.bot.getBestModuleForService("dogerpc") - + # Per channel games self.games = {} - + def scramble(self, args, prefix, trailing): channel = args[0] if channel[0] == "#": # Ignore messages from users without a dogewallet password prefixObj = self.bot.decodePrefix(prefix) - if self.attr.getKey(prefixObj.nick, "password")==None: + if self.attr.getKey(prefixObj.nick, "password") is None: return - if not channel in self.games: - self.games[channel]=scrambleGame(self, channel) + if channel not in self.games: + self.games[channel] = scrambleGame(self, channel) self.games[channel].scramble(args, prefix, trailing) + def ondisable(self): self.log.info("DogeScramble: Unload requested, ending games...") for game in self.games: self.games[game].gameover() + class scrambleGame: def __init__(self, master, channel): self.master = master @@ -68,12 +68,12 @@ class scrambleGame: # Cooldown between words self.nextTimer = None # How many guesses submitted this round - self.guesses = 0; + self.guesses = 0 # How many games in a row where nobody guessed - self.gamesWithoutGuesses = 0; + self.gamesWithoutGuesses = 0 # What file are we using - self.category_file = None; - # How many words in this category have been used? + self.category_file = None + # How many words in this category have been used? self.category_count = 0 # How long between categories self.change_category_after_words = self.master.config["categoryduration"] @@ -86,30 +86,33 @@ class scrambleGame: # name of last winner for decreasing return self.lastwinner = None self.lastwinvalue = 0 - - self.delayHint = self.master.config["hintDelay"]; - self.delayNext = self.master.config["delayNext"]; - self.maxHints = self.master.config["maxHints"]; - self.abortAfterNoGuesses = self.master.config["abortAfterNoGuesses"]; - + + self.delayHint = self.master.config["hintDelay"] + self.delayNext = self.master.config["delayNext"] + self.maxHints = self.master.config["maxHints"] + self.abortAfterNoGuesses = self.master.config["abortAfterNoGuesses"] + def gameover(self): - self.clearTimers(); + self.clearTimers() self.running = False + def clearTimers(self): self.clearTimer(self.nextTimer) self.clearTimer(self.hintTimer) + def clearTimer(self, timer): if timer: timer.cancel() + def scramble(self, args, prefix, trailing): prefix = self.master.bot.decodePrefix(prefix) sender = prefix.nick - - senderIsOp = self.master.attr.getKey(prefix.nick, "op")=="yes" - + + senderIsOp = self.master.attr.getKey(prefix.nick, "op") == "yes" + cmd = self.master.bot.messageHasCommand(".scramble", trailing) if cmd and not self.running: - #and senderIsOp + # and senderIsOp self.running = True self.startScramble() return @@ -118,100 +121,105 @@ class scrambleGame: self.gameover() self.running = False return - + if self.currentWord and trailing.strip().lower() == self.currentWord: # Get winner withdraw address useraddr = self.master.attr.getKey(prefix.nick, "dogeaddr") userwallet = self.master.attr.getKey(prefix.nick, "dogeaccountname") - + self.master.bot.act_PRIVMSG(self.channel, "%s got the word - %s!" % (sender, self.currentWord)) - + if not useraddr: - self.master.bot.act_PRIVMSG(self.channel, "%s: to win DOGE, you must set an wallet address by PMing me \".setdogeaddr\". Next word in %s seconds." % (prefix.nick, self.delayNext)) + self.master.bot.act_PRIVMSG(self.channel, "%s: to win DOGE, you must set an wallet address by PMing me " + "\".setdogeaddr\". Next word in %s seconds." % + (prefix.nick, self.delayNext)) else: winamount = float(self.master.config["winAmount"]) if self.lastwinner == prefix.nick: winamount = self.lastwinvalue * self.master.config["decreaseFactor"] self.lastwinvalue = winamount self.lastwinner = prefix.nick - - self.master.bot.act_PRIVMSG(self.channel, "%s won %s DOGE! Next word in %s seconds." % (prefix.nick, round(winamount, 8), self.delayNext)) + + self.master.bot.act_PRIVMSG(self.channel, "%s won %s DOGE! Next word in %s seconds." % + (prefix.nick, round(winamount, 8), self.delayNext)) self.master.doge.move('', userwallet, winamount) - + self.currentWord = None self.clearTimers() self.hintsGiven = 0 self.nextTimer = Timer(self.delayNext, self.startNewWord) self.nextTimer.start() - self.guesses=0 - self.category_count+=1 + self.guesses = 0 + self.category_count += 1 self.master.log.info("DogeScramble: category_count is: %s" % (self.category_count)) if self.category_count >= self.change_category_after_words: self.should_change_category = True else: - self.guesses+=1 - + self.guesses += 1 + def startScramble(self): self.clearTimer(self.nextTimer) self.nextTimer = Timer(0, self.startNewWord) self.nextTimer.start() - + def startNewWord(self): self.currentWord = self.pickWord() self.scrambled = self.scrambleWord(self.currentWord) - self.master.bot.act_PRIVMSG(self.channel, "[Category: %s] Unscramble this: %s " % (self.category_name, self.scrambled)) - + self.master.bot.act_PRIVMSG(self.channel, "[Category: %s] Unscramble this: %s " % + (self.category_name, self.scrambled)) + self.clearTimer(self.hintTimer) self.hintTimer = Timer(self.delayHint, self.giveHint) self.hintTimer.start() - + def giveHint(self): - self.hintsGiven+=1 - - if self.hintsGiven>=len(self.currentWord) or self.hintsGiven > self.maxHints: + self.hintsGiven += 1 + + if self.hintsGiven >= len(self.currentWord) or self.hintsGiven > self.maxHints: self.abortWord() return - + blanks = "" for letter in list(self.currentWord): if letter == " ": - blanks+=" " + blanks += " " else: - blanks+="_" + blanks += "_" partFromWord = self.currentWord[0:self.hintsGiven] partFromBlanks = blanks[self.hintsGiven:] - hintstr = partFromWord+partFromBlanks - + hintstr = partFromWord + partFromBlanks + self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr)) - + self.clearTimer(self.hintTimer) self.hintTimer = Timer(self.delayHint, self.giveHint) self.hintTimer.start() - + def abortWord(self): cur = self.currentWord self.currentWord = None - self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was '%s'. Next word in %s seconds." % (cur, self.delayNext)) + self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was '%s'. Next word in %s seconds." % + (cur, self.delayNext)) self.hintsGiven = 0 self.clearTimer(self.nextTimer) - - if self.guesses==0: - self.gamesWithoutGuesses+=1 + + if self.guesses == 0: + self.gamesWithoutGuesses += 1 if self.gamesWithoutGuesses >= self.abortAfterNoGuesses: self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scramble to start again.") self.gameover() return else: - self.gamesWithoutGuesses=0 - + self.gamesWithoutGuesses = 0 + self.nextTimer = Timer(self.delayNext, self.startNewWord) self.nextTimer.start() - + def catFileNameToStr(self, s): - s=s.split(".")[0] - s=s.replace("_", " ") + s = s.split(".")[0] + s = s.replace("_", " ") return s.title() - + def pickWord(self): if self.should_change_category: # clear flags @@ -231,34 +239,33 @@ class scrambleGame: f = open(self.master.getFilePath(self.category_file), "r") lines = 0 while True: - lines+=1 + lines += 1 if f.readline() == "": break f.close() # change category picked = "" while picked == "" or picked in self.lastwords: - + skip = random.randint(0, lines) f = open(self.master.getFilePath(self.category_file), "r") - while skip>=0: + while skip >= 0: f.readline() - skip-=1 + skip -= 1 picked = f.readline().strip().lower() f.close() - self.master.log.info("DogeScramble: picked %s for %s" % (picked, self.channel)) self.lastwords.append(picked) if len(self.lastwords) > 5: self.lastwords.pop(0) return picked - + def scrambleWord(self, word): scrambled = "" for subword in word.split(" "): - scrambled+=self.scrambleIndividualWord(subword)+ " " + scrambled += self.scrambleIndividualWord(subword) + " " return scrambled.strip() - + def scrambleIndividualWord(self, word): scrambled = list(word) random.shuffle(scrambled) diff --git a/pyircbot/modules/DogeWallet.py b/pyircbot/modules/DogeWallet.py index a69b72a..2f393cf 100755 --- a/pyircbot/modules/DogeWallet.py +++ b/pyircbot/modules/DogeWallet.py @@ -7,19 +7,20 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook import time import hashlib + class DogeWallet(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] + 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] == "#": @@ -27,107 +28,116 @@ class DogeWallet(ModuleBase): 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\"") + 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: + if oldpass is 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 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]) + 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.") + 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".setdogeaddr: You must first set a password with .setpass") else: - if len(cmd.args)==2: + 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]) + 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: + if self.attr.getKey(prefix.nick, "dogeaccountname") is 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\"") - + 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".getdogebal: You must first set a password with .setpass") else: - if len(cmd.args)==1: + 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a password with .setpass") - elif useraddr==None: + elif useraddr is None: self.bot.act_PRIVMSG(prefix.nick, ".withdrawdoge: You must first set a withdraw address .setdogeaddr") else: - if len(cmd.args)==2: + 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)) + 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.") + 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)) + 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\" - ") - + 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".getdepositaddr: You must first set a password with .setpass") else: - if len(cmd.args)==1: + if len(cmd.args) == 1: if userpw == cmd.args[0]: ################# walletname = self.attr.getKey(prefix.nick, "dogeaccountname") @@ -138,16 +148,14 @@ class DogeWallet(ModuleBase): 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") else: - if len(cmd.args)==1: + if len(cmd.args) == 1: if userpw == cmd.args[0]: ################# self.attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) @@ -160,12 +168,12 @@ class DogeWallet(ModuleBase): cmd = self.bot.messageHasCommand(".logout", trailing) if cmd: loggedin = self.attr.getKey(prefix.nick, "loggedinfrom") - if loggedin == None: + if loggedin is 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")) diff --git a/pyircbot/modules/DuckHunt.py b/pyircbot/modules/DuckHunt.py index 78bbc97..3621daa 100755 --- a/pyircbot/modules/DuckHunt.py +++ b/pyircbot/modules/DuckHunt.py @@ -7,36 +7,36 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook import time import json import random from threading import Timer import os + class DuckHunt(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.hunt)] - self.loadConfig() - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [ModuleHook("PRIVMSG", self.hunt)] + self.jsonPath = self.getFilePath("scores.json") - + self.timer = None self.isDuckOut = False self.outStart = 0 self.misses = {} - + self.startHunt() - + def hunt(self, args, prefix, trailing): prefixObj = self.bot.decodePrefix(prefix) fromWho = prefixObj.nick - + cmd = self.bot.messageHasCommand("!huntscore", trailing, False) if cmd: scores = self.loadScores() - if not fromWho in scores: + if fromWho not in scores: self.bot.act_PRIVMSG(fromWho, "You have no points :(") else: scores = scores[fromWho] @@ -48,121 +48,123 @@ class DuckHunt(ModuleBase): misses = 0 for kill in scores: if kill["prime"]: - prime+=1 + prime += 1 if kill["runt"]: - runts+=1 - kills+=1 - weight+=kill["weight"] - shots+=1 - shots+=kill["misses"] - misses+=kill["misses"] - - self.bot.act_PRIVMSG(fromWho, "You've shot %s %s for a total weight of %s lbs." % (kills, self.config["animalSpeciesPlural"], weight)) - self.bot.act_PRIVMSG(fromWho, "%s prime catches, %s runts, %s bullets used and %s misses." % (prime, runts, shots, misses)) - #self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/") + runts += 1 + kills += 1 + weight += kill["weight"] + shots += 1 + shots += kill["misses"] + misses += kill["misses"] + + self.bot.act_PRIVMSG(fromWho, "You've shot %s %s for a total weight of %s lbs." % + (kills, self.config["animalSpeciesPlural"], weight)) + self.bot.act_PRIVMSG(fromWho, "%s prime catches, %s runts, %s bullets used and %s misses." % + (prime, runts, shots, misses)) + # self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/") self.log.info("DuckHunt: %s used !huntscore" % fromWho) return - + # Channel only - if not args[0][0]=="#": + if not args[0][0] == "#": return - + cmd = self.bot.messageHasCommand("!shoot", trailing, False) if cmd: if self.isDuckOut: - - if not fromWho in self.misses: - self.misses[fromWho]=0 - + + if fromWho not in self.misses: + self.misses[fromWho] = 0 + shotIn = round(time.time() - self.outStart, 2) - + if random.randint(0, 100) <= self.config["missChance"]: - self.bot.act_PRIVMSG(self.config["activeChannel"], "%s fires after %s seconds and misses!" % (fromWho, shotIn)) - self.misses[fromWho]+=1 + self.bot.act_PRIVMSG(self.config["activeChannel"], "%s fires after %s seconds and misses!" % + (fromWho, shotIn)) + self.misses[fromWho] += 1 return - + self.isDuckOut = False - + bagged = { - "species":self.config["animalSpecies"], - "gender":"M" if random.randint(0,1)==1 else "F", - "time":shotIn, - "prime":False, - "runt":False, - "weight":0.0, - "date":time.time(), - "misses":self.misses[fromWho] + "species": self.config["animalSpecies"], + "gender": "M" if random.randint(0, 1) == 1 else "F", + "time": shotIn, + "prime": False, + "runt": False, + "weight": 0.0, + "date": time.time(), + "misses": self.misses[fromWho] } - + message = "%s %s " % (fromWho, "bags") - + if random.randint(0, 100) <= self.config["primeChance"]: - bagged["prime"]=True - bagged["weight"]=self.getRandWeight(self.config["weightMax"], self.config["weightFat"]) + bagged["prime"] = True + bagged["weight"] = self.getRandWeight(self.config["weightMax"], self.config["weightFat"]) message += "a prime catch, a " elif random.randint(0, 100) <= self.config["runtChance"]: - bagged["runt"]=True - bagged["weight"]=self.getRandWeight(self.config["weightRunt"], self.config["weightMin"]) + bagged["runt"] = True + bagged["weight"] = self.getRandWeight(self.config["weightRunt"], self.config["weightMin"]) message += "a runt of a catch, a " else: - bagged["weight"]=self.getRandWeight(self.config["weightMin"], self.config["weightMax"]) + bagged["weight"] = self.getRandWeight(self.config["weightMin"], self.config["weightMax"]) message += "a " - + message += "%s lb " % (bagged["weight"]) - if bagged["gender"]=="M": - message += self.config["animalNameMale"]+" " + if bagged["gender"] == "M": + message += self.config["animalNameMale"] + " " else: - message += self.config["animalNameFemale"]+" " - + message += self.config["animalNameFemale"] + " " + message += "in %s seconds!" % shotIn self.bot.act_PRIVMSG(self.config["activeChannel"], message) - + self.addKillFor(fromWho, bagged) - + self.misses = {} - - self.startHunt(); - - + + self.startHunt() + def startHunt(self): " Creates a timer that waits a certain amount of time then sends out a bird \\_o< quack" - delay = self.config["delayMin"] + random.randint(0, self.config["delayMax"]-self.config["delayMin"]) + delay = self.config["delayMin"] + random.randint(0, self.config["delayMax"] - self.config["delayMin"]) self.timer = Timer(delay, self.duckOut) self.timer.start() print("DuckHunt: Sending out animal in %s seconds" % delay) - + def duckOut(self): self.isDuckOut = True self.bot.act_PRIVMSG(self.config["activeChannel"], self.config["animal"]) self.outStart = time.time() - + def getRandWeight(self, minW, maxW): - weight = maxW-minW; - weight = float(weight)*random.random() - return round(weight+minW, 2) - + weight = maxW - minW + weight = float(weight) * random.random() + return round(weight + minW, 2) + def getScoreFor(self, playername): return 1 - + def getScoresFor(self, playername): return 1 - + def addKillFor(self, playername, kill): scores = self.loadScores() - if scores==None: + if scores is None: scores = {} - if not playername in scores: - scores[playername]=[] + if playername not in scores: + scores[playername] = [] scores[playername].append(kill) self.saveScores(scores) - + def loadScores(self): if not os.path.exists(self.jsonPath): json.dump({}, open(self.jsonPath, 'w')) return json.load(open(self.jsonPath, 'r')) - + def saveScores(self, scores): json.dump(scores, open(self.jsonPath, 'w')) - + def ondisable(self): self.timer.cancel() diff --git a/pyircbot/modules/Error.py b/pyircbot/modules/Error.py index 9bffb89..167836e 100644 --- a/pyircbot/modules/Error.py +++ b/pyircbot/modules/Error.py @@ -1,3 +1,4 @@ + """ .. module:: Error :synopsis: Module to deliberately cause an error for testing handling. @@ -6,17 +7,17 @@ """ -#!/usr/bin/env python -from pyircbot.modulebase import ModuleBase,ModuleHook +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)] - + 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 @@ -24,5 +25,5 @@ class Error(ModuleBase): :param trailing: IRC message body :type trailing: str""" if "error" in trailing: - print(10/0) + print(10 / 0) diff --git a/pyircbot/modules/GameBase.py b/pyircbot/modules/GameBase.py index 20dcf2f..b7d6d4d 100755 --- a/pyircbot/modules/GameBase.py +++ b/pyircbot/modules/GameBase.py @@ -7,66 +7,64 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook -import random -import os -import time -from threading import Timer +from pyircbot.modulebase import ModuleBase, ModuleHook + class GameBase(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotMsg)] - self.loadConfig() + 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") # 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? + if not self.attr.getKey(prefixObj.nick, "loggedinfrom"): + # TODO 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]) + 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) + # 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" + + # 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 - diff --git a/pyircbot/modules/Inventory.py b/pyircbot/modules/Inventory.py index 1d03c88..6fca7d5 100755 --- a/pyircbot/modules/Inventory.py +++ b/pyircbot/modules/Inventory.py @@ -7,21 +7,22 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +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=[] + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] self.db = None serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: + if not serviceProviders: 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` ( @@ -31,77 +32,76 @@ class Inventory(ModuleBase): `item` varchar(64) ) ;""") c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.checkInv)] - + + self.hooks = [ModuleHook("PRIVMSG", self.checkInv)] + def checkInv(self, args, prefix, trailing): - if not args[0][0]=="#": + 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: + 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,}) + 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)}) + 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 "}) - + 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!"}) + 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)}) - - + 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,)) # + c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,)) row = c.fetchone() c.close() - return row["num"]>0 - + 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: + if row is 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() + (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: + if row is 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 63b6f50..01bd238 100644 --- a/pyircbot/modules/LMGTFY.py +++ b/pyircbot/modules/LMGTFY.py @@ -11,6 +11,7 @@ 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) @@ -24,7 +25,7 @@ class LMGTFY(ModuleBase): 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: @@ -33,6 +34,6 @@ class LMGTFY(ModuleBase): for word in message: finalUrl += urllib.parse.quote(word) if word != message[-1]: - finalUrl+="+" + finalUrl += "+" return finalUrl diff --git a/pyircbot/modules/LinkTitler.py b/pyircbot/modules/LinkTitler.py index 0cd35b6..34fa402 100755 --- a/pyircbot/modules/LinkTitler.py +++ b/pyircbot/modules/LinkTitler.py @@ -7,90 +7,93 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook from requests import get import re import time -import praw #TODO: enable/disable modules +import praw # TODO: enable/disable modules import datetime -from requests import get,head +from requests import head import html.parser from threading import Thread + class LinkTitler(ModuleBase): def __init__(self, bot, moduleName): ModuleBase.__init__(self, bot, moduleName) - self.REQUEST_SIZE_LIMIT = 10*1024 - self.hooks=[ModuleHook("PRIVMSG", self.searches)] - + self.REQUEST_SIZE_LIMIT = 10 * 1024 + self.hooks = [ModuleHook("PRIVMSG", self.searches)] + def searches(self, args, prefix, trailing): t = Thread(target=self.doLinkTitle, args=(args, prefix, trailing)) t.daemon = True t.start() - + def doLinkTitle(self, args, prefix, trailing): sender = self.bot.decodePrefix(prefix) - + # Youtube - matches = re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)', re.I).findall(trailing) - if len(matches)>0: + matches = re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)', re.I) \ + .findall(trailing) + if matches: done = [] for item in matches: - if not item in done: - vidinfo = self.get_video_description(item) + if item not in done: + vidinfo = self.get_video_description(item) if vidinfo: self.bot.act_PRIVMSG(args[0], vidinfo) done.append(item) return - + # reddit threads - matches = re.compile(r'(?:reddit\.com/.*?comments/([a-zA-Z0-9]+)/|https?://(www\.)?redd.it/([a-zA-Z0-9]+))').findall(trailing) + matches = re.compile(r'(?:reddit\.com/.*?comments/([a-zA-Z0-9]+)/|https?://(www\.)?redd.it/([a-zA-Z0-9]+))') \ + .findall(trailing) # Either [('', '', '2ibrz7')] or [('2ibrz7', '', '')] - if len(matches)>0: + if matches: done = [] for match in matches: submissionId = match[0] - if submissionId=="": + if submissionId == "": submissionId = match[-1] if submissionId in done: continue done.append(submissionId) # TODO configurable user agent r = praw.Reddit(self.config["agent"]) - submission = r.get_submission(submission_id = submissionId) - #for i in range(0,18): - # self.bot.act_PRIVMSG(args[0], "\x031,%sTEST%s\x0f" %(i,i)) - msg = "👽 \x02\x031,15REDDIT\x0f\x02 :: %(title)s \x02on \x02%(domain)s%(nsfw)s\x02 - points \x02%(points)s\x02 (%(percent)s↑) - comments \x02%(comments)s\x02 - by \x02%(author)s\x02 on \x02%(date)s\x02" % { - "title":submission.title, - "domain":submission.domain, - "nsfw": "[NSFW]" if submission.over_18 else "", - "points":submission.ups, - "percent":"%s%%" % int(submission.upvote_ratio*100), - "comments":submission.num_comments, - "author":submission.author.name, - "date":datetime.datetime.fromtimestamp(submission.created).strftime("%Y.%m.%d") - } + submission = r.get_submission(submission_id=submissionId) + msg = "👽 \x02\x031,15REDDIT\x0f\x02 :: %(title)s \x02on \x02%(domain)s%(nsfw)s\x02 - points " \ + "\x02%(points)s\x02 (%(percent)s↑) - comments \x02%(comments)s\x02 - by \x02%(author)s\x02 on " \ + "\x02%(date)s\x02" % { + "title": submission.title, + "domain": submission.domain, + "nsfw": "[NSFW]" if submission.over_18 else "", + "points": submission.ups, + "percent": "%s%%" % int(submission.upvote_ratio *100), + "comments": submission.num_comments, + "author": submission.author.name, + "date": datetime.datetime.fromtimestamp(submission.created).strftime("%Y.%m.%d") + } self.bot.act_PRIVMSG(args[0], msg) return # reddit subscribers - + # subreddits - + # generic matches = re.compile(r'(https?://([a-zA-Z0-9_\-\.]+/([^ ]+)?))').findall(trailing) - if len(matches)>0: - done=[] + if matches: + done = [] for match in matches: if match[0] in done: continue done.append(match[0]) - + headers = self.url_headers(match[0]) - + # Don't mess with unknown content types - if not "Content-Type" in headers: + if "Content-Type" not in headers: continue - + if "text/html" in headers["Content-Type"]: # Fetch HTML title title = self.url_htmltitle(match[0]) @@ -98,23 +101,29 @@ class LinkTitler(ModuleBase): self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02" % (sender.nick, title)) else: # Unknown types, just print type and size - self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02, %s" % (sender.nick, headers["Content-Type"], self.nicesize(int(headers["Content-Length"])) if "Content-Length" in headers else "unknown size")) - + self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02, %s" % + (sender.nick, headers["Content-Type"], + self.nicesize(int(headers["Content-Length"])) if + "Content-Length" in headers else "unknown size")) + return - + def nicesize(self, numBytes): "Return kb or plain bytes" if numBytes > 1024: - return "%skb" % str(int(numBytes/1024)) + return "%skb" % str(int(numBytes / 1024)) else: return "<1kb" - + def url_headers(self, url): - "HEAD requests a url to check content type & length, returns something like: {'type': 'image/jpeg', 'size': '90583'}" + """ + HEAD requests a url to check content type & length. + Returns something like: {'type': 'image/jpeg', 'size': '90583'}" + """ self.log.info("url_headers(%s)" % (url,)) resp = head(url=url, allow_redirects=True) return resp.headers - + def url_htmltitle(self, url): "Requests page html and returns title in a safe way" self.log.info("url_htmltitle(%s)" % (url,)) @@ -126,61 +135,63 @@ class LinkTitler(ModuleBase): data += chunk if len(data) > self.REQUEST_SIZE_LIMIT: break - + data = data.decode('utf-8', "ignore") - + titleMatches = re.findall(r'<title>([^<]+)', data, re.I) - if len(titleMatches)>0:# and resp.status_code==200: + if len(titleMatches) > 0: # and resp.status_code==200: h = html.parser.HTMLParser() title = h.unescape(titleMatches[0]).strip() - if len(title)>0: + if len(title) > 0: return title return None - + # For youtube def getISOdurationseconds(self, stamp): ISO_8601_period_rx = re.compile( - 'P' # designates a period - '(?:(?P\d+)Y)?' # years - '(?:(?P\d+)M)?' # months - '(?:(?P\d+)W)?' # weeks - '(?:(?P\d+)D)?' # days - '(?:T' # time part must begin with a T - '(?:(?P\d+)H)?' # hours - '(?:(?P\d+)M)?' # minutes - '(?:(?P\d+)S)?' # seconds + 'P' # designates a period + '(?:(?P\d+)Y)?' # years + '(?:(?P\d+)M)?' # months + '(?:(?P\d+)W)?' # weeks + '(?:(?P\d+)D)?' # days + '(?:T' # time part must begin with a T + '(?:(?P\d+)H)?' # hours + '(?:(?P\d+)M)?' # minutes + '(?:(?P\d+)S)?' # seconds ')?' # end of time part - ) # http://stackoverflow.com/a/16742742 + ) # http://stackoverflow.com/a/16742742 return ISO_8601_period_rx.match(stamp).groupdict() + def get_video_description(self, vid_id): - apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s&key=%s' % (vid_id, self.config["youtube_api_key"])).json() - + apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s' + '&key=%s' % (vid_id, self.config["youtube_api_key"])).json() + if not apidata['pageInfo']['totalResults']: return - + video = apidata['items'][0] snippet = video["snippet"] duration = self.getISOdurationseconds(video["contentDetails"]["duration"]) - + out = '\x02\x031,0You\x0f\x030,4Tube\x02\x0f :: \x02%s\x02' % snippet["title"] - + out += ' - length \x02' - if duration["hours"]!=None: + if duration["hours"] is not None: out += '%dh ' % int(duration["hours"]) - if duration["minutes"]!=None: + if duration["minutes"] is not None: out += '%dm ' % int(duration["minutes"]) - if duration["seconds"]!=None: + if duration["seconds"] is not None: out += "%ds\x02" % int(duration["seconds"]) - - totalvotes = float(video["statistics"]["dislikeCount"])+float(video["statistics"]["likeCount"]) + + totalvotes = float(video["statistics"]["dislikeCount"]) + float(video["statistics"]["likeCount"]) rating = float(video["statistics"]["likeCount"]) / totalvotes - out += ' - rated \x02%.2f/5\x02' % round(rating*5,1) + out += ' - rated \x02%.2f/5\x02' % round(rating * 5, 1) out += ' - \x02%s\x02 views' % self.group_int_digits(video["statistics"]["viewCount"]) upload_time = time.strptime(snippet['publishedAt'], "%Y-%m-%dT%H:%M:%S.000Z") out += ' - by \x02%s\x02 on \x02%s\x02' % (snippet['channelTitle'], time.strftime("%Y.%m.%d", upload_time)) - + return out - + def group_int_digits(self, number, delimiter=',', grouping=3): base = str(number).strip() builder = [] diff --git a/pyircbot/modules/MySQL.py b/pyircbot/modules/MySQL.py index dc1bfa3..f82a9b4 100755 --- a/pyircbot/modules/MySQL.py +++ b/pyircbot/modules/MySQL.py @@ -7,79 +7,79 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase 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() + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] + self.services = ["mysql"] 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; + if len(tables) == 0: + return False key = list(tables[0].keys())[0] for table in tables: - if table[key]==tablename: - return True; + 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: + 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): + 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() @@ -90,27 +90,28 @@ class Connection: 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.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): + 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"]: + if row["Database"] == self.config["database"]: found = True if not found: c.execute("CREATE DATABASE `%s`;" % self.config["database"]) diff --git a/pyircbot/modules/NFLLive.py b/pyircbot/modules/NFLLive.py index 056b6ac..b46df05 100644 --- a/pyircbot/modules/NFLLive.py +++ b/pyircbot/modules/NFLLive.py @@ -6,90 +6,91 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook from time import time from requests import get from lxml import etree -from datetime import datetime,timedelta +from datetime import datetime, timedelta + class NFLLive(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); + ModuleBase.__init__(self, bot, moduleName) self.cache = None - self.cacheAge=0 - - self.hooks=[ModuleHook(["PRIVMSG"], self.nflitup)] - + 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] - + replyTo = prefix.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: + if game["time"] is not None: liveGames.append(game) - elif game["quarter"]=="P" and game["startdate"].day==datetime.now().day: + elif game["quarter"] == "P" and game["startdate"].day == datetime.now().day: gamesLaterToday.append(game) - elif game["startdate"].day==datetime.now().day: + 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 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": + 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": + elif len(cmd.args) > 0 and cmd.args[0] == "scores": if not liveGamesStr == "": msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr) if not gamesTodayStr == "": @@ -107,28 +108,28 @@ class NFLLive(ModuleBase): 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: + if len(msgPieces) == 1: msg = "No games!" - - if len(msg)>0: + + if len(msg) > 0: # The message can be long so chunk it into pieces splitting at commas - while len(msg)>0: + while len(msg) > 0: piece = msg[0:330] msg = msg[330:] - while not piece[-1:]=="," and len(msg)>0: - piece+=msg[0:1] + 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"], @@ -139,17 +140,17 @@ class NFLLive(ModuleBase): 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"], @@ -158,68 +159,70 @@ class NFLLive(ModuleBase): game["home"], game["home_score"] ) - + def getNflGamesCached(self): - if time()-self.cacheAge > self.config["cache"]: + 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["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"]=[] - + 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. + "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["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": + if season == "R": return "Regular" - if season=="P": + if season == "P": return "Pre" return season diff --git a/pyircbot/modules/NickUser.py b/pyircbot/modules/NickUser.py index 61c3fc1..35d15e3 100755 --- a/pyircbot/modules/NickUser.py +++ b/pyircbot/modules/NickUser.py @@ -7,27 +7,27 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook -import time +from pyircbot.modulebase import ModuleBase, ModuleHook import hashlib + class NickUser(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.gotmsg)] - self.services=["login"] - + 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: + 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] == "#": @@ -35,37 +35,40 @@ class NickUser(ModuleBase): 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\"") + 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: + if oldpass is 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 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]) + 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.") - + 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: + if userpw is None: self.bot.act_PRIVMSG(prefix.nick, ".login: You must first set a password with .setpass") else: - if len(cmd.args)==1: + if len(cmd.args) == 1: if userpw == cmd.args[0]: ################# attr.setKey(prefix.nick, "loggedinfrom", prefix.hostname) @@ -79,12 +82,12 @@ class NickUser(ModuleBase): if cmd: attr = self.bot.getBestModuleForService("attributes") loggedin = attr.getKey(prefix.nick, "loggedinfrom") - if loggedin == None: + if loggedin is 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")) diff --git a/pyircbot/modules/PingResponder.py b/pyircbot/modules/PingResponder.py index 09bd7c9..251f632 100755 --- a/pyircbot/modules/PingResponder.py +++ b/pyircbot/modules/PingResponder.py @@ -7,37 +7,34 @@ """ -from time import time,sleep +from time import time, sleep from threading import Thread -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook + class PingResponder(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); + ModuleBase.__init__(self, bot, moduleName) self.timer = PingRespondTimer(self) - self.hooks=[ + self.hooks = [ ModuleHook("PING", self.pingrespond), ModuleHook("_RECV", self.resettimer) ] - 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 resettimer(self, msg): """Resets the connection failure timer""" self.timer.reset() - def ondisable(self): self.timer.disable() - class PingRespondTimer(Thread): "Tracks last ping from server, and reconnects if over a threshold" def __init__(self, master): @@ -47,20 +44,20 @@ class PingRespondTimer(Thread): self.master = master self.reset() self.start() - + def reset(self): "Reset the internal ping timeout counter" self.lastping = time() - + def disable(self): "Allow the thread to die" self.alive = False - + def run(self): while self.alive: sleep(5) - if time() - self.lastping > 300: #TODO: configurable timeout + if time() - self.lastping > 300: # TODO: configurable timeout self.master.log.info("No pings in %s seconds. Reconnecting" % str(time() - self.lastping)) self.master.bot.disconnect("Reconnecting...") self.reset() - \ No newline at end of file + diff --git a/pyircbot/modules/RandQuote.py b/pyircbot/modules/RandQuote.py index fb1f8f4..4fcef08 100755 --- a/pyircbot/modules/RandQuote.py +++ b/pyircbot/modules/RandQuote.py @@ -7,21 +7,22 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +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=[] + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [] self.db = None serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: + if not serviceProviders: 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` ( @@ -31,13 +32,13 @@ class RandQuote(ModuleBase): `message` varchar(2048) ) ;""") c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.logquote),ModuleHook("PRIVMSG", self.fetchquotes)] - + + self.hooks = [ModuleHook("PRIVMSG", self.logquote), + ModuleHook("PRIVMSG", self.fetchquotes)] + def fetchquotes(self, args, prefix, trailing): - if not args[0][0]=="#": + 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;") @@ -45,15 +46,14 @@ class RandQuote(ModuleBase): 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]=="#": + 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 = [] + (int(datetime.now().timestamp()), prefixObj.nick, trailing)).close() + # Trim quotes c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"]) while True: row = c.fetchone() @@ -61,7 +61,7 @@ class RandQuote(ModuleBase): 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 be0b6c0..fcbf2d7 100644 --- a/pyircbot/modules/Remind.py +++ b/pyircbot/modules/Remind.py @@ -6,25 +6,26 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook -from datetime import datetime,timedelta +from pyircbot.modulebase import ModuleBase, ModuleHook +from datetime import datetime, timedelta from threading import Thread from time import sleep import re import pytz + class Remind(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - + ModuleBase.__init__(self, bot, moduleName) + self.db = None serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: + if not serviceProviders: 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` ( @@ -35,142 +36,144 @@ class Remind(ModuleBase): `message` varchar(2048) ) ;""") c.close() - - self.hooks=[ModuleHook("PRIVMSG", self.remindin),ModuleHook("PRIVMSG", self.remindat)] - + + 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.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"]] = [] + byrecip[reminder["sender"]].append(reminder) - + reminders_bych = {} - + for recip in byrecip: reminders_pm = [] - + for reminder in byrecip[recip]: - if reminder["senderch"]=="": + 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"]] = [] 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: + 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] - + + 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: + if second is not 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) + 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: + if tz is not 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 + + # 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: + if second is 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: + if tz is not 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 remindAt3: - numScores=3 + if numScores > 3: + numScores = 3 resp = "Top %s: " % str(numScores) which = 1 - while which<=numScores: - resp+="%s: %s, " % (sortedscores[which-1]["name"], sortedscores[which-1]["score"]) - which+=1 + while which <= numScores: + resp += "%s: %s, " % (sortedscores[which - 1]["name"], sortedscores[which - 1]["score"]) + which += 1 self.master.bot.act_PRIVMSG(self.channel, resp[:-2]) cmd = self.master.bot.messageHasCommand(".scramble score", trailing) if cmd: someone = cmd.args.strip() if len(someone) > 0: - self.master.bot.act_PRIVMSG(self.channel, "%s: %s has a score of %s" % (sender, someone, self.master.getScoreNoWrite(someone))) - else: + self.master.bot.act_PRIVMSG(self.channel, "%s: %s has a score of %s" % + (sender, someone, self.master.getScoreNoWrite(someone))) + else: self.master.bot.act_PRIVMSG(self.channel, "%s: %s" % (sender, self.master.getScore(sender))) if self.currentWord and trailing.strip().lower() == self.currentWord: playerScore = self.master.getScore(sender, 1) - self.master.bot.act_PRIVMSG(self.channel, "%s guessed the word - %s! %s now has %s points. Next word in %s seconds." % (sender, self.currentWord, sender, playerScore, self.delayNext)) + self.master.bot.act_PRIVMSG(self.channel, "%s guessed the word - %s! %s now has %s points. Next word in %s " + "seconds." % (sender, self.currentWord, sender, playerScore, self.delayNext)) self.currentWord = None self.clearTimers() self.hintsGiven = 0 self.nextTimer = Timer(self.delayNext, self.startNewWord) self.nextTimer.start() - self.guesses=0 + self.guesses = 0 else: - self.guesses+=1 - + self.guesses += 1 + def startScramble(self): self.clearTimer(self.nextTimer) self.nextTimer = Timer(0, self.startNewWord) self.nextTimer.start() - + def startNewWord(self): self.currentWord = self.pickWord() self.master.log.info("Scramble: New word for %s: %s" % (self.channel, self.currentWord)) self.scrambled = self.scrambleWord(self.currentWord) self.master.bot.act_PRIVMSG(self.channel, "New word - %s " % (self.scrambled)) - + self.clearTimer(self.hintTimer) self.hintTimer = Timer(self.delayHint, self.giveHint) self.hintTimer.start() - + def giveHint(self): - self.hintsGiven+=1 - - if self.hintsGiven>=len(self.currentWord) or self.hintsGiven > self.maxHints: + self.hintsGiven += 1 + + if self.hintsGiven >= len(self.currentWord) or self.hintsGiven > self.maxHints: self.abortWord() return - + blanks = "" for letter in list(self.currentWord): if letter == " ": - blanks+=" " + blanks += " " else: - blanks+="_" + blanks += "_" partFromWord = self.currentWord[0:self.hintsGiven] partFromBlanks = blanks[self.hintsGiven:] - hintstr = partFromWord+partFromBlanks - + hintstr = partFromWord + partFromBlanks + self.master.bot.act_PRIVMSG(self.channel, "Hint: - %s" % (hintstr)) - + self.clearTimer(self.hintTimer) self.hintTimer = Timer(self.delayHint, self.giveHint) self.hintTimer.start() - + def abortWord(self): cur = self.currentWord self.currentWord = None - self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was %s. Next word in %s seconds." % (cur, self.delayNext)) + self.master.bot.act_PRIVMSG(self.channel, "Word expired - the answer was %s. Next word in %s seconds." % + (cur, self.delayNext)) self.hintsGiven = 0 self.clearTimer(self.nextTimer) - - if self.guesses==0: - self.gamesWithoutGuesses+=1 + + if self.guesses == 0: + self.gamesWithoutGuesses += 1 if self.gamesWithoutGuesses >= self.abortAfterNoGuesses: - self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scrambleon to start again.") + self.master.bot.act_PRIVMSG(self.channel, "No one seems to be playing - type .scrambleon to " + "start again.") self.gameover() return else: - self.gamesWithoutGuesses=0 - + self.gamesWithoutGuesses = 0 + self.nextTimer = Timer(self.delayNext, self.startNewWord) self.nextTimer.start() - + def pickWord(self): f = open(self.master.wordsFile, "r") skip = random.randint(0, self.master.wordsCount) - while skip>=0: + while skip >= 0: f.readline() - skip-=1 + skip -= 1 picked = f.readline().strip().lower() f.close() return picked - + def scrambleWord(self, word): scrambled = "" for subword in word.split(" "): - scrambled+=self.scrambleIndividualWord(subword)+ " " + scrambled += self.scrambleIndividualWord(subword) + " " return scrambled.strip() - + def scrambleIndividualWord(self, word): scrambled = list(word) random.shuffle(scrambled) - return ''.join(scrambled).lower() \ No newline at end of file + return ''.join(scrambled).lower() diff --git a/pyircbot/modules/Seen.py b/pyircbot/modules/Seen.py index 6676aad..8017aac 100755 --- a/pyircbot/modules/Seen.py +++ b/pyircbot/modules/Seen.py @@ -7,66 +7,68 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook 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() + self.hooks = [ModuleHook("PRIVMSG", self.lastSeen)] # if the database doesnt exist, it will be created sql = self.getSql() - c=sql.cursor() + 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: + 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`))"); + 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() + 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)) + 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(" "); + cmdargs = trailing.split(" ") # query the DB for the user - if len(cmdargs)>=2: - searchnic = cmdargs[1].lower(); + 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"])); + 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 = 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 85f2824..73f634d 100755 --- a/pyircbot/modules/Services.py +++ b/pyircbot/modules/Services.py @@ -7,26 +7,31 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +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.hooks = [ModuleHook("_CONNECT", self.doConnect), + ModuleHook("433", self.nickTaken), + ModuleHook("001", self.initservices), + ModuleHook("INVITE", self.invited), ] 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"]) + 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 + 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 @@ -35,7 +40,8 @@ class Services(ModuleBase): 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"]}) + 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() @@ -50,7 +56,8 @@ class Services(ModuleBase): """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"]}) + 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"]: @@ -60,5 +67,6 @@ class Services(ModuleBase): " 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}) + 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 f994550..47f20a9 100644 --- a/pyircbot/modules/Tell.py +++ b/pyircbot/modules/Tell.py @@ -6,25 +6,26 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook import datetime from time import mktime + class Tell(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - + ModuleBase.__init__(self, bot, moduleName) + self.db = None serviceProviders = self.bot.getmodulesbyservice("sqlite") - if len(serviceProviders)==0: + 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` ( + self.db.query("""CREATE TABLE IF NOT EXISTS `tells` ( `id` INTEGER PRIMARY KEY, `sender` varchar(64), `channel` varchar(64), @@ -32,19 +33,19 @@ class Tell(ModuleBase): `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) + if len(cmd.args) < 2: + 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 - + 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) + + if not 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.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. # @@ -121,43 +122,41 @@ class Tell(ModuleBase): #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')) - ) - + 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 @@ -168,12 +167,12 @@ class Tell(ModuleBase): 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] @@ -184,13 +183,13 @@ class Tell(ModuleBase): else: s += ', %d %s' % (count2, name2[1]) return s - + @staticmethod - def timeuntil(d, now=None): + def timeuntil(d, now=None): # not used? """ Like timesince, but returns a string measuring the time until the given time. """ if not now: now = datetime.datetime.now() - return timesince(now, d) + return Tell.timesince(now, d) diff --git a/pyircbot/modules/Test.py b/pyircbot/modules/Test.py index 01983ca..d583ce0 100755 --- a/pyircbot/modules/Test.py +++ b/pyircbot/modules/Test.py @@ -7,12 +7,13 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook + class Test(ModuleBase): def __init__(self, bot, moduleName): ModuleBase.__init__(self, bot, moduleName) - self.hooks=[ModuleHook("PRIVMSG", self.dotest)] - + self.hooks = [ModuleHook("PRIVMSG", self.dotest)] + def dotest(self, args): - print("DOTEST(%s)"%(args,)) + print("DOTEST(%s)" % (args,)) diff --git a/pyircbot/modules/TextCDC.py b/pyircbot/modules/TextCDC.py index fc36621..45192f9 100644 --- a/pyircbot/modules/TextCDC.py +++ b/pyircbot/modules/TextCDC.py @@ -13,33 +13,34 @@ 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.hooks.append(ModuleHook("PRIVMSG", self.handleMessage)) self.prefixes = [person for person in self.config["people"]] self.bot = bot self.timer = None self.setupTimer() def ondisable(self): - if self.timer != None: + if self.timer is not 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.") + # self.bot.act_PRIVMSG(channel, "POP: %s" % "Good" if setupPop() != None else "Failed.") + self.bot.act_PRIVMSG(channel, "SMTP: %s" % "Good" if self.setupSMTP() is not 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.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: @@ -62,7 +63,7 @@ class TextCDC(ModuleBase): 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"]) + 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"]: @@ -77,14 +78,15 @@ class TextCDC(ModuleBase): 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 = 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): + + def checkMail(self, bot, people, channels, imapObj=None): try: - if imapObj == None: + if imapObj is None: imapObj = self.setupIMAP() for person in people: emailAddr = people[person]["email-addr"] @@ -96,8 +98,9 @@ class TextCDC(ModuleBase): 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: + 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: @@ -105,7 +108,7 @@ class TextCDC(ModuleBase): imapObj.logout() self.setupTimer() except Exception as e: - if imapObj != None: + if imapObj is not None: imapObj.logout() self.setupTimer() - raise e + raise e diff --git a/pyircbot/modules/Triggered.py b/pyircbot/modules/Triggered.py index a7a7e43..4ce4dc1 100755 --- a/pyircbot/modules/Triggered.py +++ b/pyircbot/modules/Triggered.py @@ -6,14 +6,14 @@ """ from threading import Thread -from time import sleep,time +from time import sleep, time from pyircbot.modulebase import ModuleBase, ModuleHook -from random import randrange,choice +from random import randrange, choice + class Triggered(ModuleBase): def __init__(self, bot, moduleName): ModuleBase.__init__(self, bot, moduleName) - self.loadConfig() self.quietuntil = time() self.hooks.append(ModuleHook("PRIVMSG", self.check)) @@ -22,21 +22,21 @@ class Triggered(ModuleBase): return if not args.args[0].lower() in self.config["channels"]: return - + message = args.trailing.lower() triggered = False for word in self.config["words"]: if word.lower() in message: triggered = True break - + if not triggered: return - + msg = Thread(target=self.scream, args=(args.args[0],)) msg.daemon = True msg.start() - + self.quietuntil = time() + self.config["quiet"] def scream(self, channel): diff --git a/pyircbot/modules/UnoPlay.py b/pyircbot/modules/UnoPlay.py index 6516f20..fe66c8e 100644 --- a/pyircbot/modules/UnoPlay.py +++ b/pyircbot/modules/UnoPlay.py @@ -18,7 +18,6 @@ class UnoPlay(ModuleBase): def __init__(self, bot, moduleName): ModuleBase.__init__(self, bot, moduleName) self.servicesModule = self.bot.getmodulebyname("Services") - self.mynick = self.servicesModule.config["user"]["nick"][0] self.hooks = [ModuleHook("PRIVMSG", self.unoplay), ModuleHook("PRIVMSG", self.trigger), ModuleHook("NOTICE", self.decklisten)] @@ -30,6 +29,8 @@ class UnoPlay(ModuleBase): self.cards = [] def trigger(self, args, prefix, trailing): + if trailing.startswith("["): # anti-znc buffer playback + return if self.config["enable_trigger"] and "!jo" in trailing: self.bot.act_PRIVMSG(self.config["unochannel"], "jo") elif trailing == "jo": @@ -44,10 +45,10 @@ class UnoPlay(ModuleBase): self.bot.act_PRIVMSG(self.config["unochannel"], "jo") def unoplay(self, args, prefix, trailing): - ogtrailing = trailing + if trailing.startswith("["): # anti-znc buffer playback + return + trailing = self.stripcolors(trailing) - self.log.debug("> %s" % ogtrailing) - self.log.debug("> %s" % trailing) if self.config["unobot"] not in prefix: return @@ -60,15 +61,15 @@ class UnoPlay(ModuleBase): self.current_card = self.parsecard(message) self.log.debug(">> top card: %s" % str(self.current_card)) - if self.mynick in trailing: + if self.bot.get_nick() in trailing: self.shouldgo = True # See if someone passed to us - if "passes" in trailing and self.mynick in trailing: + if "passes" in trailing and self.bot.get_nick() in trailing: self.shouldgo = True # Play after someone was droppped - if "continuing with" in trailing and self.mynick in trailing: + if "continuing with" in trailing and self.bot.get_nick() in trailing: self.shouldgo = True # Parse misc played cards @@ -79,7 +80,7 @@ class UnoPlay(ModuleBase): self.log.debug(">> current card: %s" % str(self.current_card)) # After someone plays to us - if "to %s" % self.mynick in trailing: + if "to %s" % self.bot.get_nick() in trailing: self.shouldgo = True # After color change @@ -98,15 +99,15 @@ class UnoPlay(ModuleBase): self.current_card[2]['number'] = -1 self.current_card[2]['type'] = None self.log.debug("Color changed to %s " % self.current_card[2]['color']) - if "urrent player %s" % self.mynick in trailing: + if "urrent player %s" % self.bot.get_nick() in trailing: self.shouldgo = True # After color change to us - if "play continues with %s" % self.mynick in trailing: + if "play continues with %s" % self.bot.get_nick() in trailing: self.shouldgo = True # We need to choose a color - if "hoose a color %s" % self.mynick in trailing: + if "hoose a color %s" % self.bot.get_nick() in trailing: self.pickcolor() # Reset @@ -225,13 +226,12 @@ class UnoPlay(ModuleBase): self.bot.act_PRIVMSG(self.config["unochannel"], "pl %s" % card) def decklisten(self, args, prefix, trailing): + if trailing.startswith("["): # anti-znc buffer playback + return if self.config["unobot"] not in prefix: return - ogtrailing = trailing trailing = self.stripcolors(trailing) - self.log.debug("> %s" % (ogtrailing, )) - self.log.debug("> %s" % (trailing, )) cards = [] diff --git a/pyircbot/modules/Urban.py b/pyircbot/modules/Urban.py index 1d1727d..42ec164 100755 --- a/pyircbot/modules/Urban.py +++ b/pyircbot/modules/Urban.py @@ -7,29 +7,30 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook -import json +from pyircbot.modulebase import ModuleBase, ModuleHook from requests import get + class Urban(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.urban)] - + 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() =="": + 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"] + 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 + 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'])) diff --git a/pyircbot/modules/Weather.py b/pyircbot/modules/Weather.py index f478326..c10fb04 100755 --- a/pyircbot/modules/Weather.py +++ b/pyircbot/modules/Weather.py @@ -7,110 +7,114 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook 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"] - + ModuleBase.__init__(self, bot, moduleName) + + assert "get an API key" not in self.config["apikey"] + self.login = self.bot.getBestModuleForService("login") try: - assert not self.login == None + assert self.login is not 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: + assert self.attr is not None + except AssertionError as _ae: # NOQA self.log.error("Weather: An 'attributes' service is required") return - - self.hooks=[ModuleHook("PRIVMSG", self.weather)] - + + 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: + 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: + if weatherZip is 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") + if cmd and not args[0] == "#": + + if not cmd.args: + 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) + result = self.getWeather(weatherLoc) # NOQA 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))) + 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) + self.bot.act_PRIVMSG(fromWho, "'%s': location not found: %s" % (weatherLoc, le)) 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: + if self.attr.get(fromWho, "weather-zip") is 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]=="#": + 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: + + if unit is 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()) - + 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))) + 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) - + self.bot.act_PRIVMSG(target, "'%s': location not found: %s" % (location, le)) + def alternates_to_str(self, alternates): pieces = [] for item in alternates: @@ -120,51 +124,54 @@ class Weather(ModuleBase): item_pieces.append(item[key]) pieces.append(', '.join(item_pieces)) return ' -- '.join(pieces) - + def getWeather(self, zipcode, unit=None): - if unit==None: + if unit is 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() - + 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": + 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"] + "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: + 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 - } - + 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" @@ -173,9 +180,7 @@ class Weather(ModuleBase): 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 "⛅️" @@ -189,7 +194,7 @@ class Weather(ModuleBase): return "❄️" else: return "(%s)" % icon - + def deg_to_arrow(self, deg): if deg > 335 or deg < 0: return "↑" @@ -208,10 +213,12 @@ class Weather(ModuleBase): elif deg > 22: return "⇗" + class LocationException(Exception): pass + class LocationNotSpecificException(LocationException): def __init__(self, alternates): self.alternates = alternates - pass \ No newline at end of file + pass diff --git a/pyircbot/modules/Youtube.py b/pyircbot/modules/Youtube.py index 90435eb..24b8590 100755 --- a/pyircbot/modules/Youtube.py +++ b/pyircbot/modules/Youtube.py @@ -7,78 +7,81 @@ """ -from pyircbot.modulebase import ModuleBase,ModuleHook +from pyircbot.modulebase import ModuleBase, ModuleHook from requests import get import time import re + class Youtube(ModuleBase): def __init__(self, bot, moduleName): - ModuleBase.__init__(self, bot, moduleName); - self.hooks=[ModuleHook("PRIVMSG", self.youtube)] - + ModuleBase.__init__(self, bot, moduleName) + self.hooks = [ModuleHook("PRIVMSG", self.youtube)] + def getISOdurationseconds(self, stamp): ISO_8601_period_rx = re.compile( 'P' # designates a period - '(?:(?P\d+)Y)?' # years - '(?:(?P\d+)M)?' # months - '(?:(?P\d+)W)?' # weeks - '(?:(?P\d+)D)?' # days - '(?:T' # time part must begin with a T - '(?:(?P\d+)H)?' # hours - '(?:(?P\d+)M)?' # minutes - '(?:(?P\d+)S)?' # seconds + '(?:(?P\d+)Y)?' # years + '(?:(?P\d+)M)?' # months + '(?:(?P\d+)W)?' # weeks + '(?:(?P\d+)D)?' # days + '(?:T' # time part begins with a T + '(?:(?P\d+)H)?' # hours + '(?:(?P\d+)M)?' # minutes + '(?:(?P\d+)S)?' # seconds ')?' # end of time part - ) # http://stackoverflow.com/a/16742742 + ) # http://stackoverflow.com/a/16742742 return ISO_8601_period_rx.match(stamp).groupdict() - + def youtube(self, args, prefix, trailing): - + cmd = self.bot.messageHasCommand(".youtube", trailing) if not cmd: cmd = self.bot.messageHasCommand(".yt", trailing) - if cmd and args[0][0:1]=="#": - #TOTO search youtube - if cmd.args_str.strip() =="": + if cmd and args[0][0:1] == "#": + # TODO search youtube + if cmd.args_str.strip() == "": self.bot.act_PRIVMSG(args[0], '.youtube -- returns the first YouTube search result for ') return - j = get("http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=1", params={"q":trailing}).json() - if 'error' in j or j['data']['totalItems']==0: + j = get("http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=1", + params={"q": trailing}).json() + if 'error' in j or j['data']['totalItems'] == 0: self.bot.act_PRIVMSG(args[0], "YouTube: No results found.") else: vid_id = j['data']['items'][0]['id'] vidinfo = self.get_video_description(vid_id) if vidinfo: self.bot.act_PRIVMSG(args[0], "http://youtu.be/%s :: %s" % (vid_id, vidinfo)) - + def get_video_description(self, vid_id): - apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s&key=%s' % (vid_id, self.config["api_key"])).json() - + apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s' + '&key=%s' % (vid_id, self.config["api_key"])).json() + if not apidata['pageInfo']['totalResults']: return - + video = apidata['items'][0] snippet = video["snippet"] duration = self.getISOdurationseconds(video["contentDetails"]["duration"]) - + out = '\x02\x031,0You\x0f\x030,4Tube\x02\x0f :: \x02%s\x02' % snippet["title"] - + out += ' - \x02' - if duration["hours"]!=None: + if duration["hours"] is not None: out += '%dh ' % int(duration["hours"]) - if duration["minutes"]!=None: + if duration["minutes"] is not None: out += '%dm ' % int(duration["minutes"]) out += "%ds\x02" % int(duration["seconds"]) - - totalvotes = float(video["statistics"]["dislikeCount"])+float(video["statistics"]["likeCount"]) + + totalvotes = float(video["statistics"]["dislikeCount"]) + float(video["statistics"]["likeCount"]) rating = float(video["statistics"]["likeCount"]) / totalvotes - out += ' - rated \x02%.2f/5\x02' % round(rating*5,1) + out += ' - rated \x02%.2f/5\x02' % round(rating * 5, 1) out += ' - \x02%s\x02 views' % self.group_int_digits(video["statistics"]["viewCount"]) upload_time = time.strptime(snippet['publishedAt'], "%Y-%m-%dT%H:%M:%S.000Z") out += ' - by \x02%s\x02 on \x02%s\x02' % (snippet['channelTitle'], time.strftime("%Y.%m.%d", upload_time)) - + return out - + def group_int_digits(self, number, delimiter=',', grouping=3): base = str(number).strip() builder = [] diff --git a/pyircbot/pyircbot.py b/pyircbot/pyircbot.py index 032442e..d73cb3b 100644 --- a/pyircbot/pyircbot.py +++ b/pyircbot/pyircbot.py @@ -17,8 +17,7 @@ import os.path ParsedCommand = namedtuple("ParsedCommand", "command args args_str message") - -class PyIRCBot: +class PyIRCBot(object): """:param botconfig: The configuration of this instance of the bot. Passed by main.py. :type botconfig: dict """ @@ -44,7 +43,7 @@ class PyIRCBot: """IRC protocol class""" self.irc.servers = self.botconfig["connection"]["servers"] self.irc.port = self.botconfig["connection"]["port"] - self.irc.ipv6 = True if self.botconfig["connection"]["ipv6"]=="on" else False + self.irc.ipv6 = True if self.botconfig["connection"]["ipv6"] == "on" else False self.irc.addHook("_DISCONNECT", self.connection_closed) @@ -57,8 +56,9 @@ class PyIRCBot: self.act_MODE = self.irc.act_MODE self.act_ACTION = self.irc.act_ACTION self.act_KICK = self.irc.act_KICK - self.act_QUIT = self.irc.act_QUIT - self.get_nick = self.irc.get_nick + self.act_QUIT = self.irc.act_QUIT + self.act_PASS = self.irc.act_PASS + self.get_nick = self.irc.get_nick self.decodePrefix = IRCCore.decodePrefix # Load modules @@ -96,7 +96,6 @@ class PyIRCBot: :param message: Quit message :type message: str """ - #Close all modules self.closeAllModules() self.irc.kill(message=message, alive=not sys_exit) @@ -114,11 +113,11 @@ class PyIRCBot: def initModules(self): """load modules specified in instance config""" " append module location to path " - sys.path.append(os.path.dirname(__file__)+"/modules/") + sys.path.append(os.path.dirname(__file__) + "/modules/") " append usermodule dir to beginning of path" for path in self.botconfig["bot"]["usermodules"]: - sys.path.insert(0, path+"/") + sys.path.insert(0, path + "/") for modulename in self.botconfig["modules"]: self.loadmodule(modulename) @@ -129,12 +128,12 @@ class PyIRCBot: :param moduleName: Name of the module to import :type moduleName: str""" " check if already exists " - if not name in self.modules: + if name not in self.modules: self.log.info("Importing %s" % name) " attempt to load " try: moduleref = __import__(name) - self.modules[name]=moduleref + self.modules[name] = moduleref return (True, None) except Exception as e: " on failure (usually syntax error in Module code) print an error " @@ -156,9 +155,7 @@ class PyIRCBot: self.unloadmodule(name) " delete all references to the module" if name in self.modules: - item = self.modules[name] del self.modules[name] - del item " delete copy that python stores in sys.modules " if name in sys.modules: del sys.modules[name] @@ -170,10 +167,10 @@ class PyIRCBot: :type moduleName: str""" " check if already loaded " if name in self.moduleInstances: - self.log.warning( "Module %s already loaded" % name ) + self.log.warning("Module %s already loaded" % name) return False " check if needs to be imported, and verify it was " - if not name in self.modules: + if name not in self.modules: importResult = self.importmodule(name) if not importResult[0]: return importResult @@ -192,11 +189,9 @@ class PyIRCBot: self.moduleInstances[name].ondisable() " unload all hooks " self.unloadModuleHooks(self.moduleInstances[name]) - " remove the instance " - item = self.moduleInstances.pop(name) - " delete the instance" - del item - self.log.info( "Module %s unloaded" % name ) + " remove & delete the instance " + self.moduleInstances.pop(name) + self.log.info("Module %s unloaded" % name) return (True, None) else: self.log.info("Module %s not loaded" % name) @@ -270,7 +265,7 @@ class PyIRCBot: :param name: name of the module to return :type name: str :returns: object -- the module object""" - if not name in self.moduleInstances: + if name not in self.moduleInstances: return None return self.moduleInstances[name] @@ -293,7 +288,7 @@ class PyIRCBot: :type service: str :returns: object -- the module object, if found. None if not found.""" m = self.getmodulesbyservice(service) - if len(m)>0: + if len(m) > 0: return m[0] return None @@ -327,8 +322,8 @@ class PyIRCBot: basepath = "%s/config/%s" % (self.botconfig["bot"]["datadir"], moduleName) - if os.path.exists("%s.json"%basepath): - return "%s.json"%basepath + if os.path.exists("%s.json" % basepath): + return "%s.json" % basepath return None " Utility methods " @@ -343,7 +338,7 @@ class PyIRCBot: :param requireArgs: if true, only validate if the command use has any amount of trailing text :type requireArgs: bool""" - if not type(command)==list: + if not type(command) == list: command = [command] for item in command: cmd = PyIRCBot.messageHasCommandSingle(item, message, requireArgs) @@ -355,37 +350,27 @@ class PyIRCBot: def messageHasCommandSingle(command, message, requireArgs=False): # Check if the message at least starts with the command messageBeginning = message[0:len(command)] - if messageBeginning!=command: + if messageBeginning != command: return False # Make sure it's not a subset of a longer command (ie .meme being set off by .memes) - subsetCheck = message[len(command):len(command)+1] - if subsetCheck!=" " and subsetCheck!="": + subsetCheck = message[len(command):len(command) + 1] + if subsetCheck != " " and subsetCheck != "": return False # We've got the command! Do we need args? argsStart = len(command) args = "" if argsStart > 0: - args = message[argsStart+1:] + args = message[argsStart + 1:] if requireArgs and args.strip() == '': return False # Verified! Return the set. -<<<<<<< HEAD return ParsedCommand(command, args.split(" "), args, message) -======= - ob = type('ParsedCommand', (object,), {}) - ob.command = command - ob.args = [] if args=="" else args.split(" ") - ob.args_str = args - ob.message = message - return ob - # return (True, command, args, message) ->>>>>>> Add unoplayer module @staticmethod def load(filepath): diff --git a/pyircbot/rpc.py b/pyircbot/rpc.py index 12f7faa..5a1cb14 100644 --- a/pyircbot/rpc.py +++ b/pyircbot/rpc.py @@ -11,11 +11,12 @@ import logging from pyircbot import jsonrpc from threading import Thread + class BotRPC(Thread): """:param main: A reference to the PyIRCBot instance this instance will control :type main: PyIRCBot """ - + def __init__(self, main): Thread.__init__(self, daemon=True) self.bot = main @@ -29,86 +30,86 @@ class BotRPC(Thread): ) ) ) - - self.server.register_function( self.importModule ) - self.server.register_function( self.deportModule ) - self.server.register_function( self.loadModule ) - self.server.register_function( self.unloadModule ) - self.server.register_function( self.reloadModule ) - self.server.register_function( self.redoModule ) - self.server.register_function( self.getLoadedModules ) - self.server.register_function( self.pluginCommand ) - self.server.register_function( self.setPluginVar ) - self.server.register_function( self.getPluginVar ) - self.server.register_function( self.quit ) - self.server.register_function( self.eval ) - self.server.register_function( self.exec ) - + + self.server.register_function(self.importModule) + self.server.register_function(self.deportModule) + self.server.register_function(self.loadModule) + self.server.register_function(self.unloadModule) + self.server.register_function(self.reloadModule) + self.server.register_function(self.redoModule) + self.server.register_function(self.getLoadedModules) + self.server.register_function(self.pluginCommand) + self.server.register_function(self.setPluginVar) + self.server.register_function(self.getPluginVar) + self.server.register_function(self.quit) + self.server.register_function(self.eval) + self.server.register_function(self.exec) + self.start() - + def run(self): """Internal, starts the RPC server""" self.server.serve() - + def importModule(self, moduleName): """Import a module - + :param moduleName: Name of the module to import :type moduleName: str""" - self.log.info("RPC: calling importModule(%s)"%moduleName) + self.log.info("RPC: calling importModule(%s)" % moduleName) return self.bot.importmodule(moduleName) - + def deportModule(self, moduleName): """Remove a module's code from memory. If the module is loaded it will be unloaded silently. - + :param moduleName: Name of the module to import :type moduleName: str""" - self.log.info("RPC: calling deportModule(%s)"%moduleName) + self.log.info("RPC: calling deportModule(%s)" % moduleName) self.bot.deportmodule(moduleName) - + def loadModule(self, moduleName): """Activate a module. - + :param moduleName: Name of the module to activate :type moduleName: str""" - self.log.info("RPC: calling loadModule(%s)"%moduleName) + self.log.info("RPC: calling loadModule(%s)" % moduleName) return self.bot.loadmodule(moduleName) - + def unloadModule(self, moduleName): """Deactivate a module. - + :param moduleName: Name of the module to deactivate :type moduleName: str""" - self.log.info("RPC: calling unloadModule(%s)"%moduleName) + self.log.info("RPC: calling unloadModule(%s)" % moduleName) self.bot.unloadmodule(moduleName) - + def reloadModule(self, moduleName): """Deactivate and activate a module. - + :param moduleName: Name of the target module :type moduleName: str""" - self.log.info("RPC: calling reloadModule(%s)"%moduleName) + self.log.info("RPC: calling reloadModule(%s)" % moduleName) self.bot.unloadmodule(moduleName) return self.bot.loadmodule(moduleName) - + def redoModule(self, moduleName): """Reload a running module from disk - + :param moduleName: Name of the target module :type moduleName: str""" - self.log.info("RPC: calling redoModule(%s)"%moduleName) + self.log.info("RPC: calling redoModule(%s)" % moduleName) return self.bot.redomodule(moduleName) - + def getLoadedModules(self): """Return a list of active modules - + :returns: list -- ['ModuleName1', 'ModuleName2']""" self.log.info("RPC: calling getLoadedModules()") return list(self.bot.moduleInstances.keys()) - + def pluginCommand(self, moduleName, methodName, argList): """Run a method of an active module - + :param moduleName: Name of the target module :type moduleName: str :param methodName: Name of the target method @@ -124,10 +125,10 @@ class BotRPC(Thread): return (False, "Method not found") self.log.info("RPC: calling %s.%s(%s)" % (moduleName, methodName, argList)) return (True, method(*argList)) - + def getPluginVar(self, moduleName, moduleVarName): """Extract a property from an active module and return it - + :param moduleName: Name of the target module :type moduleName: str :param moduleVarName: Name of the target property @@ -140,10 +141,10 @@ class BotRPC(Thread): return (False, "Plugin not found") self.log.info("RPC: getting %s.%s" % (moduleName, moduleVarName)) return (True, getattr(plugin, moduleVarName)) - + def setPluginVar(self, moduleName, moduleVarName, value): """Set a property of an active module - + :param moduleName: Name of the target module :type moduleName: str :param moduleVarName: Name of the target property @@ -158,26 +159,26 @@ class BotRPC(Thread): self.log.info("RPC: setting %s.%s = %s )" % (moduleName, moduleVarName, value)) setattr(plugin, moduleVarName, value) return (True, "Var set") - + def eval(self, code): """Execute arbitrary python code on the bot - + :param code: Python code to pass to eval :type code: str""" return (True, eval(code)) - + def exec(self, code): """Execute arbitrary python code on the bot - + :param code: Python code to pass to exec :type code: str""" return (True, exec(code)) - + def quit(self, message): """Tell the bot to quit IRC and exit - + :param message: Quit message :type moduleName: str""" self.bot.kill(message=message) return (True, "Shutdown ordered") - \ No newline at end of file + diff --git a/pyircbot/rpcclient.py b/pyircbot/rpcclient.py index 43b0c31..36bce46 100644 --- a/pyircbot/rpcclient.py +++ b/pyircbot/rpcclient.py @@ -1,14 +1,16 @@ -from sys import argv,exit +from sys import argv, exit from pyircbot import jsonrpc + def connect(host, port): return jsonrpc.ServerProxy(jsonrpc.JsonRpc20(), jsonrpc.TransportTcpIp(addr=(host, port), timeout=60.0)) -if __name__=="__main__": + +if __name__ == "__main__": if len(argv) is not 3: print("Expected ip and port arguments") exit(1) - print("Connecting to pyircbot rpc on port %s:%s..." % (argv[1],argv[2])) + print("Connecting to pyircbot rpc on port %s:%s..." % (argv[1], argv[2])) rpc = connect(argv[1], int(argv[2])) print("Connected to rpc") print("Loaded modules: %s" % rpc.getLoadedModules()) diff --git a/setup.py b/setup.py index 56e4ab9..5284e53 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)