class scannerhelp(loadable): """Help for scanners""" alias = "scanhelp" usage = " [nick]" access = "member" helptext = """When a request comes in, it looks like this... [123] %s requested a Planet Scan of 1:1:1 Dists(i:17) https://game.planetarion.com/waves.pl?id=1&x=1&y=1&z=1 or... [123] %s requested a Planet Scan of 1:1:1 Dists(i:17/r:35) https://game.planetarion.com/waves.pl?id=1&x=1&y=1&z=1 The "Dists(i:17)" is the number of distorters I think the planet has, based on blocked scanners or from dev scans. The real number may be higher, or possibly lower if some distorters have been destroyed. The "r:35" in the second example means that the user requesting the scan thinks that the planet has at least 35 distorters. If you don't have as many amplifiers as the planet has distorters, it's probably not worth wasting your resources trying. The URL at the end will take you straight to the waves page in-game and will do the scan. You can then copy the URL of the scan (from the "Scan Link" on the page or from your address bar) and paste it in PM to me or in any channel where I can see it. To list open requests, use ".req l" (.request list) for a list in an abbreviated format: [123: (17/35) P 1:1:1] Alternatively, ".req links" will give a list of scan URLs to click on: [123 (17/35): http://game.planetarion.com/waves.pl?id=1&x=1&y=1&z=1] If you get blocked, you can use the "blocks" subcommand. "!req 123 b 20" would indicate that you were blocked doing the example above (the request ID, 123, is in the square brackets) and you have 20 amplifiers. If you have any problems, ask. Scanners are often idle, but usually helpful when they're around! Thanks for scanning for %s!""" % ("Anon" if Config.getboolean( "Misc", "anonscans") else Config.items("Admins")[0][0], "Anon" if Config.getboolean("Misc", "anonscans") else Config.items("Admins")[0][0], Config.get("Alliance", "name")) @route(r"(.*)") def execute(self, message, user, params): if params.group(1): if not user.is_admin(): message.alert("Insufficient access to send the help to %s." % params.group(1)) return from Hooks.scans.request import request tnick = params.group(1).strip() if not CUT.nick_in_chan(tnick, request().scanchan()): message.alert( "%s does not appear to be in the scanner channel. Aborting." % tnick) return if message.reply_type() == NOTICE_REPLY: message.notice(self.helptext, tnick, 2) else: message.privmsg(self.helptext, tnick, 2) elif message.reply_type() == PUBLIC_REPLY and not user.is_admin(): message.alert( "Insufficient access to spam the channel. Try another prefix." % params.group(1)) else: message.reply(self.helptext, 2)
def robocop(self, message, scantype, pa_id, x, y, z, names, scanner, reqs, old=False): nicks = [] reply = "Old " if old else "" reply += "%s on %s:%s:%s " % ( PA.get(scantype, "name"), x, y, z, ) if ("showscanner" in Config.options("Misc") and Config.getboolean("Misc", "showscanner") and scanner != 'None'): reply += "from %s " % (User.load(id=scanner).name) if User.load( id=scanner) not in (None, 'None') else "" reply += Config.get("URL", "viewscan") % (pa_id, ) if old: reply += " !request cancel %s if this is suitable." % (reqs) for name in names.split(","): user = User.load(name) for nick in CUT.get_user_nicks(name): nicks.append(nick) message.privmsg(self.url(reply, user), nick) if not old: reply = "[-%s] %s on %s:%s:%s " % ( reqs, PA.get(scantype, "name"), x, y, z, ) reply += "delivered to: " reply += ", ".join(nicks) if not Config.getboolean( "Misc", "anonscans") else "Anon" if Config.getboolean("Misc", "showurls"): reply += " (%s)" % (Config.get("URL", "viewscan") % (pa_id, )) from Hooks.scans.request import request message.privmsg(reply, request().scanchan())
def execute(self, request, user, message=None, planet=None): tick = Updates.current_tick() Q = session.query(Request) Q = Q.filter(Request.user == user) Q = Q.filter(Request.tick > tick - 5) Q = Q.filter(Request.active == True) Q = Q.order_by(asc(Request.id)) open = Q.all() Q = session.query(Scan) Q = Q.join(Request.scan) Q = Q.filter(Request.user == user) Q = Q.filter(Request.tick > tick - 24) Q = Q.filter(Request.scan != None) Q = Q.order_by(desc(Request.id)) completed = Q.all() Q = session.query(Scan) Q = Q.filter(Scan.scanner == user) Q = Q.order_by(desc(Scan.id)) scans = Q[:25] return render("scans/scans.tpl", request, anonscans=Config.getboolean("Misc", "anonscans"), types=Request._requestable, open=open, completed=completed, scans=scans, message=message, planet=planet)
def request(self, message, user, planet, scan, dists, gal=False): request = Request(target=planet, scantype=scan, dists=dists) user.requests.append(request) session.commit() if not gal: requester = user.name if not Config.getboolean("Misc", "anonscans") else "Anon" dists_intel = planet.intel.dists if planet.intel else 0 message.privmsg( "[%s] %s requested a %s Scan of %s:%s:%s Dists(i:%s%s) " % ( request.id, requester, request.type, planet.x, planet.y, planet.z, dists_intel, "/r:%s" % request.dists if request.dists > 0 else "", ) + request.link, self.scanchan(), ) return request
def robonotify(self, header, body): # Check for correct "From" address? if Config.getboolean("imap", "singleaddr"): uname_re = "%s\+(.+)@.+" % Config.get("imap", "user").split("@")[0].lower() else: uname_re = "<?(.+)@.+" uname = re.findall(uname_re, header['To'].lower())[0] dsuff = Config.get("imap", "defsuffix") if dsuff: if uname[-len(dsuff):] == dsuff: uname = uname[:-len(dsuff)] else: self.forwardMail(uname, header, body) return # Check for the main notificatino types tick = re.findall("events in tick (\d+)", body)[0] newfleets = re.findall("We have detected an open jumpgate from (.+), located at (\d{1,2}):(\d{1,2}):(\d{1,2}). ".replace(" ","\s+") +\ "The fleet will approach our system in tick (\d+) and appears to have (\d+) visible ships.".replace(" ","\s+"), body) recalls = re.findall("The (.+) fleet from (\d{1,2}):(\d{1,2}):(\d{1,2}) has been recalled.".replace(" ","\s+"), body) cons = len(re.findall("Our construction team reports that .+ has been finished".replace(" ","\s+"), body)) res = len(re.findall("Our scientists report that .+ has been finished".replace(" ","\s+"), body)) # Wrap it up in a bow for line in newfleets: push("defcall", etype="new", uname=uname, tick=tick, name=line[0], x=line[1], y=line[2], z=line[3], eta=line[4], size=line[5]) for line in recalls: push("defcall", etype="rec", uname=uname, tick=tick, name=line[0], x=line[1], y=line[2], z=line[3]) if res + cons > 0: push("defcall", etype="fin", uname=uname, tick=tick, res=res, cons=cons) if len(newfleets) + len(recalls) + cons + res == 0: self.forwardMail(uname, header, body)
def execute(self, message, user, params): if not Config.get("Twilio", "sid"): message.reply("Twilio support not configured. Tell the admin. If you are the admin, configure Twilio or disable !call to prevent this message.") return rec = params.group(1) receiver=User.load(name=rec,exact=False,access="member") or User.load(name=rec) if not receiver: message.reply("Who exactly is %s?" % (rec,)) return if receiver.smsmode == "Retard": message.reply("I refuse to talk to that incompetent retard. Check %s's mydef comment and use !phone show to try sending it using your own phone." %(receiver.name,)) return if not (receiver.pubphone or user in receiver.phonefriends or user.is_admin()): message.reply("%s's phone number is private or they have not chosen to share their number with you. No call made." % (receiver.name,)) return phone = self.prepare_phone_number(receiver.phone) if not phone or len(phone) <= 7: message.reply("%s has no phone number or their phone number is too short to be valid (under 6 digits). No call made." % (receiver.name,)) return client = Client(Config.get("Twilio", "sid"), Config.get("Twilio", "auth_token")) if Config.getboolean("Twilio", "warn"): url="http://twimlets.com/echo?Twiml=%3CResponse%3E%3CSay%20voice%3D%22alice%22%20language%3D%22en-GB%22%20%3EHello.%20This%20is%20" +\ Config.get("Connection", "nick") + ".%20Stop%20wasting%20our%20credit!%3C%2FSay%3E%3CHangup%2F%3E%3C%2FResponse%3E&", else: url="http://twimlets.com/echo?Twiml=%3CResponse%3E%3CHangup%2F%3E%3C%2FResponse%3E&", tw = client.api.account.calls.create(to=phone, from_=Config.get("Twilio", "number"), url=url, timeout=Config.getint("Twilio", "timeout")) if tw.sid: message.reply("Successfully called %s." % (receiver.name)) else: message.reply("Error: Failed to get call ID from Twilio server.")
def execute(self, message, user, params): if not Config.get("Twilio", "sid"): message.reply("Twilio support not configured. Tell the admin. If you are the admin, configure Twilio or disable !call to prevent this message.") return rec = params.group(1) phone = self.prepare_phone_number(rec) if not phone or len(phone) <= 7: message.reply("No phone number or phone number is too short to be valid (under 6 digits). No call made." % (rec,)) return client = Client(Config.get("Twilio", "sid"), Config.get("Twilio", "auth_token")) if Config.getboolean("Twilio", "warn"): url="http://twimlets.com/echo?Twiml=%3CResponse%3E%3CSay%20voice%3D%22alice%22%20language%3D%22en-GB%22%20%3EHello.%20This%20is%20" +\ Config.get("Connection", "nick") + ".%20Stop%20wasting%20our%20credit!%3C%2FSay%3E%3CHangup%2F%3E%3C%2FResponse%3E&", else: url="http://twimlets.com/echo?Twiml=%3CResponse%3E%3CHangup%2F%3E%3C%2FResponse%3E&", tw = client.api.account.calls.create(to=phone, from_=Config.get("Twilio", "number"), url=url, timeout=Config.getint("Twilio", "timeout")) if tw.sid: message.reply("Successfully called %s." % (rec)) else: message.reply("Error: Failed to get call ID from Twilio server.")
def robocop(self, message, scantype, pa_id, x, y, z, names): nicks = [] reply = "%s on %s:%s:%s " % ( PA.get(scantype, "name"), x, y, z, ) reply += Config.get("URL", "viewscan") % (pa_id, ) for name in names.split(","): user = User.load(name) for nick in CUT.get_user_nicks(name): nicks.append(nick) message.privmsg(self.url(reply, user), nick) reply = "%s on %s:%s:%s " % ( PA.get(scantype, "name"), x, y, z, ) reply += "delivered to: " reply += ", ".join(nicks) if not Config.getboolean( "Misc", "anonscans") else "Anon" from Hooks.scans.request import request message.privmsg(reply, request().scanchan())
def robocop(self, message, request_id, mode): request = Request.load(request_id, active=False) if request is None: return if mode == "cancel": reply = "Cancelled scan request %s" % (request.id,) message.privmsg(reply, self.scanchan()) nicks = CUT.get_user_nicks(request.user.name) for nick in nicks: message.privmsg(reply, nick) return if mode == "block": reply = "Updated request %s dists to %s" % (request.id, request.dists,) message.privmsg(reply, self.scanchan()) nicks = CUT.get_user_nicks(request.user.name) for nick in nicks: message.privmsg(reply, nick) return user = request.user planet = request.target requester = user.name if not Config.getboolean("Misc", "anonscans") else "Anon" dists_intel = planet.intel.dists if planet.intel else 0 message.privmsg("[%s] %s requested a %s Scan of %s:%s:%s Dists(i:%s%s) " % (request.id, requester, request.type, planet.x,planet.y,planet.z, dists_intel, "/r:%s" % request.dists if request.dists > 0 else "") + request.link, self.scanchan())
def flux_passwd(self, user): if not Config.getboolean("FluxBB", "enabled"): return -1 if session.execute("SELECT username FROM %susers WHERE LOWER(username) LIKE '%s';" % (Config.get("FluxBB", "prefix"), user.name.lower())).rowcount > 0: return session.execute("UPDATE %susers SET password='******' WHERE LOWER(username) LIKE '%s';" % (Config.get("FluxBB", "prefix"), user.passwd, user.name.lower())).rowcount else: group = Config.get("FluxBB", "memgroup") if user.is_member() else Config.get("FluxBB", "galgroup") if group == 0: return -1 return session.execute("INSERT INTO %susers (group_id, username, password, email, title) VALUES ('%s', '%s', '%s', '%s', '%s');" % ( Config.get("FluxBB", "prefix"), group, user.name, user.passwd, user.email, user.level)).rowcount
def check_access(self, user): user = user or User(access=0) if not Config.getboolean("Arthur", "public") and not self.is_user(user): raise UserError("Hi! Please login below:") if getattr(self, "_USER", False) is True: if self.is_user(user) is False: raise UserError("You need to be logged in to use this feature") if user.access >= self.access: return user else: raise UserError("You don't have access to this page")
def forwardMail(self, uname, header, body): if not Config.getboolean("imap", "forwarding"): return body = "Original Message from %s\n\n" % (header['From']) + body user = User.load(uname) if user: addr = user.email else: addr = Config.get("imap", "bounce") body = "Bad username: %s\n\n" % (uname) + body if addr: self.send_email(header['Subject'], body, addr)
def request(self, message, user, planet, scan, dists, gal=False): request = Request(target=planet, scantype=scan, dists=dists) user.requests.append(request) session.commit() if not gal: requester = user.name if not Config.getboolean("Misc", "anonscans") else "Anon" dists_intel = planet.intel.dists if planet.intel else 0 message.privmsg("[%s] %s requested a %s Scan of %s:%s:%s Dists(i:%s%s) " % (request.id, requester, request.type, planet.x,planet.y,planet.z, dists_intel, "/r:%s" % request.dists if request.dists > 0 else "") + request.link, self.scanchan()) return request
def execute(self, message, user, params): planet = Planet.load(*params.group(1,3,5)) if planet is None: message.reply("No planet with coords %s:%s:%s found" % params.group(1,3,5)) return notice = "DEFCALL: %s wants %s to %s:%s:%s eta %s" % (user.name, params.group(7), params.group(1), params.group(3), params.group(5), params.group(6)) if Config.getboolean("Misc", "globaldef"): push("broadcast", notice="!#!"+notice.replace(" ","!#!")) else: message.notice(notice, Config.get("Channels", "home"))
def execute(self, message, user, params): if not Config.get("Twilio", "sid"): message.reply( "Twilio support not configured. Tell the admin. If you are the admin, configure Twilio or disable !call to prevent this message." ) return rec = params.group(1) receiver = User.load(name=rec, exact=False, access="member") or User.load(name=rec) if not receiver: message.reply("Who exactly is %s?" % (rec, )) return if receiver.smsmode == "Retard": message.reply( "I refuse to talk to that incompetent retard. Check %s's mydef comment and use !phone show to try sending it using your own phone." % (receiver.name, )) return if not (receiver.pubphone or user in receiver.phonefriends or user.is_admin()): message.reply( "%s's phone number is private or they have not chosen to share their number with you. No call made." % (receiver.name, )) return phone = self.prepare_phone_number(receiver.phone) if not phone or len(phone) <= 7: message.reply( "%s has no phone number or their phone number is too short to be valid (under 6 digits). No call made." % (receiver.name, )) return client = Client(Config.get("Twilio", "sid"), Config.get("Twilio", "auth_token")) if Config.getboolean("Twilio", "warn"): url="http://twimlets.com/echo?Twiml=%3CResponse%3E%3CSay%20voice%3D%22alice%22%20language%3D%22en-GB%22%20%3EHello.%20This%20is%20" +\ Config.get("Connection", "nick") + ".%20Stop%20wasting%20our%20credit!%3C%2FSay%3E%3CHangup%2F%3E%3C%2FResponse%3E&", else: url = "http://twimlets.com/echo?Twiml=%3CResponse%3E%3CHangup%2F%3E%3C%2FResponse%3E&", tw = client.api.account.calls.create( to=phone, from_=Config.get("Twilio", "number"), url=url, timeout=Config.getint("Twilio", "timeout")) if tw.sid: message.reply("Successfully called %s." % (receiver.name)) else: message.reply("Error: Failed to get call ID from Twilio server.")
def base_context(request): context = {"name" : Config.get("Alliance", "name"), "slogan" : Config.get("Alliance", "name"), "tick" : Updates.current_tick(), "update" : Updates.load(), "graphs" : Config.get("Misc", "graphing") != "disabled", } if getattr(request, "user", None) is not None: context["user"] = request.user context["menu"] = menu.generate(request.user) if getattr(request, "session", None) is not None: slogan, count = Slogan.search("") if slogan is not None: context["slogan"] = str(slogan) if Config.has_section("FluxBB") and Config.getboolean("FluxBB", "enabled"): context["fluxurl"] = "<br><br><a href=\"%s\">Forum</a>" % (Config.get("FluxBB", "url")) if Config.getboolean("Arthur", "showdumps"): if context.has_key("fluxurl"): context["dumpurl"] = " " else: context["dumpurl"] = "<br><br>" context["dumpurl"] += "<a href=\"/dumps/\">Dumps</a>" return context
def base_context(request): context = {"name" : Config.get("Alliance", "name"), "slogan" : Config.get("Alliance", "name"), "tick" : Updates.current_tick(), "update" : Updates.load(), "graphs" : Config.get("Misc", "graphing") != "disabled", } if getattr(request, "user", None) is not None: context["user"] = request.user context["menu"] = menu.generate(request.user) if getattr(request, "session", None) is not None: slogan, count = Slogan.search("") if slogan is not None: context["slogan"] = str(slogan) if Config.has_section("FluxBB") and Config.getboolean("FluxBB", "enabled"): context["fluxurl"] = "<br><br><a href=\"%s\">Forum</a>" % (Config.get("FluxBB", "url")) if Config.getboolean("Arthur", "showdumps"): if context["fluxurl"]: context["dumpurl"] = " " else: context["dumpurl"] = "<br><br>" context["dumpurl"] += "<a href=\"/dumps/\">Dumps</a>" return context
def execute(self, message, user, params): planet = Planet.load(*params.group(1, 3, 5)) if planet is None: message.reply("No planet with coords %s:%s:%s found" % params.group(1, 3, 5)) return notice = "DEFCALL: %s wants %s to %s:%s:%s eta %s" % ( user.name, params.group(7), params.group(1), params.group(3), params.group(5), params.group(6)) if Config.getboolean("Misc", "globaldef"): push("broadcast", notice="!#!" + notice.replace(" ", "!#!")) else: message.notice(notice, Config.get("Channels", "home"))
def robocop(self, message, scantype, pa_id, x, y, z, names, scanner, reqs, old=False): nicks = [] reply = "Old " if old else "" reply += "%s on %s:%s:%s " % (PA.get(scantype,"name"),x,y,z,) if ("showscanner" in Config.options("Misc") and Config.getboolean("Misc", "showscanner") and scanner != 'None'): reply+= "from %s " % (User.load(id=scanner).name) if User.load(id=scanner) not in (None, 'None') else "" reply += Config.get("URL","viewscan") % (pa_id,) if old: reply += " !request cancel %s if this is suitable." % (reqs) for name in names.split(","): user = User.load(name) for nick in CUT.get_user_nicks(name): nicks.append(nick) message.privmsg(self.url(reply, user), nick) if not old: reply = "[-%s] %s on %s:%s:%s " % (reqs,PA.get(scantype,"name"),x,y,z,) reply+= "delivered to: " reply+= ", ".join(nicks) if not Config.getboolean("Misc", "anonscans") else "Anon" if Config.getboolean("Misc", "showurls"): reply += " (%s)" % (Config.get("URL","viewscan") % (pa_id,)) from Hooks.scans.request import request message.privmsg(reply, request().scanchan())
def get_user(self, name, channel, pnick=None, pnickf=None): # Regular user check if (pnick is None) and (pnickf is None): # This shouldn't happen return None nick = self.Nicks.get(name) if (nick and nick.puser) is not None: # They already have a user associated pnick = nick.puser elif pnickf is not None: # Call the pnick function, might raise PNickParseError try: pnick = pnickf() except PNickParseError: return None user = User.load(name=pnick) if user is None and Config.getboolean("Misc", "autoreg"): if nick and not nick.puser: if "galmate" in Config.options("Access"): access = Config.getint("Access", "galmate") else: access = 0 user = User.load(name=pnick, active=False) if user is None: user = User(name=pnick, access=access) session.add(user) else: user.active = True user.access = access session.commit() if user is None: return None if (nick is not None) and self.mode_is("rapid", "join"): if self.Pusers.get(user.name) is None: # Add the user to the tracker self.Pusers[user.name] = Puser(user.name) if nick.puser is None: # Associate the user and nick nick.puser = user.name self.Pusers[user.name].nicks.add(nick.name) # Return the SQLA User return user
def robocop(self, message, scantype, pa_id, x, y, z, names): nicks = [] reply = "%s on %s:%s:%s " % (PA.get(scantype,"name"),x,y,z,) reply+= Config.get("URL","viewscan") % (pa_id,) for name in names.split(","): user = User.load(name) for nick in CUT.get_user_nicks(name): nicks.append(nick) message.privmsg(self.url(reply, user), nick) reply = "%s on %s:%s:%s " % (PA.get(scantype,"name"),x,y,z,) reply+= "delivered to: " reply+= ", ".join(nicks) if not Config.getboolean("Misc", "anonscans") else "Anon" from Hooks.scans.request import request message.privmsg(reply, request().scanchan())
def execute(self, request, user, message=None): tick = Updates.current_tick() Q = session.query(Request) Q = Q.filter(Request.user == user) Q = Q.filter(Request.tick > tick - 5) Q = Q.filter(Request.active == True) Q = Q.order_by(asc(Request.id)) mine = Q.all() Q = session.query(Request) Q = Q.filter(Request.tick > tick - 5) Q = Q.filter(Request.active == True) Q = Q.order_by(asc(Request.id)) everyone = Q.all() return render("scans/requests.tpl", request, anonscans=Config.getboolean("Misc", "anonscans"), types=Request._requestable, mine=mine, everyone=everyone, message=message)
def parse_N(self, scan_id, scan, page): #incoming fleets #<td class=left valign=top>Incoming</td><td valign=top>851</td><td class=left valign=top>We have detected an open jumpgate from Tertiary, located at 18:5:11. The fleet will approach our system in tick 855 and appears to have roughly 95 ships.</td> for m in re.finditer( '<td class="left" valign="top">Incoming</td><td valign="top">(\d+)</td><td class="left" valign="top">We have detected an open jumpgate from ([^<]+), located at <a[^>]+>(\d+):(\d+):(\d+)</a>. The fleet will approach our system in tick (\d+) and appears to have (\d+) visible ships.</td>', page): fleetscan = FleetScan() newstick = m.group(1) fleetname = m.group(2) originx = m.group(3) originy = m.group(4) originz = m.group(5) arrivaltick = m.group(6) numships = m.group(7) fleetscan.mission = "Unknown" fleetscan.fleet_name = fleetname fleetscan.launch_tick = newstick fleetscan.landing_tick = int(arrivaltick) fleetscan.fleet_size = numships owner = PlanetHistory.load_planet( originx, originy, originz, newstick, closest=not Config.getboolean("Misc", "catchup")) if owner is None: continue fleetscan.owner = owner fleetscan.target = scan.planet fleetscan.in_cluster = fleetscan.owner.x == fleetscan.target.x fleetscan.in_galaxy = fleetscan.in_cluster and fleetscan.owner.y == fleetscan.target.y try: scan.fleets.append(fleetscan) session.commit() except Exception, e: session.rollback() scanlog("Exception in news: %s" % (str(e), ), traceback=True) continue scanlog('Incoming: ' + newstick + ':' + fleetname + '-' + originx + ':' + originy + ':' + originz + '-' + arrivaltick + '|' + numships)
def join(message): # Someone is joining a channel if message.get_nick() != Merlin.nick: # Someone is joining a channel we're in try: u = User.load(name=message.get_pnick()) if u is None: return tells = u.newtells for tell in tells: if Config.getboolean("Misc", "tellmsg"): message.privmsg("Message from %s: %s" % (tell.sender.name, tell.message), message.get_nick()) else: message.notice("Message from %s: %s" % (tell.sender.name, tell.message), message.get_nick()) tell.read = True session.commit() except PNickParseError: return
def join(message): # Someone is joining a channel if message.get_nick() != Merlin.nick: # Someone is joining a channel we're in try: u = User.load(name=message.get_pnick()) if u is None: return tells = u.newtells for tell in tells: if Config.getboolean("Misc", "tellmsg"): message.privmsg( "Message from %s: %s" % (tell.sender.name, tell.message), message.get_nick()) else: message.notice( "Message from %s: %s" % (tell.sender.name, tell.message), message.get_nick()) tell.read = True session.commit() except PNickParseError: return
originx = m.group(3) originy = m.group(4) originz = m.group(5) arrivaltick = m.group(6) fleetscan.mission = "Attack" fleetscan.fleet_name = fleetname fleetscan.launch_tick = newstick fleetscan.landing_tick = arrivaltick target = PlanetHistory.load_planet( originx, originy, originz, newstick, closest=not Config.getboolean("Misc", "catchup")) if target is None: continue fleetscan.owner = scan.planet fleetscan.target = target fleetscan.in_cluster = fleetscan.owner.x == fleetscan.target.x fleetscan.in_galaxy = fleetscan.in_cluster and fleetscan.owner.y == fleetscan.target.y try: scan.fleets.append(fleetscan) session.commit() except Exception, e: session.rollback() scanlog("Exception in news: %s" % (str(e), ), traceback=True) continue
session.execute(text("INSERT INTO %scookie_log (log_time,year,week,howmany,giver_id,receiver_id) SELECT log_time,year,week,howmany,giver_id,receiver_id FROM %s.%scookie_log;" % (prefix, round, old_prefix))) print " - smslog" session.execute(text("INSERT INTO %ssms_log (sender_id,receiver_id,phone,sms_text,mode) SELECT sender_id,receiver_id,phone,sms_text,mode FROM %s.%ssms_log;" % (prefix, round, old_prefix))) except DBAPIError, e: print "An error occurred during migration: %s" %(str(e),) session.rollback() print "Reverting to previous schema" """session.execute(text("DROP SCHEMA public CASCADE;")) session.execute(text("ALTER SCHEMA %s RENAME TO public;" % (round,)))""" session.commit() sys.exit() else: session.commit() finally: session.close() if Config.has_section("FluxBB") and Config.getboolean("FluxBB", "enabled"): tables = session.execute(text("SELECT table_name FROM information_schema.tables WHERE table_schema='%s' AND table_name LIKE '%s%%';" % (round, Config.get("FluxBB", "prefix")))) for t in tables: session.execute(text("CREATE TABLE %s AS SELECT * FROM %s.%s;" % (t[0], round, t[0]))) session.commit() session.close() if round == "temp": print "Deleting temporary schema" session.execute(text("DROP SCHEMA temp CASCADE;")) session.commit() session.close() print "Inserting ship stats" shipstats.main()
def execute(self, message, user, params): tick = Updates.current_tick() # Galaxy Scan if params.group(5) is None: # Access Control: # Uncomment this and change "group" to the lowest group that can request galscans. # if not user.is_group(): # message.alert("Insufficient access for galaxy scans.") # return galaxy = Galaxy.load(*params.group(1,3)) if galaxy is None: message.alert("No galaxy with coords %s:%s" % params.group(1,3)) return planets = galaxy.planets galscan = Config.has_option("Misc", "galscans") and Config.getboolean("Misc", "galscans") else: planet = Planet.load(*params.group(1,3,5)) if planet is None: message.alert("No planet with coords %s:%s:%s" % params.group(1,3,5)) return planets = [planet] galscan = False # Scan Quota if Config.has_section("ScanQuota"): opts = Config.options("ScanQuota") q = [] for o in opts: if int(o) >= user.access: q.append(int(o)) if q: ScanQuota = Config.getint("ScanQuota", str(min(q))) reqs = session.query(Request.id).filter(Request.requester_id == user.id).filter(Request.tick == tick).count() if (reqs + len(planets) * len(params.group(6).upper())) > ScanQuota: message.reply("This request will exceed your scan quota for this tick (%d scans remaining). " % (ScanQuota - reqs) +\ "Try searching with !planet, !dev, !unit, !news, !jgp, !au.") return dists = int(params.group(7) or 0) galdists = [] mergescans = (not galscan) and (Config.has_option("Misc", "maxscans") and len(planets)*len(params.group(6)) > Config.getint("Misc", "maxscans")) for planet in planets: if galscan or mergescans: galdists.append(planet.intel.dists if planet.intel else 0) if len(galdists) < len(planets): continue types = 0 for scantype in params.group(6).upper(): # Reject requests for incoming scans if not PA.getboolean(scantype, "request"): message.alert("%s scans cannot be requested." % (PA.get(scantype, "name"))) continue types += 1 if galscan or mergescans: # Request the scans for i in range(len(planets)): request = self.request(message, user, planets[i], scantype, galdists[i], galscan or mergescans) # Inform the requester if galscan and (message.get_chan() != self.scanchan()): message.reply("Requested a Galaxy %s Scan of %s:%s. !request cancel %s:%s to cancel the request." % (request.type, planet.x, planet.y, request.id-len(planets)+1, request.id)) # Check for existing scans scan = planet.scan(scantype) if scan and request.tick - scan.tick < PA.getint(scantype,"expire"): message.reply("%s Scan of %s:%s:%s is already available from %s ticks ago: %s. !request cancel %s if this is suitable." % ( scantype, planet.x, planet.y, planet.z, request.tick - scan.tick, scan.link, request.id,)) # Cancel requests with a 0-tick old scan, if required if (request.tick == scan.tick): req0age = Config.getint("Misc", "req0agej") if request.scantype == "J" else Config.getint("Misc", "req0age") if req0age == 1: Q = session.query(Request).filter(Request.tick == request.tick).filter(Request.planet_id == request.planet_id) Q = Q.filter(Request.scantype == request.scantype).filter(Request.requester_id == request.requester_id) if Q.count() == 1: request.active = False message.reply("Request %s cancelled due to an existing scan. If you really need a newer one, repeat your scan request." % (request.id)) message.privmsg("Cancelled scan request %s due to existing scan" % (request.id), self.scanchan()) elif req0age == 0: request.active = False message.reply("Request %s cancelled due to an existing scan." % (request.id)) message.privmsg("Cancelled scan request %s due to existing scan" % (request.id), self.scanchan()) # Tell the scanners requester = user.name if not Config.getboolean("Misc", "anonscans") else "Anon" if galscan: message.privmsg("[%s:%s] %s requested a Galaxy %s Scan of %s:%s Max Dists(i:%s%s) " % (request.id-len(planets)+1, request.id, requester, request.type, planet.x, planet.y, max(galdists), "/r:%s" % dists if dists > 0 else "") + Config.get("URL", "reqgscan") % (planet.x, planet.y) , self.scanchan()) else: request = self.request(message, user, planet, scantype, dists) if message.get_chan() != self.scanchan(): message.reply("Requested a %s Scan of %s:%s:%s. !request cancel %s to cancel the request." % (request.type, planet.x, planet.y, planet.z, request.id,)) # Check for existing scans scan = planet.scan(scantype) if scan and request.tick - scan.tick < PA.getint(scan.scantype,"expire"): message.reply("%s Scan of %s:%s:%s is already available from %s ticks ago: %s. !request cancel %s if this is suitable." % ( scantype, planet.x, planet.y, planet.z, request.tick - scan.tick, scan.link, request.id,)) # Cancel requests with a 0-tick old scan, if required if (request.tick == scan.tick): req0age = Config.getint("Misc", "req0agej") if request.scantype == "J" else Config.getint("Misc", "req0age") if req0age == 1: Q = session.query(Request).filter(Request.tick == request.tick).filter(Request.planet_id == request.planet_id) Q = Q.filter(Request.scantype == request.scantype).filter(Request.requester_id == request.requester_id) if Q.count() == 1: request.active = False message.reply("Request %s cancelled due to an existing scan. If you really need a newer one, repeat your scan request." % (request.id)) message.privmsg("Cancelled scan request %s due to existing scan" % (request.id), self.scanchan()) elif req0age == 0: request.active = False message.reply("Request %s cancelled due to an existing scan." % (request.id)) message.privmsg("Cancelled scan request %s due to existing scan" % (request.id), self.scanchan()) if mergescans: message.reply("Requested %d scans. !request cancel %s:%s to cancel the request." % (len(planets) * types, request.id-len(planets)*types+1, request.id)) message.privmsg("[%s:%s] %s requested %d scans (%s) Max Dists(i:%s%s). !request links for details " % (request.id-len(planets)*types+1, request.id, requester, len(planets)*types, params.group(6).upper(), max(galdists), "/r:%s" % dists if dists > 0 else ""), self.scanchan()) session.commit()
def robocop(self, message, etype, uname="Unknown", tick=0, x=0, y=0, z=0, name="", eta=0, size=0, res=0, cons=0): notice = "" email = "" if name[:3] == "!#!": name = " ".join(name[3:].split("!#!")) user = User.load(uname) if user is None: errorlog("Defcall: Invalid user in email. Idiot.") uname = "%s (whoever that is??)" % (uname) ucoords = "x:x:x" addr = Config.get("imap", "bounce") email = "Bad username in notifications: %s\n\nOriginal notification:\n\n\n" % (uname) else: uname = "%s%s" % (user.name, ("(%s)" % (user.alias)) if user.alias else "") if user.planet: ucoords = "%d:%d:%d" % (user.planet.x, user.planet.y, user.planet.z) else: ucoords = "idiot" addr = user.email if etype != "fin": p = Planet.load(x,y,z) if p is None: errorlog("Defcall: Invalid planet in email. Probably an exile.") if etype == "new": # Message to DC channel / main channel. Request scans. if p is None: arace = "??" aally = "Unknown" else: arace = p.race i = p.intel if i and i.alliance: aally = i.alliance.name else: aally = "Unknown" notice = "DEFCALL: %s (%s) has incoming eta %s(%s) from %s:%s:%s (%s, %s) - Fleet: %s Visible Ships: %s" % (uname, ucoords, eta, int(eta)-int(tick), x, y, z, arace, aally, name, size) email += "Notification from Planetarion in tick %s\n\n" % (tick) +\ "Incoming Fleet %s from %s:%s:%s with %s visible ships expected to land in tick %s." % (name, x, y, z, size, eta) +\ "\n\nThis has been reported to the %s DCs." % (Config.get("Alliance", "name")) elif etype == "rec": # Message to DC channel *and* main channel notice = "RECALL: %s (%s) has had a recall: Fleet: %s from %s:%s:%s" % (uname, ucoords, name, x, y, z) email += "Notification from Planetarion in tick %s\n\n" % (tick) +\ "Incoming Fleet %s from %s:%s:%s has recalled." % (name, x, y, z) +\ "\n\nThis has been reported to %s." % (Config.get("Alliance", "name")) elif etype == "fin": # Nothing to see here. Move along. notice = "" what = "" if int(res): what = "research" if int(cons): what += " and construction" else: what = "construction" email += "Notification from Planetarion in tick %s\n\nAll %s has finished and none is queued." % (tick, what) else: return # Send email - pref? if notice: if Config.getboolean("Misc", "globaldef"): push("broadcast", notice="!#!"+notice.replace(" ","!#!")) else: if etype == "new" and Config.has_option("Channels", "def"): message.notice(notice, Config.get("Channels", "def")) else: message.notice(notice, Config.get("Channels", "home")) if email and addr: self.send_email("Relayed PA Notifications from tick %s" % (tick), email, addr) # Check for scans if etype == "new" and p and user: if Config.has_option("Misc", "autoscans"): scantypes = Config.get("Misc", "autoscans") else: scantypes = "A" scanage = (Config.getint("Misc", "scanage") or 2) for stype in scantypes: scan = p.scan(stype) if scan and (int(tick) - scan.tick <= scanage): return else: req = Request(target=p, scantype=stype, dists=0) user.requests.append(req) session.commit() push("request", request_id=req.id, mode="request")
def execute(self, message, user, params): tick = Updates.current_tick() # Galaxy Scan if params.group(5) is None: # Access Control: # Uncomment this and change "group" to the lowest group that can request galscans. # if not user.is_group(): # message.alert("Insufficient access for galaxy scans.") # return galaxy = Galaxy.load(*params.group(1, 3)) if galaxy is None: message.alert("No galaxy with coords %s:%s" % params.group(1, 3)) return planets = galaxy.planets galscan = Config.has_option("Misc", "galscans") and Config.getboolean("Misc", "galscans") else: planet = Planet.load(*params.group(1, 3, 5)) if planet is None: message.alert("No planet with coords %s:%s:%s" % params.group(1, 3, 5)) return planets = [planet] galscan = False # Scan Quota if Config.has_section("ScanQuota"): opts = Config.options("ScanQuota") q = [] for o in opts: if int(o) >= user.access: q.append(int(o)) if q: ScanQuota = Config.getint("ScanQuota", str(min(q))) reqs = ( session.query(Request.id) .filter(Request.requester_id == user.id) .filter(Request.tick == tick) .count() ) if (reqs + len(planets) * len(params.group(6).upper())) > ScanQuota: message.reply( "This request will exceed your scan quota for this tick (%d scans remaining). " % (ScanQuota - reqs) + "Try searching with !planet, !dev, !unit, !news, !jgp, !au." ) return dists = int(params.group(7) or 0) galdists = [] mergescans = (not galscan) and ( Config.has_option("Misc", "maxscans") and len(planets) * len(params.group(6)) > Config.getint("Misc", "maxscans") ) for planet in planets: if galscan or mergescans: galdists.append(planet.intel.dists if planet.intel else 0) if len(galdists) < len(planets): continue types = 0 for scantype in params.group(6).upper(): # Reject requests for incoming scans if not PA.getboolean(scantype, "request"): message.alert("%s scans cannot be requested." % (PA.get(scantype, "name"))) continue types += 1 if galscan or mergescans: # Request the scans for i in range(len(planets)): request = self.request(message, user, planets[i], scantype, galdists[i], galscan or mergescans) # Inform the requester if galscan and (message.get_chan() != self.scanchan()): message.reply( "Requested a Galaxy %s Scan of %s:%s. !request cancel %s:%s to cancel the request." % (request.type, planet.x, planet.y, request.id - len(planets) + 1, request.id) ) # Check for existing scans scan = planet.scan(scantype) if scan and request.tick - scan.tick < PA.getint(scantype, "expire"): message.reply( "%s Scan of %s:%s:%s is already available from %s ticks ago: %s. !request cancel %s if this is suitable." % (scantype, planet.x, planet.y, planet.z, request.tick - scan.tick, scan.link, request.id) ) # Cancel requests with a 0-tick old scan, if required if request.tick == scan.tick: req0age = ( Config.getint("Misc", "req0agej") if request.scantype == "J" else Config.getint("Misc", "req0age") ) if req0age == 1: Q = ( session.query(Request) .filter(Request.tick == request.tick) .filter(Request.planet_id == request.planet_id) ) Q = Q.filter(Request.scantype == request.scantype).filter( Request.requester_id == request.requester_id ) if Q.count() == 1: request.active = False message.reply( "Request %s cancelled due to an existing scan. If you really need a newer one, repeat your scan request." % (request.id) ) message.privmsg( "Cancelled scan request %s due to existing scan" % (request.id), self.scanchan() ) elif req0age == 0: request.active = False message.reply("Request %s cancelled due to an existing scan." % (request.id)) message.privmsg( "Cancelled scan request %s due to existing scan" % (request.id), self.scanchan() ) # Tell the scanners requester = user.name if not Config.getboolean("Misc", "anonscans") else "Anon" if galscan: message.privmsg( "[%s:%s] %s requested a Galaxy %s Scan of %s:%s Max Dists(i:%s%s) " % ( request.id - len(planets) + 1, request.id, requester, request.type, planet.x, planet.y, max(galdists), "/r:%s" % dists if dists > 0 else "", ) + Config.get("URL", "reqgscan") % (planet.x, planet.y), self.scanchan(), ) else: request = self.request(message, user, planet, scantype, dists) if message.get_chan() != self.scanchan(): message.reply( "Requested a %s Scan of %s:%s:%s. !request cancel %s to cancel the request." % (request.type, planet.x, planet.y, planet.z, request.id) ) # Check for existing scans scan = planet.scan(scantype) if scan and request.tick - scan.tick < PA.getint(scan.scantype, "expire"): message.reply( "%s Scan of %s:%s:%s is already available from %s ticks ago: %s. !request cancel %s if this is suitable." % (scantype, planet.x, planet.y, planet.z, request.tick - scan.tick, scan.link, request.id) ) # Cancel requests with a 0-tick old scan, if required if request.tick == scan.tick: req0age = ( Config.getint("Misc", "req0agej") if request.scantype == "J" else Config.getint("Misc", "req0age") ) if req0age == 1: Q = ( session.query(Request) .filter(Request.tick == request.tick) .filter(Request.planet_id == request.planet_id) ) Q = Q.filter(Request.scantype == request.scantype).filter( Request.requester_id == request.requester_id ) if Q.count() == 1: request.active = False message.reply( "Request %s cancelled due to an existing scan. If you really need a newer one, repeat your scan request." % (request.id) ) message.privmsg( "Cancelled scan request %s due to existing scan" % (request.id), self.scanchan() ) elif req0age == 0: request.active = False message.reply("Request %s cancelled due to an existing scan." % (request.id)) message.privmsg( "Cancelled scan request %s due to existing scan" % (request.id), self.scanchan() ) if mergescans: message.reply( "Requested %d scans. !request cancel %s:%s to cancel the request." % (len(planets) * types, request.id - len(planets) * types + 1, request.id) ) message.privmsg( "[%s:%s] %s requested %d scans (%s) Max Dists(i:%s%s). !request links for details " % ( request.id - len(planets) * types + 1, request.id, requester, len(planets) * types, params.group(6).upper(), max(galdists), "/r:%s" % dists if dists > 0 else "", ), self.scanchan(), ) session.commit()
for m in re.finditer('<td class="left" valign="top">Launch</td><td valign="top">(\d+)</td><td class="left" valign="top">The ([^,]+) fleet has been launched, heading for <a[^>]+>(\d+):(\d+):(\d+)</a>, on a mission to Attack. Arrival tick: (\d+)</td>', page): fleetscan = FleetScan() newstick = m.group(1) fleetname = m.group(2) originx = m.group(3) originy = m.group(4) originz = m.group(5) arrivaltick = m.group(6) fleetscan.mission = "Attack" fleetscan.fleet_name = fleetname fleetscan.launch_tick = newstick fleetscan.landing_tick = arrivaltick target = PlanetHistory.load_planet(originx,originy,originz,newstick,closest=not Config.getboolean("Misc", "catchup")) if target is None: continue fleetscan.owner = scan.planet fleetscan.target = target fleetscan.in_cluster = fleetscan.owner.x == fleetscan.target.x fleetscan.in_galaxy = fleetscan.in_cluster and fleetscan.owner.y == fleetscan.target.y try: scan.fleets.append(fleetscan) session.commit() except Exception, e: session.rollback() scanlog("Exception in news: %s"%(str(e),), traceback=True) continue
def parse_N(self, scan_id, scan, page): #incoming fleets #<td class=left valign=top>Incoming</td><td valign=top>851</td><td class=left valign=top>We have detected an open jumpgate from Tertiary, located at 18:5:11. The fleet will approach our system in tick 855 and appears to have roughly 95 ships.</td> for m in re.finditer('<td class="left" valign="top">Incoming</td><td valign="top">(\d+)</td><td class="left" valign="top">We have detected an open jumpgate from ([^<]+), located at <a[^>]+>(\d+):(\d+):(\d+)</a>. The fleet will approach our system in tick (\d+) and appears to have (\d+) visible ships.</td>', page): fleetscan = FleetScan() newstick = m.group(1) fleetname = m.group(2) originx = m.group(3) originy = m.group(4) originz = m.group(5) arrivaltick = m.group(6) numships = m.group(7) fleetscan.mission = "Unknown" fleetscan.fleet_name = fleetname fleetscan.launch_tick = newstick fleetscan.landing_tick = int(arrivaltick) fleetscan.fleet_size = numships owner = PlanetHistory.load_planet(originx,originy,originz,newstick,closest=not Config.getboolean("Misc", "catchup")) if owner is None: continue fleetscan.owner = owner fleetscan.target = scan.planet fleetscan.in_cluster = fleetscan.owner.x == fleetscan.target.x fleetscan.in_galaxy = fleetscan.in_cluster and fleetscan.owner.y == fleetscan.target.y try: scan.fleets.append(fleetscan) session.commit() except Exception, e: session.rollback() scanlog("Exception in news: %s"%(str(e),), traceback=True) continue scanlog('Incoming: ' + newstick + ':' + fleetname + '-' + originx + ':' + originy + ':' + originz + '-' + arrivaltick + '|' + numships)
text( "INSERT INTO %ssms_log (sender_id,receiver_id,phone,sms_text,mode) SELECT sender_id,receiver_id,phone,sms_text,mode FROM %s.%ssms_log;" % (prefix, round, old_prefix))) except DBAPIError, e: print "An error occurred during migration: %s" % (str(e), ) session.rollback() print "Reverting to previous schema" """session.execute(text("DROP SCHEMA public CASCADE;")) session.execute(text("ALTER SCHEMA %s RENAME TO public;" % (round,)))""" session.commit() sys.exit() else: session.commit() finally: session.close() if Config.has_section("FluxBB") and Config.getboolean("FluxBB", "enabled"): tables = session.execute( text( "SELECT table_name FROM information_schema.tables WHERE table_schema='%s' AND table_name LIKE '%s%%';" % (round, Config.get("FluxBB", "prefix")))) for t in tables: session.execute( text("CREATE TABLE %s AS SELECT * FROM %s.%s;" % (t[0], round, t[0]))) session.commit() session.close() if round == "temp": print "Deleting temporary schema" session.execute(text("DROP SCHEMA temp CASCADE;")) session.commit()
def robocop(self, message, etype, uname="Unknown", tick=0, x=0, y=0, z=0, name="", eta=0, size=0, res=0, cons=0): notice = "" email = "" if name[:3] == "!#!": name = " ".join(name[3:].split("!#!")) user = User.load(uname) if user is None: errorlog("Defcall: Invalid user in email. Idiot.") uname = "%s (whoever that is??)" % (uname) ucoords = "x:x:x" addr = Config.get("imap", "bounce") email = "Bad username in notifications: %s\n\nOriginal notification:\n\n\n" % ( uname) else: uname = "%s%s" % (user.name, ("(%s)" % (user.alias)) if user.alias else "") if user.planet: ucoords = "%d:%d:%d" % (user.planet.x, user.planet.y, user.planet.z) else: ucoords = "idiot" addr = user.email if etype != "fin": p = Planet.load(x, y, z) if p is None: errorlog( "Defcall: Invalid planet in email. Probably an exile.") if etype == "new": # Message to DC channel / main channel. Request scans. if p is None: arace = "??" aally = "Unknown" else: arace = p.race i = p.intel if i and i.alliance: aally = i.alliance.name else: aally = "Unknown" notice = "DEFCALL: %s (%s) has incoming eta %s(%s) from %s:%s:%s (%s, %s) - Fleet: %s Visible Ships: %s" % ( uname, ucoords, eta, int(eta) - int(tick), x, y, z, arace, aally, name, size) email += "Notification from Planetarion in tick %s\n\n" % (tick) +\ "Incoming Fleet %s from %s:%s:%s with %s visible ships expected to land in tick %s." % (name, x, y, z, size, eta) +\ "\n\nThis has been reported to the %s DCs." % (Config.get("Alliance", "name")) elif etype == "rec": # Message to DC channel *and* main channel notice = "RECALL: %s (%s) has had a recall: Fleet: %s from %s:%s:%s" % ( uname, ucoords, name, x, y, z) email += "Notification from Planetarion in tick %s\n\n" % (tick) +\ "Incoming Fleet %s from %s:%s:%s has recalled." % (name, x, y, z) +\ "\n\nThis has been reported to %s." % (Config.get("Alliance", "name")) elif etype == "fin": # Nothing to see here. Move along. notice = "" what = "" if int(res): what = "research" if int(cons): what += " and construction" else: what = "construction" email += "Notification from Planetarion in tick %s\n\nAll %s has finished and none is queued." % ( tick, what) else: return # Send email - pref? if notice: if Config.getboolean("Misc", "globaldef"): push("broadcast", notice="!#!" + notice.replace(" ", "!#!")) else: if etype == "new" and Config.has_option("Channels", "def"): message.notice(notice, Config.get("Channels", "def")) else: message.notice(notice, Config.get("Channels", "home")) if email and addr: self.send_email("Relayed PA Notifications from tick %s" % (tick), email, addr) # Check for scans if etype == "new" and p and user: if Config.has_option("Misc", "autoscans"): scantypes = Config.get("Misc", "autoscans") else: scantypes = "A" scanage = (Config.getint("Misc", "scanage") or 2) for stype in scantypes: scan = p.scan(stype) if scan and (int(tick) - scan.tick <= scanage): return else: req = Request(target=p, scantype=stype, dists=0) user.requests.append(req) session.commit() push("request", request_id=req.id, mode="request")
def robonotify(self, header, body): if Config.getboolean("imap", "singleaddr"): uname_re = "%s\+(.+)@.+" % Config.get( "imap", "user").split("@")[0].lower() try: uname = re.findall(uname_re, header['To'].lower())[0] except IndexError: uname = "Unknown" else: uname_re = "<?(.+)@.+" uname = re.findall(uname_re, header['To'].lower())[0] dsuff = Config.get("imap", "defsuffix") if dsuff: if uname[-len(dsuff):] == dsuff: uname = uname[:-len(dsuff)] else: self.forwardMail(uname, header, body) return # Check the email "From" header says it's from Planetarion. if not "@planetarion.com" in header['From'].lower(): self.forwardMail(uname, header, body) return # Get the tick number (if this is a notification email). try: tick = re.findall("events in tick (\d+)", body)[0] except IndexError: self.forwardMail(uname, header, body) return # Check for the main notificatino types newfleets = re.findall("We have detected an open jumpgate from (.+), located at (\d{1,2}):(\d{1,2}):(\d{1,2}). ".replace(" ","\s+") +\ "The fleet will approach our system in tick (\d+) and appears to have (\d+) visible ships.".replace(" ","\s+"), body) recalls = re.findall( "The (.+) fleet from (\d{1,2}):(\d{1,2}):(\d{1,2}) has been recalled." .replace(" ", "\s+"), body) cons = len( re.findall( "Our construction team reports that .+ has been finished". replace(" ", "\s+"), body)) res = len( re.findall( "Our scientists report that .+ has been finished".replace( " ", "\s+"), body)) # Wrap it up in a bow for line in newfleets: push("defcall", etype="new", uname=uname, tick=tick, name=("!#!" + line[0].replace(" ", "!#!")), x=line[1], y=line[2], z=line[3], eta=line[4], size=line[5]) for line in recalls: push("defcall", etype="rec", uname=uname, tick=tick, name=("!#!" + line[0].replace(" ", "!#!")), x=line[1], y=line[2], z=line[3]) if res + cons > 0: push("defcall", etype="fin", uname=uname, tick=tick, res=res, cons=cons) if len(newfleets) + len(recalls) + cons + res == 0: self.forwardMail(uname, header, body)
class Idler(threading.Thread): if Config.getboolean("imap", "ssl"): imap = imaplib2.IMAP4_SSL(Config.get("imap", "host")) else: imap = imaplib2.IMAP4(Config.get("imap", "host")) stopWaitingEvent = threading.Event() #Now, this stopWaitingEvent thing -- it really does make the whole thing work. Basically, #it holds a boolean value which is set and cleared using, oddly enough, the methods set() and #clear(). But, the good thing about it is that it has another method, wait(), which holds #execution until it has been set(). I cannot thank threading.Event() enough, I really couldn't #have done it without you! knownAboutMail = [] # will be a list of IDs of messages in the inbox killNow = False # stops execution of thread to allow propper closing of conns. """ Initialise (sorry, I'm from the UK) everything to get ready for PUSHed mail. """ def __init__(self, GMailUsername, GMailPassword): os.system('clear') debugMsg('DEBUG is ENABLED') debugMsg('__init__() entered') try: #establish connection to IMAP Server self.imap.LOGIN(GMailUsername, GMailPassword) self.imap.SELECT("INBOX") #get the IDs of all messages in the inbox and put in knowAboutMail typ, data = self.imap.SEARCH(None, 'ALL') self.knownAboutMail = data[0].split() #now run the inherited __init__ method to create thread threading.Thread.__init__(self) except: #Uh Oh, something went wrong print 'ERROR: IMAP Issue. It could be one (or more) of the following:' print '- The imaplib2.py file needs to be in the same directory as this file' print '- You\'re not connected to the internet' print '- Google\'s mail server(s) is/are down' print '- Your username and/or password is incorrect' sys.exit(1) debugMsg('__init__() exited') """ The method invoked when the thread id start()ed. Enter a loop executing waitForServer() untill kill()ed. waitForServer() can, and should, be continuously executed to be alerted of new mail. """ def run(self): debugMsg('run() entered') #loop until killNow is set by kill() method while not self.killNow: self.waitForServer() debugMsg('run() exited') """ Relay email contents to merlin via robocop. """ def robonotify(self, header, body): # Check for correct "From" address? uname = re.findall("(.+)@.+", header['To'])[0].split()[1] dsuff = Config.get("imap", "defsuffix") if dsuff: if uname[-len(dsuff):] == dsuff: uname = uname[:-len(dsuff)] else: self.forwardMail(uname, header, body) return tick = re.findall("events in tick (\d+)", body)[0] newfleets = re.findall("We have detected an open jumpgate from (.+), located at (\d{1,2}):(\d{1,2}):(\d{1,2}). " +\ "The fleet will approach our system in tick (\d+) and appears to have (\d+) visible ships.", body) recalls = re.findall( "The (.+) fleet from (\d{1,2}):(\d{1,2}):(\d{1,2}) has been recalled.", body) cons = len( re.findall( "Our construction team reports that .+ has been finished", body)) res = len( re.findall("Our scientists report that .+ has been finished", body)) # Wrap it up in a bow for line in newfleets: push("defcall", etype="new", uname=uname, tick=tick, name=line[0], x=line[1], y=line[2], z=line[3], eta=line[4], size=line[5]) for line in recalls: push("defcall", etype="rec", uname=uname, tick=tick, name=line[0], x=line[1], y=line[2], z=line[3]) if res + cons > 0: push("defcall", etype="fin", uname=uname, tick=tick, res=res, cons=cons) if len(newfleets) + len(recalls) + cons + res == 0: self.forwardMail(uname, header, body) """ Decide whether to forward mail. """ def forwardMail(self, uname, header, body): if not Config.getboolean("imap", "forwarding"): return body = "Original Message from %s\n\n" % (header['From']) + body user = User.load(uname) if user: addr = user.email else: addr = Config.get("imap", "bounce") body = "Bad username: %s\n\n" % (uname) + body if addr: self.send_email(header['Subject'], body, addr) """ Send an email using smtplib. """ def send_email(self, subject, message, addr): try: if (Config.get("smtp", "port") == "0"): smtp = SMTP("localhost") else: smtp = SMTP(Config.get("smtp", "host"), Config.get("smtp", "port")) if not ((Config.get("smtp", "host") == "localhost") or (Config.get("smtp", "host") == "127.0.0.1")): try: smtp.starttls() except SMTPException as e: raise SMSError("unable to shift connection into TLS: %s" % (str(e), )) try: smtp.login(Config.get("smtp", "user"), Config.get("smtp", "pass")) except SMTPException as e: raise SMSError("unable to authenticate: %s" % (str(e), )) try: smtp.sendmail( Config.get("smtp", "frommail"), addr, "To:%s\nFrom:%s\nSubject:%s\n%s\n" % (addr, "\"%s\" <%s>" % (Config.get("Alliance", "name"), Config.get("smtp", "frommail")), subject, message)) except SMTPSenderRefused as e: raise SMSError("sender refused: %s" % (str(e), )) except SMTPRecipientsRefused as e: raise SMSError("unable to send: %s" % (str(e), )) smtp.quit() except (socket.error, SSLError, SMTPException, SMSError) as e: return "Error sending message: %s" % (str(e), ) """ Name says it all really: get (just) the specified header fields from the server for the specified message ID. """ def getMessageHeaderFieldsById(self, id, fields_tuple): debugMsg('getMessageHeaderFieldsById() entered') #get the entire header typ, header = self.imap.FETCH(id, '(RFC822.HEADER)') #get individual lines headerlines = header[0][1].splitlines() #get the lines that start with the values in fields_tuple results = {} for field in fields_tuple: results[field] = '' for line in headerlines: if line.startswith(field): results[field] = line debugMsg('getMessageHeaderFieldsById() exited') return results #which is a dictionary containing the the requested fields """ The main def for displaying messages. It draws on getMessageHeaderFieldsById() and growlnotify() to do so. """ def showNewMailMessages(self): debugMsg('showNewMailMessages() entered') #get IDs of all UNSEEN messages typ, data = self.imap.SEARCH(None, 'UNSEEN') debugMsg('data - new mail IDs:') debugMsg(data, 0) for id in data[0].split(): if not id in self.knownAboutMail: #get From and Subject fields from header headerFields = self.getMessageHeaderFieldsById( id, ('From', 'To', 'Subject')) print self.imap.fetch(id, '(BODY[TEXT])')[1][0][1] debugMsg('headerFields dict. (from showNewMailMessage()):') debugMsg(headerFields, 0) #notify self.robonotify(headerFields, self.imap.fetch(id, '(BODY[TEXT])')[1][0][1]) #add this message to the list of known messages self.knownAboutMail.append(id) debugMsg('showNewMailMessages() exited') """ Called to stop the script. It stops the continuous while loop in run() and therefore stops the thread's execution. """ def kill(self): self.killNow = True # to stop while loop in run() self.timeout = True # keeps waitForServer() nice self.stopWaitingEvent.set( ) # to let wait() to return and let execution continue """ This is the block of code called by the run() method of the therad. It is what does all the waiting for new mail (well, and timeouts). """ def waitForServer(self): debugMsg('waitForServer() entered') #init self.newMail = False self.timeout = False self.IDLEArgs = '' self.stopWaitingEvent.clear() def _IDLECallback(args): self.IDLEArgs = args self.stopWaitingEvent.set() #_IDLECallack() is entered when the IMAP server responds to the IDLE command when new #mail is received. The self.stopWaitingEvent.set() allows the .wait() to return and #therefore the rest of waitForServer(). #attach callback function, and let server know it should tell us when new mail arrives self.imap.idle(timeout=60 * ServerTimeout, callback=_IDLECallback) #execution will stay here until either: # - a new message is received; or # - the timeout has happened # - we set the timout -- the RFC says the server has the right to forget about # us after 30 mins of inactivity (i.e. not communicating with server for 30 mins). # By sending the IDLE command every 29 mins, we won't be forgotten. # - Alternatively, the kill() method has been invoked. self.stopWaitingEvent.wait() #self.IDLEArgs has now been filled (if not kill()ed) if not self.killNow: # skips a chunk of code to sys.exit() more quickly. try: b = bool( self.IDLEArgs[0][1][0] == ('IDLE terminated (Success)')) except TypeError: b = False self.timeout = True if b: # This (above) is sent when either: there has been a timeout (server sends); or, there # is new mail. We have to check manually to see if there is new mail. typ, data = self.imap.SEARCH( None, 'UNSEEN') # like before, get UNSEEN message IDs debugMsg('Data: ') debugMsg(data, 0) #see if each ID is new, and, if it is, make newMail True for id in data[0].split(): if not id in self.knownAboutMail: self.newMail = self.newMail or True else: self.timeout = True # gets executed if there are UNSEEN messages that we have been notified of, # but we haven't yet read. In this case, it response was just a timeout. if data[0] == '': # no IDs, so it was a timeout (but no notified but UNSEEN mail) self.timeout = True #now there has either been a timeout or a new message -- Do something... if self.newMail: debugMsg('INFO: New Mail Received') self.showNewMailMessages() elif self.timeout: debugMsg('INFO: A Timeout Occurred') debugMsg('waitForServer() exited')