async def send_embed(channel, header, text): """ Sends embed to channel, splitting if necessary """ if len(text) < 1000: embed = discord.Embed(color=color_pick(181, 0, 0)) embed.add_field(name=header, value=text, inline=False) embed.set_footer( text= "If someone is no longer in your clan, please notify a Chat Mod " "to have their Member role removed.", icon_url="http://www.mayodev.com/images/dangerbot.png") await channel.send(embed=embed) else: coll = "" for line in text.splitlines(keepends=True): if len(coll) + len(line) > 1000: embed = discord.Embed(color=color_pick(181, 0, 0)) embed.add_field(name=header, value=coll, inline=False) await channel.send(embed=embed) header = "Continued..." coll = "" coll += line embed = discord.Embed(color=color_pick(181, 0, 0)) embed.add_field(name=header, value=coll, inline=False) embed.set_footer( text= "If someone is no longer in your clan, please notify a Chat Mod " "to have their Member role removed.", icon_url="http://www.mayodev.com/images/dangerbot.png") await channel.send(embed=embed)
async def elder(self, ctx, command: str = "help"): """Help menu for elder staff""" embed = discord.Embed(title="Reddit Oak Elder Help Menu", description="All the elder commands you need but can't remember how to use!", color=color_pick(66, 134, 244)) embed.add_field(name="Commands:", value="-----------", inline=True) if command in ["help", "role"]: role = ("Adds the specified role to the specified user if they do not have it. " "Removes the role if they already have it.") embed.add_field(name="/role <@discord mention> <discord role>", value=role, inline=False) if command in ["help", "warn"]: warn_list = "Lists all strikes for all users. Sorted by user (alphabetically)." embed.add_field(name="/warn", value=warn_list, inline=False) warn_add = ("Adds a strike to the specified player with the specified reason. The bot will " "respond with a list of all strikes for that player. A DM will be sent to the player " "so they know that they have received a warning.") embed.add_field(name="/warn <in-game name> <reason for warning>", value=warn_add, inline=False) warn_remove = ("Removes the specified warning (warning ID). You will need to do /warn list " "first to obtain the warning ID.") embed.add_field(name="/warn remove <warning ID>", value=warn_remove, inline=False) if command in ["help", "kick"]: kick = ('Removes specified player from the Oak Table adding the reason you supply to the notes. ' 'Removes the Member role from their Discord account. For players with spaces in ' 'their name, "user quotes".') embed.add_field(name="/kick <in-game name> <reason for kick>", value=kick, inline=False) if command in ["help", "ban"]: ban = ("Removes specified player from the Oak Table adding the reason you " "supply and flags them as a permanent ban. Kicks the player from the Discord server. " "For players with spaces in their name, 'use quotes'.") embed.add_field(name="/ban <in-game name> <reason for ban>", value=ban, inline=False) if command in ["help", "unconfirmed"]: un_list = ("Lists all players who have not yet confirmed the rules. " "If they have been in the clan for more than 2 days, you will see a :boot:") embed.add_field(name="/unconfirmed", value=un_list, inline=False) un_kick = "Move specified player to No Confirmation." embed.add_field(name="/unconfirmed kick <in-game name>", value=un_kick, inline=False) un_move = ("Move specified player to Regular Member " "(if they failed the quiz or didn't move for some other reason.") embed.add_field(name="/unconfirmed move <in-game name>", value=un_move, inline=False) await ctx.send(embed=embed) self.bot.logger.info(f"{ctx.command} by {ctx.author} in {ctx.channel} | Request: {command}")
async def warn(self, ctx, player: str = None, *, warning=None): """Command to add warnings for players /warn list (or just /warn) will show a list of all warnings To add a warning, use: /warn TubaKid This is a warning /warn #RVP02LQU This is also a warning For names with spaces, use quotes: /warn "Professor Mahon" This is another warning To remove a warning, request the list first to obtain the warning ID. /warn remove # """ conn = self.bot.pool if authorized(ctx.author.roles) or ctx.author.id == 251150854571163648: if player is None: sql = ("SELECT COUNT(strike_num) AS num_warnings, player_name " "FROM oak_warn_list " "GROUP BY player_name " "ORDER BY player_name") strikes = await conn.fetch(sql) embed = discord.Embed(title="Reddit Oak Warning Count", description="All warnings expire after 60 days.", color=color_pick(181, 0, 0)) embed_text = "" for row in strikes: embed_text += f"{row['player_name']} - {row['num_warnings']} warnings\n" embed.add_field(name="Warning Counts", value=embed_text) return await ctx.send(embed=embed) if player == "list": sql = ("SELECT player_name, strike_num || '. ' || warning || ' (' || DATE(timestamp) || ') [' " "|| warning_id || ']' AS warning_text " "FROM oak_warn_list " "ORDER BY player_name, strike_num") strike_list = await conn.fetch(sql) strikes = {} name = "" for strike in strike_list: if strike['player_name'] != name: name = strike['player_name'] strikes[name] = "" strikes[name] += strike['warning_text'] + "\n" print(strikes) embed = discord.Embed(title="Reddit Oak Watchlist", description="All warnings expire after 60 days.", color=color_pick(181, 0, 0)) for name, strike in strikes.items(): embed.add_field(name=name, value=strike, inline=False) embed.set_footer(icon_url=("https://openclipart.org/image/300px/svg_to_png/109/" "molumen-red-round-error-warning-icon.png"), text="To remove a strike, use /warn remove <Warning ID>") self.bot.logger.debug(f"{ctx.command} by {ctx.author} in {ctx.channel} | Request: List warnings") return await ctx.send(embed=embed) elif player == "remove": warning_id = warning sql = ("SELECT strike_num, player_name, timestamp, warning, warning_id " "FROM oak_warn_list " "WHERE warning_id = $1") fetch = await conn.fetchrow(sql, int(warning_id)) if fetch is None: return await ctx.send("No warning exists with that ID. Please check the ID and try again.") response = await ctx.prompt(f"Are you sure you want to remove\n{fetch['warning']}\n" f"from {fetch['player_name']}?") if not response: return await ctx.send(content="Removal cancelled. Maybe try again later if you feel up to it.") sent_msg = await ctx.send(content="Removal in progress...") sql = "DELETE FROM oak_warnings WHERE warning_id = $1" await conn.execute(sql, int(warning_id)) self.bot.logger.debug(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Request: Removal of {fetch['warning']} for {fetch['player_name']}") await sent_msg.edit(content=f"Warning **{fetch['warning']}** " f"removed for **{fetch['player_name']}**.") else: # add a player warning if player.startswith("#"): member = await self.bot.coc.get_player(player) else: clan = await self.bot.coc.get_clan(clans['Reddit Oak']) member = clan.get_member_by(name=player) if not member: clan = await self.bot.coc.get_clan(clans['Reddit Quercus']) member = clan.get_member_by(name=player) if not member: self.bot.logger.warning(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Problem: {player} not found in Clash API | Warning: {warning}") return await ctx.send("You have provided an invalid player name. Please try again.") # Everything looks good, let's add to the database sql = ("INSERT INTO oak_warnings (player_tag, timestamp, warning) " "VALUES ($1, $2, $3)") await conn.execute(sql, member.tag[1:], datetime.utcnow(), warning) sql = ("SELECT player_name, strike_num || '. ' || warning || ' (' || " "DATE(timestamp) || ')' AS warning_text " "FROM oak_warn_list WHERE player_name = $1 " "ORDER BY strike_num") strikes = await conn.fetch(sql, member.name) discord_id = await self.bot.links.get_link(member.tag) user = ctx.guild.get_member(discord_id) header = f"**Warnings for {member.name}**" content = "" for strike in strikes: content += strike['warning_text'] + "\n" await ctx.send(header + "\n" + content) await user.send("New warning added:\n" + content) self.bot.logger.debug(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Request: {member.name} warned for {warning}") else: self.bot.logger.warning(f"User not authorized - " f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Request: Warning for {player} for {warning}") await ctx.send("Wait a minute punk! You aren't allowed to use that command")
async def player(self, ctx, *, player: PlayerConverter = None): """Provide details on the specified player""" if not player: self.bot.logger.warning(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Problem: No valid player name or tag was provided.") return await ctx.send(f"{emojis['other']['redx']} You must provide a valid in-game name or tag for this " f"command. Try `/player TubaKid`") # pull non-in-game stats from db with Sql() as cursor: sql = (f"SELECT tag, numWars, avgStars, warStats, joinDate, slackId " f"FROM coc_oak_players " f"WHERE tag = ?") cursor.execute(sql, player.tag[1:]) oak_stats = cursor.fetchone() try: if not oak_stats: self.bot.logger.warning(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Problem: {player.name} not found in SQL database") return await ctx.send(f"{emojis['other']['redx']} The player you provided was not found in the " f"database. Please try again.") except: self.bot.logger.error(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Unknown error has occurred") return await ctx.send(f"{emojis['other']['redx']} Something has gone horribly wrong. " f"<@251150854571163648> I was trying to look up {player.name} " f"but the world conspired against me.") # retrieve player info from coc.py player_tag = f"#{oak_stats.tag}" player = await self.bot.coc.get_player(player_tag) troop_levels = builder_levels = spell_levels = hero_levels = hero_pets_levels = builder_hero = \ sm_levels = super_troop_levels = "" sm_troops = enums.SIEGE_MACHINE_ORDER super_troops = enums.SUPER_TROOP_ORDER count = 0 for troop in player.home_troops: if troop.name in super_troops: # We're ignoring super troops at this time continue if troop.name not in sm_troops: count += 1 if troop.name == "Minion": count = 1 if troop_levels[-2:] == "\n": troop_levels += "\n" else: troop_levels += "\n\n" if troop.name not in enums.HERO_PETS_ORDER: troop_levels += f"{emojis['troops'][troop.name]}{str(troop.level)} " if count % 6 == 0: troop_levels += "\n" else: sm_levels += f"{emojis['siege'][troop.name]}{str(troop.level)} " count = 0 for spell in player.spells: count += 1 if spell.name == "Poison Spell" and spell_levels[-2:] != "\n": spell_levels += "\n" count = 1 spell_levels += f"{emojis['spells'][spell.name]}{str(spell.level)} " count = 0 # Handle Super Troops for troop in player.home_troops: if troop.name in super_troops: count += 1 if troop.is_active: super_troop_levels += f"{emojis['super_troops_active'][troop.name]}{str(troop.level)} " else: super_troop_levels += f"{emojis['super_troops'][troop.name]}{str(troop.level)} " if count % 6 == 0: super_troop_levels += "\n" count = 0 for troop in player.builder_troops: count += 1 builder_levels += f"{emojis['build_troops'][troop.name]}{str(troop.level)} " if count % 6 == 0: builder_levels += "\n" # Test for number of heroes if len(player.heroes) > 0: for hero in player.heroes: if hero.name != "Battle Machine": hero_levels += f"{emojis['heroes'][hero.name]}{str(hero.level)} " else: builder_hero = f"{emojis['heroes'][hero.name]}{str(hero.level)}" for troop in player.home_troops: if troop.name in enums.HERO_PETS_ORDER: hero_pets_levels += f"{emojis['hero_pets'][troop.name]}{str(troop.level)} " embed = discord.Embed(title=f"{emojis['league'][leagues_to_emoji[player.league.name]]} " f"{player.name} " f"({player.tag})", color=color_pick(226, 226, 26)) embed.add_field(name="Town Hall", value=f"{emojis['th_icon'][player.town_hall]} {str(player.town_hall)}", inline=True) embed.add_field(name="Trophies", value=player.trophies, inline=True) embed.add_field(name="Best Trophies", value=player.best_trophies, inline=True) embed.add_field(name="War Stars", value=player.war_stars, inline=True) embed.add_field(name="Attack Wins", value=player.attack_wins, inline=True) embed.add_field(name="Defense Wins", value=player.defense_wins, inline=True) embed.add_field(name="Wars in Oak", value=oak_stats.numWars, inline=True) embed.add_field(name="Avg. Stars per War", value=str(round(oak_stats.avgStars, 2)), inline=True) embed.add_field(name="This Season", value=oak_stats.warStats, inline=False) embed.add_field(name="Troop Levels", value=troop_levels, inline=False) if super_troop_levels != "": embed.add_field(name="Super Troops", value=super_troop_levels, inline=False) if sm_levels != "": embed.add_field(name="Siege Machines", value=sm_levels, inline=False) embed.add_field(name="Spell Levels", value=spell_levels, inline=False) if hero_levels != "": embed.add_field(name="Heroes", value=hero_levels, inline=False) if hero_pets_levels != "": embed.add_field(name="Hero Pets", value=hero_pets_levels, inline=False) embed.add_field(name="Builder Hall Level", value=f"{emojis['bh_icon'][player.builder_hall]} {str(player.builder_hall)}", inline=False) embed.add_field(name="Versus Trophies", value=str(player.versus_trophies), inline=True) embed.add_field(name="Versus Battle Wins", value=str(player.versus_attack_wins), inline=True) embed.add_field(name="Best Versus Trophies", value=str(player.best_versus_trophies), inline=True) embed.add_field(name="Troop Levels", value=builder_levels, inline=False) if builder_hero != "": embed.add_field(name="Hero", value=builder_hero, inline=False) embed.set_footer(icon_url=player.clan.badge.url, text=f"Member of Reddit Oak since {oak_stats.joinDate.strftime('%e %B, %Y')}") self.bot.logger.debug(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Request complete: /player {player.name}") await ctx.send(embed=embed)
async def siege_request(self, ctx, *, siege_req: str = "help"): """- For requesting siege machines Options: - ww, wall wrecker - air1, blimp, battle blimp, bb - air2, stone, slam, slammer, stone slammer - barracks, sb **Example:** /siege wall wrecker /siege blimp /siege Stone Slammer /siege barracks """ user_id = ctx.author.id if siege_req == "help": embed = discord.Embed(title="The Arborist by Reddit Oak", color=color_pick(15, 250, 15)) embed.add_field(name="Commands:", value="-----------", inline=True) siege = ("Posts request for the specified siege machine in Discord and tags those players that can donate." "\n**ground**: Wall Wrecker" "\n**blimp**: Battle Blimp" "\n**slammer**: Stone Slammer" "\n**barracks**: Siege Barracks") embed.add_field(name="/siege <siege type>", value=siege, inline=False) embed.set_footer(icon_url="https://openclipart.org/image/300px/svg_to_png/122449/1298569779.png", text="The Arborist proudly maintained by TubaKid.") await ctx.send(embed=embed) return if siege_req in ["ww", "wall wrecker"]: siege_name = "Wall Wrecker" thumb = "https://coc.guide/static/imgs/troop/siege-machine-ram.png" elif siege_req in ["blimp", "air1", "bb", "battle blimp"]: siege_name = "Battle Blimp" thumb = "https://coc.guide/static/imgs/troop/siege-machine-flyer.png" elif siege_req in ["stone", "slammer", "slam", "air2", "stone slammer"]: siege_name = "Stone Slammer" thumb = "https://coc.guide/static/imgs/troop/siege-bowler-balloon.png" elif siege_req in ["barracks", "sb", "seige barracks", "baracks"]: siege_name = "Siege Barracks" thumb = "https://coc.guide/static/imgs/troop/siege-machine-carrier.png" else: await ctx.send("You have provided an invalid siege machine type. " "Please specify `ground`, `blimp`, `slammer`, or `barracks`") return sent_msg = await ctx.send(f"One moment while I check to see who has those.") donors = [] requestor = None # get requestor player tag from Discord ID clan = await self.bot.coc.get_clan("#CVCJR89") requestor_tag = await self.bot.links.get_linked_players(user_id) # Remove any links for player tags that aren't currently in Oak if len(requestor_tag) > 1: clan_tags = [x.tag for x in clan.members] for tag in requestor_tag: if tag not in clan_tags: requestor_tag.remove(tag) # If still more than one, prompt user for correct player if len(requestor_tag) > 1: prompt_text = "You have more than one player in Reddit Oak. Please select the correct player:" counter = 1 for tag in requestor_tag: player = await self.bot.coc.get_player(tag) prompt_text += f"\n{counter}. {player.name} ({player.tag})" prompt = ctx.prompt(prompt_text, additional_options=len(requestor_tag)) requestor_tag = requestor_tag[prompt - 1] # find oak players with the requested siege machine async for player in clan.get_detailed_members(): if siege_name in [troop.name for troop in player.siege_machines]: discord_id = await self.bot.links.get_discord_links(player.tag) donors.append(f"{player.name}: <@{discord_id}>") if requestor_tag == player.tag: requestor = player.name if not requestor: requestor = ctx.author.name await sent_msg.delete() embed = discord.Embed(title=f"{siege_name} Request", description=f"{requestor} has requested a {siege_name}", color=0xb5000) embed.set_footer(icon_url=thumb, text="Remember to select your siege machine when you attack!") content = "**Potential donors include:**\n" content += "\n".join(donors) await ctx.send(embed=embed) await ctx.send(content)
async def help(self, ctx, command: str = "all"): """ Welcome to The Arborist""" desc = """All commands must begin with a slash. You can type /help <command> to display only the help for that command.""" # ignore requests for help with the war command if command == "war": return # respond if help is requested for a command that does not exist if command not in ["all", "siege", "player", "elder"]: self.bot.logger.warning(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Problem: /help {command} - command does not exist") await ctx.send(f"{emojis['other']['redx']} You have provided a command that does not exist. " f"Perhaps try /help to see all commands.") return embed = discord.Embed(title="The Arborist by Reddit Oak", description=desc, color=color_pick(15, 250, 15)) embed.add_field(name="Commands:", value="-----------", inline=True) if command in ["all", "siege"]: siege = ("Posts request for the specified siege machine in Discord and tags those players that can donate." "\n**ground**: Wall Wrecker" "\n**blimp**: Battle Blimp" "\n**slammer**: Stone Slammer" "\n**barracks**: Siege Barracks") embed.add_field(name="/siege <siege type>", value=siege, inline=False) if command in ["all", "player"]: player = ("Display vital statistics on the requested player. This includes information " "on in game stats as well as stats while in Reddit Oak.") embed.add_field(name="/player <in game name>", value=player, inline=False) if command in ["all", "avatar"]: avatar = "Provides an enlarged version of the specified player's avatar." embed.add_field(name="/avatar <Discord Mention or ID>", value=avatar, inline=False) if command == "elder": elder = "To display help for elder commands, please type /elder." embed.add_field(name="/elder", value=elder, inline=False) embed.set_footer(icon_url="https://openclipart.org/image/300px/svg_to_png/122449/1298569779.png", text="The Arborist proudly maintained by TubaKid.") self.bot.logger.debug(f"{ctx.command} by {ctx.author} in {ctx.channel} | " f"Request complete: /help {command}") await ctx.send(embed=embed)