import socket,random,subprocess,threading,time,cPickle,string,time,prss,randwords,pstocks,chaninfo,primefac,re; import os.path as fs; from email.mime.text import MIMEText; #with open("zws.txt","rb") as f: # zws = f.readAll() zws = "" class NotRunningException (Exception): pass; log_format = "{} [{}] {}: {}" def filterNick(nick): if len(nick) == 1: print "Either "+nick+" is not a nick, or the person who has that nick needs a longer nick." return nick s = nick[0]+zws+nick[1:] return s.encode('utf-8') def log(channel, nick, message): with open("/home/minerobber/log.txt","ab") as fh: fh.write(log_format.format(time.strftime("%Y-%m-%d %H:%M:%S"),channel,nick,message)) with open("/home/minerobber/logs/"+time.strftime("%Y-%m-%d")+".txt","ab") as fh: fh.write(log_format.format(time.strftime("%Y-%m-%d %H:%M:%S"),channel,nick,message)) def logPublic(channel, nick, message): with open("/home/minerobber/public_html/public_msgs.txt","ab") as fh: fh.write(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+" ["+channel+"] "+nick+": "+message) def announce(channel, nick, ann, prssfeed): prssfeed.addItem("Announcement by {} in {}".format(channel, nick),"https://tilde.town/~{}".format(nick),ann) with open(fs.expanduser("~/public_html/announcements/rss.xml"),"wb") as rss: rss.write(prssfeed.make()) with open(fs.expanduser("~/public_html/announcements/index.text"),"ab") as html: html.write('{}, while in {}, announced: "{}" \n'.format(nick, channel, ann)); subprocess.check_output(["make -C ~/public_html/announcements -B"],shell=True) cPickle.dump(prssfeed,open("/home/minerobber/misc/annrss","wb")) def logJoin(channel,nick): with open("/home/minerobber/log.txt","ab") as fh: fh.write(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+" "+nick+" joined "+channel) def logPart(channel,nick): with open("/home/minerobber/log.txt","ab") as fh: fh.write(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())+" "+nick+" left "+channel) class minerbot: def __init__(self, name, user, owner): self.name = name; self.owner = owner; self.user = user; self.running = False; self.reload = 0; def begin(self, server, port, channels): # declare variables here self.irc = socket.socket(); self.randWordsList = randwords.corpus; self.userList = []; if fs.isfile("/home/minerobber/userList"): self.load(); self.running = True; self.channels = channels; self.emailTemplate = ["~{0} wanted me to tell you:\n"] self.rss = prss.PageRSS("Announcements","https://tilde.town/~minerobber/announcements","Announcements made by tilde.town members in IRC.",time.localtime()) if fs.isfile("/home/minerobber/misc/annrss"): self.rss = cPickle.load(open("/home/minerobber/misc/annrss")) # self.guessgameplaying = False; # self.gg_players = {}; # self.gg_state = "not-active"; # self.gg_votes = {} # connecting, as well as main loop self.irc.connect((server, port)); self.irc.send("NICK " + self.name + "\r\n"); self.irc.send("USER " + self.user + " 8 * Making an IRC bot is fun!\r\n"); for chan in channels: self.irc.send("JOIN " + chan + "\r\n"); while self.running: data = self.irc.recv(4096); #print data; if data.find("PING") != -1: self.irc.send("PONG " + data.split()[1] + "\r\n"); elif data.find("NAMES") != -1 and not fs.isfile("/home/minerobber/userList"): users = data[5:-3] for name in users.split(" "): table = [] table.append(name) table.append(0) self.userList.append(table); elif data.find("JOIN") != -1: logJoin(data.split(" ")[2],data.split(" ")[0].split("!")[0][1:]) if data.split(" ")[2] == "#music" and data.split(" ")[0].split("!")[0][1:] != self.nick: self.say(data.split(" ")[2],"Hello, "+data.split(" ")[0].split("!")[0][1:]+"! If you want to listen along, the URL is: https://tilde.town/~kc/jukebot/listen") elif data.find("PART") != -1: logPart(data.split(" ")[2],data.split(" ")[0].split("!")[0][1:]) elif data.find("PRIVMSG") != -1: self.spokento(data) self.save() return self.reload; def runCMD(self, cmd): i = subprocess.check_output([cmd],shell=True) if "\n" in i: i.replace("\n","; ") return i; def sendEmail(self, channel, sender, recipient, text): msg = MIMEText("~{0} wanted me to tell you:\n> {1}".format(sender,text)) msg["From"] = "minerbot-messages@tilde.town" msg["To"] = "{0}@tilde.town".format(recipient) msg["Subject"] = "Message from ~{0}".format(sender) msg["Cc"] = "{0}@tilde.town".format(sender) mail = subprocess.Popen("{0} -t -oi".format(which("sendmail")).split(" "),stdin=subprocess.PIPE) mail.communicate(msg.as_string()) self.say(channel,"Mail sent!") # mail.close() def spokento(self, data): if not self.running: raise Exception("spokento was prematurely called."); data_parts = data.split(" ",3); id = data_parts[0]; nick = id.split("!")[0][1:]; message = data_parts[3][1:]; channel = data_parts[2]; print "[" + channel + "] " + nick + ": " + message; log(channel, nick, message) self.rss.pubDate = time.localtime() if message.find(self.owner) != -1: logPublic(channel,nick,message) elif message.find("!log") == 0: logPublic(channel,nick,message.split(" ",2)[1]) if message.find("!quit") == 0 and nick == self.owner: self.irc.send("QUIT :beep boop I'm a bot.\r\n"); self.running = False; elif message.find("!rollcall") == 0: self.say(channel,"Hey! I'm " + self.name + "! Use '!help' to see a list of my commands.") elif message.find("!twitch ") == 0: mparts = message.split(" ") if len(mparts) == 2: c = chaninfo.ChannelInfo(mparts[1][:-2],"h7lvoe263k42haljbhlbv2ag1nzt5nd") if not c.isOnline(): return verb = "playing" if c.getDisplay(): verb = "being" self.say(channel, '{} is {} {}. Status: "{}"; Viewers: {!s}'.format(c.channel,verb,c.getGame(),c.getStatus(),c.getViewers())) elif message.strip == "!last5said": with open("/home/minerobber/log.txt") as f: lines = [] for line in f: lines.append(line.strip()) for line in lines[:-5]: self.say(nick,line) elif message.find("!dicegame") == 0: message_parts = message.split(" ",2) sides = 6 choice = int(random.uniform(1,sides)) b_choice = int(random.uniform(1,sides)) if b_choice > choice: winMSG = "I win!" else: if b_choice == choice: winMSG = "It's a tie!" else: winMSG = "You win!" self.say(channel,"You rolled a " + str(choice) + ", and I rolled a " + str(b_choice) + ". " + winMSG) elif channel == self.name and nick == self.owner: print(self.owner + " used the global to say: \"" + message + "\"") for chan in self.channels: self.say(chan,"[GLOBAL] " + message) elif message.find("!online") == 0: results = self.runCMD("onlinepeople4irc") leet = string.maketrans("aelosiuUc","43105|Uu(") self.say(channel, results.translate(leet)) elif message.find("!reload") == 0 and self.owner == nick: self.irc.send("QUIT :Reloading bot code...\r\n"); self.reload = 1; self.running = False; # elif message.find("!shop") == 0: #nyi elif message.find("!tbadmin") == 0 and self.owner == nick: message_parts = message.split(" ") if message_parts[1] == "add": for i in self.userList: if message_parts[2] == i[0]: i[1] += int(message_parts[3]) self.save() elif message_parts[1] == "rm": for i in self.userList: if message_parts[2] == i[0]: i[1] += int(message_parts[3]) self.save() elif message_parts[1] == "view": for i in self.userList: if message_parts[2] == i[0]: self.say(channel,i[0] + " has " + str(i[1]) + " tildebucks.") elif message.find("!announce ") == 0: announce(channel, nick, " ".join(message.split(" ")[1:]), self.rss) # elif message.find("!guessing_game") == 0 and self.gg_state == "not-active": # thread = threading.Thread(target=self.playGuessingGame, args=(channel)); # elif message.find("!join_guessing_game") == 0 and self.gg_state == "player-wait": # self.gg_players.append(nick); elif message.find("!save") == 0 and nick == self.owner: self.save() elif message.find("!load") == 0 and nick == self.owner: self.load() #elif message.find("!money") == 0: # for i in self.userList: # if i[0] == nick: # self.say(channel,(nick + " has " + str(i[1]) + " tildebucks.")) elif message.find("!update-qdb-commits") == 0 and self.owner == nick: self.say(channel, self.runCMD("/home/minerobber/qdb-commits-gen.sh")) elif message.find("!run ") == 0 and self.owner == nick: prog = message.split(" ",1)[1] nprog = which(prog.split(" ",1)[0]) if nprog is None: self.say(channel,"Error: program does not exist!") else: self.say(channel,self.runCMD(prog.replace(prog.split(" ",1)[0],nprog))) elif message.find("!tell") == 0: parts = message.split(" ",2) if not len(parts) == 3: self.say(channel,"Usage: !tell ") else: self.sendEmail(channel,nick,parts[1],parts[2][:-2]) # elif message.find("!get-gg-state") == 0 and self.owner == nick: # self.say(channel, "GG-State: " + self.gg_state) elif message.find("!getStock") == 0: ticker = pstocks.StockTicker(format="{0} ({1}) is currently priced at {2}. (a change of {3})") parts = message.split(" ") if len(parts) < 2: self.say(channel, "Usage: '!getStock '") return for symbol in parts[1:]: ticker.addSymbol(symbol) for message in ticker.getTickerValues(): self.say(channel,message) elif message.find("!mbtilde ") == 0: if nick != self.owner: self.say(channel,"Don't tell me what to do!") return if channel != "#bots": self.say(channel,"Remember: tildebot only awards tildes in \#bots.") parts = message.split(" ",2) parts = [s.rstrip() for s in parts] if parts[1] == "request": self.say("#bots","!tilde") if parts[1] == "add": nums = [int(s) for s in parts[2].split(" ")] self.say(channel,"{!s}".format(sum(nums))) if parts[1] == "sub": nums = [int(s) for s in parts[2].split(" ",1)] self.say(channel,"{!s}".format(nums[1]-nums[0])) if parts[1] == "mult": nums = [int(s) for s in parts[2].split(" ",1)] self.say(channel,"{!s}".format(nums[1]*nums[0])) if parts[1] == "divide": nums = [int(s) for s in parts[2].split(" ",1)] try: self.say(channel,"{!s}".format(nums[0]/nums[1])) except ZeroDivisionError: self.say(channel,"Bugger off!") if parts[1] == "mod": nums = [int(s) for s in parts[2].split(" ",1)] self.say(channel,"{!s}".format(nums[0]%nums[1])) if parts[1] == "factor": self.say(channel,"{!s},{!s}".format(*list(primefac.primefac(int(parts[2])))) elif message.find("!rpg") and channel=="#rpg": parts = message.split(" ") if len(parts) < 2: self.say(channel,"Usage: !rpg [parameters]") if parts[1]=="roll" and len(parts)==3: descriptor = parts[2].rstrip() t = re.match(descriptor,r"^(\d+)d(\d+)$") if not t: self.say(channel,"Usage: !rpg roll d") self.say(channel,"Example: !rpg roll 1d100") number = t.match(0) sides = # elif message.find("!helpme") == 0: # help_parts = message.split(" ",2) # if len(help_parts) < 2: # self.say(channel,"Commands: 'dicegame', 'online', 'tell';") # else: # if help_parts[1] == "dicegame": # self.say(channel,"Play a dice game against " + self.name + ". Usage: '!dicegame'") # elif help_parts[1] == "online": # self.say(channel,"Returns a list of online users, as well as a user count. Usage: '!online'") # elif help_parts[1] == "tell": # self.say(channel,"Sends a user an email to let them know of something. Usage: '!tell '") # elif help_parts[1] == "words": # self.say(channel,"Generates a random string of words. Usage: '!words'") # elif help_parts[1] == "getStock": # self.say(channel,"Gets stock info for a specific ticker. Usage: '!getStock '") # elif self.gg_state == "player-wait-vote" and message.find("!guess"): # if nick in self.gg_players: # for i in self.gg_votes: # if i[0] == nick: # return; # self.gg_votes.append({nick,int(message.split(" ")[1])}) # if self.gg_state == "player-wait-vote": # playerDouble = {}; # for i in self.gg_votes: # if i[0] in self.gg_players: # playerDouble.append(i[0]) # if self.gg_players == playerDouble: # self.gg_state = "all-voted" # self.randWordsList.append(message) def say(self, chan, message): self.irc.send("PRIVMSG " + chan + " :" + message + "\r\n") if chan.find("#") == 0: log(chan, self.name, message) def playGuessingGame(self,chan): self.gg_players = [] self.say(chan, "Generating number to guess...") gg_number = random.uniform(1,100) self.gg_state = "player-wait" self.say(chan, "Game will start soon! To join, type \"!join_guessing_game\"") wait(15, True) self.gg_state = "started" self.say(chan, "The game has started!") playerString = "Players: " for player in self.gg_players: playerString += player + " " self.say(chan, playerString) self.say(chan,"How to play: guess a number between 1 and 100.") self.gg_state = "player-wait-vote" while not self.gg_state == "all-voted": wait(5,False) correct_players = []; for i in self.gg_votes: if gg_number == i[1]: correct_players.append(i[0]) correctVoteString = "Winners: " for player in correct_players: correctVoteString += player + " " self.say(chan, correctVoteString) self.say(chan, "All of the winners get 3 tildebucks!") for user in self.userList: if user[0] in correct_players: user[1] += 3; self.save() self.gg_state = "not-active" def save(self): cPickle.dump(self.userList, open("/home/minerobber/userList","wb")) def load(self): cPickle.load(open("/home/minerobber/userList","rb")) def num(s): try: int(s) except ValueError: float(s) def wait(s, minutes): if minutes: time.sleep(s * 60) else: time.sleep(s) def which(s): import os def is_exe(fpath): return fs.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = fs.split(s) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, s) if is_exe(exe_file): return exe_file return None;