import socket,random,subprocess,threading,time,cPickle,string,time,prss,randwords,pstocks,chaninfo,primefac,choose_parser,notes,mbtilde,kwam,lua,urn,scplugin,json,math; import os.path as fs; from email.mime.text import MIMEText; import markovuniverse as mu; zws = "" class NotRunningException (Exception): pass; log_format = "{} [{}] {}: {}" def loadLNR(): with open("/home/minerobber/lnr.txt") as f: return [l.rstrip() for l in f if l.rstrip()] def saveLNR(lnr): with open("/home/minerobber/lnr.txt","w") as f: f.write("\n".join(lnr)) 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 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) def getBotanyPlantPath(user,file="{0}_plant_data.json"): return "/home/{{0}}/.botany/{}".format(file).format(user) def hasBotanyPlant(user): return fs.exists(getBotanyPlantPath(user)) def hasVisitors(user): return fs.exists(getBotanyPlantPath(user,"visitors.json")) def getBotanyPlant(user): if not hasBotanyPlant(user): return False, {} else: with open(getBotanyPlantPath(user)) as f: data = json.load(f) # if not hasVisitors(user): # TODO: fix this return True, data # else: # with open(getBotanyPlantPath(user,"visitors.json")): # return True, data, json.load(f) #def getBotanyLastWatered(dlw,vt): # rvt = [] # for t in vt: # if t < time.time(): # rvt.append(t) # if not rvt: # return dlw # allts = [dlw]+rvt # allts.sort() # diffs = [(j-i)/86400.0 for i, j in zip(allts[:-1], allts[1:])] # lve = next((x for x in diffs if x > 5), None) # if not lve: # return allts[-1] # return allts[:diffs.index(lve)+1][-1] #this should actually work, believe it or not 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 = []; self.running = True; self.channels = channels; self.kwam = kwam.KWAMPlugin(self) self.lua = lua.LuaPlugin(self) 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.notebook = notes.DatedNotebook() self.solver = mbtilde.Solver(self) self.urn = urn.UrnPlugin(self) if fs.isfile("/home/minerobber/lnr.txt"): self.lnr = loadLNR() else: self.lnr = [] saveLNR([]) # self.xkcd = xkcdpl.XKCDPlugin(self) # self.g7sc = scplugin.G7SCPlugin(self,"!shinycalc") #self.quiz = quiz.QuizGame("/home/minerobber/quiz.txt") # 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"); self.irc.recv(4096) for chan in channels: self.irc.send("JOIN " + chan + "\r\n"); while self.running: data = self.irc.recv(4096); 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("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 addNote(self,touser,fromuser,msg): # print "ADDNOTE: {} {} {}".format(touser,fromuser,msg) self.notebook.addNote(touser,fromuser,msg) def readNotes(self,user): # print "READNOTES: {}".format(user) for msg in self.notebook.readNotes(user): self.say(user,msg) def haveNotesFor(self,user): # print "CHECK: {}".format(user) return self.notebook.checkNotes(user) 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]; if nick=="tilde.town": return print "[" + channel + "] " + nick + ": " + message; log(channel, nick, message) self.rss.pubDate = time.localtime() if self.haveNotesFor(nick): self.readNotes(nick) if message.strip()=="!water minerbot": self.mention(channel, nick, "BZZ-ZAP! Please don't water me! I'm not waterproofed yet!!!!! BZZ-BZZ-ZZZ-ZAP!") # elif message.find("!urlme "): # parts = message.split() # if len(parts)>1: # self.mention(channel,nick,"https://tilde.town/~{}/{}".format(nick,parts[1])) # elif message.find("!talklikeme"): # self.say(channel,"!talklike "+nick) # elif message.strip()=="!lnrdebug": # self.say(channel,"lnr = {!s}".format(self.lnr)) # return elif message.find("!plant ")==0: ok, data = getBotanyPlant(message.split(" ",1)[1].rstrip()) if not ok: self.mention(channel,nick,"{} doesn't have a plant".format(message.split(" ",1)[1].rstrip())) else: # user requested does indeed have a plant, and data is the loaded JSON representation of said plant msg = "{d[owner]}'s ".format(d=data) status = data["description"] if data["is_dead"]: status = "dead "+status status+=" was watered" lastwatered = data["last_watered"] #,[x['timestamp'] for x in visitors]) if (lastwatered-time.time())<=(24*60*60): status+=" today" hours = str(int(math.floor((time.time()-lastwatered)/3600))) if hours=='0': seconds = str(int(math.floor((time.time()-lastwatered)/60))) if seconds=='1': status+=" (about a second ago)" else: status+=" (about {} seconds ago)".format(seconds) elif hours=='1': status+=" (about an hour ago)" else: status+=" (about {} hours ago)".format(hours) self.mention(channel,nick,msg+status) elif message.find("!steven-universe") == 0: self.mention(channel, nick, "\"{}\" - {}".format(*mu.new_episode())) elif 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 + "! I'm a mess of code. Ask my developer (my nick, but replace bot with \"obber\") about commands of mine!") elif message.find("!twitch ") == 0: mparts = message.split(" ") if len(mparts) == 2: c = chaninfo.ChannelInfo(mparts[1][:-2],"h7lvoe263k42haljbhlbv2ag1nzt5nd") if not c.isOnline(): self.say(channel,"{} is not streaming.".format(c.channel)) 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.find("!choose ") == 0: self.say(channel,nick+": "+random.choice(choose_parser.parse(message))) 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: # d = False # if message.startwith("s "): # d=True # message=message.split(" ",2)[1] print(self.owner + " used the global to say: \"" + message + "\"") # if d: self.globalmsg(message) # else: # self.globalmsg("[GLOBAL] "+message) # self.globalmsg(("[GLOBAL] " if not d else "") + message) elif message.find("!lnr") == 0: if nick==self.owner and len(message.split(" "))>1: self.lnr.append(message.split(" ",1)[1].rstrip()) saveLNR(self.lnr) self.mention(channel,nick,"I'll remember that...") return for i in self.lnr: self.mention(nick,nick,"Late night rambling: {}".format(i)) self.mention(channel,nick,"All late night ramblings have been PM'd to you.") 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\r\n") == 0 and self.owner == nick: self.irc.send("QUIT :Reloading bot code...\r\n"); self.reload = 1; self.running = False; elif message.find("!announce ") == 0: announce(channel, nick, " ".join(message.split(" ")[1:]), self.rss) elif message.find("!reloadchannels") == 0 and nick == self.owner: channels = [] with open("/home/minerobber/misc/mbchan.txt") as f: channels = f.readlines() self.channels_2 = channels for channel in self.channels_2: if channel not in self.channels: self.irc.send("JOIN {}\r\n".format(channel)) self.channels = self.channels_2 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("!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: self.say(nick,"DEPRECATED: Use new \"!note\" command (same syntax)") elif message.find("!note ") == 0: parts = message.split(" ",2) if not len(parts) == 3: self.mention(channel,nick,"usage: !note ") else: self.addNote(parts[1],nick,parts[2]) self.mention(channel,nick,"Message sent for {}.".format(parts[1])) elif message.find("!getStock") == 0: self.mention(channel,nick,"This command is out-of-service while my developer figures out what's wrong with me.") return 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.") result = self.solver.run(message) if result: if type(result)==str: self.say(channel,result) else: self.say(channel,str(result)) elif message.strip()=="!discord": self.mention(channel,nick,"The discord invite link is: https://discord.gg/RS8gJAV") self.solver.answer(channel,nick,message) #self.xkcd.answer(channel,nick,message) #self.xkcd.check_for_new_xkcd(channel) self.kwam.answer(channel,nick,message) # self.g7sc.handleCommand(channel,nick,message) # self.urn.onMessage(self,channel,nick,message) # self.lua.handleCommand(channel,nick,message) def globalmsg(self,message): for chan in self.channels: self.say(chan,message) def say(self, chan, message): try: if type(message)!=str: message = str(message) message = message.replace("\n","") self.irc.send("PRIVMSG " + chan + " :" + message + "\r\n") if chan.find("#") == 0: log(chan, self.name, message) print("[{}] {}: {}".format(chan,self.name,message)) except: self.say(chan,"Failed to send message, try again!") def mention(self,chan,nick,message): self.say(chan,nick+": "+message) # def playQuizGame(self,channel,nick,message): # if self.quiz.notPlaying(nick): # self.quiz.addPlayer(nick) # self.mention(channel,nick,self.quiz.getQuestion(nick)+" Use !a, !b, !c, or !d to answer.") # else: # if message.rstrip() not in ("!a","!b","!c","!d"): # return # if not self.quiz.answer(nick,message[1]): # self.mention(channel,nick,"Close, but no dice! Try again!") # else: # self.mention(channel,nick,"Correct!") # self.quiz.addPoint(nick) # self.quiz.save() def save(self): # self.quiz.save() saveLNR(self.lnr) return def load(self): # self.quiz.load() return 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;