#Todo: # # #4. Control panel for bot. # # send(messagetype, target, subtarget, message) import socket import sys from time import sleep import random chambered_round = 0 #Maintains connection to server #Allows messages to be sent by other methods/classes class irc_connection: def __init__(self): try: #Get the target server(s) and nickname(s) from the config file ircbot.cfg configfile = open("fullmetalmadda.cfg", "r") self.servers = configfile.readline().partition("=")[2].split(";") configfile.close() self.connection = socket.socket ( socket.AF_INET, socket.SOCK_STREAM ) except: #Config file could not be opened, bail out print("Could not read config file fullmetalmadda.cfg. Time to die.") sys.exit(1) if(len(self.servers) == 0): #No servers? What do you want the poor bot to do? print("No servers found on line 1 of fullmetalmadda.cfg.") print("First line of file should read:") print("servers=irc.something.net:6667;irc.somethingelse.net:6667;irc.andsoon.etc:6667") sys.exit(2) for server in self.servers: try: #Are we connected? if(self.connection.getpeername()): #Yes, we don't need to keep trying break except: #No, we should try to connect to this server #try: server = server.rstrip("\n").split(":") self.connection.connect( (server[0], int(server[1])) ) #except: #We can't connect to this server. Move on. #pass def readbuffer(self): data = self.connection.recv(2048) return str(data, "ascii").split("\n") def sendraw(self,message): #Send a raw message- must be sent as bytes try: self.connection.send(message) except: #Probably not encoded correctly, attempt a fix self.connection.send(bytes(message, "ascii")) def sendaction(self, target, message): self.connection.send(bytes("PRIVMSG " + target + " :\x01ACTION " + message + "\x01\r\n", "ascii")) def send(self,messagetype, target, subtarget, message): #Send a message of a specific type if(messagetype == "PONG"): self.connection.send(bytes(messagetype + " " + message + "\r\n", "ascii")) elif(messagetype == "JOIN"): self.connection.send(bytes(messagetype + " :" + target + "\r\n", "ascii")) elif(messagetype == "KICK"): self.connection.send(bytes(messagetype + " " + target + " " + subtarget + " :" + message + " \r\n", "ascii")) elif(messagetype == "PART"): self.connection.send(bytes(messagetype + " " + target + " :" + message + "\r\n", "ascii")) elif(messagetype == "QUIT"): self.connection.send(bytes(messagetype + " :" + message + "\r\n", "ascii")) elif(messagetype == "PRIVMSG" or messagetype == "NOTICE"): self.connection.send(bytes(messagetype + " " + target + " :" + message + "\r\n", "ascii")) elif(messagetype == "INVITE"): #Peculiar format to this one- INVITE user :#channel self.connection.send(bytes(messagetype + " " + subtarget + " :" + target + "\r\n", "ascii")) elif(messagetype == "NICK"): self.connection.send(bytes(messagetype + " " + target + "\r\n", "ascii")) elif(messagetype == "USER"): self.connection.send(bytes(messagetype + " " + target + " " + subtarget + " * :" + message + "\r\n", "ascii")) elif(messagetype == "MODE"): self.connection.send(bytes(messagetype + " " + target + " " + message + " " + subtarget + "\r\n", "ascii")) else: #Probably a typo. For the moment we'll just silently ignore it pass #Class for processing messages from IRC class irc_message: #Method to initialise message processing class def __init__(self, data, conn, mynick): self.data = data.rstrip("\r").rstrip("\n") self.nickname = "" self.fullname = "" self.hostaddress = "" self.messagetype = "" self.message = "" self.target = "" self.channel = "" self.irc_connection = conn self.my_nickname = mynick procdata = data.partition(" ") #Get the details of the message sender userdetails = procdata[0].partition("!") if(userdetails[1] == ""): #Server, use placeholders for nickname and fullname self.nickname = "#:server:#" self.fullname = self.nickname self.hostaddress = userdetails[2].rstrip() else: #User, get full details self.nickname = userdetails[0].lstrip(":").rstrip() self.userdetails = userdetails[2].partition("@") self.fullname = userdetails[0].rstrip() self.hostaddress = userdetails[2].rstrip() #Get the message type- PING is a special case if(procdata[0].rstrip() == "PING"): self.messagetype = "PING" else: procdata = procdata[2].partition(" ") self.messagetype = procdata[0].rstrip() #Process the message based on its type if(self.messagetype == "PING"): #Server checking client is still responsive. Automatically respond self.message = procdata[2].rstrip() self.target = "#:self:#" self.channel = self.target self.irc_connection.send("PONG", "", "", self.message) elif(self.messagetype == "433"): #Server response indicating nickname already in use. self.message = procdata[2].rstrip() self.target = "#:self:#" self.channel = self.target elif(self.messagetype == "JOIN"): #Someone joined a channel self.message = "" self.target = procdata[2].lstrip(":").rstrip() self.channel = self.target elif(self.messagetype == "KICK"): #Someone got kicked procdata = procdata[2].partition(" ") self.message = procdata[2].partition(":")[2].rstrip() self.target = procdata[2].partition(":")[0].rstrip() self.channel = procdata[0].rstrip() elif(self.messagetype == "QUIT" or self.messagetype == "PART"): #Someone left a channel or the network procdata = procdata[2].partition(" ") self.message = procdata[2].lstrip(":").rstrip() self.target = procdata[0].rstrip() self.channel = self.target elif(self.messagetype == "PRIVMSG" or self.messagetype == "NOTICE"): #Someone is communicating with the client or a channel procdata = procdata[2].partition(" ") self.message = procdata[2].lstrip(":").rstrip() self.target = procdata[0].rstrip() if(self.target.find("#") > -1 or self.target.find("&") > -1): #It's public communication on a channel self.channel = self.target else: #It's private communication self.channel = "#:PRIVATE:#" elif(self.messagetype == "INVITE"): #Someone has sent an invite to somewhere procdata = procdata[2].partition(" ") self.message = "" self.target = procdata[0].rstrip() self.channel = procdata[2].lstrip(":").rstrip() else: #This message type is unknown self.message = procdata[2].rstrip() self.target = "#:UNKNOWN:#" self.channel = self.target def reply(self,response): #Define how to respond to each type of action if(self.messagetype == "NOTICE"): #These should not be subject to an auto-reply, so we will not define a reply method for them #If they are to be replied to, it must be done explicitly as a new message pass elif(self.messagetype == "KICK"): if(self.target == self.my_nickname): #Someone just kicked the client. A reply should rejoin then send a message to the channel self.irc_connection.send("JOIN", self.channel, "", "") self.irc_connection.send("PRIVMSG", self.channel, "", response) else: #Someone else just got kicked from a channel. Our default response will be a message to the channel self.irc_connection.send("PRIVMSG", self.channel, "", response) elif(self.messagetype == "PRIVMSG"): #Someone is talking to us or to a channel we're in. Respond to them/the channel if(self.channel == "#:PRIVATE:#"): self.irc_connection.send("PRIVMSG", self.nickname, "", response) else: self.irc_connection.send("PRIVMSG", self.target, "", response) elif(self.messagetype == "JOIN"): #We saw someone join a channel, respond to the channel they're on self.irc_connection.send("PRIVMSG", self.nickname, "", response) elif(self.messagetype == "PART" or self.messagetype == "QUIT"): #We saw someone leave a channel we can see, or the network itself. Respond to the channel they were in. self.irc_connection.send("PRIVMSG", self.channel, "", response) elif(self.messagetype == "INVITE"): #We saw an invite to a channel #Just in case some quirk of our status or the server allows us to see other invites, #we check to make sure the invite is to us if(self.target == self.my_nickname): #Default response is to join te channel then send a message self.irc_connection.send("JOIN", self.channel, "", "") self.irc_connection.send("PRIVMSG", self.channel, "", response) if __name__ == "__main__": messagequeue = [] irc = irc_connection() user_defined = False nickname_set = False channels_joined = False nickname_index = 0 mynick = "" #Get nickname(s) to use and channel(s) to join configfile = open("fullmetalmadda.cfg", "r") #First line is servers, ignore it configfile.readline() nicknames = configfile.readline().partition("=")[2].split(";") channels = configfile.readline().partition("=")[2].split(";") configfile.close() if(len(nicknames) == 0): #No nickname? Sadly we can't be the nameless one. print("No nicknames found on line 2 of fullmetalmadda.cfg.") print("Second line of file should read:") print("nicknames=FullMetalMadda;AnotherNick;Yetanothernick;etcetcnick") sys.exit(3) if(len(channels) == 0): #No channels defined. Non-critical, but print warning print("No channels found on line 3 of fullmetalmadda.cfg.") print("Third line of file should read:") print("channels=#channel1;#channel2") print("No channels will be joined as none have been specified.") channels_joined = True while True: if(len(messagequeue) == 0): data = irc.readbuffer() for msg in data: msg.rstrip("\r") if(msg != ""): messagequeue.append(msg) else: message = irc_message(messagequeue.pop(), irc, mynick) print("NICK:" + message.nickname + " TYPE:" + message.messagetype + " TARGET:" + message.target + " CHANNEL:" + message.channel + " MESSAGE:" + message.message + ":") print(message.data) if(message.messagetype == "433"): #Nickname already in use, use next one nickname_index+=1 nickname_set = False if(nickname_index > len(nicknames)): #All nickname options in use, bomb out print("All nicknames specified in fullmetalmadda.cfg are in use on the network.") print("Exiting.") sys.exit(4) #Check logoon process has been completed if(not nickname_set): irc.send("NICK", nicknames[nickname_index], "", "") nickname_set = True mynick = nicknames[nickname_index] print("Nick:" + mynick) elif(not user_defined): irc.send("USER", nicknames[nickname_index], "8", "Python IRC bot") user_defined = True elif(not channels_joined): for channel in channels: irc.send("JOIN", channel, "", "") channels_joined = True else: if(message.messagetype == "PRIVMSG" and message.message == "!roll"): message.reply(str(random.randint(1,6))) if(message.messagetype == "KICK"): message.reply("test") if(message.messagetype == "PRIVMSG" and message.message == "kick"): irc.send("KICK",message.channel,message.nickname,"Fine, knock yourself out.") #Auto Ops allowedopslist = ['Oot', 'syn', 'ryll', 'rince'] if(message.messagetype == "JOIN"and message.nickname in allowedops): irc.send("MODE", message.channel, "+o") #Comical command responses if(message.messagetype == "PRIVMSG" and message.message == "!boobs"): message.reply("boing") if(message.messagetype == "PRIVMSG" and message.message == "!rickroll"): rickroll=['Never gonna give you up','Never gonna let you down'] message.reply(random.sample(rickroll,1)) if(message.messagetype == "PRIVMSG" and message.message == "!donk"): donk = ['Put A Donk On It!', 'Electro', 'Bassline', 'Thats sick That Is'] message.reply(random.sample(donk,1)) #Conditional trigger if(message.messagetype == "PRIVMSG" and message.message == "Hehe"): chambered_round = 1 if(chambered_round == 1 and message.nickname == "Skei"): chambered_round = 0 irc.send("KICK", message.channel, message.nickname, "What does this button do?")