Below is a basic IRC bot written in python which grabs the builder bot statuses from the arm.koji website and sends a message to a channel if the enable or ready statuses change to “no”. The lbot.py code is a shared python library/module and various python bot scripts can use this module to achieve different tasks for IRC messaging/action.
Edit: Last updated on June 30, 2011
lbot.py
import os import re import signal import select import socket import sys import time import urllib def formtime(): currsecs = time.localtime() return time.strftime("%H:%M:%S", currsecs) def fakehand(s, f): pass def readurls(urlnlist=[], filename="lbot.txt"): waittime = (5 * 60) while (1): outpstri = "" for urlnitem in urlnlist: signal.signal(signal.SIGALRM, fakehand) signal.alarm(waittime / 2) try: uobjdata = urllib.urlopen(urlnitem) tempstri = uobjdata.read() print("fetched [%d] bytes from [%s]" % (len(tempstri), urlnitem)) except: outpstri = "" break outpstri += tempstri signal.alarm(0) if (outpstri): fileobjc = open("/tmp/%s" % (filename), "w") fileobjc.write(outpstri) fileobjc.close() time.sleep(waittime) sockline_s = {} def sockline(sockpref, sockobjc): global sockline_s if (not sockpref in sockline_s.keys()): sockline_s[sockpref] = "" timeread = 0.25 chekstri = sockline_s[sockpref].find("\n") while (chekstri == -1): (readlist, sendlist, errolist) = select.select([sockobjc], [], [], timeread) if (sockobjc in readlist): tempread = sockobjc.recv(1024) if (len(tempread) == 0): print("socket error...") sys.exit(0) sockline_s[sockpref] += tempread chekstri = sockline_s[sockpref].find("\n") else: break chekstri += 1 reslstri = sockline_s[sockpref][:chekstri] sockline_s[sockpref] = sockline_s[sockpref][chekstri:] if ((len(reslstri) > 0) and (sockpref != "")): outpstri = ("%s %s [RECV] %s" % (formtime(), sockpref, reslstri.strip())) print(outpstri) return reslstri sockobjc = None def socksend(sockpref, sendstri): global sockobjc tempstri = sendstri.strip() if ((len(tempstri) > 0) and (sockpref != "")): outpstri = ("%s %s [SEND] %s" % (formtime(), sockpref, tempstri)) print(outpstri) sockobjc.send(sendstri + "\r\n") sendtime = 0 sendwait = 1 sockpref = "IRC" sendlist = [] def waitsend(sendstri=""): global sockobjc global sendtime global sendwait global sockpref global sendlist if (sendstri != ""): sendlist.append(sendstri) if (len(sendlist) > 0): prestime = time.time() difftime = (prestime - sendtime) if (difftime >= sendwait): socksend(sockpref, sendlist[0]) sendlist.pop(0) sendtime = prestime nickname = "" chanblst = [] def ircbinit(nickstri, chanlist): global sockobjc global sockpref global nickname global chanblst hostname = "irc.freenode.net" hostport = 6667 idenname = "alex" fillname = "bob" realname = "charlie" nickname = nickstri chanblst = chanlist sockobjc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sockobjc.connect((hostname, hostport)) socksend(sockpref, "NICK %s" % (nickname)) socksend(sockpref, "USER %s %s %s :%s" % (idenname, hostname, fillname, realname)) jointime = 0 joinwait = (5 * 60) def ircbmain(pastdata, presdata): global sockpref global sockobjc global nickname global chanblst global jointime global joinwait # check for a valid socket object first if (sockobjc == None): print("null error...") return -1 # set some shared method vars prestime = time.time() # read a line from the socket object and exit if error readdata = sockline(sockpref, sockobjc) waitsend() if (type(readdata) == type(-1)): sockobjc.close() print("socket error...") return -2 readdata = readdata.strip() # if server ping the send pong back chekstri = readdata.find("PING :") if (chekstri == 0): chekstri = readdata.find(":") sendstri = ("PONG %s" % (readdata[chekstri:])) socksend(sockpref, sendstri) readdata = "" # delay send the join channel commands if connect or time chekstri = readdata.find("001 %s" % (nickname)) difftime = (prestime - jointime) if ((chekstri != -1) or ((jointime != 0) and (difftime > joinwait))): for channame in chanblst: sendstri = ("JOIN %s" % (channame)) waitsend(sendstri) jointime = prestime readdata = "" # check to see if we successfuly joined at least one channel regxobjc = re.match("^:?%s[^ ]* JOIN :.*" % (nickname), readdata) if (regxobjc): jointime = prestime readdata = "" # if we have not joined a channel yet then return if ((jointime == 0) or (not pastdata) or (not presdata)): return readdata # if list size was requested then send the length of the list regxobjc = re.match("^:?[^ ]* PRIVMSG ([^ ]*) :%s numb.*" % (nickname), readdata) if (regxobjc): sendstri = ("PRIVMSG %s :[#] %d" % (regxobjc.group(1), len(presdata))) socksend(sockpref, sendstri) # if a list tail was requested then send the specified number of items regxobjc = re.match("^:?[^ ]* PRIVMSG ([^ ]*) :%s tail(.*)" % (nickname), readdata) if (regxobjc): try: tailnumb = regxobjc.group(2).strip() tailnumb = (-1 * int(tailnumb)) except: tailnumb = -1 for presitem in presdata[tailnumb:]: sendstri = ("PRIVMSG %s :[*] %s" % (regxobjc.group(1), presitem)) waitsend(sendstri) # print out any list diffs for presitem in presdata: if (presitem in pastdata): continue for channame in chanblst: sendstri = ("PRIVMSG %s :[+] %s" % (channame, presitem)) waitsend(sendstri) return readdata
sbot.py
import os import re import sys import time import lbot def fechfile(filename, urlnstri=""): resllist = [] try: fileobjc = file("/tmp/%s" % (filename)) readdata = fileobjc.read() fileobjc.close() except: return resllist readdata = readdata.replace("\t", "").replace("\r", "").replace("\n", "") readdata = readdata.replace("<tr", "\n<tr") readlist = readdata.split("\n") for readitem in readlist: regxobjc = re.match(".*<a href=\"hostinfo\?hostID=[0-9]*\">([^<]*cdot[^<]*)</a>.*<td>([0-9]+)-([0-9]+)-([0-9]+) ([0-9]+):([0-9]+):([0-9]+)</td>.*", readitem) if (not regxobjc): continue hostname = regxobjc.group(1) edthour = (int(regxobjc.group(5)) - 4) edthour = str(edthour) checkind = int(regxobjc.group(2) + regxobjc.group(3) + regxobjc.group(4) + regxobjc.group(5) + regxobjc.group(6)) timesecs = (time.time() + (4 * 60 * 60) - (30 * 60)) locltime = time.localtime(timesecs) presdate = int(time.strftime("%Y%m%d%H%M", locltime)) if (checkind < presdate): hoststri = ("[%s] last check-in [%s-%s-%s %s:%s:%s]" % (hostname, regxobjc.group(2), regxobjc.group(3), regxobjc.group(4), edthour, regxobjc.group(6), regxobjc.group(7))) resllist.append(hoststri) return resllist def ircbmain(): if (len(sys.argv) < 4): print("Usage: %s <nick> - <chan 0> ... <chan N>" % (sys.argv[0])) sys.exit(0) nickname = "" chanlist = [] x = 1 l = len(sys.argv) f = 0 while (x < l): if (sys.argv[x] == "-"): f += 1 elif (f == 0): nickname = sys.argv[x] elif (f == 1): chanlist.append(sys.argv[x]) x += 1 p = os.fork() if (p == 0): templist = [] for x in range(0, 3): templist.append("http://arm.koji.fedoraproject.org/koji/hosts?state=all&order=name&start=%d" % (x * 50)) lbot.readurls(urlnlist=templist, filename=nickname) sys.exit(0) pastdata = [] presdata = [] lbot.ircbinit(nickname, chanlist) while (1): tempdata = fechfile(nickname) if (tempdata): presdata = tempdata if (not pastdata): pastdata = presdata ircdstri = lbot.ircbmain(pastdata, presdata) if (type(ircdstri) == type(-1)): break if (presdata): pastdata = presdata ircbmain()