def cd(client, tokens, message, server, profile, msg, help=None): """ Display when your cooldowns are expected to be done. Usage: • `rcd cd [<cooldown_types> [...<cooldown_types>]]` Example: • `rcd cd` • `rcd` • `rcd daily weekly` """ implicit_invocation = False if tokens[0] in CoolDown.COOLDOWN_MAP or re.match(r"<@!?(?P<user_id>\d+)>", tokens[0]): # allow implicit invocation of cd tokens, implicit_invocation = ["cd", *tokens], True nickname = message.author.name cooldown_filter = lambda x: True # show all filters by default if tokens[0] != "cd": return None if help and len(tokens) == 1: return {"msg": HelpMessage(cd.__doc__)} elif len(tokens) > 1: mentioned_profile = Profile.from_tag(tokens[1], client, server, message) if mentioned_profile: profile = mentioned_profile cd_args = set(tokens[2:]) nickname = profile.last_known_nickname else: cd_args = set(tokens[1:]) # means there are cooldown type arguments to filter on if cd_args: cooldown_filter = lambda x: x in cd_args profile_tz = pytz.timezone(profile.timezone) now = datetime.datetime.now(tz=datetime.timezone.utc) default = datetime.datetime(1790, 1, 1, tzinfo=datetime.timezone.utc) msg = "" cooldowns = { _cd[0]: _cd[1] for _cd in CoolDown.objects.filter(profile_id=profile.pk).order_by("after").values_list("type", "after") } all_cooldown_types = set([c[0] for c in CoolDown.COOLDOWN_TYPE_CHOICES]) if not profile.player_guild: all_cooldown_types = all_cooldown_types - {"guild"} else: cooldowns["guild"] = profile.player_guild.after if profile.player_guild.after else default selected_cooldown_types = sorted( filter(cooldown_filter, all_cooldown_types), key=lambda x: cooldowns[x] if x in cooldowns else default, ) for cooldown_type in selected_cooldown_types: after = cooldowns.get(cooldown_type, None) if after: if after > now: cooldown_after = cooldowns[cooldown_type].astimezone(profile_tz) msg += f":clock2: `{cooldown_type:12} {cooldown_after.strftime(profile.time_format):>20}`\n" else: msg += f":white_check_mark: `{cooldown_type:12} {'Ready!':>20}` \n" if not msg: msg = "Please use `rpg cd` or an EPIC RPG command to populate your cooldowns.\n" return {"msg": NormalMessage(msg, title=f"**{nickname}'s** Cooldowns ({profile.timezone})")}
def marry(client, tokens, message, server, profile, msg, help=None): """ # Marriage Help «Indicate that you are married to another player. Used to track `hunt together` results.» ## Usage • `rcd marry @<player>` ## Examples ``` rcd marry @your_friend ``` """ partner = Profile.from_tag(tokens[-1], client, server, message) if not partner: return HelpMessage(marry.__doc__, title="Marriage Help") if partner.pk == profile.pk: return NormalMessage( "I'm afraid I can't let you marry yourself... you'll ruin my statistics!", title=":(", footer="and marriage is only about statistics...", ) message = f"<@!{profile.pk}> and <@!{partner.pk}> got married!!?!? " message += random.choice( ["I give it like, 3 months, tops.", "What a time to be alive!", "It's a match made in heaven :heart_eyes:"] ) profile.update(partner=partner) partner.update(parner=profile) return NormalMessage(message, title="Witness the Newlyweds :wedding:!")
def stats(client, tokens, message, server, profile, msg, help=None): """ This command shows the output of {long} stats that the helper bot has managed to collect. Usage: • `rcd {long}|{short} [num_minutes] [@player]` Examples: • `rcd {long}` show your own {long} stats • `rcd {long} 3` for the last 3 minutes • `rcd {short} @player` show a player's {long} stats """ minutes, _all = None, False token_map = { "gambling": ("gambling", "g"), "g": ("gambling", "g"), "drops": ("drops", "dr"), "dr": ("drops", "dr"), "hunts": ("hunts", "hu"), "hu": ("hunts", "hu"), } if tokens[0] not in token_map: return None long, short = token_map[tokens[0]] if help: return {"msg": HelpMessage(stats.__doc__.format(long=long, short=short))} if len(tokens) > 1: mentioned_profile = Profile.from_tag(tokens[-1], client, server, message) if mentioned_profile: tokens = tokens[:-1] profile = mentioned_profile elif tokens[-1] == "all": _all = True tokens = tokens[:-1] if re.match(r"^([0-9\* ]+)$", tokens[-1]): min_tokens, prod = tokens[-1].replace(" ", "").split("*"), 1 minutes = functools.reduce(operator.mul, [int(t) for t in min_tokens], 1) if not mentioned_profile and not minutes and not _all: return { "msg": ErrorMessage( f"`rcd {' '.join(tokens)}` is not valid invocation of `rcd {long}`. " "Example usage: `rcd {short} 5 @player`", title="Stats Usage Error", ) } uid = None if _all else profile.uid name = server.name if _all else client.get_user(int(profile.uid)).name if long == "gambling": return { "msg": NormalMessage( "", fields=Gamble.objects.stats(uid, minutes, server.id), title=f"{name}'s Gambling Addiction" ) } elif long == "hunts": return { "msg": NormalMessage("", fields=Hunt.objects.hunt_stats(uid, minutes, server.id), title=f"{name}'s Carnage") } elif long == "drops": return { "msg": NormalMessage("", fields=Hunt.objects.drop_stats(uid, minutes, server.id), title=f"{name}'s Drops") }
def __init__(self, client, incoming, server=None): super().__init__(client, incoming, server) if not self.should_trigger: return if self.incoming.embeds: self.content = self.incoming.content self.embed = self.incoming.embeds[0] self.profile = Profile.from_embed_icon(self.client, self.server, self.incoming, self.embed)
def logs(client, tokens, message, server, profile, msg, help=None): """ # Logs Help Ask for the future log value of your current inventory! «You may need to know how many logs you will have in A10 before you can decide whether you should progress to the next area. This command will tell you how many logs you will have in area 10 based on your current inventory.» The command assumes you are in A5 if no area is provided. ## Usage • `rcd logs [a{n}=a5] [@player=@you]` ## Examples • `rcd logs` assuming that I am in A5, how many logs am I gonna have in A10? • `rcd logs a7` now that I am in A7, how many logs am I gonna have in A10? • `rcd logs @kevin` how many logs is Kevin gonna have in area A10? """ if help or not tokens: return {"msg": HelpMessage(logs.__doc__)} full_message, metadata = " ".join(tokens), {"area": 5} area_indicator = re.search(r" ([aA])?(\d{1,2})", full_message) if area_indicator: area = int(area_indicator.groups()[1]) start, end = area_indicator.span() tokens, metadata["area"] = tokenize(f"{full_message[0:start]}{full_message[end:]}"), area if not 1 <= metadata["area"] <= 15: return {"msg": ErrorMessage("Only areas 1-15 are valid!", title="Logs Error")} mentioned_profile = Profile.from_tag(tokens[-1], client, server, message) if mentioned_profile: profile, metadata["snoop"] = mentioned_profile, profile.uid open_sentinels = list(Sentinel.objects.filter(trigger=0, profile=profile, action="logs")) len(open_sentinels) == 0 and Sentinel.objects.create(trigger=0, profile=profile, action="logs", metadata=metadata) for sentinel in open_sentinels: sentinel.metadata.get("snoop", -1) == metadata.get("snoop", -1) and sentinel.update(metadata=metadata) _area = f'Area {metadata["area"]}' if metadata.get("snoop", None): return { "msg": NormalMessage( "Busybody, eh? Okay, I'll check next time they open their inventory.", title=f"Snoop Lawgs ({_area})" ) } return { "msg": NormalMessage( "Okay, the next time I see your inventory, I'll say how many logs you should have in Area 10.", title=f"Logs ({_area})", ) }
def _profile(client, tokens, message, server, profile, msg, help=None): """ #Profile Help «When called without any arguments, e.g. `rcd profile` this will display profile-related information. Otherwise, it will treat your input as a profile related sub-command.» ## Sub Commands • `timezone`, `tz`: Set the timezone information for your profile • `timeformat`, `tf`: Set the date and time formatting for your profile • `multiplier`, `mp`: Reduce or increase your cooldown durations • `notify`, `n`: Set which cooldowns the bot will notify you for • `marry`: Indicate that you are married to another player ## Examples • `rcd profile` Displays your profile information • `rcd p tz <timezone>` Sets your timezone to the provided timezone. • `rcd p on` Enables notifications for your profile. • `rcd p notify hunt on` Turns on hunt notifications for your profile. • `rcd p hunt on` Turns on hunt notifications for your profile. """ if help and len(tokens) == 1: return {"msg": HelpMessage(_profile.__doc__)} elif len(tokens) > 1: # allow other commands to be namespaced by profile if that's how the user calls it if tokens[1] in { *timezone.entry_tokens, *timeformat.entry_tokens, *multiplier.entry_tokens, *notify.entry_tokens, *marry.entry_tokens, }: return {"tokens": tokens[1:]} maybe_profile = Profile.from_tag(tokens[1], client, server, message) if not maybe_profile: return {"error": 1} profile = maybe_profile notifications = "" for k, v in model_to_dict(profile).items(): if isinstance(v, bool): notifications += f"{':ballot_box_with_check:' if v else ':x:'} `{k:25}`\n" return { "msg": NormalMessage( "", fields=( ("Timezone", f"`{profile.timezone}`"), ("Time Format", f"`{profile.time_format}`"), ("Cooldown Multiplier", f"`{profile.cooldown_multiplier}`"), ("Married", f"To <@!{profile.partner_id}>" if profile.partner_id else "Nope."), ("Notifications Enabled", notifications), ), ) }
def ban(client, tokens, message, server, profile, msg, help=None): """ # Ban Help Someone has been naughty... ## Usage • `rcd admin ban @player` • `rcd ban @player` • `rcd unban @player` • `rcd ban unban @player` """ naughty = Profile.from_tag(tokens[-1], client, server, message) if not naughty: return HelpMessage(ban.__doc__) banned = "unban" not in tokens naughty.update(banned="unban" not in tokens) if banned: return NormalMessage(f"Okay, <@!{naughty.pk}> can no longer use `rcd` commands.", title=f"Player Banned :(") return NormalMessage(f"Okay, <@!{naughty.pk}> can use `rcd` commands!", title=f"Player Un-Banned :)")
def wed(client, tokens, message, server, profile, msg, help=None): """ # Wedding Help «Ensure the blissful union of two players.» ## Usage • `rcd wed @player_one @player_two` • `rcd admin wed @player_one @player_two` """ if len(tokens) < 3: return HelpMessage(wed.__doc__) groom = Profile.from_tag(tokens[-2], client, server, message) if not groom: return HelpMessage(wed.__doc__) return marry( dict( client=client, message=message, tokens=["marry", tokens[-1]], server=server, profile=groom, msg=msg, ) )
def cd(client, tokens, message, server, profile, msg, help=None): """ # EPIC Helper Cooldowns Display when your cooldowns are expected to be done. ## Usage • `rcd cd [<cooldown_types>]` shows all cooldowns • `rcd rd [<cooldown_types>]` shows ready cooldowns ## Examples ``` rcd rcd cd rrd rcd rd rcd daily weekly ``` """ if help: return {"msg": HelpMessage(cd.__doc__)} if tokens[0] not in {"cd", "rd"}: # allow for implicit or default invocation of rcd tokens = ["cd", *tokens[1:]] if tokens[0] == "" else ["cd", *tokens] nickname = message.author.name cooldown_filter = lambda x: True # show all filters by default if len(tokens) > 1: mentioned_profile = Profile.from_tag(tokens[-1], client, server, message) if mentioned_profile: profile = mentioned_profile cd_args = set(tokens[2:]) nickname = profile.last_known_nickname else: cd_args = set(tokens[1:]) # means there are cooldown type arguments to filter on if cd_args: cooldown_filter = lambda x: x in cd_args profile_tz = pytz.timezone(profile.timezone) now = datetime.datetime.now(tz=datetime.timezone.utc) default = datetime.datetime(1790, 1, 1, tzinfo=datetime.timezone.utc) msg, warn = "", False cooldowns = { _cd[0]: _cd[1] for _cd in CoolDown.objects.filter(profile_id=profile.pk).order_by("after").values_list("type", "after") } all_cooldown_types = set([c[0] for c in CoolDown.COOLDOWN_TYPE_CHOICES]) if not profile.player_guild: all_cooldown_types = all_cooldown_types - {"guild"} else: warn = True if profile.player_guild.raid_dibbs else False cooldowns["guild"] = profile.player_guild.after if profile.player_guild.after else default selected_cooldown_types = sorted( filter(cooldown_filter, all_cooldown_types), key=lambda x: cooldowns[x] if x in cooldowns else default, ) for cooldown_type in selected_cooldown_types: after = cooldowns.get(cooldown_type, None) if not after or after <= now: icon = ":warning:" if warn and cooldown_type == "guild" else ":white_check_mark:" warning = " (dibbs) " if warn and cooldown_type == "guild" else "" msg += f"{icon} `{cooldown_type + warning:15} {'Ready!':>20}` \n" elif tokens[0] == "cd": # don't show if "rd" command cooldown_after = cooldowns[cooldown_type].astimezone(profile_tz) icon = ":warning:" if warn and cooldown_type == "guild" else ":clock2:" warning = " (dibbs) " if warn and cooldown_type == "guild" else "" msg += f"{icon} `{cooldown_type + warning:15} {cooldown_after.strftime(profile.time_format):>20}`\n" if not msg: msg = ( "All commands on cooldown! (You may need to use `rpg cd` to populate your cooldowns for the first time.)\n" ) event_info = "" active_events = Event.objects.active().values_list("event_name", flat=True) if active_events: event_info = ", ".join(active_events) + " event(s) currently active.\n" return NormalMessage( msg, title=f"**{nickname}'s** Cooldowns", footer=f"{event_info}Timezone is {profile.timezone}, change with rcd p tz.", )