A CDoT Python IRC Bot

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()

Leave a comment