async def hod(self, context, recheck=True, channel_name: str = None): channel = (await self.get_channel( context, channel_name)) if channel_name else context.message.channel await channel.send("Checking raidlogs, back in a bit!") await self.monitor_clans(self.allclans) message = "" for raid in Raid.select().where( Raid.name == "dreadsylvania", Raid.end == None, Raid.clan_name == "The Hogs of Destiny"): message = "__**HOD BANISH STATUS**__ \n" for location in ["forest", "village", "castle"]: for element in [("stinky", "stench"), ("spooky", "spooky"), ("sleazy", "sleaze"), ("hot", "hot"), ("cold", "cold")]: data = "{{\"element\": \"{}\", \"location\": \"{}\"}}".format( element[0], location) if not (location == "village" and element[1] in ["spooky", "stench"]): if not Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishElement, Log.data == data).exists(): message += "{} banish needed in {} {} \n".format( str(element[1]).capitalize(), str(location).capitalize(), banish_locations[(location, element[1])]) village_banishes = [ Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishElement, Log.data == "{\"element\": \"spooky\", \"location\": \"village\"}"). exists(), Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishElement, Log.data == "{\"element\": \"stinky\", \"location\": \"village\"}"). exists(), Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishType, Log.data == "{\"location\": \"village\", \"type\": \"ghosts\"}").count( ), Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishType, Log.data == "{\"location\": \"village\", \"type\": \"zombies\"}"). count() ] if village_banishes[0]: if village_banishes[2] < 2: message += "{} ghost banish{} still needed. \n".format( "One" if village_banishes[2] else "Two", "" if village_banishes[2] else "es") elif village_banishes[1]: if village_banishes[3] < 2: message += "{} zombie banish{} still needed. \n".format( "One" if village_banishes[3] else "Two", "" if village_banishes[3] else "es") elif village_banishes[2]: message += "{} banish needed in {} {} \n".format( "Spooky", "Village", banish_locations[("village", "spooky")]) if village_banishes[3] < 2: message += "One ghost banish still needed. \n" elif village_banishes[3]: message += "{} banish needed in {} {} \n".format( "Stench", "Village", banish_locations[("village", "stench")]) if village_banishes[3] < 2: message += "One zombie banish still needed. \n" else: message += "Couldn't tell if this is a spooky ghost or a stench zombie instance. \n" message += "Either banish spooky {} or stench {} from the village. \n".format( banish_locations[("village", "spooky")], banish_locations[("village", "stench")]) if message == "__**HOD BANISH STATUS**__ \n": message += "All banishes complete. \n" message += "(You may also want to run !clan hod)" if channel_name: await context.send("Sending summary to {}".format(channel.name)) await channel.send(message)
async def summary(self, context, recheck=True, send_message=True, channel_name: str = None, description: str = None): """ Post a summary of all the Dreadsylvania instances currently being monitored. :param channel_name: Channel to post the summary to. If not specified, the bot will respond to you in a PM :param description: Text to appear inline with the summary. :return: """ channel = (await self.get_channel( context, channel_name)) if channel_name else context.message.channel if recheck: await channel.send("Checking raidlogs, back in a bit!") await self.monitor_clans(self.clans) message = "__DREAD STATUS__\n" if description is not None: message += "{}\n\n".format(description) for raid in Raid.select().where( Raid.name == "dreadsylvania", Raid.end == None, Raid.clan_name << [x[0] for x in self.clans]): skip_clan = False for clan in excluded_clans: if raid.clan_id in clan: skip_clan = True if skip_clan is True: print("Skipping " + raid.clan_name) continue else: summary = json.loads(raid.summary) kills = {"forest": 1000, "village": 1000, "castle": 1000} for line in summary: m = kills_pattern.match(line.replace(",", "")) if m: kills[m.group(2).lower()] -= int(m.group(1)) extra = None if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadMachineFix).exists(): machine_uses = Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadMachineUse).count() left = 3 - machine_uses extra = " ({} skill{} left)".format( left, "" if left == 1 else "s") else: extra = " (needs capacitor)" message += "**{}**: {}/{}/{}{}\n".format( raid.clan_name, kills["forest"], kills["village"], kills["castle"], extra or "") message += "\n" if channel_name: await context.send("Sending summary to {}".format(channel.name)) if send_message: await channel.send(message) else: return message
async def clan(self, context, clanname, recheck=True, send_message=True, channel_name: str = None): channel = (await self.get_channel( context, channel_name)) if channel_name else context.message.channel tocheck = None for clan in self.allclans: if clanname.lower() in clan[0].lower() or clanname.lower( ) in clan[2]: tocheck = clan break if recheck: await channel.send("Checking raidlogs, back in a bit!") await self.monitor_clans([tocheck]) message = "" for raid in Raid.select().where(Raid.name == "dreadsylvania", Raid.end == None, Raid.clan_name == tocheck[0]): message = "__**STATUS UPDATE FOR {}**__ \n".format( tocheck[0].upper()) summary = json.loads(raid.summary) kills = {"forest": 1000, "village": 1000, "castle": 1000} for line in summary: m = kills_pattern.match(line.replace(",", "")) if m: kills[m.group(2).lower()] -= int(m.group(1)) message += "{}/{}/{} kills remaining\n\n".format( kills["forest"], kills["village"], kills["castle"]) message += "__FOREST__ \n" if kills["forest"]: if not Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadUnlock, Log.data == "{\"location\": \"attic of the cabin\"}").exists(): message += "**Cabin attic needs unlocking** \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadUnlock, Log.data == "{\"location\": \"fire watchtower\"}").exists(): message += "Watchtower open, you can grab freddies if you like \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"Dreadsylvanian auditor's badge\"}" ).exists(): message += "~~Auditor's badge claimed~~ \n" else: message += "Auditor's badge available (Cabin -> Basement -> Lockbox) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadBanishElement, Log.data == "{\"element\": \"spooky\", \"location\": \"forest\"}" ).exists(): message += "~~Intricate music box parts claimed~~ \n" else: message += "Intricate music box parts available (Cabin -> Attic -> Music Box as AT (also banishes spooky from forest)) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"blood kiwi\"}").exists(): message += "~~Blood kiwi claimed~~ \n" else: message += "Blood kiwi available (Tree, Root Around -> Look Up + Climb -> Stomp) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"chunk of moon-amber\"}").exists(): message += "~~Moon-amber claimed~~ \n" else: message += "Moon-amber available (Tree -> Climb -> Shiny Thing (requires muscle class)) \n" else: message += "~~Forest fully cleared~~ \n" message += "__VILLAGE__ \n" if kills["village"]: if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadUnlock, Log.data == "{\"location\": \"schoolhouse\"}").exists(): message += "Schoolhouse is open, go get your pencils! \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadUnlock, Log.data == "{\"location\": \"master suite\"}").exists(): message += "Master suite is open, grab some eau de mort? \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadHangee).exists(): message += "~~Hanging complete~~ \n" else: message += "Hanging available (Square, Gallows -> Stand on Trap Door + Gallows -> Pull Lever) \n" else: message += "~~Village fully cleared~~ \n" message += "__CASTLE__ \n" if kills["castle"]: if not Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadUnlock, Log.data == "{\"location\": \"lab\"}").exists(): message += "**Lab needs unlocking** \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadMachineFix).exists(): machine_uses = Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadMachineUse).count() left = 3 - machine_uses if left: message += "{} skill{} available.\n".format( left, "" if left == 1 else "s") else: message += "~~All skills claimed~~ \n" else: message += "Machine needs repairing (with skull capacitor) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"roast beast\"}").exists(): message += "~~Dreadful roast claimed~~ \n" else: message += "Dreadful roast available (Great Hall -> Dining Room -> Grab roast) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"wax banana\"}").exists(): message += "~~Wax banana claimed~~ \n" else: message += "Wax banana available (Great Hall -> Dining Room -> Levitate (requires myst class) \n" if Log.select().where( Log.raid == raid, Log.action == RaidAction.DreadGotItem, Log.data == "{\"item\": \"stinking agaric\"}").exists(): message += "~~Stinking agaricus claimed~~ \n" else: message += "Stinking agaricus available (Dungeons -> Guard Room -> Break off bits) \n" else: message += "~~Castle fully cleared~~ \n" if channel_name: await context.send("Sending summary to {}".format(channel.name)) if send_message: await channel.send(message) else: return message
async def skills(self, context, recheck=True, send_message=True, since: str = "2019-06-06", limit: int = None, channel_name: str = None): channel = await self.get_channel( context, channel_name) if channel_name else context.channel if recheck: await channel.send("Checking raidlogs, back in a bit!") await self.monitor_clans(self.clans) since = datetime.strptime( since, "%Y-%m-%d") if since is not None else datetime.now() - timedelta( days=365) if limit is None: limit = len(self.clans) * 3 elif limit == 0: limit = None Skills = Log.alias() skills_query = Skills.select(Skills.user_id, (fn.COUNT(Skills.id) + (fn.IFNULL(PriorActivity.skills, 0))).alias("skills"))\ .join_from(Skills, PriorActivity, JOIN.LEFT_OUTER, on=(Skills.user_id == PriorActivity.id))\ .join_from(Skills, Raid)\ .where(Skills.action == RaidAction.DreadMachineUse, Raid.start >= since, Raid.clan_name << [x[0] for x in self.clans])\ .group_by(Skills.user_id)\ .alias("sq") right_joined_skills_query = PriorActivity.select((PriorActivity.id).alias("user_id"), (fn.IFNULL(PriorActivity.skills, skills_query.c.skills)).alias("skills"))\ .join_from(PriorActivity, skills_query, JOIN.LEFT_OUTER, on=(skills_query.c.user_id == PriorActivity.id)) skills_query = skills_query | right_joined_skills_query #DIY FULL OUTER JOIN kills_query = Log.select(Log.user_id, Log.username.alias("Username"), (fn.SUM(Log.turns)+ (fn.IFNULL(PriorActivity.kills, 0))).alias("kills"))\ .join_from(Log, PriorActivity, JOIN.LEFT_OUTER, on=(Log.user_id == PriorActivity.id))\ .join_from(Log, Raid)\ .where(Log.action == RaidAction.Victory, Raid.name == "dreadsylvania", Raid.start >= since, Raid.clan_name in [x[0] for x in self.clans])\ .group_by(Log.user_id) rankings_query = Log.select(kills_query.c.username.alias("Username"), kills_query.c.kills.alias("Kills"), fn.IFNULL(skills_query.c.skills, 0).alias("Skills"), (kills_query.c.kills / (fn.IFNULL(skills_query.c.skills, 0) + 0.5)).alias("KillsPerSkill"))\ .join_from(Log, skills_query, JOIN.LEFT_OUTER, on=(Log.user_id == skills_query.c.user_id))\ .join_from(Log, kills_query, JOIN.LEFT_OUTER, on=(Log.user_id == kills_query.c.user_id))\ .group_by(kills_query.c.user_id)\ .order_by(SQL("KillsPerSkill").desc())\ rankings = [ x for x in [r for r in rankings_query.dicts()] if x["Username"] and not x["Username"].lower() in excluded_list ] table = tabulate(rankings, headers="keys") table = table[:1900] message = "__SKILL RANKINGS__ \n```\n{}\n```".format(table) if channel_name: await context.send("Sending skills to {}".format(channel.name)) if send_message: await channel.send(message) else: return message