Created
December 1, 2015 02:20
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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