Skip to content

Instantly share code, notes, and snippets.

@Taiiwo
Created December 1, 2015 02:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Taiiwo/a8319308a851de6a3f11 to your computer and use it in GitHub Desktop.
Save Taiiwo/a8319308a851de6a3f11 to your computer and use it in GitHub Desktop.
A library for creating IRC interfaces to messaging services, allowing them to be used via IRC clients.
import socket
import thread
import re
class IRCd:
debug = True
servername = socket.getfqdn("")[:140]
events = []
handlers = {}
def __init__(self, host="127.0.0.1", port=6667):
self.host = host
self.port = port
# Client connection handler
def clientThread(self, conn):
# first, the client needs to register
self.user = {}
# define some temporary handlers for init
def nick(m, client, data):
# set the client's nick
client.user['nick'] = m.group(1)
client.debug("Set nick to "+ user['nick'])
# remove this handler after first run
client.remove_handler("^NICK\s([\w\d]+)")
return True
def user(m, client, data):
# set the clients supplied details
client.user['username'] = m.group(1)
client.user['mode'] = m.group(2)
client.user['real name'] = m.group(3)
# if we have a handler for the user command, run it now
if 'user' in client.handlers:
client.handlers['users'](m)
self.debug("Set user to " + user['username'])
# send the required startup messages
welcomeMessage = "Welcome...I guess.."
serverMessage = "Your host is %s, running TaiiwoIRCd" %
client.servername
creationMessage = "This server was created sometime"
versionMessage = "%s TaiiwoIRCd-1.3.3.7 o o" % client.servername
nick = client.user['nick']
reply = client.response("001", nick, "", welcomeMessage)
reply += client.response("002", nick, "", serverMessage)
reply += client.response("003", nick, "", creationMessage)
reply += client.response("004", nick, "", versionMessage)
# remove this handler after first run
client.remove_handler("^USER\s([\w\d]+)\s(\d)\s.\s:?([\w\d\s]+)")
return reply
# apply the temporary handlers
self.add_handler("^NICK\s([\w\d]+)", nick)
self.add_handler("^USER\s([\w\d]+)\s(\d)\s.\s:?([\w\d\s]+)", user)
# now we start the main loop waiting for user input
while True:
# wait for input from the client
data = conn.recv(1024)
self.debug(data)
if not data:
# the client disconnected
break
reply = False
# figure out what the client wants and respond
for event in events:
# check if the event regex matches data
m = re.match(event[0], data)
if m:
# if so, run the handler function
reply = event[1](m, self, data)
reply = "" if typeof(reply) != str() else reply
break
if reply:
# send the reply to the server
self.debug(reply)
conn.sendall(reply)
else:
# we didn't have a handler for the command
self.debug("Command no recognised: " + data)
# client disconnected
conn.close()
# Used to start the sever when all the handlers have been set
def start(self):
# create socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.debug('Socket created')
try:
# bind socket to port
s.bind((self.host, self.port))
except socket.error as msg:
quit('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
self.debug('Socket bound to %s on %s' % (self.host, self.port))
s.listen(10)
self.debug('Socket now listening...')
while 1:
# wait for clients and send their connection to a thread
conn, addr = s.accept()
self.debug('Connected with ' + addr[0] + ':' + str(addr[1]))
thread.start_new_thread(self.clientThread, (conn,))
s.close()
# Adds a hander for when the IRC client sends a command
# regex[string] = a regex that matches the desired IRC command
# handler[function] = parameters:
# [0] = a regex match object of [2], matched with regex[string]
# [1] = an instance of self, can be used like an API to call methods
# from this class
# [2] = an unformatted string of data received from IRC
def add_handler(self, regex, handler):
self.events.append([regex, handler])
return True
# Removes a hander added by add_handler
# regex[string] = the regex of the handler to remove
def remove_handler(self, regex):
for event in self.events:
if event[0] == regex:
self.events.remove(event)
return True
# Adds user-defined functions for use in preset add_hander functions
# name[string] = name of handler
# handler[function] = can take any parameters and do anything
def add_user_handler(self, name, handler):
handlers[name] = handler
# Adds a handler for the LIST command.
# handler[function] = takes no parameters and returns a list of dicts in
# the form:
# {
# 'name': Name of channel
# 'topic': Current channel topic
# 'numUsers': Number of users active in channel
# }
def add_list_handler(self, handler):
# add the handler to handlers so we can use if from anywhere
self.add_user_handler('list', handler)
# create a function to bind to the event
def list_handler(m, client, data):
# get list of channels
channels = client.handlers['list']()
reply = ""
# for each channel
for channel in channels:
# write an IRC reply and append it
reply += client.response(
322,
client.user['nick'],
channel['name'] +
" " +
channel['numUsers'],
channel['topic']
)
# finally, add the end of list message
reply += client.reponse(323, client.user['nick'], "", ":End of /LIST")
return reply
# now set a handler for the LIST command
self.add_handler("^LIST", list_handler)
# A helper function that formats an IRC response
def response(self, code, nick, value, message):
value = value + " " if value != "" else ""
return ":%s %s %s %s:%s\r\n" % (self.servername, code, nick, value, message)
# Used to only print output if debugging is on
def debug(self, msg):
if self.debug:
print(msg)
return True
else:
return False
if __name__ == '__main__':
ircd = IRCd()
ircd.start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment