From 57ae7a52cdcb8b3d1c13b79f4c1b8d87f414ff7b Mon Sep 17 00:00:00 2001 From: aikaterna <20862007+aikaterna@users.noreply.github.com> Date: Fri, 18 Feb 2022 18:03:37 -0800 Subject: [PATCH] discord.py 2.0 fun Invites will not work because discord-ext-menus needs to be updated in Red --- away/away.py | 9 +++-- blurplefy/blurplefy.py | 28 ++++++++-------- cah/cah.py | 21 +++++++++--- invites/invites.py | 6 ++-- otherbot/otherbot.py | 38 +++++++++------------ seen/seen.py | 2 +- tools/tools.py | 76 ++++++++++++++++++++++++++---------------- 7 files changed, 102 insertions(+), 78 deletions(-) diff --git a/away/away.py b/away/away.py index 7e65115..269e684 100644 --- a/away/away.py +++ b/away/away.py @@ -1,7 +1,6 @@ import discord from redbot.core import Config, commands, checks from typing import Optional, Literal -import datetime import re IMAGE_LINKS = re.compile(r"(http[s]?:\/\/[^\"\']*\.(?:png|jpg|jpeg|gif|png))") @@ -37,7 +36,7 @@ class Away(commands.Cog): def _draw_play(self, song): song_start_time = song.start total_time = song.duration - current_time = datetime.datetime.utcnow() + current_time = discord.utils.utcnow() elapsed_time = current_time - song_start_time sections = 12 loc_time = round((elapsed_time / total_time) * sections) # 10 sections @@ -60,7 +59,7 @@ class Away(commands.Cog): """ Makes the embed reply """ - avatar = author.avatar_url_as() # This will return default avatar if no avatar is present + avatar = author.display_avatar # This will return default avatar if no avatar is present color = author.color if message: link = IMAGE_LINKS.search(message) @@ -226,7 +225,7 @@ class Away(commands.Cog): return False @commands.Cog.listener() - async def on_message(self, message: discord.Message): + async def on_message_without_command(self, message: discord.Message): guild = message.guild if not guild or not message.mentions or message.author.bot: return @@ -667,7 +666,7 @@ class Away(commands.Cog): if ctx.channel.permissions_for(ctx.me).embed_links: em = discord.Embed(description=msg[:2048], color=author.color) - em.set_author(name=f"{author.display_name}'s away settings", icon_url=author.avatar_url) + em.set_author(name=f"{author.display_name}'s away settings", icon_url=author.avatar.url) await ctx.send(embed=em) else: await ctx.send(f"{author.display_name} away settings\n" + msg) diff --git a/blurplefy/blurplefy.py b/blurplefy/blurplefy.py index 0e3e4ac..89d648e 100644 --- a/blurplefy/blurplefy.py +++ b/blurplefy/blurplefy.py @@ -85,14 +85,14 @@ class Blurplefy(commands.Cog): await ctx.send("{}, starting blurple image analysis.".format(ctx.message.author.mention)) link = ctx.message.attachments if user is None and not link: - picture = ctx.author.avatar_url + picture = ctx.author.avatar.url else: if not user: if len(link) != 0: for image in link: picture = image.url else: - picture = user.avatar_url + picture = user.avatar.url try: async with self.session.request("GET", str(picture)) as r: response = await r.read() @@ -109,7 +109,7 @@ class Blurplefy(commands.Cog): await ctx.send("{}, starting blurple image analysis.".format(ctx.message.author.mention)) link = ctx.message.attachments if user is None and not link: - picture = ctx.author.avatar_url + picture = ctx.author.avatar.url role_check = True elif not user: if len(link) != 0: @@ -117,7 +117,7 @@ class Blurplefy(commands.Cog): picture = image.url role_check = False else: - picture = user.avatar_url + picture = user.avatar.url role_check = False try: @@ -180,7 +180,7 @@ class Blurplefy(commands.Cog): blurple_role_obj = discord.utils.get(ctx.guild.roles, id=blurple_role_id) if ( blurplenesspercentage > 75 - and picture == ctx.author.avatar_url + and picture == ctx.author.avatar.url and blurple_role_obj not in ctx.author.roles and percentblurple > 5 ): @@ -190,7 +190,7 @@ class Blurplefy(commands.Cog): ) ) await ctx.author.add_roles(blurple_role_obj) - elif picture == ctx.author.avatar_url and blurple_role_obj not in ctx.author.roles: + elif picture == ctx.author.avatar.url and blurple_role_obj not in ctx.author.roles: await ctx.send( "{}, your profile pic does not have enough blurple (over 75% in total and over 5% blurple), therefore you are not eligible for the role {}.".format( ctx.message.author.display_name, blurple_role_obj.name @@ -206,14 +206,14 @@ class Blurplefy(commands.Cog): await ctx.send("{}, starting blurple image analysis.".format(ctx.message.author.mention)) link = ctx.message.attachments if user is None and not link: - picture = ctx.author.avatar_url + picture = ctx.author.avatar.url else: if not user: if len(link) != 0: for image in link: picture = image.url else: - picture = user.avatar_url + picture = user.avatar.url try: async with self.session.request("GET", str(picture)) as r: response = await r.read() @@ -380,12 +380,14 @@ class Blurplefy(commands.Cog): @commands.command() async def countdown(self, ctx): """Countdown to Discord's 7th Anniversary.""" - embed = discord.Embed(name="", colour=0x7289DA) - timeleft = datetime.datetime(2021, 5, 13) + datetime.timedelta(hours=7) - datetime.datetime.utcnow() - embed.set_author(name="Time left until Discord's 6th Anniversary") + embed = discord.Embed() + embed.description="\N{ZERO WIDTH SPACE}" + embed.colour=0x7289DA + timeleft = datetime.datetime(2021, 5, 13, tzinfo=datetime.timezone.utc) + datetime.timedelta(hours=7) - discord.utils.utcnow() + embed.set_author(name="Time left until Discord's 7th Anniversary") if int(timeleft.total_seconds()) < 0: - timeleft = datetime.datetime(2022, 5, 13) + datetime.timedelta(hours=7) - datetime.datetime.utcnow() - embed.set_author(name="Time left until Discord's 6th Anniversary") + timeleft = datetime.datetime(2022, 5, 13, tzinfo=datetime.timezone.utc) + datetime.timedelta(hours=7) - discord.utils.utcnow() + embed.set_author(name="Time left until Discord's 7th Anniversary") embed.add_field( name="Countdown to midnight, May 13, California time (UTC-7):", value=("{}".format(self._dynamic_time(int(timeleft.total_seconds())))), diff --git a/cah/cah.py b/cah/cah.py index 53833be..210fe5f 100644 --- a/cah/cah.py +++ b/cah/cah.py @@ -374,7 +374,11 @@ class CardsAgainstHumanity(commands.Cog): member["Task"] = None # Set running to false game["Running"] = False - self.games.remove(game) + try: + self.games.remove(game) + except ValueError: + # game was in progress but players left, leaving the open game with less than 3 people + pass return False async def typing(self, game, typeTime=5): @@ -702,7 +706,7 @@ class CardsAgainstHumanity(commands.Cog): await self.sendToUser(user, msg) async def drawCard(self, game): - with open(str(bundled_data_path(self)) + "/deck.json", "r") as deck_file: + with open(str(bundled_data_path(self)) + "/deck.json", "r", encoding='utf-8') as deck_file: deck = json.load(deck_file) # Draws a random unused card and shuffles the deck if needed totalDiscard = len(game["Discard"]) @@ -752,7 +756,7 @@ class CardsAgainstHumanity(commands.Cog): i += 1 async def drawBCard(self, game): - with open(str(bundled_data_path(self)) + "/deck.json", "r") as deck_file: + with open(str(bundled_data_path(self)) + "/deck.json", "r", encoding='utf-8') as deck_file: deck = json.load(deck_file) # Draws a random black card totalDiscard = len(game["BDiscard"]) @@ -890,7 +894,11 @@ class CardsAgainstHumanity(commands.Cog): if message == None: msg = "Ooookay, you say *nothing...*" return await self.sendToUser(ctx.author, msg) + long_message = False msg = f"*{ctx.author.name}* says: {message}" + if len(msg) > 1999: + msg = msg[:1990] + " [...]" + long_message = True for member in userGame["Members"]: if member["IsBot"]: continue @@ -901,7 +909,10 @@ class CardsAgainstHumanity(commands.Cog): else: # Update member's time member["Time"] = int(time.time()) - await self.sendToUser(ctx.author, "Message sent!") + msg = "Message sent!" + if long_message: + msg += " Your message was cut short to keep it under the 2000 character limit." + await self.sendToUser(ctx.author, msg) @commands.command() async def lay(self, ctx, *, card=None): @@ -1069,7 +1080,7 @@ class CardsAgainstHumanity(commands.Cog): """Starts a new Cards Against Humanity game.""" try: embed = discord.Embed(color=discord.Color.green()) - embed.set_author(name="**Setting up the game...**") + embed.set_author(name="Setting up the game...") await ctx.author.send(embed=embed) except discord.errors.Forbidden: return await ctx.send("You must allow Direct Messages from the bot for this game to work.") diff --git a/invites/invites.py b/invites/invites.py index d0a2955..ab567cb 100644 --- a/invites/invites.py +++ b/invites/invites.py @@ -42,7 +42,7 @@ class Invites(commands.Cog): @invites.command() async def show(self, ctx: commands.Context, invite_code_or_url: str = None): """Show the stats for an invite, or show all invites.""" - if not ctx.me.permissions_in(ctx.channel).administrator: + if not ctx.channel.permissions_for(ctx.guild.me).administrator: return await self._send_embed(ctx, PERM_MSG) if not invite_code_or_url: @@ -57,7 +57,7 @@ class Invites(commands.Cog): @invites.command() async def leaderboard(self, ctx: commands.Context, list_all_invites: bool = False): """List pinned invites or all invites in a leaderboard style.""" - if not ctx.me.permissions_in(ctx.channel).administrator: + if not ctx.channel.permissions_for(ctx.guild.me).administrator: return await self._send_embed(ctx, PERM_MSG) if not list_all_invites: @@ -96,7 +96,7 @@ class Invites(commands.Cog): @invites.command() async def pin(self, ctx: commands.Context, invite_code_or_url: str): """Pin an invite to the leaderboard.""" - if not ctx.me.permissions_in(ctx.channel).administrator: + if not ctx.channel.permissions_for(ctx.guild.me).administrator: return await self._send_embed(ctx, PERM_MSG) invite_code = await self._find_invite_code(invite_code_or_url) diff --git a/otherbot/otherbot.py b/otherbot/otherbot.py index cd9dfae..8f7e4df 100644 --- a/otherbot/otherbot.py +++ b/otherbot/otherbot.py @@ -4,7 +4,6 @@ import discord from redbot.core.bot import Red from redbot.core import commands, checks, Config -from datetime import datetime DEFAULT_OFFLINE_EMOJI = "\N{LARGE RED CIRCLE}" DEFAULT_ONLINE_EMOJI = "\N{WHITE HEAVY CHECK MARK}" @@ -12,7 +11,7 @@ DEFAULT_ONLINE_EMOJI = "\N{WHITE HEAVY CHECK MARK}" class Otherbot(commands.Cog): __author__ = ["aikaterna", "Predä 。#1001"] - __version__ = "0.10" + __version__ = "0.11" async def red_delete_data_for_user( self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, @@ -126,7 +125,8 @@ class Otherbot(commands.Cog): else: msg += f"**{name}**: {guild_data[attr]}\n" em.description = msg - em.set_thumbnail(url=guild.icon_url) + if guild.icon: + em.set_thumbnail(url=guild.icon.url) await ctx.send(embed=em) else: msg = "```\n" @@ -346,7 +346,7 @@ class Otherbot(commands.Cog): await self.generate_cache() @commands.Cog.listener() - async def on_member_update(self, before: discord.Member, after: discord.Member): + async def on_presence_update(self, before: discord.Member, after: discord.Member): if after.guild is None or not after.bot: return @@ -368,19 +368,16 @@ class Otherbot(commands.Cog): em = discord.Embed( color=0x8B0000, description=f"{after.mention} is offline. {data['offline_emoji']}", - timestamp=datetime.utcnow(), + timestamp=discord.utils.utcnow(), ) if not data["ping"]: await channel.send(embed=em) else: - if discord.version_info.minor < 4: - await channel.send(f"<@&{data['ping']}>", embed=em) - else: - await channel.send( - f"<@&{data['ping']}>", - embed=em, - allowed_mentions=discord.AllowedMentions(roles=True), - ) + await channel.send( + f"<@&{data['ping']}>", + embed=em, + allowed_mentions=discord.AllowedMentions(roles=True), + ) else: if not data["ping"]: await channel.send(f"{after.mention} is offline. {data['offline_emoji']}") @@ -401,19 +398,16 @@ class Otherbot(commands.Cog): em = discord.Embed( color=0x008800, description=f"{after.mention} is back online. {data['online_emoji']}", - timestamp=datetime.utcnow(), + timestamp=discord.utils.utcnow(), ) if not data["ping"]: await channel.send(embed=em) else: - if discord.version_info.minor < 4: - await channel.send(f"<@&{data['ping']}>", embed=em) - else: - await channel.send( - f"<@&{data['ping']}>", - embed=em, - allowed_mentions=discord.AllowedMentions(roles=True), - ) + await channel.send( + f"<@&{data['ping']}>", + embed=em, + allowed_mentions=discord.AllowedMentions(roles=True), + ) else: if not data["ping"]: await channel.send( diff --git a/seen/seen.py b/seen/seen.py index c0e377c..ecc651e 100644 --- a/seen/seen.py +++ b/seen/seen.py @@ -117,7 +117,7 @@ class Seen(commands.Cog): elif output[2] > 1: ts += "{} minutes ago".format(output[2]) em = discord.Embed(colour=discord.Color.green()) - avatar = author.avatar_url or author.default_avatar_url + avatar = author.display_avatar em.set_author(name="{} was seen {}".format(author.display_name, ts), icon_url=avatar) await ctx.send(embed=em) diff --git a/tools/tools.py b/tools/tools.py index 54380c1..eed752a 100644 --- a/tools/tools.py +++ b/tools/tools.py @@ -213,6 +213,8 @@ class Tools(commands.Cog): discord.TextChannel: "Text Channel", discord.VoiceChannel: "Voice Channel", discord.CategoryChannel: "Category", + discord.StageChannel: "Stage Channel", + discord.Thread: "Thread Channel", } load = "```\nLoading channel info...```" @@ -223,41 +225,57 @@ class Tools(commands.Cog): data = "```ini\n" if caller == "invoke" or channel.guild != ctx.guild: - data += "[Server]: {}\n".format(channel.guild.name) - data += "[Name]: {}\n".format(cf.escape(str(channel))) - data += "[ID]: {}\n".format(channel.id) - data += "[Private]: {}\n".format(yesno[isinstance(channel, discord.abc.PrivateChannel)]) - if isinstance(channel, discord.TextChannel) and channel.topic != "": - data += "[Topic]: {}\n".format(channel.topic) - data += "[Position]: {}\n".format(channel.position) - data += "[Created]: {}\n".format(self._dynamic_time(channel.created_at)) - data += "[Type]: {}\n".format(typemap[type(channel)]) + data += "[Server]: {}\n".format(channel.guild.name) + data += "[Name]: {}\n".format(cf.escape(str(channel))) + data += "[ID]: {}\n".format(channel.id) + data += "[Private]: {}\n".format(yesno[isinstance(channel, discord.abc.PrivateChannel)]) + if isinstance(channel, discord.TextChannel) and channel.topic != None: + data += "[Topic]: {}\n".format(channel.topic) + try: + data += "[Position]: {}\n".format(channel.position) + except AttributeError: + # this is a thread + data += "[Parent Channel]: {} ({})\n".format(channel.parent.name, channel.parent.id) + data += "[Parent Position]: {}\n".format(channel.parent.position) + try: + data += "[Created]: {}\n".format(self._dynamic_time(channel.created_at)) + except AttributeError: + # this is a thread + data += "[Updated]: {}\n".format(self._dynamic_time(channel.archive_timestamp)) + data += "[Type]: {}\n".format(typemap[type(channel)]) + if isinstance(channel, discord.TextChannel) and channel.is_news(): + data += "[News Channel]: {}\n".format(channel.is_news()) if isinstance(channel, discord.VoiceChannel): - data += "[Users]: {}\n".format(len(channel.members)) - data += "[User limit]: {}\n".format(channel.user_limit) - data += "[Bitrate]: {}kbps\n".format(int(channel.bitrate / 1000)) + data += "[Users]: {}\n".format(len(channel.members)) + data += "[User limit]: {}\n".format(channel.user_limit) + data += "[Bitrate]: {}kbps\n".format(int(channel.bitrate / 1000)) data += "```" await asyncio.sleep(1) await waiting.edit(content=data) @commands.guild_only() @commands.command() - async def eid(self, ctx, emoji: discord.Emoji): + async def eid(self, ctx, emoji: str = None): """Get an id for an emoji.""" + if not isinstance(emoji, discord.Emoji): + return await ctx.send("I can't see that emoji in any of the servers I'm in.") + await ctx.send(f"**ID for {emoji}:** {emoji.id}") @commands.guild_only() @commands.command() - async def einfo(self, ctx, emoji: discord.Emoji): + async def einfo(self, ctx, emoji: str = None): """Emoji information.""" - e = emoji + if not isinstance(emoji, discord.Emoji): + return await ctx.send("I can't see that emoji in any of the servers I'm in.") + m = ( - f"{str(e)}\n" + f"{str(emoji)}\n" f"```ini\n" - f"[NAME]: {e.name}\n" - f"[GUILD]: {e.guild}\n" - f"[URL]: {e.url}\n" - f"[ANIMATED]: {e.animated}" + f"[NAME]: {emoji.name}\n" + f"[GUILD]: {emoji.guild}\n" + f"[URL]: {emoji.url}\n" + f"[ANIMATED]: {emoji.animated}" "```" ) await ctx.send(m) @@ -265,7 +283,7 @@ class Tools(commands.Cog): @commands.guild_only() @commands.command() @checks.mod_or_permissions(manage_guild=True) - async def inrole(self, ctx, *, rolename): + async def inrole(self, ctx, *, rolename: str): """Check members in the role specified.""" guild = ctx.guild await ctx.trigger_typing() @@ -286,7 +304,7 @@ class Tools(commands.Cog): if len(roles) == 1: role = roles[0] elif len(roles) < 1: - await ctx.send("No roles were found.") + await ctx.send(f"No roles containing `{rolename}` were found.") return else: msg = ( @@ -555,7 +573,8 @@ class Tools(commands.Cog): em.add_field(name="Position", value=role.position) em.add_field(name="Valid Permissions", value="{}".format(perms_we_have)) em.add_field(name="Invalid Permissions", value="{}".format(perms_we_dont)) - em.set_thumbnail(url=role.guild.icon_url) + if guild.icon: + em.set_thumbnail(url=role.guild.icon.url) try: await loadingmsg.edit(embed=em) except discord.HTTPException: @@ -654,12 +673,12 @@ class Tools(commands.Cog): data = "```ini\n" data += "[Name]: {}\n".format(guild.name) data += "[ID]: {}\n".format(guild.id) - data += "[Region]: {}\n".format(guild.region) data += "[Owner]: {}\n".format(guild.owner) data += "[Users]: {}/{}\n".format(online, total_users) data += "[Text]: {} channels\n".format(len(text_channels)) data += "[Voice]: {} channels\n".format(len(voice_channels)) data += "[Emojis]: {}\n".format(len(guild.emojis)) + data += "[Stickers]: {}\n".format(len(guild.stickers)) data += "[Roles]: {} \n".format(len(guild.roles)) data += "[Created]: {}\n```".format(self._dynamic_time(guild.created_at)) await asyncio.sleep(1) @@ -731,7 +750,7 @@ class Tools(commands.Cog): data += "[Streaming]: [{}]({})\n".format(cf.escape(str(actstream.name)), cf.escape(actstream.url)) if actcustom := discord.utils.get(user.activities, type=discord.ActivityType.custom): if actcustom.name is not None: - data += "[Custom status]: {}\n".format(cf.escape(str(actcustom.name))) + data += "[Custom Status]: {}\n".format(cf.escape(str(actcustom.name))) passed = (ctx.message.created_at - user.created_at).days data += "[Created]: {}\n".format(self._dynamic_time(user.created_at)) joined_at = self.fetch_joined_at(user, ctx.guild) @@ -791,12 +810,11 @@ class Tools(commands.Cog): @staticmethod def _dynamic_time(time): try: - date_join = datetime.datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f") + date_join = datetime.datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f%z") except ValueError: time = f"{str(time)}.0" - date_join = datetime.datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f") - date_now = datetime.datetime.now(datetime.timezone.utc) - date_now = date_now.replace(tzinfo=None) + date_join = datetime.datetime.strptime(str(time), "%Y-%m-%d %H:%M:%S.%f%z") + date_now = discord.utils.utcnow() since_join = date_now - date_join mins, secs = divmod(int(since_join.total_seconds()), 60)