def __init__(self, dbUser, dbPassword, dbDatabase, nickservPassword): self.db = DbAccess(dbUser, dbPassword, dbDatabase) # Just setting this variable sets the nickserv login password # (Maybe? We still do our own procesing later) self.password = nickservPassword print "Connected to MySQL server" self.trackedpresent = dict() self.gotwhoischannel = False self.seenQuery = re.compile( "\s*seen\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)[\s\?]*" ) self.tellQuery = re.compile( "\s*tell\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)\s*(.+)" ) self.factoidQuery = re.compile( "(.+)[?!](\s*$|\s*\|\s*([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)$)" ) self.factoidSet = re.compile("(.+?)\s(is|are)(\salso)?\s(.+)") self.googleQuery = re.compile( "\s*google\s+(.*?)\s+for\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)" ) self.thingMention = re.compile( "http(s)?:\/\/www.thingiverse.com\/thing:(\d+)", re.IGNORECASE) self.youtubeMention = re.compile( "http(s)?:\/\/(www\.youtube\.com\/watch\?v=|youtu\.be\/)([\w\-]*)(\S*)", re.IGNORECASE) self.uptimeStart = datetime.now() self.lurkerReplyChannel = ""
def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName)
def target_add(w_parms): # target add # print "Adding target -- opts %s " % str(w_parms) sql = """INSERT INTO targets (`target-handle`, `create-datetime`, `target-description`, ipv4, ipv6, domain) VALUES (%s, NOW(), %s, %s, %s, %s)""" # values = { "target-handle": "", "target-description": "", "ipv4": "", "ipv6": "", "domain": "" } print "Adding target:" for el in w_parms: if el[0] == "-f": el0parts = el[1].partition('=') if el0parts[1] != "": values[el0parts[0]] = el0parts[2] print "Values: %s" % str(values) # connect to DB db = DbAccess() db.cursor.execute(sql, (values["target-handle"], values["target-description"], values["ipv4"], values["ipv6"], values["domain"])) db.conn.close()
def target_list(): db = DbAccess() print "# Listing targets" print "#" db.cursor.execute("SELECT * FROM targets ORDER BY `id` DESC") cnt = 0 print "{0:5} | {1:14} | {2:20} | {3:20} | {4:30} | {5:15} | {6:20}". \ format("id","target-handle", "target-description", "create-datetime", "domain", "ipv4", "ipv6") while True: row = db.cursor.fetchone() if row == None: break print "{0:5} | {1:14} | {2:20} | {3:20} | {4:30} | {5:15} | {6:20}". \ format(row["id"], row["target-handle"], row["target-description"][:19], str(row["create-datetime"]), row["domain"], row["ipv4"], row["ipv6"]) cnt = cnt + 1 print "#" print "# Total targets %s" % (cnt) db.conn.close()
def target_import(w_parms): filename = None for x in w_parms: if x[0] == "-f": filename = x[1] break print "Importing targets from file: %s" % str(filename) # parse xml file try: doc1 = parse(filename) targets = doc1.getElementsByTagName("target") except Exception as e: # print "Error parsing XML file, aborting." print "Exception ocurred: %s" % str(e.args) return db = DbAccess() for t in targets: # values = {"target-handle":"", "target-description":"", "ipv4":"", "ipv6":"", "domain":""} values = {} print "Inserting target found: %s - %s" % (str( t.getAttribute("handle")), t.childNodes[0].nodeValue) values["target-handle"] = t.getAttribute("handle") values["domain"] = t.getAttribute("domain") values["ipv4"] = t.getAttribute("ipv4") values["ipv6"] = t.getAttribute("ipv6") values["target-description"] = t.childNodes[0].nodeValue values["create-datetime"] = time.strftime("%Y-%m-%d %H:%M:%S") try: # save to db db.autoInsert("targets", values) print "Success inserting record" except Exception as e: # print "Error parsing XML file, aborting." print "Warning - exception ocurred while inserting record: %s" % str( e.args) return # END target_import ################################################################################
class DbAccessYoutubeRefTest(unittest.TestCase): def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName) def test_youtube_refs(self): testItem = "I7nVrT00ST4" testTitle = "Pro Riders Laughing" # Verify that referencing an item the first time causes the ref count to # be set to 1 rows = self.db.addYoutubeRef(testItem) self.assertEquals( len(rows), 1, "First youtube ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 1, "First youtube ref returned wrong number of references.") self.assertIsNone( data[1], "First youtube ref returned a title when it shouldn't have.") rows = self.db.addYoutubeRef(testItem) self.assertEquals( len(rows), 1, "First youtube ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 2, "Second youtube ref returned wrong number of references.") self.assertIsNone( data[1], "Second youtube ref returned a title when it shouldn't have.") self.db.addYoutubeTitle(testItem, testTitle) rows = self.db.addYoutubeRef(testItem) self.assertEquals( len(rows), 1, "First youtube ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 3, "Third youtube ref returned wrong number of references.") self.assertEquals( data[1], testTitle, "Third youtube ref returned the wrong title: '%s'" % data[1]) def tearDown(self): self.db.deleteAllYoutubeRefs()
def __init__(self): self.db = DbAccess() self.trackedpresent = dict() self.gotwhoischannel = False self.seenQuery = re.compile("\s*seen\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)[\s\?]*") self.tellQuery = re.compile("\s*tell\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)\s*(.+)") self.factoidQuery = re.compile("(.+)[?!](\s*$|\s*\|\s*([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)$)") self.factoidSet = re.compile("(.+?)\s(is|are)(\salso)?\s(.+)") self.googleQuery = re.compile("\s*google\s+(.*?)\s+for\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)") self.uptimeStart = datetime.datetime.now() self.lurkerReplyChannel = ""
def target_del(w_parms): sql1 = """DELETE FROM targets WHERE id = %s""" sql2 = """DELETE FROM targets WHERE target-handle = %s""" print "Deleting targets" values = {} print "Adding target:" for el in w_parms: if el[0] == "-f": el0parts = el[1].partition('=') if el0parts[1] != "": values[el0parts[0]] = el0parts[2] rk = values.popitem() print "Deleting targets where: %s" % str(rk) db = DbAccess() if rk[0] == "id": db.cursor.execute(sql1, (rk[1])) if rk[1] == "target-handle": db.cursor.execute(sql2, (rk[1])) print "Affected rows %s" % (db.cursor.rowcount) db.conn.close()
def target_update(w_parms): # target add # print "Adding target -- opts %s " % str(w_parms) sql = """UPDATE targets SET """ # # values = {"target-description":"", "ipv4":"", "ipv6":"", "domain":""} values = {} print "Adding target:" for el in w_parms: if el[0] == "-f": el0parts = el[1].partition('=') if el0parts[1] != "": values[el0parts[0]] = el0parts[2] sql = sql + "`{0}` = %({1})s, ".format(el0parts[0], el0parts[0]) sql = sql[0:len(sql) - 2] + " WHERE `target-handle`=%(target-handle)s" print "Values: %s" % str(values) # print "Sql: %s" % (sql) # connect to DB db = DbAccess() db.cursor.execute(sql, values) db.conn.close()
from RESTClient import RESTClient, Services from DbAccess import DbAccess # Get last/todays rates client = RESTClient(Services.TODAYS_RATES) data = client.call_service() db = DbAccess() # Get item item = data.find('nsp:Cube', client.namespaces) # Insert entry in DB today = item.__getitem__(0).attrib['time'] today_id = db.insert_entry(today) if today_id == -1: today_id = db.entry_get_id(today)[0] # Insert rates in DB for rate in item.find('nsp:Cube', client.namespaces): db.insert_exchangeRate( today_id, rate.attrib['currency'], rate.attrib['rate'] )
from RESTClient import RESTClient, Services from DbAccess import DbAccess # Get last/todays rates client = RESTClient(Services.ALL_RATES) data = client.call_service() db = DbAccess() list = data.find('nsp:Cube', client.namespaces) # Insert entries in DB for entry in list: entry_date = entry.attrib['time'] entry_date_id = db.insert_entry(entry_date) if entry_date_id == -1: entry_date_id = db.entry_get_id(entry_date)[0] # Insert rates in DB for rate in entry: db.insert_exchangeRate(entry_date_id, rate.attrib['currency'], rate.attrib['rate'])
from RESTClient import RESTClient, Services from DbAccess import DbAccess # Get last/todays rates client = RESTClient(Services.TODAYS_RATES) data = client.call_service() db = DbAccess() # Get item item = data.find('nsp:Cube', client.namespaces) # Insert entry in DB today = item.__getitem__(0).attrib['time'] today_id = db.insert_entry(today) if today_id == -1: today_id = db.entry_get_id(today)[0] # Insert rates in DB for rate in item.find('nsp:Cube', client.namespaces): db.insert_exchangeRate(today_id, rate.attrib['currency'], rate.attrib['rate'])
class Gthx(irc.IRCClient): """An IRC bot for #reprap.""" restring = "" def __init__(self, dbUser, dbPassword, dbDatabase, nickservPassword): self.db = DbAccess(dbUser, dbPassword, dbDatabase) # Just setting this variable sets the nickserv login password # (Maybe? We still do our own procesing later) self.password = nickservPassword print "Connected to MySQL server" self.trackedpresent = dict() self.gotwhoischannel = False self.seenQuery = re.compile( "\s*seen\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)[\s\?]*" ) self.tellQuery = re.compile( "\s*tell\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)\s*(.+)" ) self.factoidQuery = re.compile( "(.+)[?!](\s*$|\s*\|\s*([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)$)" ) self.factoidSet = re.compile("(.+?)\s(is|are)(\salso)?\s(.+)") self.googleQuery = re.compile( "\s*google\s+(.*?)\s+for\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)" ) self.thingMention = re.compile( "http(s)?:\/\/www.thingiverse.com\/thing:(\d+)", re.IGNORECASE) self.youtubeMention = re.compile( "http(s)?:\/\/(www\.youtube\.com\/watch\?v=|youtu\.be\/)([\w\-]*)(\S*)", re.IGNORECASE) self.uptimeStart = datetime.now() self.lurkerReplyChannel = "" def connectionMade(self): if self.password: self.log("IRC Connection made -- sending CAP REQ") self.sendLine('CAP REQ :sasl') else: self.log("IRC Connection made - no nickserv password") irc.IRCClient.connectionMade(self) def irc_CAP(self, prefix, params): self.log("Got irc_CAP") if params[1] != 'ACK' or params[2].split() != ['sasl']: print 'sasl not available' self.quit('') sasl = ('{0}\0{0}\0{1}'.format( self.nickname, self.password)).encode('base64').strip() self.sendLine('AUTHENTICATE PLAIN') self.sendLine('AUTHENTICATE ' + sasl) def irc_903(self, prefix, params): self.log("Got SASL connection successful.") self.sendLine('CAP END') def irc_904(self, prefix, params): print 'sasl auth failed', params self.quit('') def connectionLost(self, reason): irc.IRCClient.connectionLost(self, reason) self.log("[disconnected at %s]" % time.asctime(time.localtime(time.time()))) self.emailClient.send( "%s disconnected" % self.nickname, "%s is disconnected from the server.\n\n%s" % (self.nickname, reason)) def log(self, message): """Write a message to the screen.""" timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time())) print '%s %s' % (timestamp, message) # callbacks for events def signedOn(self): """Called when bot has succesfully signed on to server.""" self.log("Signed on to the IRC server") self.channelList = [ channel for channel in self.factory.channels.split(',') ] for channelm in self.channelList: self.log("Joining channel %s" % channelm) self.join(channelm) if (trackednick == None): self.trackedpresent[channelm] = False self.gotwhoischannel = False # kthx uses: "\s*(${names})[:;,-]?\s*" to match nicks if (trackednick): self.matchNick = "(%s|%s)(:|;|,|-|\s)+(.+)" % (self.nickname, trackednick) print "Querying WHOIS %s at startup" % trackednick self.whois(trackednick) else: self.matchNick = "(%s)(:|;|,|-|\s)+(.+)" % (self.nickname) print "Running in standalone mode." def joined(self, channel): """Called when the bot joins the channel.""" self.log("[I have joined %s as '%s']" % (channel, self.nickname)) message = "I have joined channel %s as '%s'\n" % (channel, self.nickname) self.emailClient.threadsend("%s connected" % self.nickname, message) def userJoined(self, user, channel): """ Called when I see another user joining a channel. """ print "%s joined channel %s" % (user, channel) # TODO: Change this to verify the IP address before setting it if trackednick and (user == trackednick) and (self.trackedpresent[channel] == False): self.trackedpresent[channel] = True print "%s is here!" % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has joined channel %s" % (user, channel)) def userLeft(self, user, channel): """ Called when I see another user leaving a channel. """ print "%s left channel %s" % (user, channel) if trackednick and (user == trackednick) and (self.trackedpresent[channel]): self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has left channel %s" % (user, channel)) def userQuit(self, user, quitMessage): """ Called when I see another user disconnect from the network. """ safeQuitMessage = quitMessage.decode("utf-8") print "%s disconnected : %s" % (user, safeQuitMessage) if trackednick and (user == trackednick): for channel in self.channelList: self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has quit: %s" % (user, quitMessage)) def userKicked(self, kickee, channel, kicker, message): """ Called when I observe someone else being kicked from a channel. """ print "In %s, %s kicked %s : %s" % (channel, kicker, kickee, message) if trackednick and (kickee == trackednick) and (self.trackedpresent[channel]): self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has been kicked from %s by %s: %s" % (kickee, channel, kicker, message)) def userRenamed(self, oldname, newname): """ A user changed their name from oldname to newname. """ print "%s renamed to %s" % (oldname, newname) if (trackednick == None): return if oldname == trackednick: for channel in self.channelList: self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has been renamed to %s" % (oldname, newname)) if newname == trackednick: self.whois(trackednick) print "%s is here!" % trackednick self.emailClient.threadsend( "%s status" % self.nickname, "%s has been renamed to %s--checking WHOIS" % (oldname, newname)) def irc_unknown(self, prefix, command, params): print "Unknown command '%s' '%s' '%s'" % (prefix, command, params) if (command == 'RPL_NAMREPLY'): if (self.lurkerReplyChannel == ""): return users = params[3].split() for user in users: self.channelCount = self.channelCount + 1 rows = self.db.seen(user) if len(rows) == 0: self.lurkerCount = self.lurkerCount + 1 elif (command == 'RPL_ENDOFNAMES'): if (self.lurkerReplyChannel == ""): return print "Got RPL_ENDOFNAMES" self.msg( self.lurkerReplyChannel, "%d of the %d users in %s right now have never said anything." % (self.lurkerCount, self.channelCount, params[1])) self.lurkerReplyChannel = "" def irc_RPL_WHOISCHANNELS(self, prefix, params): """This method is called when the client recieves a reply for whois. params[0]: requestor params[1]: nick requested params[2]: list of channels in common """ print "Got WHOISCHANNELS with prefix '%s' and params '%s'" % (prefix, params) print "%s is in channels %s" % (params[1], params[2]) self.gotwhoischannel = True trackedchannels = params[2].translate(None, '@').split(" ") for channel in self.channelList: if channel in trackedchannels: self.trackedpresent[channel] = True print "%s is in %s!!" % (params[1], channel) self.emailClient.threadsend( "%s status" % self.nickname, "%s is in channel %s" % (params[1], params[2])) else: self.trackedpresent[channel] = False print "%s is NOT in %s!!" % (params[1], channel) def irc_RPL_WHOISUSER(self, prefix, params): print "Got WHOISUSER with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISSERVER(self, prefix, params): print "Got WHOISSERVER with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISOPERATOR(self, prefix, params): print "Got WHOISOPERATOR with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISIDLE(self, prefix, params): print "Got WHOISIDLE with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_ENDOFWHOIS(self, prefix, params): print "Got ENDOFWHOIS with prefix '%s' and params '%s'" % (prefix, params) if not self.gotwhoischannel: if (trackednick != None): print "No response from %s. Must not be present." % trackednick for channel in self.channelList: self.trackedpresent[channel] = False self.emailClient.threadsend( "%s status" % self.nickname, "%s is not in channel %s" % (trackednick, channel)) def getFactoidString(self, query): answer = self.db.getFactoid(query) if answer: for i, factoid in enumerate(answer): if i == 0: if factoid[3].startswith( "<reply>") or factoid[3].startswith("<action>"): fstring = factoid[3] break else: fstring = query fstring += "%s" % " are " if factoid[2] else " is " if i > 0: fstring += "also " fstring += factoid[3] if i < len(answer) - 1: fstring += " and" return fstring else: return None def moodToString(self, mood): if mood < -100: return "suicidal!" if mood < -50: return "really depressed." if mood < -10: return "depressed." if mood < 0: return "kinda bummed." if mood == 0: return "meh, okay I guess." if mood < 10: return "alright." if mood < 50: return "pretty good." return "great, Great, GREAT!!" def privmsg(self, user, channel, msg): """Called when the bot receives a message, both public and private.""" user = user.split('!', 1)[0] # By default, don't reply to anything canReply = False private = False replyChannel = channel parseMsg = msg directAddress = False # Debug print ALL messages #print "Message from '%s' on '%s': '%s'" % (user, channel, msg) # Check to see if they're sending me a private message if channel == self.nickname: canReply = True private = True replyChannel = user self.log("Private message from %s: %s" % (user, msg)) if str.lower(user) == "nickserv": self.log("Nickserv says: %s" % msg) if parseMsg.startswith("whois "): whoisnick = parseMsg.split(" ", 1)[1] print "Doing a whois '%s'" % whoisnick self.whois(whoisnick) # Update the seen database, but only if it's not a private message if channel in self.channelList and not private: self.db.updateSeen(user, channel, msg) # If kthx said something, mark him as here and ignore everything he says if user == trackednick and not private: if (self.trackedpresent[channel] == False): self.trackedpresent[channel] = True self.emailClient.threadsend( "%s status" % self.nickname, "%s spoke in %s unexpectedly and got marked as present: %s" % (user, channel, msg)) return # If kthx is gone, then we can always reply if not private and not self.trackedpresent[channel]: canReply = True # Check to see if we have a tell waiting for this user tells = self.db.getTell(user) if tells: for message in tells: print "Found tell for '%s' from '%s'" % (user, message[Tell.author]) author = message[Tell.author] timestring = timesincestring(message[Tell.timestamp]) text = message[Tell.message] inTracked = message[Tell.inTracked] # We have 3 cases: # 1) kthx was around when this tell happened and is still around now. # In this case, we assume kthx will relay the message and just delete it # 2) kthx was around when this tell happened and is not here now. # In this case, we want to send the message and mention that kthx may repeat it # 3) kthx was not around when this tell happened and may or may not be here now # Whether or not kthx is now here, we need to say the message # 4) gthx was specifically addressed for this tell # Whether or not kthx is now here, we need to say the message # # If we can't reply, it means that kthx is present. In that # case, the tell has already been erased, so in both cases, # we're good. if canReply or not inTracked: if inTracked: self.msg( replyChannel, "%s: %s ago <%s> tell %s %s (%s may repeat this)" % (user, timestring, author, user, text, trackednick)) else: self.msg( replyChannel, "%s: %s ago <%s> tell %s %s" % (user, timestring, author, user, text)) # Check for specifically addressed messages m = re.match(self.matchNick, parseMsg) if m: print "Found message addressed to '%s'. My nick is '%s'." % ( m.group(1), self.nickname) parseMsg = m.group(3) # Mark it as a direct address so we can look for a factoid directAddress = True # If it's addressed directly to me, we can reply if m.group(1) == self.nickname: canReply = True # Check for status query if canReply and parseMsg == "status?": if (trackednick): if (private): reply = "%s: OK; Up for %s; " % ( VERSION, timesincestring(self.uptimeStart)) for channel in self.channelList: reply += "%s %s; " % (channel, "PRESENT" if self.trackedpresent[channel] else "GONE") else: reply = "%s: OK; Up for %s; %s is %s" % ( VERSION, timesincestring( self.uptimeStart), trackednick, "PRESENT" if self.trackedpresent[channel] else "GONE") else: reply = "%s: OK; Up for %s; standalone mode" % ( VERSION, timesincestring(self.uptimeStart)) mood = self.db.mood() reply += " mood: %s" % self.moodToString(mood) self.msg(replyChannel, reply) return # Check for lurker query if canReply and parseMsg == "lurkers?": self.msg(replyChannel, "Looking for lurkers...") self.lurkerReplyChannel = replyChannel self.lurkerCount = 0 self.channelCount = 0 print "Sending request 'NAMES %s'" % channel self.sendLine("NAMES %s" % channel) return # Check for tell query m = self.tellQuery.match(parseMsg) if m and directAddress: print "Got tell from '%s' for '%s' message '%s'." % ( user, m.group(1), m.group(2)) # The is in the tracked bot if the tracked bot is present and it was not a message # specifically directed to us. This is a little tricky since the only way to know # that a message was specifically directed to us is to see if it was a direct address # and we can reply success = self.db.addTell( user, m.group(1), m.group(2), not (directAddress and canReply) and self.trackedpresent[channel]) if success and canReply: self.msg( replyChannel, "%s: I'll pass that on when %s is around." % (user, m.group(1))) return # Check for seen query if canReply: m = self.seenQuery.match(parseMsg) if m: queryname = m.group(1) print "%s asked about '%s'" % (user, queryname) rows = self.db.seen(queryname) if len(rows) == 0: reply = "Sorry, I haven't seen %s." % queryname self.msg(replyChannel, reply) for i, row in enumerate(rows): reply = "%s was last seen in %s %s ago saying '%s'." % ( row[Seen.name], row[Seen.channel], timesincestring( row[Seen.timestamp]), row[Seen.message]) self.msg(replyChannel, reply) if i >= 2: # Don't reply more than 3 times to a seen query break return # Check for google query if directAddress: if canReply: m = self.googleQuery.match(parseMsg) if m: queryname = urllib.quote_plus(m.group(1)) foruser = m.group(2) print "%s asked to google '%s' for %s" % (user, queryname, foruser) reply = "%s: http://lmgtfy.com/?q=%s" % (foruser, queryname) self.msg(replyChannel, reply) return # Check for setting a factoid factoid = None if directAddress: factoid = self.factoidSet.match(parseMsg) if factoid: invalidwords = re.match( '(here|how|it|something|that|this|what|when|where|which|who|why|you)', factoid.group(1), re.IGNORECASE) if not invalidwords: safeFactoid = factoid.group(1).decode("utf-8") print "%s tried to set factoid '%s'." % (user, safeFactoid) success = self.db.addFactoid( user, factoid.group(1), True if factoid.group(2) == 'are' else False, factoid.group(4), True if not factoid.group(3) else False) if canReply: if success: self.msg(replyChannel, "%s: Okay." % user) else: self.msg( replyChannel, "I'm sorry, %s. I'm afraid I can't do that." % user) # Check for getting a factoid if canReply: f = self.factoidQuery.match(parseMsg) if f: safeFactoid = f.group(1).decode("utf-8") print "factoid query from %s:%s for '%s'" % (user, channel, safeFactoid) answer = self.getFactoidString(f.group(1)) if answer: # Replace !who and !channel in the reply answer = re.sub("!who", user, answer) answer = re.sub("!channel", channel, answer) if answer.startswith("<reply>"): answer = answer[7:] if answer.startswith("<action>"): self.describe(replyChannel, answer[8:]) else: if (f.group(3)): answer = "%s, %s" % (f.group(3), answer) self.msg(replyChannel, answer) # Check for info request if canReply and parseMsg.startswith("info "): query = parseMsg[5:] if query[-1:] == "?": query = query[:-1] safeFactoid = query.decode("utf-8") print "info request for '%s' ReplyChannel is '%s'" % (safeFactoid, replyChannel) refcount = 0 answer = self.db.infoFactoid(query) if answer: count = answer[0][6] if not count: count = "0" print "Factoid '%s' has been referenced %s times" % ( safeFactoid, count) self.msg( replyChannel, "Factoid '%s' has been referenced %s times" % (query, count)) for factoid in answer: user = factoid[3] value = factoid[2] if not user: user = "******" if value: print "At %s, %s set to: %s" % (factoid[4], user, value) self.msg( replyChannel, "At %s, %s set to: %s" % (factoid[4], user, value)) else: print "At %s, %s deleted this item" % (factoid[4], user) self.msg( replyChannel, "At %s, %s deleted this item" % (factoid[4], user)) else: print "No info for factoid '%s'" % safeFactoid self.msg(replyChannel, "Sorry, I couldn't find an entry for %s" % query) # Check for forget request if directAddress and parseMsg.startswith("forget "): query = parseMsg[7:] print "forget request for '%s'" % query forgotten = self.db.forgetFactoid(query, user) if canReply: if forgotten: self.msg(replyChannel, "%s: I've forgotten about %s" % (user, query)) else: self.msg( replyChannel, "%s: Okay, but %s didn't exist anyway" % (user, query)) # Check for thingiverse mention if canReply: match = self.thingMention.search(parseMsg) if match: thingId = int(match.group(2)) print "Match for thingiverse query item %s" % thingId rows = self.db.addThingiverseRef(thingId) refs = int(rows[0][0]) title = rows[0][1] if title is None: print "Attemping to get title for thingiverse ID %s" % thingId agent = Agent(reactor) titleQuery = agent.request( 'GET', 'https://www.thingiverse.com/thing:%s' % thingId, Headers({'User-Agent': ['gthx IRC bot']}), None) def titleResponse(title): if title: title = unescape(title) self.db.addThingiverseTitle(thingId, title) print "The title for thing %s is: %s " % (thingId, title) reply = 'https://www.thingiverse.com/thing:%s => %s => %s IRC mentions' % ( thingId, title, refs) self.msg(replyChannel, reply) else: print "No title found for thing %s" % (thingId) reply = 'https://www.thingiverse.com/thing:%s => ???? => %s IRC mentions' % ( thingId, refs) self.msg(replyChannel, reply) def queryResponse(response): if response.code == 200: finished = Deferred() finished.addCallback(titleResponse) response.deliverBody(TitleParser(finished)) return finished print "Got error response from thingiverse query: %s" % ( response) titleResponse(None) return None titleQuery.addCallback(queryResponse) else: print "Already have a title for thing %s: %s" % (thingId, title) reply = 'https://www.thingiverse.com/thing:%s => %s => %s IRC mentions' % ( thingId, title, refs) self.msg(replyChannel, reply) # Check for youtube mention if canReply: match = self.youtubeMention.search(parseMsg) if match: youtubeId = match.group(3) fullLink = match.group(0) print "Match for youtube query item %s" % youtubeId rows = self.db.addYoutubeRef(youtubeId) refs = int(rows[0][0]) title = rows[0][1] if title is None: print "Attemping to get title for youtubeId %s" % youtubeId agent = Agent(reactor) titleQuery = agent.request( 'GET', 'https://www.youtube.com/watch?v=%s' % youtubeId, Headers({'User-Agent': ['gthx IRC bot']}), None) def titleResponse(title): if title: title = unescape(title) self.db.addYoutubeTitle(youtubeId, title) print "The title for video %s is: %s " % ( youtubeId, title) reply = '%s => %s => %s IRC mentions' % ( fullLink, title, refs) print "Reply is: %s" % reply self.msg(replyChannel, reply) print "Message sent." else: print "No title found for youtube video %s" % ( youtubeId) reply = '%s => ???? => %s IRC mentions' % ( fullLink, refs) self.msg(replyChannel, reply) def queryResponse(response): if response.code == 200: finished = Deferred() finished.addCallback(titleResponse) response.deliverBody(TitleParser(finished)) return finished print "Got error response from youtube query: %s:%s" % ( response.code, response.phrase) pprint(list(response.headers.getAllRawHeaders())) titleResponse(None) return None titleQuery.addCallback(queryResponse) else: print "Already have a title for item %s: %s" % (youtubeId, title) reply = '%s => %s => %s IRC mentions' % (fullLink, title, refs) self.msg(replyChannel, reply) def action(self, sender, channel, message): m = re.match( "([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)", sender) if m: sender = m.group(1) print "* %s %s" % (sender, message) self.db.updateSeen(sender, channel, "* %s %s" % (sender, message))
class DbAccessTellTest(unittest.TestCase): missinguser = "******" def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName) def test_user_with_no_tells(self): user = "******" data = self.db.getTell(user) self.assertEquals( len(data), 0, "Wrong number of tells returned for a user who doesn't have any waiting" ) def test_get_with_no_tells(self): data = self.db.getTell(DbAccessTellTest.missinguser) self.assertFalse( data, "Got a valid return for a user with no tells waiting") def test_add_and_get_and_verify_tell(self): teller = "talker" receiver = "randomuser" message = "Ping me when you can" kthxKnows = False success = self.db.addTell(teller, receiver, message, kthxKnows) self.assertTrue(success, "Failed to add a tell to a user") data = self.db.getTell(receiver) self.assertTrue(data, "Got no tells for a user with tells waiting") self.assertEquals(len(data), 1, "Got wrong number of tells for a user") tell = data[0] self.assertEquals(tell[1], teller, "Got wrong author for a tell") self.assertEquals(tell[2], receiver, "Got wrong recipient for a tell") self.assertEquals(tell[4], message, "Got wrong message for a tell") self.assertEquals(tell[5], kthxKnows, "Got wrong inTracked for a tell") delta = datetime.now() - tell[3] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a tell date set: delta is %d" % delta.total_seconds()) # Now get again for the same user to make sure they've been cleared data = self.db.getTell(receiver) self.assertFalse(data, "Got tells for a user with none waiting") def test_add_and_get_and_verify_unicode_tell(self): teller = "😇talker" receiver = "random🚕user" message = "☎️ me when you can" kthxKnows = True success = self.db.addTell(teller, receiver, message, kthxKnows) self.assertTrue(success, "Failed to add a tell to a user") data = self.db.getTell(receiver) self.assertTrue(data, "Got no tells for a user with tells waiting") self.assertEquals(len(data), 1, "Got wrong number of tells for a user") tell = data[0] self.assertEquals(tell[1], teller, "Got wrong author for a tell") self.assertEquals(tell[2], receiver, "Got wrong recipient for a tell") self.assertEquals(tell[4], message, "Got wrong message for a tell") self.assertEquals(tell[5], kthxKnows, "Got wrong inTracked for a tell") delta = datetime.now() - tell[3] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a tell date set: delta is %d" % delta.total_seconds()) # Now get again for the same user to make sure they've been cleared data = self.db.getTell(receiver) self.assertFalse(data, "Got tells for a user with none waiting") def test_multiple_tells(self): teller = "talker" teller2 = "another_talker" teller3 = "schminkebob" receiver = "randomuser" message = "Ping me when you can" message2 = "Can you give me a ring please?" message3 = "WAKE UP!!" kthxKnows = False kthxKnows2 = True kthxKnows3 = False success = self.db.addTell(teller, receiver, message, kthxKnows) self.assertTrue(success, "Failed to add a tell to a user") success = self.db.addTell(teller2, receiver, message2, kthxKnows2) self.assertTrue(success, "Failed to add a second tell to a user") success = self.db.addTell(teller3, receiver, message3, kthxKnows3) self.assertTrue(success, "Failed to add a third tell to a user") data = self.db.getTell(receiver) self.assertTrue(data, "Got no tells for a user with tells waiting") self.assertEquals(len(data), 3, "Got wrong number of tells for a user") tell = data[0] self.assertEquals(tell[1], teller, "Got wrong author for a tell") self.assertEquals(tell[2], receiver, "Got wrong recipient for a tell") self.assertEquals(tell[4], message, "Got wrong message for a tell") self.assertEquals(tell[5], kthxKnows, "Got wrong inTracked for a tell") delta = datetime.now() - tell[3] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a tell date set: delta is %d" % delta.total_seconds()) tell = data[1] self.assertEquals(tell[1], teller2, "Got wrong author for a tell") self.assertEquals(tell[2], receiver, "Got wrong recipient for a tell") self.assertEquals(tell[4], message2, "Got wrong message for a tell") self.assertEquals(tell[5], kthxKnows2, "Got wrong inTracked for a tell") delta = datetime.now() - tell[3] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a tell date set: delta is %d" % delta.total_seconds()) tell = data[2] self.assertEquals(tell[1], teller3, "Got wrong author for a tell") self.assertEquals(tell[2], receiver, "Got wrong recipient for a tell") self.assertEquals(tell[4], message3, "Got wrong message for a tell") self.assertEquals(tell[5], kthxKnows3, "Got wrong inTracked for a tell") delta = datetime.now() - tell[3] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a tell date set: delta is %d" % delta.total_seconds()) # Now get again for the same user to make sure they've been cleared data = self.db.getTell(receiver) self.assertFalse(data, "Got tells for a user with none waiting") def tearDown(self): self.db.deleteAllTells()
class DbAccessFactoidTest(unittest.TestCase): def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName) def test_get_missing_factoid(self): missingFactoid = "missingfactoid" data = self.db.getFactoid(missingFactoid) self.assertFalse( data, "Got a valid return from a factoid that shouldn't exist") def test_add_factoid(self): user = "******" item = "somefactoid" isAre = False definition = "a good thing to talk about" overwrite = False success = self.db.addFactoid(user, item, isAre, definition, overwrite) self.assertTrue(success, "Failed to add a new factoid") data = self.db.getFactoid(item) self.assertEqual( len(data), 1, "Returned wrong number of results for a factoid: %d" % (len(data), )) factoid = data[0] # TODO: I really need to make this data access more intuitive to use self.assertEquals( factoid[1], item, "Factoid failed to retrieve the item field correctly") self.assertEquals( factoid[2], isAre, "Factoid failed to retrieve the is/are field correctly") self.assertEquals( factoid[3], definition, "Factoid failed to retrieve the defintion correctly") self.assertEquals(factoid[4], user, "Factoid failed to retrieve the user correctly") delta = datetime.now() - factoid[5] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a factoid date set: delta is %d" % delta.total_seconds()) self.assertFalse( factoid[6], "Factoid failed to retrieve the locked flag correctly") def test_add_unicode_factoid(self): user = "******" item = "some🐶factoid" isAre = False definition = "a good thing to eat 🍕🌮🍜🎂🍧" overwrite = False success = self.db.addFactoid(user, item, isAre, definition, overwrite) self.assertTrue(success, "Failed to add a new factoid") data = self.db.getFactoid(item) self.assertEqual( len(data), 1, "Returned wrong number of results for a factoid: %d" % (len(data), )) factoid = data[0] self.assertEquals( factoid[1], item, "Factoid failed to retrieve the item field correctly") self.assertEquals( factoid[2], isAre, "Factoid failed to retrieve the is/are field correctly") self.assertEquals( factoid[3], definition, "Factoid failed to retrieve the defintion correctly") self.assertEquals(factoid[4], user, "Factoid failed to retrieve the user correctly") delta = datetime.now() - factoid[5] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a factoid date set: delta is %d" % delta.total_seconds()) self.assertFalse( factoid[6], "Factoid failed to retrieve the locked flag correctly") def test_add_to_factoid_then_replace(self): user = "******" user2 = "someotheruser" user3 = "someguy" item = "somefactoidToReplace" isAre = True definition = "a good thing to talk about" definition2 = "something people write" replacement = "stupid facts" success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") success = self.db.addFactoid(user2, item, isAre, definition2, False) self.assertTrue(success, "Failed to add a new factoid") data = self.db.getFactoid(item) self.assertEqual( len(data), 2, "Returned wrong number of results for a multi-factoid: %d" % (len(data), )) factoid = data[0] self.assertEquals( factoid[1], item, "Factoid failed to retrieve the item field correctly") self.assertEquals( factoid[2], isAre, "Factoid failed to retrieve the is/are field correctly") self.assertEquals( factoid[3], definition, "Factoid failed to retrieve the defintion correctly") self.assertEquals(factoid[4], user, "Factoid failed to retrieve the user correctly") delta = datetime.now() - factoid[5] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a factoid date set: delta is %d" % delta.total_seconds()) self.assertFalse( factoid[6], "Factoid failed to retrieve the locked flag correctly") factoid = data[1] self.assertEquals( factoid[1], item, "Factoid failed to retrieve the item field correctly") self.assertEquals( factoid[2], isAre, "Factoid failed to retrieve the is/are field correctly") self.assertEquals( factoid[3], definition2, "Factoid failed to retrieve the defintion correctly") self.assertEquals(factoid[4], user2, "Factoid failed to retrieve the user correctly") delta = datetime.now() - factoid[5] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a factoid date set: delta is %d" % delta.total_seconds()) self.assertFalse( factoid[6], "Factoid failed to retrieve the locked flag correctly") # Now overwrite these definitions with a totally new one success = self.db.addFactoid(user3, item, isAre, replacement, True) self.assertTrue(success, "Failed to replace a new factoid") data = self.db.getFactoid(item) self.assertEqual( len(data), 1, "Returned wrong number of results for a replacedfactoid: %d" % (len(data), )) factoid = data[0] self.assertEquals( factoid[1], item, "Factoid failed to retrieve the item field correctly") self.assertEquals( factoid[2], isAre, "Factoid failed to retrieve the is/are field correctly") self.assertEquals( factoid[3], replacement, "Factoid failed to retrieve the defintion correctly") self.assertEquals(factoid[4], user3, "Factoid failed to retrieve the user correctly") delta = datetime.now() - factoid[5] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a factoid date set: delta is %d" % delta.total_seconds()) self.assertFalse( factoid[6], "Factoid failed to retrieve the locked flag correctly") def test_forget_factoid(self): user = "******" user2 = "someotheruser" user3 = "someguy" item = "somefactoidToForget" isAre = True definition = "a good thing to talk about" definition2 = "something people write" success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") success = self.db.addFactoid(user2, item, isAre, definition2, False) self.assertTrue(success, "Failed to add a new factoid") wasDeleted = self.db.forgetFactoid(item, user3) self.assertTrue(wasDeleted, "Incorect return value from forgetFactoid") data = self.db.getFactoid(item) self.assertEqual( len(data), 0, "Returned wrong number of results for a forgotten factoid: %d" % (len(data), )) def test_forget_factoid_that_doesnt_exist(self): user = "******" item = "somefactoidThatIsntHere" wasDeleted = self.db.forgetFactoid(item, user) self.assertFalse(wasDeleted, "Incorrect return value from forgetFactoid") def test_lock_and_replace_or_forget_factoid(self): user = "******" item = "lockedfactoid" isAre = True definition = "a good thing to talk about" overwrite = False success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") self.db.lockFactoid(item) data = self.db.getFactoid(item) self.assertEqual( len(data), 1, "Returned wrong number of results for a factoid: %d" % (len(data), )) factoid = data[0] self.assertTrue( factoid[6], "Factoid does not appear to be locked when it should be") wasDeleted = self.db.forgetFactoid(item, user) self.assertFalse(wasDeleted, "Was incorrrectly able to delete a locked factoid") success = self.db.addFactoid(user, item, isAre, definition, True) self.assertFalse(success, "Was incorrectly able to replace a locked factoid") def test_factoid_info(self): user = "******" counteditem = "countedfactoid" isAre = False definition = "something to test" overwrite = False # Verify that this factoid has no history to start info = self.db.infoFactoid(counteditem) self.assertFalse(info, "Factoid has info when it shouldn't.") # Add the factoid success = self.db.addFactoid(user, counteditem, isAre, definition, overwrite) self.assertTrue(success, "Failed to add a new factoid") # Verify that it now has some history info = self.db.infoFactoid(counteditem) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there's just one of info self.assertEqual(len(info), 1, "Factoid doesn't have the correct amount of info") # And verify that the info is correct history = info[0] self.assertEqual(history[1], counteditem, "Factoid history has the wrong item name") self.assertEqual(history[2], definition, "Factoid history has the wrong definition") self.assertEqual(history[3], user, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history has the wrong item name the second place: %s" % history[5]) self.assertIsNone( history[6], "Factoid history has the wrong count: %s" % history[6]) self.assertIsNone( history[7], "Factoid history has the wrong last referenced time: %s" % history[7]) # Now do a reference... data = self.db.getFactoid(counteditem) # ...and verify that the info gets updated info = self.db.infoFactoid(counteditem) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there's still just one of info self.assertEqual(len(info), 1, "Factoid doesn't have the correct amount of info") # And verify that the info is correct history = info[0] self.assertEqual(history[1], counteditem, "Factoid history has the wrong item name") self.assertEqual(history[2], definition, "Factoid history has the wrong definition") self.assertEqual(history[3], user, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertEqual( history[5], counteditem, "Factoid history has the wrong item name the second place: %s" % history[5]) self.assertEqual( history[6], 1, "Factoid history has the wrong count: %s" % history[6]) delta = datetime.now() - history[7] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) # Finally do a few more references data = self.db.getFactoid(counteditem) data = self.db.getFactoid(counteditem) data = self.db.getFactoid(counteditem) data = self.db.getFactoid(counteditem) # ...and verify that the info gets updated info = self.db.infoFactoid(counteditem) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there's still just one of info self.assertEqual(len(info), 1, "Factoid doesn't have the correct amount of info") # And verify that the info is correct history = info[0] self.assertEqual(history[1], counteditem, "Factoid history has the wrong item name") self.assertEqual(history[2], definition, "Factoid history has the wrong definition") self.assertEqual(history[3], user, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertEqual( history[5], counteditem, "Factoid history has the wrong item name the second place: %s" % history[5]) self.assertEqual( history[6], 5, "Factoid history has the wrong count: %s" % history[6]) delta = datetime.now() - history[7] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) def test_get_factoid_doesnt_set_refs(self): user = "******" nonexistantitem = "not_a_factoid" # Verify that this factoid has no history to start info = self.db.infoFactoid(nonexistantitem) self.assertFalse(info, "Factoid has info when it shouldn't.") # Now do a reference... data = self.db.getFactoid(nonexistantitem) self.assertFalse(data, "Factoid exists when it shouldn't.") # Verify that this factoid still has no history info = self.db.infoFactoid(nonexistantitem) self.assertFalse(info, "Factoid has info when it shouldn't.") def test_info_for_forget_factoid(self): user = "******" userWhoDeletes = "killer" item = "forgottenfactoid" isAre = False definition = "something to test" # Add the factoid success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") # Verify that it now has some history info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there's just one of info self.assertEqual(len(info), 1, "Factoid doesn't have the correct amount of info") # Now forget it.. status = self.db.forgetFactoid(item, userWhoDeletes) # ...and verify that the info gets updated info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there are 2 entries now self.assertEqual(len(info), 2, "Factoid doesn't have the correct amount of info") # Verify that the first entry correctly specifies the add history = info[0] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual( history[2], definition, "Factoid history has the wrong definition: '%s'" % history[2]) self.assertEqual(history[3], user, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the second entry correctly specifies the forget history = info[1] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertIsNone( history[2], "Factoid history for forgotten entry has a definition when it shouldn't" ) self.assertEqual( history[3], userWhoDeletes, "Factoid history for forgotten entry has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Now reference this factoid and see if it gets added to the ref count data = self.db.getFactoid(item) info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") self.assertEqual(len(info), 2, "Factoid doesn't have the correct amount of info") # Verify that there is still no ref count history = info[0] self.assertIsNone( history[5], "Factoid history ref does not contain the correct item.") self.assertIsNone( history[6], "Factoid history has a ref count when it shouldn't: %s" % history[6]) self.assertIsNone( history[7], "Factoid history has a reference time when it shouldn't") def test_ref_count_for_forget_factoid(self): user = "******" userWhoDeletes = "killer" item = "hereGoneBackAgain" isAre = False definition = "a factoid that keeps disappearing" # Add the factoid success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") # Verify that it now has some history info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") self.assertEqual(len(info), 1, "Factoid doesn't have the correct amount of info") # Reference it data = self.db.getFactoid(item) # Now forget it.. status = self.db.forgetFactoid(item, userWhoDeletes) # ...and verify that the ref count still exists info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") self.assertEqual(len(info), 2, "Factoid doesn't have the correct amount of info") # Verify that the ref count still exists history = info[0] self.assertEquals( history[6], 1, "Factoid history ref count is incorrect: %s" % history[6]) history = info[1] self.assertEquals( history[6], 1, "Factoid history ref count is incorrect: %s" % history[6]) # Now reference again now that it's deleted... data = self.db.getFactoid(item) info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") self.assertEqual(len(info), 2, "Factoid doesn't have the correct amount of info") # Verify that the ref count hasn't changed history = info[0] self.assertEquals( history[6], 1, "Factoid history ref count is incorrect: %s" % history[6]) history = info[1] self.assertEquals( history[6], 1, "Factoid history ref count is incorrect: %s" % history[6]) def test_info_for_forget_factoid(self): user = "******" user2 = "otherguy" userWhoDeletes = "killer" item = "multifactoid" isAre = False definition = "something to test" definition2 = "something to forget" # Add the factoid success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") # Add a second entry success = self.db.addFactoid(user2, item, isAre, definition2, False) self.assertTrue(success, "Failed to add a second entry to a factoid") # Now forget it.. status = self.db.forgetFactoid(item, userWhoDeletes) # Verify that it now has some history info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") # Verify that there are 3 info entries self.assertEqual(len(info), 3, "Factoid doesn't have the correct amount of info") # Verify that the first entry correctly specifies the forget history = info[0] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertIsNone( history[2], "Factoid history has the wrong definition: '%s'" % history[2]) self.assertEqual(history[3], userWhoDeletes, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the second entry correctly specifies the second add history = info[1] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual( history[2], definition2, "Factoid history has the wrong definition: '%s'" % history[2]) self.assertEqual(history[3], user2, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the third entry correctly specifies the first add history = info[2] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual( history[2], definition, "Factoid history for forgotten entry has a definition when it shouldn't" ) self.assertEqual( history[3], user, "Factoid history for forgotten entry has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") def test_overwrite_multipart_factoid(self): user = "******" user2 = "otherguy" user3 = "overwriter" item = "multiforgottenfactoid" isAre = True definition = "something to test" definition2 = "something to forget" definition3 = "the REAL definition" # Add the factoid success = self.db.addFactoid(user, item, isAre, definition, False) self.assertTrue(success, "Failed to add a new factoid") # Add a second entry success = self.db.addFactoid(user2, item, isAre, definition2, False) self.assertTrue(success, "Failed to add a second entry to a factoid") # Overwrite them with a new one success = self.db.addFactoid(user3, item, isAre, definition3, True) self.assertTrue(success, "Failed to overwrite a multi-part factoid") # Verify that it now has some history info = self.db.infoFactoid(item) self.assertTrue(info, "Factoid has no info when it should.") self.assertEqual(len(info), 4, "Factoid doesn't have the correct amount of info") # Verify that the first entry correctly specifies the overwrite history = info[0] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual( history[2], definition3, "Factoid history has wrong definition: '%s'" % history[2]) self.assertEqual(history[3], user3, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the second entry correctly specifies a forget (trigger by the overwrite) history = info[1] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertIsNone( history[2], "Factoid history has the definition when it shouldn't: '%s'" % history[2]) self.assertEqual(history[3], user3, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the third entry correctly specifies the second add history = info[2] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual(history[2], definition2, "Factoid history has a definition: %s" % history[2]) self.assertEqual( history[3], user2, "Factoid history for forgotten entry has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") # Verify that the fourth entry correctly specifies the first add history = info[3] self.assertEqual(history[1], item, "Factoid history has the wrong item name") self.assertEqual( history[2], definition, "Factoid history has the wrong definition: '%s'" % history[2]) self.assertEqual(history[3], user, "Factoid history has the wrong username") delta = datetime.now() - history[4] self.assertLess( delta.total_seconds(), 2, "Factoid history has the wrong time. delta is %d" % delta.total_seconds()) self.assertIsNone( history[5], "Factoid history ref count has an item name when it shouldn't.") self.assertIsNone(history[6], "Factoid history has a count when it shouldn't") self.assertIsNone( history[7], "Factoid history has a last referenced time when it shouldn't.") def test_mood(self): # Testing mood here because it relies on factoids # Test that mood is initially 0 even with botsnack and botsmack # not in the DB mood = self.db.mood() self.assertEqual(mood, 0, "Empty DB returned wrong mood") # Add the factoids success = self.db.addFactoid("testuser", "botsnack", 0, "<reply>Thank you!", False) success = self.db.addFactoid("testuser", "botsmack", 0, "<reply>OUCH!", False) # Give the bot 1 snack... self.db.getFactoid("botsnack") # ...and make sure the mood is 1 mood = self.db.mood() self.assertEqual(mood, 1, "After botsnack returned the wrong mood") # Smack it down... self.db.getFactoid("botsmack") # ...and verify we're back at 0 mood = self.db.mood() self.assertEqual(mood, 0, "After equal snacks and smacks, mood is not 0") # A couple more snacks... self.db.getFactoid("botsnack") self.db.getFactoid("botsnack") # Verify we're at 2 mood = self.db.mood() self.assertEqual(mood, 2, "After 2 snacks, mood is not 2") # 4 smacks... self.db.getFactoid("botsmack") self.db.getFactoid("botsmack") self.db.getFactoid("botsmack") self.db.getFactoid("botsmack") # Now we should be at -2 mood = self.db.mood() self.assertEqual(mood, -2, "After 4 smacks, mood is not -22") def tearDown(self): # Clear all factoids and history self.db.deleteAllFactoids()
class DbAccessSeenTest(unittest.TestCase): missinguser = "******" seenuser = "******" seenuser2 = "seenuser2" unicodeuser = "******" unicodemessage = "I love 🌮s" def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName) def test_missing_seen(self): data = self.db.seen(DbAccessSeenTest.missinguser) self.assertEqual(len(data), 0, "Returned data for a user that hasn't been seen") def test_valid_seen(self): user = DbAccessSeenTest.seenuser channel = "#test_channel" message = "Running unit tests..." self.db.updateSeen(user, channel, message) rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen") data = rows[0] self.assertEqual(data[Seen.name], user, "Wrong username returned for seen user") self.assertEqual(data[Seen.channel], channel, "Wrong channel returned for a seen user") delta = datetime.now() - data[Seen.timestamp] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a seen user: delta is %d" % delta.total_seconds()) self.assertEqual(data[Seen.message], message, "Wrong message returned for a seen user") self.db.deleteSeen(user) def test_update_of_existing_seen(self): user = DbAccessSeenTest.seenuser2 channel = "#test_channel" message = "Running unit tests..." self.db.updateSeen(user, channel, message) # First test the normal case rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen") data = rows[0] delta = datetime.now() - data[Seen.timestamp] self.assertLess( delta.total_seconds(), 2, 'Wrong time returned for a seen user: delta is %d' % (delta.total_seconds(), )) # Now wait a couple seconds and verify that the delta has changed time.sleep(2) rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen") data = rows[0] delta = datetime.now() - data[Seen.timestamp] self.assertGreater( delta.total_seconds(), 1, 'Wrong time returned for a seen user: delta is %d' % delta.total_seconds()) # Now update the same user again, then verify that the time has been updated. self.db.updateSeen(user, channel, message) rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen") data = rows[0] delta = datetime.now() - data[Seen.timestamp] self.assertLess( delta.total_seconds(), 2, 'Wrong time returned for a seen user: delta is %d' % delta.total_seconds()) def test_update_of_seen_with_nick_including_backslashes(self): # These were found to cause problems in the real system user = "******" channel = "#test_channel" message = "Running unit tests..." self.db.updateSeen(user, channel, message) # First test the normal case rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen: %d" % len(rows)) # Now update seen a few more times self.db.updateSeen(user, channel, message) self.db.updateSeen(user, channel, message) self.db.updateSeen(user, channel, message) self.db.updateSeen(user, channel, message) self.db.updateSeen(user, channel, message) rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen") def test_sql_injection_through_seen(self): # SELECT * FROM seen WHERE name LIKE # blah; DROP TABLE SEEN; SELECT * FROM SEEN WHERE name LIKE blah # ORDER BY timestamp DESC LIMIT 3 # These were found to cause problems in the real system #user = "******" user = "******" channel = "#test_channel" message = "Running unit tests..." self.db.updateSeen(user, channel, message) # First test the normal case rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a user that has been seen: %d" % len(rows)) def test_unicode_seen(self): user = DbAccessSeenTest.unicodeuser channel = "#test_channel" message = DbAccessSeenTest.unicodemessage self.db.updateSeen(user, channel, message) rows = self.db.seen(user) self.assertEqual( len(rows), 1, "Returned incorrect data for a unicode user that has been seen") data = rows[0] self.assertEqual(data[Seen.name], user, "Wrong username returned for a unicode seen user") self.assertEqual(data[Seen.channel], channel, "Wrong channel returned for a unicode seen user") delta = datetime.now() - data[Seen.timestamp] self.assertLess( delta.total_seconds(), 2, "Wrong time returned for a unicode seen user: delta is %d" % delta.total_seconds()) self.assertEqual(data[Seen.message], message, "Wrong message returned for a unicode seen message") self.db.deleteSeen(user) def tearDown(self): self.db.deleteSeen(DbAccessSeenTest.missinguser) self.db.deleteSeen(DbAccessSeenTest.seenuser) self.db.deleteSeen(DbAccessSeenTest.seenuser2)
class DbAccessThingiverseTest(unittest.TestCase): def setUp(self): config = ConfigParser.ConfigParser() results = config.read('gthx.config.local') if not results: raise SystemExit("Failed to read config file 'gthx.config.local'") dbUser = config.get('MYSQL', 'GTHX_MYSQL_USER') dbPassword = config.get('MYSQL', 'GTHX_MYSQL_PASSWORD') dbName = config.get('MYSQL', 'GTHX_MYSQL_DATABASE') self.db = DbAccess(dbUser, dbPassword, dbName) def test_thingiverse_refs(self): testItem = 1234 testTitle = "The most wonderful thing in the world" rows = self.db.addThingiverseRef(testItem) self.assertEquals( len(rows), 1, "First thingiverse ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 1, "First thingiverse ref returned wrong number of references.") self.assertIsNone( data[1], "First thingiverse ref returned a title when it shouldn't have.") rows = self.db.addThingiverseRef(testItem) self.assertEquals( len(rows), 1, "Second thingiverse ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 2, "Second thingiverse ref returned wrong number of references.") self.assertIsNone( data[1], "Second thingiverse ref returned a title when it shouldn't have.") rows = self.db.addThingiverseRef(testItem) self.assertEquals( len(rows), 1, "Third thingiverse ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 3, "Third thingiverse ref returned wrong number of references.") self.assertIsNone( data[1], "Third thingiverse ref returned a title when it shouldn't have.") self.db.addThingiverseTitle(testItem, testTitle) rows = self.db.addThingiverseRef(testItem) self.assertEquals( len(rows), 1, "Fourth thingiverse ref returned the wrong number of rows.") data = rows[0] self.assertEquals( data[0], 4, "Fourth thingiverse ref returned wrong number of references.") self.assertEquals(data[1], testTitle, "Fourth thingiverse ref returned the wrong title.") def tearDown(self): self.db.deleteAllThingiverseRefs()
class Gthx(irc.IRCClient): """An IRC bot for #reprap.""" restring = "" def __init__(self): self.db = DbAccess() self.trackedpresent = dict() self.gotwhoischannel = False self.seenQuery = re.compile("\s*seen\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)[\s\?]*") self.tellQuery = re.compile("\s*tell\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)\s*(.+)") self.factoidQuery = re.compile("(.+)[?!](\s*$|\s*\|\s*([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)$)") self.factoidSet = re.compile("(.+?)\s(is|are)(\salso)?\s(.+)") self.googleQuery = re.compile("\s*google\s+(.*?)\s+for\s+([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)") self.uptimeStart = datetime.datetime.now() self.lurkerReplyChannel = "" def connectionMade(self): self.log("IRC Connection made") irc.IRCClient.connectionMade(self) def connectionLost(self, reason): irc.IRCClient.connectionLost(self, reason) self.log("[disconnected at %s]" % time.asctime(time.localtime(time.time()))) self.emailClient.send("%s disconnected" % self.nickname, "%s is disconnected from the server.\n\n%s" % (self.nickname, reason)) def log(self, message): """Write a message to the screen.""" timestamp = time.strftime("[%H:%M:%S]", time.localtime(time.time())) print '%s %s' % (timestamp, message) # callbacks for events def signedOn(self): """Called when bot has succesfully signed on to server.""" self.log("Signed on to the IRC server") # Seems like a good time to also register with the nickserv nickservPassword = os.getenv("GTHX_NICKSERV_PASSWORD") if (nickservPassword): print "Identifying with the nickserv" self.msg("nickserv", "IDENTIFY %s" % nickservPassword) self.channelList = [channel for channel in self.factory.channels.split(',')] for channelm in self.channelList: self.log("Joining channel %s" % channelm) self.join(channelm) if (trackednick == None): self.trackedpresent[channelm] = False self.gotwhoischannel = False # kthx uses: "\s*(${names})[:;,-]?\s*" to match nicks if (trackednick): self.matchNick = "(%s|%s)(:|;|,|-|\s)+(.+)" % (self.nickname, trackednick) print "Querying WHOIS %s at startup" % trackednick self.whois(trackednick) else: self.matchNick = "(%s)(:|;|,|-|\s)+(.+)" % (self.nickname) print "Running in standalone mode." message += "Running in standalone mode." def joined(self, channel): """Called when the bot joins the channel.""" self.log("[I have joined %s as '%s']" % (channel, self.nickname)) message = "I have joined channel %s as '%s'\n" % (channel, self.nickname) self.emailClient.threadsend("%s connected" % self.nickname, message) def userJoined(self, user, channel): """ Called when I see another user joining a channel. """ print "%s joined channel %s" % (user, channel) # TODO: Change this to verify the IP address before setting it if trackednick and (user == trackednick) and (self.trackedpresent[channel] == False): self.trackedpresent[channel] = True print "%s is here!" % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has joined channel %s" % (user, channel)) def userLeft(self, user, channel): """ Called when I see another user leaving a channel. """ print "%s left channel %s" % (user, channel) if trackednick and (user == trackednick) and (self.trackedpresent[channel]): self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has left channel %s" % (user, channel)) def userQuit(self, user, quitMessage): """ Called when I see another user disconnect from the network. """ print "%s disconnected : %s" % (user, quitMessage) if trackednick and (user == trackednick): for channel in self.channelList: self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has quit: %s" % (user, quitMessage)) def userKicked(self, kickee, channel, kicker, message): """ Called when I observe someone else being kicked from a channel. """ print "In %s, %s kicked %s : %s" % (channel, kicker, kickee, message) if trackednick and (kickee == trackednick) and (self.trackedpresent[channel]): self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has been kicked from %s by %s: %s" % (kickee, channel, kicker, message)) def userRenamed(self, oldname, newname): """ A user changed their name from oldname to newname. """ print "%s renamed to %s" % (oldname, newname) if (trackednick == None): return if oldname == trackednick: for channel in self.channelList: self.trackedpresent[channel] = False print "%s is gone." % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has been renamed to %s" % (oldname, newname)) if newname == trackednick: self.whois(trackednick) print "%s is here!" % trackednick self.emailClient.threadsend("%s status" % self.nickname, "%s has been renamed to %s--checking WHOIS" % (oldname, newname)) def irc_unknown(self, prefix, command, params): #print "Unknown command '%s' '%s' '%s'" % (prefix, command, params) if (command == 'RPL_NAMREPLY'): if (self.lurkerReplyChannel == ""): return users = params[3].split() for user in users: self.channelCount = self.channelCount + 1 rows = self.db.seen(user) if len(rows) == 0: self.lurkerCount = self.lurkerCount + 1 elif (command == 'RPL_ENDOFNAMES'): if (self.lurkerReplyChannel == ""): return print "Got RPL_ENDOFNAMES" self.msg(self.lurkerReplyChannel,"%d of the %d users in %s right now have never said anything." % (self.lurkerCount, self.channelCount, params[1])) self.lurkerReplyChannel = "" def irc_RPL_WHOISCHANNELS(self, prefix, params): """This method is called when the client recieves a reply for whois. params[0]: requestor params[1]: nick requested params[2]: list of channels in common """ print "Got WHOISCHANNELS with prefix '%s' and params '%s'" % (prefix, params) print "%s is in channels %s" % (params[1], params[2]) self.gotwhoischannel = True trackedchannels = params[2].split(" ") for channel in self.channelList: if channel in trackedchannels: self.trackedpresent[channel] = True print "%s is in %s!!" % (params[1], channel) self.emailClient.threadsend("%s status" % self.nickname, "%s is in channel %s" % (params[1], params[2])) else: self.trackedpresent[channel] = False print "%s is NOT in %s!!" % (params[1], channel) def irc_RPL_WHOISUSER(self, prefix, params): print "Got WHOISUSER with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISSERVER(self, prefix, params): print "Got WHOISSERVER with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISOPERATOR(self, prefix, params): print "Got WHOISOPERATOR with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_WHOISIDLE(self, prefix, params): print "Got WHOISIDLE with prefix '%s' and params '%s'" % (prefix, params) def irc_RPL_ENDOFWHOIS(self, prefix, params): print "Got ENDOFWHOIS with prefix '%s' and params '%s'" % (prefix, params) if not self.gotwhoischannel: if (trackednick != None): print "No response from %s. Must not be present." % trackednick for channel in self.channelList: self.trackedpresent[channel] = False self.emailClient.threadsend("%s status" % self.nickname, "%s is not in channel %s" % (trackednick, channel)) def getFactoidString(self, query): answer = self.db.getFactoid(query) if answer: for i, factoid in enumerate(answer): if i == 0: if factoid[3].startswith("<reply>") or factoid[3].startswith("<action>"): fstring = factoid[3] break else: fstring = query fstring += "%s" % " are " if factoid[2] else " is " if i > 0: fstring += "also " fstring += factoid[3] if i < len(answer) - 1: fstring += " and" return fstring else: return None def privmsg(self, user, channel, msg): """Called when the bot receives a message, both public and private.""" user = user.split('!', 1)[0] # By default, don't reply to anything canReply = False private = False replyChannel = channel parseMsg = msg directAddress = False # Debug print ALL messages #print "Message from '%s' on '%s': '%s'" % (user, channel, msg) # Check to see if they're sending me a private message if channel == self.nickname: canReply = True private = True replyChannel = user self.log("Private message from %s: %s" % (user, msg)) if str.lower(user) == "nickserv": self.log("Nickserv says: %s" % msg) if parseMsg.startswith("whois "): whoisnick = parseMsg.split(" ",1)[1] print "Doing a whois '%s'" % whoisnick self.whois(whoisnick) # Update the seen database, but only if it's not a private message if channel in self.channelList and not private: self.db.updateSeen(user,channel,msg) # If kthx said something, mark him as here and ignore everything he says if user == trackednick and not private: if (self.trackedpresent[channel] == False): self.trackedpresent[channel] = True self.emailClient.threadsend("%s status" % self.nickname, "%s spoke in %s unexpectedly and got marked as present: %s" % (user, channel,msg)) return # If kthx is gone, then we can always reply if not private and not self.trackedpresent[channel]: canReply = True # Check to see if we have a tell waiting for this user tells = self.db.getTell(user) if tells: for message in tells: print "Found tell for '%s' from '%s'" % (user, message[Tell.author]) author = message[Tell.author] timestring = timesincestring(message[Tell.timestamp]) text = message[Tell.message] inTracked = message[Tell.inTracked] # We have 3 cases: # 1) kthx was around when this tell happened and is still around now. # In this case, we assume kthx will relay the message and just delete it # 2) kthx was around when this tell happened and is not here now. # In this case, we want to send the message and mention that kthx may repeat it # 3) kthx was not around when this tell happened and may or may not be here now # Whether or not kthx is now here, we need to say the message # 4) gthx was specifically addressed for this tell # Whether or not kthx is now here, we need to say the message # # If we can't reply, it means that kthx is present. In that # case, the tell has already been erased, so in both cases, # we're good. if canReply or not inTracked: if inTracked: self.msg(replyChannel,"%s: %s ago <%s> tell %s %s (%s may repeat this)" % (user, timestring, author, user, text, trackednick)) else: self.msg(replyChannel,"%s: %s ago <%s> tell %s %s" % (user, timestring, author, user, text)) # Check for specifically addressed messages m = re.match(self.matchNick, parseMsg) if m: print "Found message addressed to '%s'. My nick is '%s'." % (m.group(1), self.nickname) parseMsg = m.group(3) # Mark it as a direct address so we can look for a factoid directAddress = True # If it's addressed directly to me, we can reply if m.group(1) == self.nickname: canReply = True # Check for status query if canReply and parseMsg == "status?": if (trackednick): if (private): reply = "%s: OK; Up for %s; " % (VERSION, timesincestring(self.uptimeStart)) for channel in self.channelList: reply += "%s %s; " % (channel, "PRESENT" if self.trackedpresent[channel] else "GONE") else: reply = "%s: OK; Up for %s; %s is %s" % (VERSION, timesincestring(self.uptimeStart), trackednick, "PRESENT" if self.trackedpresent[channel] else "GONE") else: reply = "%s: OK; Up for %s; standalone mode" % (VERSION, timesincestring(self.uptimeStart)) self.msg(replyChannel, reply) return # Check for lurker query if canReply and parseMsg == "lurkers?": self.msg(replyChannel, "Looking for lurkers...") self.lurkerReplyChannel = replyChannel self.lurkerCount = 0 self.channelCount = 0 print "Sending request 'NAMES %s'" % channel self.sendLine("NAMES %s" % channel) return # Check for tell query m = self.tellQuery.match(parseMsg) if m and directAddress: print "Got tell from '%s' for '%s' message '%s'." % (user, m.group(1), m.group(2)) # The is in the tracked bot if the tracked bot is present and it was not a message # specifically directed to us. This is a little tricky since the only way to know # that a message was specifically directed to us is to see if it was a direct address # and we can reply success = self.db.addTell(user, m.group(1), m.group(2), not (directAddress and canReply) and self.trackedpresent[channel]) if success and canReply: self.msg(replyChannel, "%s: I'll pass that on when %s is around." % (user, m.group(1))) return # Check for seen query if canReply: m = self.seenQuery.match(parseMsg) if m: queryname = m.group(1) print "%s asked about '%s'" % (user, queryname) rows = self.db.seen(queryname) if len(rows) == 0: reply = "Sorry, I haven't seen %s." % queryname self.msg(replyChannel, reply) for i,row in enumerate(rows): reply = "%s was last seen in %s %s ago saying '%s'." % (row[Seen.name], row[Seen.channel], timesincestring(row[Seen.timestamp]), row[Seen.message]) self.msg(replyChannel, reply) if i >= 2: # Don't reply more than 3 times to a seen query break return # Check for google query if canReply: m = self.googleQuery.match(parseMsg) if m: queryname = urllib.quote_plus(m.group(1)) foruser = m.group(2) print "%s asked to google '%s' for %s" % (user, queryname, foruser) reply = "%s: http://lmgtfy.com/?q=%s" % (foruser, queryname) self.msg(replyChannel, reply) return # Check for setting a factoid factoid = None if directAddress: factoid = self.factoidSet.match(parseMsg) if factoid: invalidwords = re.match('(here|how|it|something|that|this|what|when|where|which|who|why|you)', factoid.group(1), re.IGNORECASE) if not invalidwords: print "%s tried to set factoid '%s'." % (user, factoid.group(1)) success = self.db.addFactoid(user, factoid.group(1), True if factoid.group(2) == 'are' else False, factoid.group(4), True if not factoid.group(3) else False) if canReply: if success: self.msg(replyChannel, "%s: Okay." % user) else: self.msg(replyChannel, "I'm sorry, %s. I'm afraid I can't do that." % user) # Check for getting a factoid if canReply: f = self.factoidQuery.match(parseMsg) if f: print "factoid query from %s:%s for '%s'" % (user, channel, f.group(1)) answer = self.getFactoidString(f.group(1)) if answer: # Replace !who and !channel in the reply answer = re.sub("!who", user, answer) answer = re.sub("!channel", channel, answer) if answer.startswith("<reply>"): answer = answer[7:] if answer.startswith("<action>"): self.describe(replyChannel, answer[8:]) else: if (f.group(3)): answer = "%s, %s" % (f.group(3), answer) self.msg(replyChannel, answer) # Check for info request if canReply and parseMsg.startswith("info "): query = parseMsg[5:] if query[-1:] == "?": query = query[:-1] print "info request for '%s' ReplyChannel is '%s'" % (query, replyChannel) refcount = 0 answer = self.db.infoFactoid(query) if answer: for factoid in answer: user = factoid[3] value = factoid[2] if not user: user = "******" if value: print "At %s, %s set to: %s" % (factoid[4], user, value) self.msg(replyChannel, "At %s, %s set to: %s" % (factoid[4], user, value)) else: print "At %s, %s deleted this item" % (factoid[4], user) self.msg(replyChannel, "At %s, %s deleted this item" % (factoid[4], user)) else: print "No info for factoid '%s'" % query self.msg(replyChannel, "Sorry, I couldn't find an entry for %s" % query) # Check for forget request if directAddress and parseMsg.startswith("forget "): query = parseMsg[7:] print "forget request for '%s'" % query forgotten = self.db.forgetFactoid(query, user) if canReply: if forgotten: self.msg(replyChannel, "%s: I've forgotten about %s" % (user, query)) else: self.msg(replyChannel, "%s: Okay, but %s didn't exist anyway" % (user, query)) def action(self, sender, channel, message): m = re.match("([a-zA-Z\*_\\\[\]\{\}^`|\*][a-zA-Z0-9\*_\\\[\]\{\}^`|-]*)", sender) if m: sender = m.group(1) print "* %s %s" % (sender, message) self.db.updateSeen(sender, channel, "* %s %s" % (sender, message))
from RESTClient import RESTClient, Services from DbAccess import DbAccess # Get last/todays rates client = RESTClient(Services.ALL_RATES) data = client.call_service() db = DbAccess() list = data.find('nsp:Cube', client.namespaces) # Insert entries in DB for entry in list: entry_date = entry.attrib['time'] entry_date_id = db.insert_entry(entry_date) if entry_date_id == -1: entry_date_id = db.entry_get_id(entry_date)[0] # Insert rates in DB for rate in entry: db.insert_exchangeRate( entry_date_id, rate.attrib['currency'], rate.attrib['rate'] )