def record_account_creation_dates(self): # Build list of unrecorded members members = [] for member in self.guild.members: if checker.has_role(member, Messaging.PLAYER_ROLE_NAME): members.append(member) members = zbot.db.get_unrecorded_members(members) # Map members with their account id players_info = wot_utils.get_players_info( [member.display_name for member in members], self.app_id ) members_account_ids, members_account_data = {}, {} for player_name, account_id in players_info.items(): for member in members: result = utils.PLAYER_NAME_PATTERN.match(member.display_name) if result: display_name = result.group(1) if display_name.lower() == player_name.lower(): members_account_ids[member] = account_id members_account_data.setdefault(member, {})['display_name'] = display_name # Map members with their account creation date players_details = wot_utils.get_players_details( list(members_account_ids.values()), self.app_id ) for member, account_id in members_account_ids.items(): members_account_data[member].update(creation_date=players_details[account_id][0]) zbot.db.update_accounts_data(members_account_data)
async def check_contacts(self, context): await context.message.add_reaction(self.WORK_IN_PROGRESS_EMOJI) contacts_by_clan = {} for member in context.guild.members: if checker.has_role(member, Stats.CLAN_CONTACT_ROLE_NAME): clan_tag = member.display_name.split(' ')[-1] # Remove clan tag delimiters replacements = {(re.escape(char)): '' for char in ['[', ']']} pattern = re.compile('|'.join(replacements.keys())) clan_tag = pattern.sub( lambda m: replacements[re.escape(m.group(0))], clan_tag) contacts_by_clan.setdefault(clan_tag, []).append(member) contacts = set([ contact for contacts in contacts_by_clan.values() for contact in contacts ]) await self.check_contacts_clan_tag(context, contacts) await self.check_clans_single_contact(context, contacts_by_clan) await self.check_contacts_recruiting_permissions( context, contacts_by_clan, self.app_id) await context.message.remove_reaction(self.WORK_IN_PROGRESS_EMOJI, self.user) await context.message.add_reaction(self.WORK_DONE_EMOJI)
async def celebrate_account_anniversaries(self): # Check if not running above frequency today = utils.bot_tz_now() last_anniversaries_celebration = zbot.db.get_metadata('last_anniversaries_celebration') if last_anniversaries_celebration: last_anniversaries_celebration_localized = converter.to_utc(last_anniversaries_celebration) if last_anniversaries_celebration_localized.date() == converter.to_utc(today).date(): logger.debug(f"Prevented sending anniversaries celebration because running above defined frequency.") return # Get anniversary data self.record_account_creation_dates() account_anniversaries = zbot.db.get_anniversary_account_ids( today, self.MIN_ACCOUNT_CREATION_DATE ) member_anniversaries = {} for years, account_ids in account_anniversaries.items(): for account_id in account_ids: member = self.guild.get_member(account_id) if member and checker.has_role(member, Messaging.PLAYER_ROLE_NAME): member_anniversaries.setdefault(years, []).append(member) # Remove celebration emojis in names from previous anniversaries for member in self.guild.members: if self.CELEBRATION_EMOJI in member.display_name: try: await member.edit( nick=member.display_name.replace(self.CELEBRATION_EMOJI, '').rstrip() ) except (discord.Forbidden, discord.HTTPException): pass # Add celebration emojis for today's anniversaries for year, members in member_anniversaries.items(): for member in members: try: await member.edit( nick=member.display_name + " " + self.CELEBRATION_EMOJI * year ) except (discord.Forbidden, discord.HTTPException): pass # Announce anniversaries (after updating names to refresh the cache) celebration_channel = self.guild.get_channel(self.CELEBRATION_CHANNEL_ID) if member_anniversaries: await celebration_channel.send("**Voici les anniversaires du jour !** 🎂") for year in sorted(member_anniversaries.keys(), reverse=True): for member in member_anniversaries[year]: await celebration_channel.send( f" • {member.mention} fête ses **{year}** ans sur World of Tanks ! 🥳" ) zbot.db.update_metadata('last_anniversaries_celebration', today)
async def check_players(self, context, add_reaction=True): add_reaction and await context.message.add_reaction(self.WORK_IN_PROGRESS_EMOJI) members = [] for member in self.guild.members: if checker.has_role(member, self.PLAYER_ROLE_NAME): members.append(member) await self.check_players_matching_name(context, members, self.app_id) await self.check_players_unique_name(context, members) add_reaction and await context.message.remove_reaction(self.WORK_IN_PROGRESS_EMOJI, self.user) add_reaction and await context.message.add_reaction(self.WORK_DONE_EMOJI)
async def check_everyone_clan_tag(context, members): """Check that no member has an unauthorized clan tag.""" unauthorized_clan_tag_members = [] for member in members: if re.search(r'[ ]*[\[{].{2,5}[\]}][ ]*', member.display_name) and \ not checker.has_role(member, Stats.CLAN_CONTACT_ROLE_NAME): unauthorized_clan_tag_members.append(member) if unauthorized_clan_tag_members: for block in utils.make_message_blocks([ f"Le joueur {member.mention} arbore un tag de clan sans être contact de clan." for member in unauthorized_clan_tag_members ]): await context.send(block) else: await context.send("Aucun joueur n'arbore de tag de clan sans être contact de clan. :ok_hand: ") return unauthorized_clan_tag_members
async def check_contacts(self, context, add_reaction=True): add_reaction and await context.message.add_reaction(self.WORK_IN_PROGRESS_EMOJI) contacts, contacts_by_clan = [], {} for member in self.guild.members: if checker.has_role(member, Stats.CLAN_CONTACT_ROLE_NAME): contacts.append(member) result = utils.PLAYER_NAME_PATTERN.match(member.display_name) if result: # Malformed member names handled by check_players_matching_name clan_tag = result.group(3) if clan_tag: # Missing clan tag handled by check_contacts_clan_tag contacts_by_clan.setdefault(clan_tag, []).append(member) await self.check_contacts_clan_tag(context, contacts) await self.check_clans_single_contact(context, contacts_by_clan) await self.check_contacts_recruiting_permissions(context, contacts_by_clan, self.app_id) add_reaction and await context.message.remove_reaction(self.WORK_IN_PROGRESS_EMOJI, self.user) add_reaction and await context.message.add_reaction(self.WORK_DONE_EMOJI)
async def inspect_recruitment(self, context, member: typing.Union[discord.Member, str] = None, *, options=''): """Post the status of the recruitment announces monitoring.""" if isinstance(member, str): # Option mistakenly captured as member name options += f" {member}" member = None require_contact_role = not utils.is_option_enabled(options, 'all') recruitment_channel = self.guild.get_channel( self.RECRUITMENT_CHANNEL_ID) zbot.db.update_recruitment_announces( await recruitment_channel.history().flatten()) # Get the record of each author's last announce (deleted or not) author_last_announce_data = {} for announce_data in zbot.db.load_recruitment_announces_data( query={'author': member.id} if member else {}, order=[('time', -1)]): # Associate each author with his/her last announce data if announce_data['author'] not in author_last_announce_data: author_last_announce_data[announce_data['author']] = { 'last_announce_time': announce_data['time'], 'message_id': announce_data['_id'] } # Enhance announces data with additional information min_timespan = datetime.timedelta( # Apply a tolerance of 2 days for players interpreting the 30 days range as "one month". # This is a subtraction because the resulting value is the number of days to wait before posting again. days=self.MIN_RECRUITMENT_ANNOUNCE_TIMESPAN - self.RECRUITMENT_ANNOUNCE_TIMESPAN_TOLERANCE) today = utils.bot_tz_now() for author_id, announce_data in author_last_announce_data.items(): last_announce_time_localized = converter.to_utc( announce_data['last_announce_time']) next_announce_time_localized = last_announce_time_localized + min_timespan author_last_announce_data[author_id] = { 'last_announce_time': last_announce_time_localized, 'next_announce_time': next_announce_time_localized, 'is_time_elapsed': next_announce_time_localized <= today } # Bind the member to the announce data, filter, and order by date asc member_announce_data = { self.guild.get_member(author_id): _ for author_id, _ in author_last_announce_data.items() } filtered_member_announce_data = { author: _ for author, _ in member_announce_data.items() if author is not None # Still member of the server and not checker.has_any_mod_role(context, author, print_error=False ) # Ignore moderation messages and (not require_contact_role or checker.has_role(author, Stats.CLAN_CONTACT_ROLE_NAME)) } ordered_member_announce_data = sorted( filtered_member_announce_data.items(), key=lambda elem: elem[1]['last_announce_time']) # Post the status of announces data if ordered_member_announce_data: await context.send("Statut du suivi des annonces de recrutement :") for block in utils.make_message_blocks([ f"• {author.mention} : {converter.to_human_format(announce_data['last_announce_time'])} " + ("✅" if announce_data['is_time_elapsed'] else f"⏳ (→ {converter.to_human_format(announce_data['next_announce_time'])})" ) for author, announce_data in ordered_member_announce_data ]): await context.send(block) else: await context.send("Aucun suivi enregistré.")