async def update_pushboard(self, guild_id): guild_config = await self.get_guild_config(guild_id) if not guild_config.updates_toggle: return if not guild_config.pushboard: return sql = "SELECT DISTINCT clan_tag FROM clans WHERE guild_id = $1" fetch = await self.bot.pool.fetch(sql, guild_id) clans = await self.bot.coc.get_clans((n[0] for n in fetch)).flatten() players = [] for n in clans: players.extend(p for p in n.itermembers) sql = ("SELECT player_tag, current_trophies, current_attack_wins - starting_attack_wins AS attacks " "FROM players " "WHERE player_tag = ANY($1::TEXT[]) " "ORDER BY current_trophies " "LIMIT 100") fetch = await self.bot.pool.fetch(sql, [n.tag for n in players]) db_players = [DatabasePlayer(bot=self.bot, record=n) for n in fetch] players = {n.tag: n for n in players if n.tag in set(x.player_tag for x in db_players)} message_count = math.ceil(len(db_players) / 20) messages = await self.get_updates_messages(guild_id, number_of_msg=message_count) if not messages: return for i, v in enumerate(messages): player_data = db_players[i * 20: (i + 1) * 20] table = CLYTable() for x, y in enumerate(player_data): index = i * 20 + x if guild_config.pushboard_render == 2: table.add_row([index, y.current_trophies, players.get(y.player_tag, MockPlayer()).name]) else: table.add_row([index, y.current_trophies, y.attacks, players.get(y.player_tag, MockPlayer()).name]) fmt = table.render_option_2() if \ guild_config.pushboard_render == 2 else table.render_option_1() e = discord.Embed(color=self.bot.color, description=fmt, timestamp=datetime.utcnow()) e.set_author(name=guild_config.pushboard_title or "Trophy Push Leaderboard", icon_url=guild_config.icon_url or "https://cdn.discordapp.com/emojis/" "592028799768592405.png?v=1") e.set_footer(text="Last Updated") await v.edit(embed=e, content=None)
async def update_global_board(self): query = """SELECT player_tag, donations FROM players WHERE season_id=$1 ORDER BY donations DESC NULLS LAST LIMIT 100; """ fetch_top_players = await self.bot.pool.fetch( query, await self.bot.seasonconfig.get_season_id()) players = await self.bot.coc.get_players( (n[0] for n in fetch_top_players)).flatten() top_players = { n.tag: n for n in players if n.tag in set(x['player_tag'] for x in fetch_top_players) } messages = await self.get_board_messages(663683345108172830, number_of_msg=5) if not messages: return for i, v in enumerate(messages): player_data = fetch_top_players[i * 20:(i + 1) * 20] table = CLYTable() for x, y in enumerate(player_data): index = i * 20 + x table.add_row( [index, y[1], top_players.get(y['player_tag'], mock).name]) fmt = table.donationboard_2() e = discord.Embed(colour=self.bot.colour, description=fmt, timestamp=datetime.utcnow()) e.set_author(name="Global Donationboard", icon_url=self.bot.user.avatar_url) e.set_footer(text='Last Updated') await v.edit(embed=e, content=None)
async def remove_event(self, ctx, *, event_name: str = None): """Removes a currently running event. **Parameters** :key: The event name to remove. **Format** :information_source: `+remove event EVENT_NAME` **Example** :white_check_mark: `+remove event my special event` **Required Permissions** :warning: Manage Server """ if event_name: # Event name provided query = """DELETE FROM events WHERE guild_id = $1 AND event_name = $2 RETURNING id; """ fetch = await self.bot.pool.fetchrow(query, ctx.guild.id, event_name) if fetch: return await ctx.send(f"{event_name} has been removed.") # No event name provided or I didn't understand the name I was given query = """SELECT id, event_name, start FROM events WHERE guild_id = $1 ORDER BY start""" fetch = await self.bot.pool.fetch(query, ctx.guild.id) if len(fetch) == 0 or not fetch: return await ctx.send( "I have no events to remove. You should create one... then remove it." ) elif len(fetch) == 1: query = "DELETE FROM events WHERE id = $1" await ctx.db.execute(query, fetch[0]['id']) return await ctx.send(f"{fetch[0]['event_name']} has been removed." ) table = CLYTable() fmt = f"Events on {ctx.guild}:\n\n" reactions = [] counter = 0 for event in fetch: days_until = event['start'].date() - datetime.datetime.utcnow( ).date() table.add_row([counter, days_until.days, event['event_name']]) counter += 1 reactions.append(f"{counter}\N{combining enclosing keycap}") render = table.events_list() fmt += f'{render}\n\nPlease select the reaction that corresponds with the event you would ' \ f'like to remove.' e = discord.Embed(colour=self.bot.colour, description=fmt) msg = await ctx.send(embed=e) for r in reactions: await msg.add_reaction(r) def check(r, u): return str( r ) in reactions and u.id == ctx.author.id and r.message.id == msg.id try: r, u = await self.bot.wait_for('reaction_add', check=check, timeout=60.0) except asyncio.TimeoutError: await msg.clear_reactions() return await ctx.send( "We'll just hang on to all the events we have for now.") index = reactions.index(str(r)) query = "DELETE FROM events WHERE id = $1" await ctx.db.execute(query, fetch[index]['id']) await msg.delete() ctx.bot.utils.event_config.invalidate(ctx.bot.utils, ctx.guild.id) self.bot.dispatch('event_register') return await ctx.send(f"{fetch[index]['event_name']} has been removed." )
async def get_board_fmt(self, guild_id, season_id, board_type): board_config = await self.bot.utils.get_board_configs( guild_id, board_type) if not board_config: board_config = SlimDummyBoardConfig( board_type, 2, f"{board_type.capitalize()}Board", None, 'donations' if board_type == "donation" else "trophies") else: board_config = board_config[0] clans = await self.bot.get_clans(guild_id) players = [] for n in clans: players.extend(p for p in n.itermembers) top_players = await self.bot.donationboard.get_top_players( players, board_type, board_config.sort_by, False, season_id=season_id) if not top_players: e = discord.Embed(colour=self.bot.colour, title='No Data Found.') return [e] players = { n.tag: n for n in players if n.tag in set(x['player_tag'] for x in top_players) } message_count = math.ceil(len(top_players) / 20) embeds = [] for i in range(message_count): player_data = top_players[i * 20:(i + 1) * 20] table = CLYTable() for x, y in enumerate(player_data): index = i * 20 + x if board_config.render == 2: table.add_row( [index, y[1], players.get(y['player_tag'], mock).name]) else: table.add_row([ index, y[1], y[2], players.get(y['player_tag'], mock).name ]) render = get_render_type(board_config, table) fmt = render() e = discord.Embed(colour=self.bot.donationboard.get_colour( board_type, False), description=fmt, timestamp=datetime.utcnow()) e.set_author(name=board_config.title, icon_url=board_config.icon_url or 'https://cdn.discordapp.com/' 'emojis/592028799768592405.png?v=1') e.set_footer( text= f'Historical {board_type.capitalize()}Board; Season {season_id} - Page {i+1}/{message_count}' ) embeds.append(e) return embeds
class TablePaginator(Pages): def __init__(self, ctx, data, title=None, page_count=1, rows_per_table=20, description=''): super().__init__(ctx, entries=[i for i in range(page_count)], per_page=1) self.table = CLYTable() self.data = [(i, v) for (i, v) in enumerate(data)] self.entries = [None for _ in range(page_count)] self.rows_per_table = rows_per_table self.title = title self.message = None self.ctx = ctx self.description = description if getattr(ctx, 'config', None): try: self.icon_url = ctx.config.icon_url or ctx.guild.icon_url self.title = ctx.config.title or title except AttributeError: self.icon_url = ctx.guild.icon_url else: self.icon_url = ctx.guild.icon_url async def get_page(self, page): try: entry = self.entries[page - 1] if entry: return entry except IndexError: pass if not self.message: self.message = await self.channel.send('Loading...') else: await self.message.edit(content='Loading...', embed=None) entry = await self.prepare_entry(page) self.entries[page - 1] = entry return self.entries[page - 1] async def prepare_entry(self, page): self.table.clear_rows() base = (page - 1) * self.rows_per_table data = self.data[base:base + self.rows_per_table] for n in data: self.table.add_row(n) render = get_render_type(self.ctx.config, self.table) return render() async def get_embed(self, entries, page, *, first=False): if self.maximum_pages > 1: if self.show_entry_count: text = f'Page {page}/{self.maximum_pages} ({len(self.entries)} entries)' else: text = f'Page {page}/{self.maximum_pages}' self.embed.set_footer(text=text) self.embed.description = self.description + entries self.embed.set_author( name=textwrap.shorten(self.title, width=240, placeholder='...'), icon_url=self.icon_url ) return self.embed async def show_page(self, page, *, first=False): self.current_page = page entries = await self.get_page(page) embed = await self.get_embed(entries, page, first=first) if not self.message: self.message = await self.ctx.send("Loading...") if not self.paginating: return await self.message.edit(content=None, embed=embed) await self.message.edit(content=None, embed=embed) if not first: return for (reaction, _) in self.reaction_emojis: if self.maximum_pages == 2 and reaction in ('\u23ed', '\u23ee'): # no |<< or >>| buttons if we only have two pages # we can't forbid it if someone ends up using it but remove # it from the default set continue await self.message.add_reaction(reaction)
async def update_board(self, channel_id): config = await self.bot.utils.board_config(channel_id) if not config: return if not config.toggle: return if not config.channel: return if config.in_event: query = """SELECT DISTINCT clan_tag FROM clans WHERE channel_id=$1 AND in_event=$2""" fetch = await self.bot.pool.fetch(query, channel_id, config.in_event) else: query = "SELECT DISTINCT clan_tag FROM clans WHERE channel_id=$1" fetch = await self.bot.pool.fetch(query, channel_id) clans = await self.bot.coc.get_clans((n[0] for n in fetch)).flatten() players = [] for n in clans: players.extend(p for p in n.itermembers) try: top_players = await self.get_top_players(players, config.type, config.sort_by, config.in_event) except: log.error( f"{clans} channelid: {channel_id}, guildid: {config.guild_id}," f" sort: {config.sort_by}, event: {config.in_event}, type: {config.type}" ) return players = { n.tag: n for n in players if n.tag in set(x['player_tag'] for x in top_players) } message_count = math.ceil(len(top_players) / 20) messages = await self.get_board_messages(channel_id, number_of_msg=message_count) if not messages: return for i, v in enumerate(messages): player_data = top_players[i * 20:(i + 1) * 20] table = CLYTable() for x, y in enumerate(player_data): index = i * 20 + x if config.render == 2: table.add_row( [index, y[1], players.get(y['player_tag'], mock).name]) else: table.add_row([ index, y[1], y[2], players.get(y['player_tag'], mock).name ]) render = get_render_type(config, table) fmt = render() e = discord.Embed(colour=self.get_colour(config.type, config.in_event), description=fmt, timestamp=datetime.utcnow()) e.set_author(name=f'Event in Progress!' if config.in_event else config.title, icon_url=config.icon_url or 'https://cdn.discordapp.com/' 'emojis/592028799768592405.png?v=1') e.set_footer(text='Last Updated') await v.edit(embed=e, content=None)
async def edit_event(self, ctx, *, event_name: str = None): """Edit a variety of settings for the current event. **Parameters** :key: Event name **Format** :information_source: `+edit event EVENT_NAME` **Example** :white_check_mark: `+edit event Donation Bot Event` **Required Permissions** :warning: Manage Server """ if event_name: query = """SELECT id FROM events WHERE guild_id = $1 AND event_name = $2""" fetch = await self.bot.pool.fetchrow(query, ctx.guild.id, event_name) if fetch: event_id = fetch['id'] else: # ideally this would just display a list of events and let the user pick, but I # couldn't figure out the proper sequence of if event_name/if event_id return await ctx.send("There is no event on this server with that name. Try `+edit event` " "to pick from a list of events on this server.") else: # No event name provided or I didn't understand the name I was given query = """SELECT id, event_name, start FROM events WHERE guild_id = $1 ORDER BY start""" fetch = await self.bot.pool.fetch(query, ctx.guild.id) if len(fetch) == 0 or not fetch: return await ctx.send("There are no events currently set up on this server. " "Try `+add event`") elif len(fetch) == 1: event_id = fetch[0]['id'] else: table = CLYTable() fmt = f"Events on {ctx.guild}:\n\n" reactions = [] counter = 0 for event in fetch: days_until = event['start'].date() - datetime.datetime.utcnow().date() table.add_row([counter, days_until.days, event['event_name']]) counter += 1 reactions.append(f"{counter}\N{combining enclosing keycap}") render = table.events_list() fmt += f'{render}\n\nPlease select the reaction that corresponds with the event you would ' \ f'like to remove.' e = discord.Embed(colour=self.bot.colour, description=fmt) msg = await ctx.send(embed=e) for r in reactions: await msg.add_reaction(r) def check(r, u): return str(r) in reactions and u.id == ctx.author.id and r.message.id == msg.id try: r, u = await self.bot.wait_for('reaction_add', check=check, timeout=60.0) except asyncio.TimeoutError: await msg.clear_reactions() return await ctx.send("I feel like I'm being ignored. MAybe try again later?") index = reactions.index(str(r)) event_id = fetch[index]['id'] # Now that we have the event_id, let's edit things query = """SELECT event_name, start, finish FROM events WHERE id = $1""" event = await self.bot.pool.fetchrow(query, event_id) def check_author(m): return m.author == ctx.author answer = await ctx.prompt(f"Event Name: **{event['event_name']}**\n" f"Would you like to edit the event name?") if answer: try: await ctx.send('Please enter the new name for this event.') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) new_event_name = response.content except asyncio.TimeoutError: new_event_name = event['event_name'] else: new_event_name = event['event_name'] answer = await ctx.prompt(f"Start Date: **{event['start'].date()}\n" f"Would you like to edit the date?") if answer: try: await ctx.send('Please enter the new start date. (YYYY-MM-DD)') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) new_start_date = await DateConverter().convert(ctx, response.clean_content) except (ValueError, commands.BadArgument): await ctx.send('Date must be in the YYYY-MM-DD format. I\'m going to keep ' 'the current start date and you can change it later if you like.') new_start_date = event['start'].date() except asyncio.TimeoutError: await ctx.send('Seems as though you don\'t really know the answer. I\'m just going ' 'to keep the date I have for now.') new_start_date = event['start'].date() else: new_start_date = event['start'].date() answer = await ctx.prompt(f"Start Time: **{event['start'].time()}\n" f"Would you like to edit the time?") if answer: try: await ctx.send('Please enter the new start time. (Please provide HH:MM in UTC)') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) hour, minute = map(int, response.content.split(':')) if hour < 13: try: await ctx.send('And is that AM or PM?') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) if response.content.lower() == 'pm': hour += 12 except asyncio.TimeoutError: if hour < 6: await ctx.send('Well I\'ll just go with PM then.') hour += 12 else: await ctx.send('I\'m going to assume you want AM.') new_start_time = datetime.time(hour, minute) except asyncio.TimeoutError: await ctx.send('Time\'s up my friend. Start time will remain the same!') new_start_time = event['start'].time() else: new_start_time = event['start'].time() answer = await ctx.prompt(f"End Date: **{event['finish'].date()}\n" f"Would you like to edit the date?") if answer: try: await ctx.send('Please enter the new end date. (YYYY-MM-DD)') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) new_end_date = await DateConverter().convert(ctx, response.clean_content) except (ValueError, commands.BadArgument): await ctx.send('Date must be in the YYYY-MM-DD format. I\'m going to keep ' 'the current end date and you can change it later if you like.') new_end_date = event['finish'].date() except asyncio.TimeoutError: await ctx.send('Seems as though you don\'t really know the answer. I\'m just going ' 'to keep the date I have for now.') new_end_date = event['finish'].date() else: new_end_date = event['finish'].date() answer = await ctx.prompt(f"End Time: **{event['finish'].time()}\n" f"Would you like to edit the time?") if answer: try: await ctx.send('Please enter the new end time. (Please provide HH:MM in UTC)') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) hour, minute = map(int, response.content.split(':')) if hour < 13: try: await ctx.send('And is that AM or PM?') response = await ctx.bot.wait_for('message', check=check_author, timeout=60.0) if response.content.lower() == 'pm': hour += 12 except asyncio.TimeoutError: if hour < 6: await ctx.send('Well I\'ll just go with PM then.') hour += 12 else: await ctx.send('I\'m going to assume you want AM.') new_end_time = datetime.time(hour, minute) except asyncio.TimeoutError: await ctx.send('Time\'s up my friend. Start time will remain the same!') new_end_time = event['finish'].time() else: new_end_time = event['finish'].time() # Assemble answers and update db new_start = datetime.datetime.combine(new_start_date, new_start_time) new_finish = datetime.datetime.combine(new_end_date, new_end_time) query = """UPDATE events SET event_name = $1, start = $2, finish = $3 WHERE id = $4""" await ctx.db.execute(query, new_event_name, new_start, new_finish, event_id) fmt = (f'**Event Info:**\n\n{new_event_name}\n{new_start.strftime("%d %b %Y %H:%M")}\n' f'{new_finish.strftime("%d %b %Y %H:%M")}') e = discord.Embed(colour=discord.Colour.green(), description=fmt) await ctx.send(embed=e) self.bot.dispatch('event_register')