diff --git a/antiphoneclapper/__init__.py b/antiphoneclapper/__init__.py index 2184f20..5447922 100644 --- a/antiphoneclapper/__init__.py +++ b/antiphoneclapper/__init__.py @@ -1,5 +1,7 @@ from .antiphoneclapper import AntiPhoneClapper +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + async def setup(bot): bot.add_cog(AntiPhoneClapper(bot)) diff --git a/antiphoneclapper/antiphoneclapper.py b/antiphoneclapper/antiphoneclapper.py index 2999590..1145932 100644 --- a/antiphoneclapper/antiphoneclapper.py +++ b/antiphoneclapper/antiphoneclapper.py @@ -13,9 +13,13 @@ log = logging.getLogger("red.aikaterna.antiphoneclapper") class AntiPhoneClapper(commands.Cog): """This cog deletes bad GIFs that will crash phone clients.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot - self.session = aiohttp.ClientSession() + self.session = aiohttp.ClientSession() self.config = Config.get_conf(self, 2719371001, force_registration=True) default_guild = {"watching": []} @@ -60,9 +64,7 @@ class AntiPhoneClapper(commands.Cog): else: return await ctx.send("Channel is not being watched.") await self.config.guild(ctx.guild).watching.set(channel_list) - await ctx.send( - f"{self.bot.get_channel(channel.id).mention} will not have bad gifs removed." - ) + await ctx.send(f"{self.bot.get_channel(channel.id).mention} will not have bad gifs removed.") def is_phone_clapper(self, im): limit = im.size @@ -101,9 +103,7 @@ class AntiPhoneClapper(commands.Cog): if phone_clapper: try: await m.delete() - await m.channel.send( - f"{m.author.mention} just tried to send a phone-killing GIF and I removed it." - ) + await m.channel.send(f"{m.author.mention} just tried to send a phone-killing GIF and I removed it.") return except discord.errors.Forbidden: await m.channel.send(f"Don't send GIFs that do that, {m.author.mention}") diff --git a/antiphoneclapper/info.json b/antiphoneclapper/info.json index 5d15f4e..82c0d48 100644 --- a/antiphoneclapper/info.json +++ b/antiphoneclapper/info.json @@ -11,5 +11,6 @@ "pillow" ], "short": "Deletes messages with malformed GIFs.", - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/away/__init__.py b/away/__init__.py index 3ee8de9..7f24624 100644 --- a/away/__init__.py +++ b/away/__init__.py @@ -1,5 +1,16 @@ from .away import Away +__red_end_user_data_statement__ = ( + "This cog stores data provided by users " + "for the express purpose of redisplaying. " + "It does not store user data which was not " + "provided through a command. " + "Users may remove their own content " + "without making a data removal request. " + "This cog does not support data requests, " + "but will respect deletion requests." +) + def setup(bot): bot.add_cog(Away(bot)) diff --git a/away/away.py b/away/away.py index b00cff9..655537b 100644 --- a/away/away.py +++ b/away/away.py @@ -1,6 +1,6 @@ import discord from redbot.core import Config, commands, checks -from typing import Optional +from typing import Optional, Literal import datetime import re @@ -22,6 +22,11 @@ class Away(commands.Cog): "LISTENING_MESSAGE": False, } + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + await self._away.user_from_id(user_id).clear() + def __init__(self, bot): self.bot = bot self._away = Config.get_conf(self, 8423491260, force_registration=True) @@ -69,17 +74,14 @@ class Away(commands.Cog): em.set_author(name=f"{author.display_name} is currently idle", icon_url=avatar) elif state == "dnd": em = discord.Embed(description=message, color=color) - em.set_author( - name=f"{author.display_name} is currently do not disturb", icon_url=avatar - ) + em.set_author(name=f"{author.display_name} is currently do not disturb", icon_url=avatar) elif state == "offline": em = discord.Embed(description=message, color=color) em.set_author(name=f"{author.display_name} is currently offline", icon_url=avatar) elif state == "gaming": em = discord.Embed(description=message, color=color) em.set_author( - name=f"{author.display_name} is currently playing {author.activity.name}", - icon_url=avatar, + name=f"{author.display_name} is currently playing {author.activity.name}", icon_url=avatar, ) em.title = getattr(author.activity, "details", None) thumbnail = getattr(author.activity, "large_image_url", None) @@ -89,8 +91,7 @@ class Away(commands.Cog): status = [c for c in author.activities if c.type == discord.ActivityType.playing] em = discord.Embed(description=message, color=color) em.set_author( - name=f"{author.display_name} is currently playing {status[0].name}", - icon_url=avatar, + name=f"{author.display_name} is currently playing {status[0].name}", icon_url=avatar, ) em.title = getattr(status[0], "details", None) thumbnail = getattr(status[0], "large_image_url", None) @@ -140,8 +141,7 @@ class Away(commands.Cog): em.description = message + "\n" + author.activity.url em.title = getattr(author.activity, "details", None) em.set_author( - name=f"{author.display_name} is currently streaming {author.activity.name}", - icon_url=avatar, + name=f"{author.display_name} is currently streaming {author.activity.name}", icon_url=avatar, ) elif state == "streamingcustom": activity = [c for c in author.activities if c.type == discord.ActivityType.streaming] @@ -150,8 +150,7 @@ class Away(commands.Cog): em.description = message + "\n" + activity[0].url em.title = getattr(author.activity, "details", None) em.set_author( - name=f"{author.display_name} is currently streaming {activity[0].name}", - icon_url=avatar, + name=f"{author.display_name} is currently streaming {activity[0].name}", icon_url=avatar, ) else: em = discord.Embed(color=color) @@ -191,9 +190,7 @@ class Away(commands.Cog): status = [c for c in author.activities if c.type == discord.ActivityType.playing] msg = f"{author.display_name} is currently playing {status[0].name}" elif state == "listening": - artist_title = f"{author.activity.title} by " + ", ".join( - a for a in author.activity.artists - ) + artist_title = f"{author.activity.title} by " + ", ".join(a for a in author.activity.artists) currently_playing = self._draw_play(author.activity) msg = f"{author.display_name} is currently listening to {artist_title}\n{currently_playing}" elif state == "listeningcustom": @@ -313,9 +310,7 @@ class Away(commands.Cog): await message.channel.send(msg, delete_after=delete_after) continue if streaming_msg and type(author.activity) is discord.CustomActivity: - stream_status = [ - c for c in author.activities if c.type == discord.ActivityType.streaming - ] + stream_status = [c for c in author.activities if c.type == discord.ActivityType.streaming] if not stream_status: continue streaming_msg, delete_after = streaming_msg @@ -337,9 +332,7 @@ class Away(commands.Cog): await message.channel.send(msg, delete_after=delete_after) continue if listening_msg and type(author.activity) is discord.CustomActivity: - listening_status = [ - c for c in author.activities if c.type == discord.ActivityType.listening - ] + listening_status = [c for c in author.activities if c.type == discord.ActivityType.listening] if not listening_status: continue listening_msg, delete_after = listening_msg @@ -364,9 +357,7 @@ class Away(commands.Cog): await message.channel.send(msg, delete_after=delete_after) break if gaming_msgs and type(author.activity) is discord.CustomActivity: - game_status = [ - c for c in author.activities if c.type == discord.ActivityType.playing - ] + game_status = [c for c in author.activities if c.type == discord.ActivityType.playing] if not game_status: continue for game in gaming_msgs: @@ -501,15 +492,11 @@ class Away(commands.Cog): msg = "The bot will no longer reply for you when you're mentioned while listening to Spotify." else: await self._away.user(author).LISTENING_MESSAGE.set((message, delete_after)) - msg = ( - "The bot will now reply for you when you're mentioned while listening to Spotify." - ) + msg = "The bot will now reply for you when you're mentioned while listening to Spotify." await ctx.send(msg) @commands.command(name="gaming") - async def gaming_( - self, ctx, game: str, delete_after: Optional[int] = None, *, message: str = None - ): + async def gaming_(self, ctx, game: str, delete_after: Optional[int] = None, *, message: str = None): """ Set an automatic reply when you're playing a specified game. @@ -567,7 +554,9 @@ class Away(commands.Cog): if text_only: message = "Away messages will now be embedded or text only based on the bot's permissions for embed links." else: - message = "Away messages are now forced to be text only, regardless of the bot's permissions for embed links." + message = ( + "Away messages are now forced to be text only, regardless of the bot's permissions for embed links." + ) await self._away.guild(ctx.guild).TEXT_ONLY.set(not text_only) await ctx.send(message) @@ -619,9 +608,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/away/info.json b/away/info.json index 7df0c18..2d122ac 100644 --- a/away/info.json +++ b/away/info.json @@ -8,5 +8,6 @@ "tags": [ "away" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog stores data provided by users for the express purpose of redisplaying. It does not store user data which was not provided through a command. Users may remove their own content without making a data removal request. This cog does not support data requests, but will respect deletion requests." } \ No newline at end of file diff --git a/blurplefy/__init__.py b/blurplefy/__init__.py index 19b6f43..5a4d02a 100644 --- a/blurplefy/__init__.py +++ b/blurplefy/__init__.py @@ -1,5 +1,7 @@ from .blurplefy import Blurplefy +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Blurplefy(bot)) diff --git a/blurplefy/blurplefy.py b/blurplefy/blurplefy.py index 87d59cb..a5f6a92 100644 --- a/blurplefy/blurplefy.py +++ b/blurplefy/blurplefy.py @@ -7,7 +7,6 @@ from PIL import Image, ImageEnhance, ImageSequence from io import BytesIO import aiohttp import asyncio -import copy import datetime import io import math @@ -15,12 +14,16 @@ from resizeimage import resizeimage from redbot.core import Config, commands, checks blurple = (114, 137, 218) -blurplehex = 0x7289da +blurplehex = 0x7289DA darkblurple = (78, 93, 148) white = (255, 255, 255) class Blurplefy(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): """Blurplefy images and check content of images.""" self.bot = bot @@ -38,9 +41,7 @@ class Blurplefy(commands.Cog): """Toggle a role award for having a blurple profile picture.""" blurple_role_id = await self.config.guild(ctx.guild).blurple_role() if blurple_role_id is None: - await ctx.send( - "Enter the role name to award: it needs to be a valid, already existing role." - ) + await ctx.send("Enter the role name to award: it needs to be a valid, already existing role.") def check(m): return m.author == ctx.author @@ -140,11 +141,7 @@ class Blurplefy(commands.Cog): im = resizeimage.resize_width(im, (imsize[0] * downsizefraction)) imsize = list(im.size) impixels = imsize[0] * imsize[1] - await ctx.send( - "{}, image resized smaller for easier processing.".format( - ctx.message.author.display_name - ) - ) + await ctx.send("{}, image resized smaller for easier processing.".format(ctx.message.author.display_name)) image = self.blurple_imager(im, imsize) image = discord.File(fp=image, filename="image.png") @@ -155,19 +152,11 @@ class Blurplefy(commands.Cog): percentwhite = round(((noofwhitepixels / noofpixels) * 100), 2) embed = discord.Embed(title="", colour=0x7289DA) + embed.add_field(name="Total amount of Blurple", value="{}%".format(blurplenesspercentage), inline=False) + embed.add_field(name="Blurple (rgb(114, 137, 218))", value="{}%".format(percentblurple), inline=True) + embed.add_field(name="White (rgb(255, 255, 255))", value="{}\%".format(percentwhite), inline=True) embed.add_field( - name="Total amount of Blurple", value="{}%".format(blurplenesspercentage), inline=False - ) - embed.add_field( - name="Blurple (rgb(114, 137, 218))", value="{}%".format(percentblurple), inline=True - ) - embed.add_field( - name="White (rgb(255, 255, 255))", value="{}\%".format(percentwhite), inline=True - ) - embed.add_field( - name="Dark Blurple (rgb(78, 93, 148))", - value="{}\%".format(percentdblurple), - inline=True, + name="Dark Blurple (rgb(78, 93, 148))", value="{}\%".format(percentdblurple), inline=True, ) embed.add_field( name="Guide", @@ -247,20 +236,14 @@ class Blurplefy(commands.Cog): except Exception: isgif = False - await ctx.send( - "{}, image fetched, analyzing image...".format(ctx.message.author.display_name) - ) + await ctx.send("{}, image fetched, analyzing image...".format(ctx.message.author.display_name)) if impixels > maxpixelcount: downsizefraction = math.sqrt(maxpixelcount / impixels) im = resizeimage.resize_width(im, (imsize[0] * downsizefraction)) imsize = list(im.size) impixels = imsize[0] * imsize[1] - await ctx.send( - "{}, image resized smaller for easier processing".format( - ctx.message.author.display_name - ) - ) + await ctx.send("{}, image resized smaller for easier processing".format(ctx.message.author.display_name)) if isgif is False: image = self.imager(im, imsize) @@ -323,9 +306,7 @@ class Blurplefy(commands.Cog): for i in range(3): if not (blurple[i] + colourbuffer > pixel[i] > blurple[i] - colourbuffer): checkblurple = 0 - if not ( - darkblurple[i] + colourbuffer > pixel[i] > darkblurple[i] - colourbuffer - ): + if not (darkblurple[i] + colourbuffer > pixel[i] > darkblurple[i] - colourbuffer): checkdarkblurple = 0 if not (white[i] + colourbuffer > pixel[i] > white[i] - colourbuffer): checkwhite = 0 @@ -397,19 +378,11 @@ class Blurplefy(commands.Cog): @commands.command() async def countdown(self, ctx): """Countdown to Discord's 6th Anniversary.""" - embed = discord.Embed(name="", colour=0x7289da) - timeleft = ( - datetime.datetime(2020, 5, 13) - + datetime.timedelta(hours=7) - - datetime.datetime.utcnow() - ) + embed = discord.Embed(name="", colour=0x7289DA) + timeleft = datetime.datetime(2020, 5, 13) + datetime.timedelta(hours=7) - datetime.datetime.utcnow() embed.set_author(name="Time left until Discord's 6th Anniversary") if int(timeleft.total_seconds()) < 0: - timeleft = ( - datetime.datetime(2021, 5, 13) - + datetime.timedelta(hours=7) - - datetime.datetime.utcnow() - ) + 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.add_field( name="Countdown to midnight, May 13, California time (UTC-7):", diff --git a/blurplefy/info.json b/blurplefy/info.json index 8f7948e..1ef3c1c 100644 --- a/blurplefy/info.json +++ b/blurplefy/info.json @@ -18,5 +18,6 @@ "image", "profile" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/cah/__init__.py b/cah/__init__.py index 25a05f4..f6b7fa5 100644 --- a/cah/__init__.py +++ b/cah/__init__.py @@ -1,5 +1,7 @@ from .cah import CardsAgainstHumanity +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(CardsAgainstHumanity(bot)) diff --git a/cah/cah.py b/cah/cah.py index 5c87655..9fd6ef1 100644 --- a/cah/cah.py +++ b/cah/cah.py @@ -10,19 +10,19 @@ from redbot.core.data_manager import bundled_data_path class CardsAgainstHumanity(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.games = [] - self.maxBots = ( - 5 # Max number of bots that can be added to a game - don't count toward max players - ) + self.maxBots = 5 # Max number of bots that can be added to a game - don't count toward max players self.maxPlayers = 10 # Max players for ranjom joins self.maxDeadTime = 3600 # Allow an hour of dead time before killing a game self.checkTime = 300 # 5 minutes between dead time checks self.winAfter = 10 # 10 wins for the game - self.botWaitMin = ( - 5 # Minimum number of seconds before the bot makes a decision (default 5) - ) + self.botWaitMin = 5 # Minimum number of seconds before the bot makes a decision (default 5) self.botWaitMax = 30 # Max number of seconds before a bot makes a decision (default 30) self.userTimeout = 500 # 5 minutes to timeout self.utCheck = 30 # Check timeout every 30 seconds @@ -196,8 +196,7 @@ class CardsAgainstHumanity(commands.Cog): member["Task"] = None continue await self.sendToUser( - member["User"], - f"Game id: *{game['ID']}* has been closed due to inactivity.", + member["User"], f"Game id: *{game['ID']}* has been closed due to inactivity.", ) # Set running to false @@ -211,9 +210,7 @@ class CardsAgainstHumanity(commands.Cog): return True else: # Not in PM - await message.channel.send( - "Cards Against Humanity commands must be run in Direct Messages with the bot." - ) + await message.channel.send("Cards Against Humanity commands must be run in Direct Messages with the bot.") return False def randomID(self, length=8): @@ -310,8 +307,7 @@ class CardsAgainstHumanity(commands.Cog): if not newCreator["IsBot"]: newCreator["Creator"] = True await self.sendToUser( - newCreator["User"], - "The creator of this game left. **YOU** are now the creator.", + newCreator["User"], "The creator of this game left. **YOU** are now the creator.", ) break @@ -330,8 +326,7 @@ class CardsAgainstHumanity(commands.Cog): member["Task"] = None else: await self.sendToUser( - member["User"], - f"**You were removed from game id:** ***{game['ID']}.***", + member["User"], f"**You were removed from game id:** ***{game['ID']}.***", ) # Removed, no need to finish the loop break @@ -456,9 +451,7 @@ class CardsAgainstHumanity(commands.Cog): name=f"Not enough players to continue! ({len(game['Members'])}/{self.minMembers})" ) prefix = await self.bot.get_valid_prefixes() - stat_embed.set_footer( - text=f"Have other users join with: {prefix[0]}joincah {game['ID']}" - ) + stat_embed.set_footer(text=f"Have other users join with: {prefix[0]}joincah {game['ID']}") await self.sendToUser(member["User"], stat_embed, True) continue if member["IsBot"] == True: @@ -541,13 +534,9 @@ class CardsAgainstHumanity(commands.Cog): else: stat_embed.set_author(name=f"{winnerName} won!") if len(winner["Cards"]) == 1: - msg = "The **Winning** card was:\n\n{}".format( - "{}".format(" - ".join(winner["Cards"])) - ) + msg = "The **Winning** card was:\n\n{}".format("{}".format(" - ".join(winner["Cards"]))) else: - msg = "The **Winning** cards were:\n\n{}".format( - "{}".format(" - ".join(winner["Cards"])) - ) + msg = "The **Winning** cards were:\n\n{}".format("{}".format(" - ".join(winner["Cards"]))) await self.sendToUser(member["User"], stat_embed, True) await self.sendToUser(member["User"], msg) await asyncio.sleep(0.1) @@ -788,13 +777,9 @@ class CardsAgainstHumanity(commands.Cog): # Advances the game if len(game["Members"]) < self.minMembers: stat_embed = discord.Embed(color=discord.Color.red()) - stat_embed.set_author( - name=f"Not enough players to continue! ({len(game['Members'])}/{self.minMembers})" - ) + stat_embed.set_author(name=f"Not enough players to continue! ({len(game['Members'])}/{self.minMembers})") prefix = await self.bot.get_valid_prefixes() - stat_embed.set_footer( - text=f"Have other users join with: {prefix[0]}joincah {game['ID']}" - ) + stat_embed.set_footer(text=f"Have other users join with: {prefix[0]}joincah {game['ID']}") for member in game["Members"]: if member["IsBot"]: continue @@ -819,9 +804,7 @@ class CardsAgainstHumanity(commands.Cog): if member["IsBot"]: stat_embed.set_author(name=f"{self.botName} ({member['ID']}) is the WINNER!!") else: - stat_embed.set_author( - name=f"{self.displayname(member['User'])} is the WINNER!!" - ) + stat_embed.set_author(name=f"{self.displayname(member['User'])} is the WINNER!!") stat_embed.set_footer(text="Congratulations!") break if winner: @@ -935,9 +918,7 @@ class CardsAgainstHumanity(commands.Cog): user = member index = userGame["Members"].index(member) if index == userGame["Judge"]: - await self.sendToUser( - ctx.author, "You're the judge. You don't get to lay cards this round." - ) + await self.sendToUser(ctx.author, "You're the judge. You don't get to lay cards this round.") return for submit in userGame["Submitted"]: if submit["By"]["User"] == ctx.author: @@ -954,9 +935,7 @@ class CardsAgainstHumanity(commands.Cog): stat_embed.set_author( name=f"Not enough players to continue! ({len(userGame['Members'])}/{self.minMembers})" ) - stat_embed.set_footer( - text=f"Have other users join with: {prefix[0]}joincah {userGame['ID']}" - ) + stat_embed.set_footer(text=f"Have other users join with: {prefix[0]}joincah {userGame['ID']}") return await self.sendToUser(ctx.author, stat_embed, True) numberCards = userGame["BlackCard"]["Pick"] @@ -992,9 +971,7 @@ class CardsAgainstHumanity(commands.Cog): ) return await self.showHand(ctx, ctx.author) if c < 1 or c > len(user["Hand"]): - await self.sendToUser( - ctx.author, f"Card numbers must be between 1 and {len(user['Hand'])}." - ) + await self.sendToUser(ctx.author, f"Card numbers must be between 1 and {len(user['Hand'])}.") return await self.showHand(ctx, ctx.author) cards.append(user["Hand"][c - 1]["Text"]) # Remove from user's hand @@ -1009,14 +986,10 @@ class CardsAgainstHumanity(commands.Cog): try: card = int(card) except Exception: - await self.sendToUser( - ctx.author, f"You need to lay a valid card with `{prefix[0]}lay [card number]`" - ) + await self.sendToUser(ctx.author, f"You need to lay a valid card with `{prefix[0]}lay [card number]`") return await self.showHand(ctx, ctx.author) if card < 1 or card > len(user["Hand"]): - await self.sendToUser( - ctx.author, f"Card numbers must be between 1 and {len(user['Hand'])}." - ) + await self.sendToUser(ctx.author, f"Card numbers must be between 1 and {len(user['Hand'])}.") return await self.showHand(ctx, ctx.author) # Valid card newSubmission = {"By": user, "Cards": [user["Hand"].pop(card - 1)["Text"]]} @@ -1068,9 +1041,7 @@ class CardsAgainstHumanity(commands.Cog): except Exception: card = -1 if card < 0 or card >= totalUsers: - return await self.sendToUser( - ctx.author, f"Your pick must be between 1 and {totalUsers}." - ) + return await self.sendToUser(ctx.author, f"Your pick must be between 1 and {totalUsers}.") # Pick is good! await self.winningCard(ctx, userGame, card) @@ -1099,9 +1070,7 @@ class CardsAgainstHumanity(commands.Cog): 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." - ) + return await ctx.send("You must allow Direct Messages from the bot for this game to work.") # Check if the user is already in game userGame = await self.userGame(ctx.author) @@ -1178,9 +1147,7 @@ class CardsAgainstHumanity(commands.Cog): 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." - ) + return await ctx.send("You must allow Direct Messages from the bot for this game to work.") # Check if the user is already in game userGame = await self.userGame(ctx.author) @@ -1253,9 +1220,7 @@ class CardsAgainstHumanity(commands.Cog): for member in game["Members"]: if member["IsBot"]: continue - await self.sendToUser( - member["User"], f"***{self.displayname(ctx.author)}*** **joined the game!**" - ) + await self.sendToUser(member["User"], f"***{self.displayname(ctx.author)}*** **joined the game!**") # We got a user! currentTime = int(time.time()) @@ -1328,9 +1293,7 @@ class CardsAgainstHumanity(commands.Cog): # We are the creator - let's check the number of bots if botCount >= self.maxBots: # Too many bots! - return await self.sendToUser( - ctx.author, f"You already have enough bots (max is {self.maxBots})." - ) + return await self.sendToUser(ctx.author, f"You already have enough bots (max is {self.maxBots}).") # We can get another bot! botID = self.randomBotID(userGame) lobot = { @@ -1350,9 +1313,7 @@ class CardsAgainstHumanity(commands.Cog): for member in userGame["Members"]: if member["IsBot"]: continue - await self.sendToUser( - member["User"], f"***{self.botName} ({botID})*** **joined the game!**" - ) + await self.sendToUser(member["User"], f"***{self.botName} ({botID})*** **joined the game!**") # await self.nextPlay(ctx, userGame) # Check if adding put us at minimum members @@ -1391,9 +1352,7 @@ class CardsAgainstHumanity(commands.Cog): if member["User"] == ctx.author: if not member["Creator"]: # You didn't make this game - return await self.sendToUser( - ctx.author, "Only the player that created the game can add bots." - ) + return await self.sendToUser(ctx.author, "Only the player that created the game can add bots.") member["Time"] = int(time.time()) if number == None: # No number specified - let's add the max number of bots @@ -1407,9 +1366,7 @@ class CardsAgainstHumanity(commands.Cog): # We are the creator - let's check the number of bots if botCount >= self.maxBots: # Too many bots! - return await self.sendToUser( - ctx.author, f"You already have enough bots (max is {self.maxBots})." - ) + return await self.sendToUser(ctx.author, f"You already have enough bots (max is {self.maxBots}).") if number > (self.maxBots - botCount): number = self.maxBots - botCount @@ -1483,9 +1440,7 @@ class CardsAgainstHumanity(commands.Cog): if member["User"] == ctx.author: if not member["Creator"]: # You didn't make this game - return await self.sendToUser( - ctx.author, "Only the player that created the game can remove bots." - ) + return await self.sendToUser(ctx.author, "Only the player that created the game can remove bots.") member["Time"] = int(time.time()) # We are the creator - let's check the number of bots if id == None: @@ -1761,9 +1716,7 @@ class CardsAgainstHumanity(commands.Cog): if member["User"] == ctx.author: if not member["Creator"]: # You didn't make this game - return await self.sendToUser( - ctx.author, "Only the player that created the game can remove bots." - ) + return await self.sendToUser(ctx.author, "Only the player that created the game can remove bots.") # We are the creator - let's check the number of bots if setting == None: # Output idle kick status diff --git a/cah/info.json b/cah/info.json index e82ea87..4e78f86 100644 --- a/cah/info.json +++ b/cah/info.json @@ -14,5 +14,6 @@ "cards", "games" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/chatchart/__init__.py b/chatchart/__init__.py index 1d48999..544e427 100644 --- a/chatchart/__init__.py +++ b/chatchart/__init__.py @@ -1,5 +1,7 @@ from .chatchart import Chatchart +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Chatchart(bot)) diff --git a/chatchart/chatchart.py b/chatchart/chatchart.py index 65c9bb0..66221e9 100644 --- a/chatchart/chatchart.py +++ b/chatchart/chatchart.py @@ -5,7 +5,6 @@ # Thanks to violetnyte for suggesting this cog. import discord import heapq -import os from io import BytesIO from typing import Optional import matplotlib @@ -20,6 +19,10 @@ from redbot.core import commands class Chatchart(commands.Cog): """Show activity.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot @@ -85,7 +88,7 @@ class Chatchart(commands.Cog): """ Generates a pie chart, representing the last 5000 messages in the specified channel. """ - e = discord.Embed(description="Loading...", colour=0x00ccff) + e = discord.Embed(description="Loading...", colour=0x00CCFF) e.set_thumbnail(url="https://i.imgur.com/vSp4xRk.gif") em = await ctx.send(embed=e) @@ -119,9 +122,9 @@ class Chatchart(commands.Cog): msg_data["users"][whole_name]["msgcount"] = 1 msg_data["total count"] += 1 - if msg_data['users'] == {}: + if msg_data["users"] == {}: await em.delete() - return await ctx.message.channel.send(f'Only bots have sent messages in {channel.mention}') + return await ctx.message.channel.send(f"Only bots have sent messages in {channel.mention}") for usr in msg_data["users"]: pd = float(msg_data["users"][usr]["msgcount"]) / float(msg_data["total count"]) @@ -129,12 +132,7 @@ class Chatchart(commands.Cog): top_ten = heapq.nlargest( 20, - [ - (x, msg_data["users"][x][y]) - for x in msg_data["users"] - for y in msg_data["users"][x] - if y == "percent" - ], + [(x, msg_data["users"][x][y]) for x in msg_data["users"] for y in msg_data["users"][x] if y == "percent"], key=lambda x: x[1], ) others = 100 - sum(x[1] for x in top_ten) diff --git a/chatchart/info.json b/chatchart/info.json index 42427fe..90eb45d 100644 --- a/chatchart/info.json +++ b/chatchart/info.json @@ -14,5 +14,6 @@ "requirements": [ "matplotlib" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/dadjokes/__init__.py b/dadjokes/__init__.py index 7196d02..17e33f4 100644 --- a/dadjokes/__init__.py +++ b/dadjokes/__init__.py @@ -1,5 +1,7 @@ from .dadjokes import DadJokes +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(DadJokes(bot)) diff --git a/dadjokes/dadjokes.py b/dadjokes/dadjokes.py index 3a5b492..eea620b 100644 --- a/dadjokes/dadjokes.py +++ b/dadjokes/dadjokes.py @@ -1,16 +1,21 @@ from redbot.core import commands import aiohttp + class DadJokes(commands.Cog): """Random dad jokes from icanhazdadjoke.com""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot @commands.command() async def dadjoke(self, ctx): """Gets a random dad joke.""" - api = 'https://icanhazdadjoke.com/' - async with aiohttp.request('GET', api, headers={'Accept': 'text/plain'}) as r: + api = "https://icanhazdadjoke.com/" + async with aiohttp.request("GET", api, headers={"Accept": "text/plain"}) as r: result = await r.text() await ctx.send(f"`{result}`") diff --git a/dadjokes/info.json b/dadjokes/info.json index 58c1d1b..733815e 100644 --- a/dadjokes/info.json +++ b/dadjokes/info.json @@ -6,5 +6,6 @@ "install_msg": "Gets a random dad joke from icanhazdadjoke.com. Thanks for installing.", "short": "Random dad jokes", "tags": ["jokes", "dad", "dadjokes"], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/dictionary/__init__.py b/dictionary/__init__.py index e943b1d..8b2ce43 100644 --- a/dictionary/__init__.py +++ b/dictionary/__init__.py @@ -1,5 +1,7 @@ from .dictionary import Dictionary +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Dictionary(bot)) diff --git a/dictionary/dictionary.py b/dictionary/dictionary.py index 459f49b..dd09c65 100644 --- a/dictionary/dictionary.py +++ b/dictionary/dictionary.py @@ -12,6 +12,10 @@ class Dictionary(commands.Cog): """Word, yo Parts of this cog are adapted from the PyDictionary library.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() diff --git a/dictionary/info.json b/dictionary/info.json index 49c1b48..5198304 100644 --- a/dictionary/info.json +++ b/dictionary/info.json @@ -11,5 +11,6 @@ "requirements": [ "beautifulsoup4" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/dungeon/__init__.py b/dungeon/__init__.py index ce1d2e5..c33f837 100644 --- a/dungeon/__init__.py +++ b/dungeon/__init__.py @@ -1,5 +1,9 @@ from .dungeon import Dungeon +__red_end_user_data_statement__ = ( + "This cog does not persistently store end user data. " "This cog does store discord IDs as needed for operation. " +) + def setup(bot): bot.add_cog(Dungeon(bot)) diff --git a/dungeon/dungeon.py b/dungeon/dungeon.py index 6331ecb..993157a 100644 --- a/dungeon/dungeon.py +++ b/dungeon/dungeon.py @@ -1,5 +1,6 @@ -import asyncio import datetime +from typing import Literal + import discord import logging from redbot.core import Config, commands, checks, modlog @@ -12,6 +13,20 @@ log = logging.getLogger("red.aikaterna.dungeon") class Dungeon(commands.Cog): """Auto-quarantine suspicious users.""" + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + if requester == "discord": + # user is deleted, just comply + + data = await self.config.all_guilds() + for guild_id, guild_data in data.items(): + if user_id in guild_data.get("bypass", []): + bypass = guild_data.get("bypass", []) + bypass = set(bypass) + bypass.discard(user_id) + await self.config.guild_from_id(guild_id).bypass.set(list(bypass)) + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2700081001, force_registration=True) @@ -58,17 +73,13 @@ class Dungeon(commands.Cog): return await ctx.send("No dungeon role set.") try: - await user.edit( - roles=[], reason=f"Removing all roles, {ctx.message.author} is banishing user" - ) + await user.edit(roles=[], reason=f"Removing all roles, {ctx.message.author} is banishing user") except discord.Forbidden: return await ctx.send( "I need permission to manage roles or the role hierarchy might not allow me to do this. I need a role higher than the person you're trying to banish." ) - await user.add_roles( - dungeon_role_obj, reason=f"Adding dungeon role, {ctx.message.author} is banishing user" - ) + await user.add_roles(dungeon_role_obj, reason=f"Adding dungeon role, {ctx.message.author} is banishing user") if blacklist: blacklist_msg = ", blacklisted from the bot," @@ -89,9 +100,7 @@ class Dungeon(commands.Cog): """Sets the announcement channel for users moved to the dungeon.""" await self.config.guild(ctx.guild).announce_channel.set(channel.id) announce_channel_id = await self.config.guild(ctx.guild).announce_channel() - await ctx.send( - f"User announcement channel set to: {self.bot.get_channel(announce_channel_id).mention}." - ) + await ctx.send(f"User announcement channel set to: {self.bot.get_channel(announce_channel_id).mention}.") @dungeon.command() async def autoban(self, ctx): @@ -106,9 +115,7 @@ class Dungeon(commands.Cog): auto_ban = await self.config.guild(ctx.guild).auto_ban() if not ban_message: await self.config.guild(ctx.guild).auto_ban_message.set(None) - return await ctx.send( - "Auto-ban message removed. No message will be sent on an auto-ban." - ) + return await ctx.send("Auto-ban message removed. No message will be sent on an auto-ban.") await self.config.guild(ctx.guild).auto_ban_message.set(str(ban_message)) await self.config.guild(ctx.guild).auto_ban.set(True) await ctx.send(f"Auto-ban has been turned on.\nMessage to send on ban:\n{ban_message}") @@ -254,13 +261,10 @@ class Dungeon(commands.Cog): role_check = True try: await user.remove_roles( - dungeon_role_obj, - reason=f"Removing dungeon role, verified by {ctx.message.author}.", + dungeon_role_obj, reason=f"Removing dungeon role, verified by {ctx.message.author}.", ) if not user_role_obj: - return await ctx.send( - "Dungeon role removed, but no member role is set so I can't award one." - ) + return await ctx.send("Dungeon role removed, but no member role is set so I can't award one.") await user.add_roles(user_role_obj, reason="Adding member role.") except discord.Forbidden: return await ctx.send( @@ -281,9 +285,7 @@ class Dungeon(commands.Cog): try: await user.send(dm_message) except discord.Forbidden: - await ctx.send( - f"I couldn't DM {user} to let them know they've been verified, they've blocked me." - ) + await ctx.send(f"I couldn't DM {user} to let them know they've been verified, they've blocked me.") @dungeon.command() async def autosetup(self, ctx): @@ -291,11 +293,7 @@ class Dungeon(commands.Cog): You must deny the default role (@ everyone) from viewing or typing in other channels in your server manually. """ try: - overwrites = { - ctx.guild.default_role: discord.PermissionOverwrite( - send_messages=False, read_messages=False - ) - } + overwrites = {ctx.guild.default_role: discord.PermissionOverwrite(send_messages=False, read_messages=False)} dungeon_role = await ctx.guild.create_role(name="Dungeon") @@ -304,9 +302,7 @@ class Dungeon(commands.Cog): dungeon_role, read_messages=True, send_messages=False, read_message_history=True ) - dungeon_channel = await ctx.guild.create_text_channel( - "Silenced", category=dungeon_category - ) + dungeon_channel = await ctx.guild.create_text_channel("Silenced", category=dungeon_category) await dungeon_channel.set_permissions( dungeon_role, read_messages=True, send_messages=False, read_message_history=True ) @@ -409,8 +405,7 @@ class Dungeon(commands.Cog): user_role_obj = discord.utils.get(member.guild.roles, id=user_role_id) try: await member.add_roles( - user_role_obj, - reason="User has bypassed Dungeon checks. Assigning member role.", + user_role_obj, reason="User has bypassed Dungeon checks. Assigning member role.", ) except discord.Forbidden: pass @@ -444,9 +439,7 @@ class Dungeon(commands.Cog): log.debug(perm_msg) return try: - await member.guild.ban( - member, reason="Dungeon auto-ban", delete_message_days=0 - ) + await member.guild.ban(member, reason="Dungeon auto-ban", delete_message_days=0) except discord.Forbidden: if announce_channel: return await channel_object.send( @@ -466,14 +459,7 @@ class Dungeon(commands.Cog): else: try: await modlog.create_case( - self.bot, - member.guild, - now, - "ban", - member, - member.guild.me, - until=None, - channel=None, + self.bot, member.guild, now, "ban", member, member.guild.me, until=None, channel=None, ) except RuntimeError: log.error( diff --git a/dungeon/info.json b/dungeon/info.json index 56fe4e3..686742d 100644 --- a/dungeon/info.json +++ b/dungeon/info.json @@ -15,5 +15,6 @@ "dungeon", "autoban" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store end user data. This cog does store discord IDs as needed for operation. " } \ No newline at end of file diff --git a/hunting/__init__.py b/hunting/__init__.py index 7fabfef..85807d6 100644 --- a/hunting/__init__.py +++ b/hunting/__init__.py @@ -1,5 +1,14 @@ from .hunting import Hunting +__red_end_user_data_statement__ = ( + "This cog does not persistently store end user data. " + "This cog does store discord IDs as needed for operation. " + "This cog does store user stats for the cog such as their score. " + "Users may remove their own content without making a data removal request." + "This cog does not support data requests, " + "but will respect deletion requests." +) + async def setup(bot): bot.add_cog(Hunting(bot)) diff --git a/hunting/hunting.py b/hunting/hunting.py index 6fc0e9b..4da5630 100644 --- a/hunting/hunting.py +++ b/hunting/hunting.py @@ -1,7 +1,8 @@ +from typing import Literal + import asyncio import discord import datetime -import itertools import math import random import time @@ -11,12 +12,17 @@ from redbot.core.utils.chat_formatting import bold, box, humanize_list, humanize from redbot.core.utils.menus import menu, DEFAULT_CONTROLS -__version__ = "3.1.3" +__version__ = "3.1.4" class Hunting(commands.Cog): """Hunting, it hunts birds and things that fly.""" + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + await self.config.user_from_id(user_id).clear() + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2784481002, force_registration=True) @@ -92,9 +98,7 @@ class Hunting(commands.Cog): header = "{score:{score_len}}{name:2}\n".format( score="# Birds Shot", score_len=score_len + 5, - name="Name" - if not str(ctx.author.mobile_status) in ["online", "idle", "dnd"] - else "Name", + name="Name" if not str(ctx.author.mobile_status) in ["online", "idle", "dnd"] else "Name", ) temp_msg = header for account in sorted_acc: @@ -124,9 +128,7 @@ class Hunting(commands.Cog): colour=await ctx.bot.get_embed_color(location=ctx.channel), description=box(title, lang="prolog") + (box(page, lang="md")), ) - embed.set_footer( - text=f"Page {humanize_number(pages)}/{humanize_number(math.ceil(len(temp_msg) / 800))}" - ) + embed.set_footer(text=f"Page {humanize_number(pages)}/{humanize_number(math.ceil(len(temp_msg) / 800))}") pages += 1 page_list.append(embed) if len(page_list) == 1: @@ -252,7 +254,9 @@ class Hunting(commands.Cog): await self.config.guild(ctx.guild).hunt_interval_minimum.set(interval_min) await self.config.guild(ctx.guild).hunt_interval_maximum.set(interval_max) await self.config.guild(ctx.guild).wait_for_bang_timeout.set(bang_timeout) - message += f"Timing has been set:\nMin time {interval_min}s\nMax time {interval_max}s\nBang timeout {bang_timeout}s" + message += ( + f"Timing has been set:\nMin time {interval_min}s\nMax time {interval_max}s\nBang timeout {bang_timeout}s" + ) await ctx.send(bold(message)) @hunting.command() @@ -280,9 +284,7 @@ class Hunting(commands.Cog): if channel.id not in self.paused_games: self.paused_games.append(channel.id) await channel.send( - bold( - "It seems there are no hunters here. The hunt will be resumed when someone treads here again." - ) + bold("It seems there are no hunters here. The hunt will be resumed when someone treads here again.") ) return False @@ -306,9 +308,7 @@ class Hunting(commands.Cog): return False if channel != message.channel: return False - return ( - message.content.lower().split(" ")[0] == "bang" if message.content else False - ) + return message.content.lower().split(" ")[0] == "bang" if message.content else False try: bang_msg = await self.bot.wait_for("message", check=check, timeout=timeout) @@ -346,9 +346,7 @@ class Hunting(commands.Cog): bang_now = time.time() time_for_bang = "{:.3f}".format(bang_now - now) - bangtime = ( - "" if not await self.config.guild(guild).bang_time() else f" in {time_for_bang}s" - ) + bangtime = "" if not await self.config.guild(guild).bang_time() else f" in {time_for_bang}s" if random.randrange(0, 17) > 1: await self._add_score(guild, author, animal) @@ -379,9 +377,7 @@ class Hunting(commands.Cog): self.in_game.append(message.channel.id) guild_data = await self.config.guild(message.guild).all() - wait_time = random.randrange( - guild_data["hunt_interval_minimum"], guild_data["hunt_interval_maximum"] - ) + wait_time = random.randrange(guild_data["hunt_interval_minimum"], guild_data["hunt_interval_maximum"]) self.next_bang[message.guild.id] = datetime.datetime.fromtimestamp( int(time.mktime(datetime.datetime.utcnow().timetuple())) + wait_time ) diff --git a/hunting/info.json b/hunting/info.json index 356c790..cca94fa 100644 --- a/hunting/info.json +++ b/hunting/info.json @@ -4,5 +4,6 @@ "install_msg": "Check out [p]hunting to get started.", "short": "A bird hunting game.", "tags": ["hunting", "hunt", "game"], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store end user data. This cog does store discord IDs as needed for operation. This cog does store user stats for the cog such as their score. Users may remove their own content without making a data removal request. This cog does not support data requests, but will respect deletion requests." } \ No newline at end of file diff --git a/icyparser/__init__.py b/icyparser/__init__.py index 004966a..d789f80 100644 --- a/icyparser/__init__.py +++ b/icyparser/__init__.py @@ -1,5 +1,7 @@ from .icyparser import IcyParser +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(IcyParser(bot)) diff --git a/icyparser/icyparser.py b/icyparser/icyparser.py index 14dd40d..5e3872d 100644 --- a/icyparser/icyparser.py +++ b/icyparser/icyparser.py @@ -7,6 +7,10 @@ from redbot.core import commands class IcyParser(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() @@ -69,9 +73,7 @@ class IcyParser(commands.Cog): f"Can't read the stream information for <{player.current.uri if not url else url}>, it may not be an Icecast or Shoutcast radio station or there may be no stream information available." ) song = f"**[{icy[0]}]({player.current.uri if not url else url})**\n" - embed = discord.Embed( - colour=await ctx.embed_colour(), title="Now Playing", description=song - ) + embed = discord.Embed(colour=await ctx.embed_colour(), title="Now Playing", description=song) if icy[2]: embed.set_thumbnail(url=icy[1]) await ctx.send(embed=embed) diff --git a/icyparser/info.json b/icyparser/info.json index f29d288..45e6bbf 100644 --- a/icyparser/info.json +++ b/icyparser/info.json @@ -10,5 +10,6 @@ "icecast", "shoutcast" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/inspirobot/__init__.py b/inspirobot/__init__.py index a362be9..2c1ec4d 100644 --- a/inspirobot/__init__.py +++ b/inspirobot/__init__.py @@ -1,5 +1,7 @@ from .inspirobot import Inspirobot +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Inspirobot(bot)) diff --git a/inspirobot/info.json b/inspirobot/info.json index 537a8f7..5a134d9 100644 --- a/inspirobot/info.json +++ b/inspirobot/info.json @@ -12,5 +12,6 @@ "inspire", "inspirobot" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/inspirobot/inspirobot.py b/inspirobot/inspirobot.py index 264e19a..626054f 100644 --- a/inspirobot/inspirobot.py +++ b/inspirobot/inspirobot.py @@ -5,6 +5,11 @@ from redbot.core import commands class Inspirobot(commands.Cog): """Posts images generated by https://inspirobot.me""" + + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() @@ -13,9 +18,7 @@ class Inspirobot(commands.Cog): async def inspireme(self, ctx): """Fetch a random "inspirational message" from the bot.""" try: - async with self.session.request( - "GET", "http://inspirobot.me/api?generate=true" - ) as page: + async with self.session.request("GET", "http://inspirobot.me/api?generate=true") as page: pic = await page.text(encoding="utf-8") em = discord.Embed() em.set_image(url=pic) diff --git a/luigipoker/__init__.py b/luigipoker/__init__.py index d38bf2a..091866b 100644 --- a/luigipoker/__init__.py +++ b/luigipoker/__init__.py @@ -1,5 +1,7 @@ from .luigipoker import LuigiPoker +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(LuigiPoker(bot)) diff --git a/luigipoker/info.json b/luigipoker/info.json index b080d15..0ff2e18 100644 --- a/luigipoker/info.json +++ b/luigipoker/info.json @@ -4,5 +4,6 @@ "install_msg": "Thanks for installing.", "short": "A Luigi poker minigame.", "tags": ["poker", "game"], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/luigipoker/luigipoker.py b/luigipoker/luigipoker.py index 54f77c6..716be78 100644 --- a/luigipoker/luigipoker.py +++ b/luigipoker/luigipoker.py @@ -1,7 +1,7 @@ import asyncio import logging from random import randint -from redbot.core import commands, checks +from redbot.core import commands from redbot.core.utils.chat_formatting import box from redbot.core.utils.predicates import MessagePredicate @@ -83,8 +83,11 @@ class Deck: class LuigiPoker(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return - __version__ = "0.1.1" + __version__ = "0.1.2" def __init__(self, bot): self.bot = bot @@ -165,8 +168,7 @@ class LuigiPoker(commands.Cog): return await self.fold(ctx) else: log.error( - "LuigiPoker: Something broke unexpectedly in _play_response. Please report it.", - exc_info=True, + "LuigiPoker: Something broke unexpectedly in _play_response. Please report it.", exc_info=True, ) async def hit(self, ctx): @@ -176,9 +178,7 @@ class LuigiPoker(commands.Cog): "Examples: `1,3,5` or `4, 5`" ) try: - user_resp = await ctx.bot.wait_for( - "message", timeout=60, check=MessagePredicate.same_context(ctx) - ) + user_resp = await ctx.bot.wait_for("message", timeout=60, check=MessagePredicate.same_context(ctx)) except asyncio.TimeoutError: await ctx.send("No response.") return await self.fold(ctx) diff --git a/noflippedtables/__init__.py b/noflippedtables/__init__.py index bf8963f..38cb211 100644 --- a/noflippedtables/__init__.py +++ b/noflippedtables/__init__.py @@ -1,5 +1,7 @@ from .noflippedtables import NoFlippedTables +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): - bot.add_cog(NoFlippedTables(bot)) + bot.add_cog(NoFlippedTables(bot)) diff --git a/noflippedtables/info.json b/noflippedtables/info.json index 37195e3..f8a3ccc 100644 --- a/noflippedtables/info.json +++ b/noflippedtables/info.json @@ -5,5 +5,6 @@ "description" : "Unflip all the flipped tables.", "install_msg" : "Usage: [p]help tableset", "tags" : ["noflippedtables", "no flip", "tables"], - "disabled" : false + "disabled" : false, + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/noflippedtables/noflippedtables.py b/noflippedtables/noflippedtables.py index 9cb2b05..96507fa 100644 --- a/noflippedtables/noflippedtables.py +++ b/noflippedtables/noflippedtables.py @@ -8,6 +8,10 @@ from redbot.core.utils.chat_formatting import box class NoFlippedTables(commands.Cog): """For the table sympathizers""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2712290002, force_registration=True) diff --git a/nolinks/__init__.py b/nolinks/__init__.py index 551e0f8..f7c388b 100644 --- a/nolinks/__init__.py +++ b/nolinks/__init__.py @@ -1,5 +1,7 @@ from .nolinks import NoLinks +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): - bot.add_cog(NoLinks(bot)) \ No newline at end of file + bot.add_cog(NoLinks(bot)) diff --git a/nolinks/info.json b/nolinks/info.json index 0ed2554..2f5d361 100644 --- a/nolinks/info.json +++ b/nolinks/info.json @@ -13,5 +13,6 @@ "links", "automod" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/nolinks/nolinks.py b/nolinks/nolinks.py index 72534e0..d83caa1 100644 --- a/nolinks/nolinks.py +++ b/nolinks/nolinks.py @@ -3,10 +3,15 @@ import re from redbot.core import Config, commands, checks LINKS = re.compile( - "(([\w]+:)?//)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,63}(:[\d]+)?(/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?" - ) + "(([\w]+:)?//)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,63}(:[\d]+)?(/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?" +) + class NoLinks(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2740131001, force_registration=True) @@ -128,12 +133,8 @@ class NoLinks(commands.Cog): sentence = message.content.split() for word in sentence: if self._match_url(word): - msg = "**Message Removed in** {} ({})\n".format( - message.channel.mention, message.channel.id - ) - msg += "**Message sent by**: {} ({})\n".format( - message.author.name, message.author.id - ) + msg = "**Message Removed in** {} ({})\n".format(message.channel.mention, message.channel.id) + msg += "**Message sent by**: {} ({})\n".format(message.author.name, message.author.id) msg += "**Message content**:\n{}".format(message.content) if message_channel: await message_channel.send(msg) diff --git a/otherbot/__init__.py b/otherbot/__init__.py index 5112bf8..148cb4f 100644 --- a/otherbot/__init__.py +++ b/otherbot/__init__.py @@ -1,5 +1,9 @@ from .otherbot import Otherbot +__red_end_user_data_statement__ = ( + "This cog does not persistently store end user data. " "This cog does store discord IDs as needed for operation. " +) + async def setup(bot): cog = Otherbot(bot) diff --git a/otherbot/info.json b/otherbot/info.json index c9eb8cd..c300e99 100644 --- a/otherbot/info.json +++ b/otherbot/info.json @@ -1,9 +1,16 @@ { - "author": ["aikaterna", "Predä 。#1001"], - "description": "Alerts a role when bot(s) go offline.", - "install_msg": "Thanks for installing, have fun.", - "permissions": ["manage_roles"], - "short": "Alerts a role when bot(s) go offline.", - "tags": ["bots"], - "type": "COG" + "author": [ + "aikaterna", "Predä 。#1001" + ], + "description": "Alerts a role when bot(s) go offline.", + "install_msg": "Thanks for installing, have fun.", + "permissions" : [ + "manage_roles" + ], + "short": "Alerts a role when bot(s) go offline.", + "tags": [ + "bots" + ], + "type": "COG", + "end_user_data_statement": "This cog does not persistently store end user data. This cog does store discord IDs as needed for operation. " } diff --git a/otherbot/otherbot.py b/otherbot/otherbot.py index d7bfc8a..cd9dfae 100644 --- a/otherbot/otherbot.py +++ b/otherbot/otherbot.py @@ -1,3 +1,5 @@ +from typing import Literal + import discord from redbot.core.bot import Red from redbot.core import commands, checks, Config @@ -10,7 +12,21 @@ DEFAULT_ONLINE_EMOJI = "\N{WHITE HEAVY CHECK MARK}" class Otherbot(commands.Cog): __author__ = ["aikaterna", "Predä 。#1001"] - __version__ = "0.9" + __version__ = "0.10" + + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + if requester == "discord": + # user is deleted, just comply + + data = await self.config.all_guilds() + for guild_id, guild_data in data.items(): + if user_id in guild_data.get("watching", []): + bypass = guild_data.get("watching", []) + bypass = set(bypass) + bypass.discard(user_id) + await self.config.guild_from_id(guild_id).bypass.set(list(bypass)) def __init__(self, bot: Red): self.bot = bot diff --git a/partycrash/__init__.py b/partycrash/__init__.py index 2b7db1f..b530ded 100644 --- a/partycrash/__init__.py +++ b/partycrash/__init__.py @@ -1,5 +1,7 @@ from .partycrash import PartyCrash +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(PartyCrash(bot)) diff --git a/partycrash/info.json b/partycrash/info.json index 9527a60..81e8975 100644 --- a/partycrash/info.json +++ b/partycrash/info.json @@ -11,5 +11,6 @@ "tags": [ "invite" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/partycrash/partycrash.py b/partycrash/partycrash.py index 0ab2e00..9efdf48 100644 --- a/partycrash/partycrash.py +++ b/partycrash/partycrash.py @@ -8,6 +8,10 @@ class PartyCrash(commands.Cog): """Partycrash inspired by v2 Admin by Will Does not generate invites, only lists existing invites.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot @@ -75,8 +79,6 @@ class PartyCrash(commands.Cog): try: await self._get_invites(guild, ctx) except discord.errors.Forbidden: - return await ctx.send( - f"I don't have permission to get invites for {guild.name}." - ) + return await ctx.send(f"I don't have permission to get invites for {guild.name}.") except asyncio.TimeoutError: return await ctx.send("No server number entered, try again later.") diff --git a/pingtime/__init__.py b/pingtime/__init__.py index 651013a..a33f9a4 100644 --- a/pingtime/__init__.py +++ b/pingtime/__init__.py @@ -1,5 +1,7 @@ from .pingtime import Pingtime +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Pingtime(bot)) diff --git a/pingtime/info.json b/pingtime/info.json index bb04894..59a694e 100644 --- a/pingtime/info.json +++ b/pingtime/info.json @@ -9,5 +9,6 @@ "pingtime", "latency" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/pingtime/pingtime.py b/pingtime/pingtime.py index 1cf553a..b450b53 100644 --- a/pingtime/pingtime.py +++ b/pingtime/pingtime.py @@ -3,8 +3,14 @@ from redbot.core import commands BaseCog = getattr(commands, "Cog", object) + class Pingtime(BaseCog): """🏓""" + + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot diff --git a/pressf/__init__.py b/pressf/__init__.py index f4e0737..eedd458 100644 --- a/pressf/__init__.py +++ b/pressf/__init__.py @@ -1,4 +1,7 @@ from .pressf import PressF +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + + def setup(bot): bot.add_cog(PressF(bot)) diff --git a/pressf/info.json b/pressf/info.json index 4a85471..475f67f 100644 --- a/pressf/info.json +++ b/pressf/info.json @@ -9,5 +9,6 @@ "pressf", "respects" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/pressf/pressf.py b/pressf/pressf.py index 6c5643b..57dd896 100644 --- a/pressf/pressf.py +++ b/pressf/pressf.py @@ -7,6 +7,10 @@ from redbot.core.utils.common_filters import filter_mass_mentions class PressF(commands.Cog): """Pay some respects.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.channels = {} @@ -41,13 +45,13 @@ class PressF(commands.Cog): f"Everyone, let's pay respects to **{filter_mass_mentions(answer)}**! Press the f reaction on the this message to pay respects." ) await message.add_reaction("\U0001f1eb") - self.channels[str(ctx.channel.id)] = {'msg_id': message.id, 'reacted': []} + self.channels[str(ctx.channel.id)] = {"msg_id": message.id, "reacted": []} await asyncio.sleep(120) try: await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): pass - amount = len(self.channels[str(ctx.channel.id)]['reacted']) + amount = len(self.channels[str(ctx.channel.id)]["reacted"]) word = "person has" if amount == 1 else "people have" await ctx.send(f"**{amount}** {word} paid respects to **{filter_mass_mentions(answer)}**.") del self.channels[str(ctx.channel.id)] @@ -56,11 +60,11 @@ class PressF(commands.Cog): async def on_reaction_add(self, reaction, user): if str(reaction.message.channel.id) not in self.channels: return - if self.channels[str(reaction.message.channel.id)]['msg_id'] != reaction.message.id: + if self.channels[str(reaction.message.channel.id)]["msg_id"] != reaction.message.id: return if user.id == self.bot.user.id: return - if user.id not in self.channels[str(reaction.message.channel.id)]['reacted']: + if user.id not in self.channels[str(reaction.message.channel.id)]["reacted"]: if str(reaction.emoji) == "\U0001f1eb": await reaction.message.channel.send(f"**{user.name}** has paid their respects.") - self.channels[str(reaction.message.channel.id)]['reacted'].append(user.id) + self.channels[str(reaction.message.channel.id)]["reacted"].append(user.id) diff --git a/pupper/__init__.py b/pupper/__init__.py index a6c3dee..a6e29ff 100644 --- a/pupper/__init__.py +++ b/pupper/__init__.py @@ -1,5 +1,7 @@ from .pupper import Pupper +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + async def setup(bot): bot.add_cog(Pupper(bot)) diff --git a/pupper/info.json b/pupper/info.json index 7463f14..2c65000 100644 --- a/pupper/info.json +++ b/pupper/info.json @@ -8,5 +8,6 @@ "tags": [ "pets" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/pupper/pupper.py b/pupper/pupper.py index e34c46a..cbd79f0 100644 --- a/pupper/pupper.py +++ b/pupper/pupper.py @@ -12,6 +12,10 @@ log = logging.getLogger("red.aikaterna.pupper") class Pupper(commands.Cog): + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2767241393, force_registration=True) @@ -47,9 +51,7 @@ class Pupper(commands.Cog): space = "\N{EN SPACE}" toggle = "Active" if guild_data["toggle"] else "Inactive" - delete_after = ( - "No deletion" if not guild_data["delete_after"] else guild_data["delete_after"] - ) + delete_after = "No deletion" if not guild_data["delete_after"] else guild_data["delete_after"] msg = f"[Channels]: {humanize_list(channel_names)}\n" msg += f"[Cooldown]: {guild_data['cooldown']} seconds\n" @@ -169,9 +171,7 @@ class Pupper(commands.Cog): async def addall(self, ctx): """Add all valid channels for the guild that the bot can speak in.""" bot_text_channels = [ - c - for c in ctx.guild.text_channels - if c.permissions_for(ctx.guild.me).send_messages is True + c for c in ctx.guild.text_channels if c.permissions_for(ctx.guild.me).send_messages is True ] channel_list = await self.config.guild(ctx.guild).channel() channels_appended = [] @@ -189,13 +189,9 @@ class Pupper(commands.Cog): second_msg = "" await self.config.guild(ctx.guild).channel.set(channel_list) if len(channels_appended) > 0: - first_msg = ( - f"{humanize_list(channels_appended)} added to the valid petting channels.\n" - ) + first_msg = f"{humanize_list(channels_appended)} added to the valid petting channels.\n" if len(channels_in_list) > 0: - second_msg = ( - f"{humanize_list(channels_in_list)}: already in the list of petting channels." - ) + second_msg = f"{humanize_list(channels_in_list)}: already in the list of petting channels." await ctx.send(f"{first_msg}{second_msg}") @channel.command() @@ -237,15 +233,10 @@ class Pupper(commands.Cog): if self.pets[message.guild.id]: return - last_time = datetime.datetime.strptime( - str(guild_data["last_pet"]), "%Y-%m-%d %H:%M:%S.%f" - ) + last_time = datetime.datetime.strptime(str(guild_data["last_pet"]), "%Y-%m-%d %H:%M:%S.%f") now = datetime.datetime.now(datetime.timezone.utc) now = now.replace(tzinfo=None) - if ( - int((now - last_time).total_seconds()) - > await self.config.guild(message.guild).cooldown() - ): + if int((now - last_time).total_seconds()) > await self.config.guild(message.guild).cooldown(): self._pet_lock(message.guild.id, True) rando_channel = random.choice(guild_data["channel"]) await asyncio.sleep(random.randint(60, 480)) @@ -279,9 +270,7 @@ class Pupper(commands.Cog): if not large_bank else guild_data["borf_msg"] ) - await rando_channel_obj.send( - content=msg, delete_after=guild_data["delete_after"] - ) + await rando_channel_obj.send(content=msg, delete_after=guild_data["delete_after"]) else: pass self._pet_lock(message.guild.id, False) diff --git a/quiz/__init__.py b/quiz/__init__.py index 8fff814..3444df6 100644 --- a/quiz/__init__.py +++ b/quiz/__init__.py @@ -1,4 +1,7 @@ from .quiz import Quiz +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + + def setup(bot): bot.add_cog(Quiz(bot)) diff --git a/quiz/info.json b/quiz/info.json index 929a6ca..f0822fc 100644 --- a/quiz/info.json +++ b/quiz/info.json @@ -4,5 +4,6 @@ "install_msg": "Thanks for installing.", "short": "Play a kahoot-like trivia game.", "tags": ["trivia", "quiz"], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/quiz/quiz.py b/quiz/quiz.py index bd73b93..487434f 100644 --- a/quiz/quiz.py +++ b/quiz/quiz.py @@ -22,6 +22,10 @@ def check_global_setting_admin(): either a bot admin or has the manage_guild permission. """ + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + async def pred(ctx: commands.Context): author = ctx.author if not await bank.is_global(): @@ -102,9 +106,7 @@ class Quiz(commands.Cog): elif category_name_or_id.isdigit(): # cat id specified if 9 <= int(category_name_or_id) >= 32: - return await ctx.send( - f"Invalid category number. Use `{ctx.prefix}quiz categories` to see a list." - ) + return await ctx.send(f"Invalid category number. Use `{ctx.prefix}quiz categories` to see a list.") category_id = category_name_or_id category_name = await self.category_name_from_id(int(category_name_or_id)) else: @@ -112,9 +114,7 @@ class Quiz(commands.Cog): try: category_name = await self.category_name_match(category_name_or_id) except RuntimeError: - return await ctx.send( - f"Invalid category name. Use `{ctx.prefix}quiz categories` to see a list." - ) + return await ctx.send(f"Invalid category name. Use `{ctx.prefix}quiz categories` to see a list.") category_id = await self.category_id_from_name(category_name) if channel.id not in self.playing_channels: @@ -221,9 +221,7 @@ class Quiz(commands.Cog): while True: for channelid in list(self.playing_channels): channelinfo = self.playing_channels[channelid] - since_start = ( - datetime.datetime.utcnow() - channelinfo["Start"] - ).total_seconds() + since_start = (datetime.datetime.utcnow() - channelinfo["Start"]).total_seconds() if since_start > 30 and not channelinfo["Started"]: channel = self.bot.get_channel(channelid) @@ -318,9 +316,7 @@ class Quiz(commands.Cog): assert answerdict[correct_letter] == html.unescape(dictionary["correct_answer"]) if await self.config.guild(channel.guild).show_answer(): - message = ( - f"Correct answer:```{correct_letter.upper()}. {answerdict[correct_letter]}```" - ) + message = f"Correct answer:```{correct_letter.upper()}. {answerdict[correct_letter]}```" await channel.send(message) # Assign scores @@ -350,11 +346,7 @@ class Quiz(commands.Cog): """Ends a quiz game.""" # non-linear credit earning .0002x^{2.9} where x is score/100 channelinfo = self.playing_channels[channel.id] - idlist = sorted( - list(channelinfo["Players"]), - key=(lambda idnum: channelinfo["Players"][idnum]), - reverse=True, - ) + idlist = sorted(list(channelinfo["Players"]), key=(lambda idnum: channelinfo["Players"][idnum]), reverse=True,) winner = channel.guild.get_member(idlist[0]) await channel.send(f"Game over! {winner.mention} won!") @@ -400,11 +392,7 @@ class Quiz(commands.Cog): """Returns a scoreboard string to be sent to the text channel.""" channelinfo = self.playing_channels[channel.id] scoreboard = "\n" - idlist = sorted( - list(channelinfo["Players"]), - key=(lambda idnum: channelinfo["Players"][idnum]), - reverse=True, - ) + idlist = sorted(list(channelinfo["Players"]), key=(lambda idnum: channelinfo["Players"][idnum]), reverse=True,) max_score = channelinfo["Players"][idlist[0]] end_len = len(str(max_score)) + 1 rank = 1 @@ -457,21 +445,15 @@ class Quiz(commands.Cog): parameters["difficulty"] = difficulty for _ in range(3): parameters["token"] = await self.get_token(server) - async with self.session.get( - "https://opentdb.com/api.php", params=parameters - ) as response: + async with self.session.get("https://opentdb.com/api.php", params=parameters) as response: response_json = await response.json() response_code = response_json["response_code"] if response_code == 0: return response_json elif response_code == 1: - raise RuntimeError( - "Question retrieval unsuccessful. Response code from OTDB: 1" - ) + raise RuntimeError("Question retrieval unsuccessful. Response code from OTDB: 1") elif response_code == 2: - raise RuntimeError( - "Question retrieval unsuccessful. Response code from OTDB: 2" - ) + raise RuntimeError("Question retrieval unsuccessful. Response code from OTDB: 2") elif response_code == 3: # Token expired. Obtain new one. log.debug("Quiz: Response code from OTDB: 3") @@ -487,9 +469,7 @@ class Quiz(commands.Cog): and saves one if one doesn't exist.""" token = await self.config.guild(server).token() if not token: - async with self.session.get( - "https://opentdb.com/api_token.php", params={"command": "request"} - ) as response: + async with self.session.get("https://opentdb.com/api_token.php", params={"command": "request"}) as response: response_json = await response.json() token = response_json["token"] await self.config.guild(server).token.set(token) @@ -503,17 +483,13 @@ class Quiz(commands.Cog): ) as response: response_code = (await response.json())["response_code"] if response_code != 0: - raise RuntimeError( - f"Token reset was unsuccessful. Response code from OTDB: {response_code}" - ) + raise RuntimeError(f"Token reset was unsuccessful. Response code from OTDB: {response_code}") async def category_selector(self): """Chooses a random category that has enough questions.""" for _ in range(10): category = random.randint(9, 32) - async with self.session.get( - "https://opentdb.com/api_count.php", params={"category": category} - ) as response: + async with self.session.get("https://opentdb.com/api_count.php", params={"category": category}) as response: response_json = await response.json() assert response_json["category_id"] == category if response_json["category_question_count"]["total_question_count"] > 39: diff --git a/region/__init__.py b/region/__init__.py index 87eedca..da3eed2 100644 --- a/region/__init__.py +++ b/region/__init__.py @@ -1,5 +1,7 @@ from .region import Region +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Region()) diff --git a/region/info.json b/region/info.json index 0b597ff..aa276c3 100644 --- a/region/info.json +++ b/region/info.json @@ -5,5 +5,6 @@ "description" : "Change the Discord server's region with a command.", "install_msg" : "Thanks for installing.", "tags" : ["voice region", "region"], - "disabled" : false + "disabled" : false, + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/region/region.py b/region/region.py index b392ef4..97f7413 100644 --- a/region/region.py +++ b/region/region.py @@ -6,6 +6,10 @@ from redbot.core.utils.chat_formatting import humanize_list class Region(commands.Cog): """Change the guild voice region.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + @checks.mod_or_permissions(administrator=True) @commands.cooldown(1, 60, discord.ext.commands.BucketType.guild) @commands.command() @@ -44,6 +48,4 @@ class Region(commands.Cog): return await ctx.send("I don't have permissions to edit this guild's settings.") except discord.errors.HTTPException: return await ctx.send(f"Error: An invalid server region was passed: `{region}`") - await ctx.send( - f"The voice server region for `{ctx.guild.name}` has been changed to `{region}`." - ) + await ctx.send(f"The voice server region for `{ctx.guild.name}` has been changed to `{region}`.") diff --git a/retrosign/__init__.py b/retrosign/__init__.py index 5c13c78..256db4e 100644 --- a/retrosign/__init__.py +++ b/retrosign/__init__.py @@ -1,5 +1,7 @@ from .retrosign import Retrosign +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Retrosign(bot)) diff --git a/retrosign/info.json b/retrosign/info.json index 57162f6..a737f1c 100644 --- a/retrosign/info.json +++ b/retrosign/info.json @@ -15,5 +15,6 @@ "retro", "80s" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/retrosign/retrosign.py b/retrosign/retrosign.py index 9dc7778..b2423de 100644 --- a/retrosign/retrosign.py +++ b/retrosign/retrosign.py @@ -6,7 +6,6 @@ from bs4 import BeautifulSoup as bs import discord from redbot.core import commands from io import BytesIO -import os from random import choice import re import unicodedata @@ -14,6 +13,11 @@ import unicodedata class Retrosign(commands.Cog): """Make an 80s retro sign. Originally by Anismash""" + + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() @@ -26,47 +30,27 @@ class Retrosign(commands.Cog): if len(texts) == 1: lenstr = len(texts[0]) if lenstr <= 15: - data = dict( - bcg=choice([1, 2, 3, 4, 5]), - txt=choice([1, 2, 3, 4]), - text1="", - text2=texts[0], - text3="", - ) + data = dict(bcg=choice([1, 2, 3, 4, 5]), txt=choice([1, 2, 3, 4]), text1="", text2=texts[0], text3="",) else: return await ctx.send("\N{CROSS MARK} Your line is too long (14 character limit)") elif len(texts) == 3: - texts[0] = unicodedata.normalize('NFD', texts[0]).encode('ascii', 'ignore') - texts[0] = texts[0].decode('UTF-8') - texts[0] = re.sub(r'[^A-Za-z0-9 ]', '', texts[0]) + texts[0] = unicodedata.normalize("NFD", texts[0]).encode("ascii", "ignore") + texts[0] = texts[0].decode("UTF-8") + texts[0] = re.sub(r"[^A-Za-z0-9 ]", "", texts[0]) if len(texts[0]) >= 15: - return await ctx.send( - "\N{CROSS MARK} Your first line is too long (14 character limit)" - ) + return await ctx.send("\N{CROSS MARK} Your first line is too long (14 character limit)") if len(texts[1]) >= 13: - return await ctx.send( - "\N{CROSS MARK} Your second line is too long (12 character limit)" - ) + return await ctx.send("\N{CROSS MARK} Your second line is too long (12 character limit)") if len(texts[2]) >= 26: - return await ctx.send( - "\N{CROSS MARK} Your third line is too long (25 character limit)" - ) + return await ctx.send("\N{CROSS MARK} Your third line is too long (25 character limit)") data = dict( - bcg=choice([1, 2, 3, 4, 5]), - txt=choice([1, 2, 3, 4]), - text1=texts[0], - text2=texts[1], - text3=texts[2], + bcg=choice([1, 2, 3, 4, 5]), txt=choice([1, 2, 3, 4]), text1=texts[0], text2=texts[1], text3=texts[2], ) else: - return await ctx.send( - "\N{CROSS MARK} please provide three words seperated by ';' or one word" - ) + return await ctx.send("\N{CROSS MARK} please provide three words seperated by ';' or one word") async with ctx.channel.typing(): - async with self.session.post( - "https://photofunia.com/effects/retro-wave", data=data - ) as response: + async with self.session.post("https://photofunia.com/effects/retro-wave", data=data) as response: if response.status == 200: soup = bs(await response.text(), "html.parser") download_url = soup.find("div", class_="downloads-container").ul.li.a["href"] diff --git a/rndstatus/__init__.py b/rndstatus/__init__.py index a055daa..61d3189 100644 --- a/rndstatus/__init__.py +++ b/rndstatus/__init__.py @@ -1,5 +1,7 @@ from .rndstatus import RndStatus +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(RndStatus(bot)) diff --git a/rndstatus/info.json b/rndstatus/info.json index 918a621..9f40380 100644 --- a/rndstatus/info.json +++ b/rndstatus/info.json @@ -9,5 +9,6 @@ "tags": [ "status" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/rndstatus/rndstatus.py b/rndstatus/rndstatus.py index d9d353a..dac922b 100644 --- a/rndstatus/rndstatus.py +++ b/rndstatus/rndstatus.py @@ -3,8 +3,7 @@ import discord from redbot.core import Config, commands, checks from redbot.core.utils import AsyncIter from random import choice as rndchoice -from collections import defaultdict, Counter, Sequence -import time +from collections import defaultdict import contextlib import asyncio import logging @@ -18,6 +17,10 @@ class RndStatus(commands.Cog): If a custom status is already set, it won't change it until it's back to none. [p]set game""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.last_change = None @@ -80,9 +83,7 @@ class RndStatus(commands.Cog): if statuses == () or "" in statuses: return await ctx.send("Current statuses: " + " | ".join(saved_status)) await self.config.statuses.set(list(statuses)) - await ctx.send( - "Done. Redo this command with no parameters to see the current list of statuses." - ) + await ctx.send("Done. Redo this command with no parameters to see the current list of statuses.") @rndstatus.command(name="streamer") async def _streamer(self, ctx: commands.Context, *, streamer=None): @@ -91,11 +92,9 @@ class RndStatus(commands.Cog): saved_streamer = await self.config.streamer() if streamer is None: - return await ctx.send(f"Current Streamer: {saved_streamer}" ) + return await ctx.send(f"Current Streamer: {saved_streamer}") await self.config.streamer.set(streamer) - await ctx.send( - "Done. Redo this command with no parameters to see the current streamer." - ) + await ctx.send("Done. Redo this command with no parameters to see the current streamer.") @rndstatus.command() async def botstats(self, ctx, *statuses: str): @@ -160,22 +159,16 @@ class RndStatus(commands.Cog): botstatus = f"{clean_prefix}help | {total_users} users | {servers} servers" if (current_game != str(botstatus)) or current_game is None: if _type == 1: - await self.bot.change_presence( - activity=discord.Streaming(name=botstatus, url=url) - ) + await self.bot.change_presence(activity=discord.Streaming(name=botstatus, url=url)) else: - await self.bot.change_presence( - activity=discord.Activity(name=botstatus, type=_type) - ) + await self.bot.change_presence(activity=discord.Activity(name=botstatus, type=_type)) else: if len(statuses) > 0: new_status = self.random_status(guild, statuses) if current_game != new_status: if (current_game != new_status) or current_game is None: if _type == 1: - await self.bot.change_presence( - activity=discord.Streaming(name=new_status, url=url) - ) + await self.bot.change_presence(activity=discord.Streaming(name=new_status, url=url)) else: await self.bot.change_presence( activity=discord.Activity(name=new_status, type=_type) diff --git a/seen/__init__.py b/seen/__init__.py index d03cab4..3aa3382 100644 --- a/seen/__init__.py +++ b/seen/__init__.py @@ -1,5 +1,10 @@ from .seen import Seen +__red_end_user_data_statement__ = ( + "This cog does not persistently store end user data. " + "This cog does store discord IDs and last seen timestamp as needed for operation. " +) + async def setup(bot): cog = Seen(bot) diff --git a/seen/info.json b/seen/info.json index a510b66..3732cde 100644 --- a/seen/info.json +++ b/seen/info.json @@ -8,5 +8,6 @@ "seen", "activity" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store end user data. This cog does store discord IDs and last seen timestamp as needed for operation. " } \ No newline at end of file diff --git a/seen/seen.py b/seen/seen.py index 6347436..c0e377c 100644 --- a/seen/seen.py +++ b/seen/seen.py @@ -1,7 +1,7 @@ import asyncio import contextlib import datetime -from typing import Union +from typing import Union, Literal import discord import time @@ -14,6 +14,15 @@ _SCHEMA_VERSION = 2 class Seen(commands.Cog): """Shows last time a user was seen in chat.""" + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + if requester in ["discord", "owner"]: + data = await self.config.all_members() + for guild_id, members in data.items(): + if user_id in members: + await self.config.member_from_ids(guild_id, user_id).clear() + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2784481001, force_registration=True) @@ -29,9 +38,7 @@ class Seen(commands.Cog): async def initialize(self): asyncio.ensure_future( - self._migrate_config( - from_version=await self.config.schema_version(), to_version=_SCHEMA_VERSION - ) + self._migrate_config(from_version=await self.config.schema_version(), to_version=_SCHEMA_VERSION) ) async def _migrate_config(self, from_version: int, to_version: int): @@ -75,9 +82,7 @@ class Seen(commands.Cog): member_seen_cache = self._cache.get(author.guild.id, {}).get(author.id, None) if not member_seen_cache and not member_seen_config: - embed = discord.Embed( - colour=discord.Color.red(), title="I haven't seen that user yet." - ) + embed = discord.Embed(colour=discord.Color.red(), title="I haven't seen that user yet.") return await ctx.send(embed=embed) if not member_seen_cache: @@ -132,10 +137,7 @@ class Seen(commands.Cog): @commands.Cog.listener() async def on_typing( - self, - channel: discord.abc.Messageable, - user: Union[discord.User, discord.Member], - when: datetime.datetime, + self, channel: discord.abc.Messageable, user: Union[discord.User, discord.Member], when: datetime.datetime, ): if getattr(user, "guild", None): if user.guild.id not in self._cache: @@ -150,18 +152,14 @@ class Seen(commands.Cog): self._cache[after.guild.id][after.author.id] = int(time.time()) @commands.Cog.listener() - async def on_reaction_remove( - self, reaction: discord.Reaction, user: Union[discord.Member, discord.User] - ): + async def on_reaction_remove(self, reaction: discord.Reaction, user: Union[discord.Member, discord.User]): if getattr(user, "guild", None): if user.guild.id not in self._cache: self._cache[user.guild.id] = {} self._cache[user.guild.id][user.id] = int(time.time()) @commands.Cog.listener() - async def on_reaction_add( - self, reaction: discord.Reaction, user: Union[discord.Member, discord.User] - ): + async def on_reaction_add(self, reaction: discord.Reaction, user: Union[discord.Member, discord.User]): if getattr(user, "guild", None): if user.guild.id not in self._cache: self._cache[user.guild.id] = {} diff --git a/snacktime/__init__.py b/snacktime/__init__.py index a0372b1..8338667 100755 --- a/snacktime/__init__.py +++ b/snacktime/__init__.py @@ -1,5 +1,7 @@ from .snacktime import Snacktime +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + async def setup(bot): bot.add_cog(Snacktime(bot)) diff --git a/snacktime/info.json b/snacktime/info.json index b248546..7016657 100644 --- a/snacktime/info.json +++ b/snacktime/info.json @@ -5,5 +5,6 @@ "description" : "snackburr will come around every-so-often if you've asked him to.\nI hear snackburr likes to come around more often when people are partyin.", "install_msg" : "A snack delivery bear has arrived ʕ•ᴥ• ʔ", "tags" : ["snack", "snacktime", "snackburr", "party", "party time"], - "disabled" : false + "disabled" : false, + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/snacktime/phrases.py b/snacktime/phrases.py index db7f6ff..9864bd6 100644 --- a/snacktime/phrases.py +++ b/snacktime/phrases.py @@ -124,8 +124,6 @@ SNACKBURR_PHRASES = { "partakes of", "ingests", ], - "ENABLE": [ - "Oh you guys want snacks?! Aight, I'll come around every so often to hand some out!" - ], + "ENABLE": ["Oh you guys want snacks?! Aight, I'll come around every so often to hand some out!"], "DISABLE": ["You guys don't want snacks anymore? Alright, I'll stop comin around."], } diff --git a/snacktime/snacktime.py b/snacktime/snacktime.py index 78812f5..3a131cf 100644 --- a/snacktime/snacktime.py +++ b/snacktime/snacktime.py @@ -17,6 +17,10 @@ log = logging.getLogger("red.aikaterna.snacktime") class Snacktime(commands.Cog): """Snackburr's passing out pb jars!""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2712291001, force_registration=True) @@ -93,9 +97,7 @@ class Snacktime(commands.Cog): first_phrase = randchoice(SNACKBURR_PHRASES["EAT_BEFORE"]) second_phrase = randchoice(SNACKBURR_PHRASES["EAT_AFTER"]) - await ctx.send( - f"`{persona} {ctx.author.display_name} {first_phrase} {second_phrase} {amount} whole pb jars!`" - ) + await ctx.send(f"`{persona} {ctx.author.display_name} {first_phrase} {second_phrase} {amount} whole pb jars!`") @commands.guild_only() @commands.group() @@ -121,9 +123,7 @@ class Snacktime(commands.Cog): msg = f"[Delivering in]: {humanize_list(channel_names)}\n" msg += f"[Event start delay]: {guild_data['EVENT_START_DELAY']} seconds\n" - msg += ( - f"[Event start variance]: {guild_data['EVENT_START_DELAY_VARIANCE']} seconds\n" - ) + msg += f"[Event start variance]: {guild_data['EVENT_START_DELAY_VARIANCE']} seconds\n" msg += f"[Friends status]: {invite_friends}\n" msg += f"[Messages before event]: {guild_data['MSGS_BEFORE_EVENT']}\n" msg += f"[Snack amount limit]: {guild_data['SNACK_AMOUNT']} pb\n" @@ -136,18 +136,14 @@ class Snacktime(commands.Cog): @snackset.command() async def errandtime(self, ctx, seconds: int): """How long snackburr needs to be out doin errands.. more or less.""" - event_start_delay_variance = await self.config.guild( - ctx.guild - ).EVENT_START_DELAY_VARIANCE() + event_start_delay_variance = await self.config.guild(ctx.guild).EVENT_START_DELAY_VARIANCE() if seconds <= event_start_delay_variance: await ctx.send("errandtime must be greater than errandvariance!") elif seconds <= 0: await ctx.send("errandtime must be greater than 0") else: await self.config.guild(ctx.guild).EVENT_START_DELAY.set(seconds) - await ctx.send( - f"snackburr's errands will now take around {round(seconds/60, 2)} minutes!" - ) + await ctx.send(f"snackburr's errands will now take around {round(seconds/60, 2)} minutes!") @snackset.command() async def errandvariance(self, ctx, seconds: int): @@ -159,9 +155,7 @@ class Snacktime(commands.Cog): await ctx.send("errandvariance must be 0 or greater!") else: await self.config.guild(ctx.guild).EVENT_START_DELAY_VARIANCE.set(seconds) - await ctx.send( - f"snackburr now might be {round(seconds/60, 2)} minutes early or late to snacktime" - ) + await ctx.send(f"snackburr now might be {round(seconds/60, 2)} minutes early or late to snacktime") @snackset.command(name="snacktime") async def snacktimetime(self, ctx, seconds: int): @@ -185,9 +179,7 @@ class Snacktime(commands.Cog): await ctx.send("snackvariance must be 0 or greater!") else: await self.config.guild(ctx.guild).SNACK_DURATION_VARIANCE.set(seconds) - await ctx.send( - f"snackburr now may have to leave snacktime {round(seconds/60, 2)} minutes early or late" - ) + await ctx.send(f"snackburr now may have to leave snacktime {round(seconds/60, 2)} minutes early or late") @snackset.command() async def msgsneeded(self, ctx, amt: int): @@ -196,9 +188,7 @@ class Snacktime(commands.Cog): await ctx.send("msgsneeded must be greater than 0") else: await self.config.guild(ctx.guild).MSGS_BEFORE_EVENT.set(amt) - await ctx.send( - f"snackburr will now wait until {amt} messages pass until he comes with snacks" - ) + await ctx.send(f"snackburr will now wait until {amt} messages pass until he comes with snacks") @snackset.command() async def amount(self, ctx, amt: int): @@ -272,13 +262,9 @@ class Snacktime(commands.Cog): return self.snacktimeCheckLock[scid] = True if seconds < 0: - await ctx.send( - f"I'm not sure where snackburr is.. He's already {round(abs(seconds/60), 2)} minutes late!" - ) + await ctx.send(f"I'm not sure where snackburr is.. He's already {round(abs(seconds/60), 2)} minutes late!") else: - await ctx.send( - f"snackburr's out on errands! I think he'll be back in {round(seconds/60, 2)} minutes" - ) + await ctx.send(f"snackburr's out on errands! I think he'll be back in {round(seconds/60, 2)} minutes") await asyncio.sleep(40) self.snacktimeCheckLock[scid] = False @@ -306,20 +292,14 @@ class Snacktime(commands.Cog): await self.config.channel(message.channel).repeatMissedSnacktimes.set(0) else: await message.channel.send(await self.get_response(message, "NO_TAKERS")) - repeat_missed_snacktimes = await self.config.channel( - message.channel - ).repeatMissedSnacktimes() - await self.config.channel(message.channel).repeatMissedSnacktimes.set( - repeat_missed_snacktimes + 1 - ) + repeat_missed_snacktimes = await self.config.channel(message.channel).repeatMissedSnacktimes() + await self.config.channel(message.channel).repeatMissedSnacktimes.set(repeat_missed_snacktimes + 1) await asyncio.sleep(2) if (repeat_missed_snacktimes + 1) > 9: # move to a setting await message.channel.send(await self.get_response(message, "LONELY")) deliver_channels = await self.config.guild(message.guild).DELIVER_CHANNELS() new_deliver_channels = deliver_channels.remove(message.channel.id) - await self.config.guild(message.guild).DELIVER_CHANNELS.set( - new_deliver_channels - ) + await self.config.guild(message.guild).DELIVER_CHANNELS.set(new_deliver_channels) await self.config.channel(message.channel).repeatMissedSnacktimes.set(0) except: log.error("Snacktime: Failed to send message in startSnack") @@ -365,9 +345,7 @@ class Snacktime(commands.Cog): # start snacktime await self.startSnack(message) # if no snack coming, schedule one - elif self.snackInProgress.get(scid, False) == False and not self.startLock.get( - scid, False - ): + elif self.snackInProgress.get(scid, False) == False and not self.startLock.get(scid, False): self.msgsPassed[scid] = self.msgsPassed.get(scid, 0) + 1 # check for collisions msgs_before_event = await self.config.guild(message.guild).MSGS_BEFORE_EVENT() @@ -385,9 +363,7 @@ class Snacktime(commands.Cog): log.debug(f"Snacktime: {message.author.name} - I got the Lock") self.lockRequests[scid] = [] # someone got through already - if self.msgsPassed[ - scid - ] < msgs_before_event or self.snackInProgress.get(scid, False): + if self.msgsPassed[scid] < msgs_before_event or self.snackInProgress.get(scid, False): log.debug("Snacktime: Lock: someone got through already.") return else: @@ -404,8 +380,7 @@ class Snacktime(commands.Cog): log.debug(f"Snacktime: activity: {message.content}") guild_data = await self.config.guild(message.guild).all() timeTillSnack = guild_data["EVENT_START_DELAY"] + randint( - -guild_data["EVENT_START_DELAY_VARIANCE"], - guild_data["EVENT_START_DELAY_VARIANCE"], + -guild_data["EVENT_START_DELAY_VARIANCE"], guild_data["EVENT_START_DELAY_VARIANCE"], ) log.debug(f"Snacktime: {str(timeTillSnack)} seconds till snacktime") self.snacktimePrediction[scid] = msgTime + guild_data["EVENT_START_DELAY"] @@ -450,10 +425,7 @@ class Snacktime(commands.Cog): userWants = False for agreePhrase in agree_phrases: # no one word answers - if ( - agreePhrase in message.content.lower() - and len(message.content.split()) > 1 - ): + if agreePhrase in message.content.lower() and len(message.content.split()) > 1: userWants = True break if userWants: @@ -478,8 +450,7 @@ class Snacktime(commands.Cog): await bank.set_balance(message.author, b.max_balance) except Exception as e: log.info( - f"Failed to send pb message. {message.author.name} didn't get pb\n", - exc_info=True, + f"Failed to send pb message. {message.author.name} didn't get pb\n", exc_info=True, ) else: diff --git a/timezone/__init__.py b/timezone/__init__.py index 5706101..cb385ee 100644 --- a/timezone/__init__.py +++ b/timezone/__init__.py @@ -1,5 +1,16 @@ from .timezone import Timezone +__red_end_user_data_statement__ = ( + "This cog stores data provided by users " + "for the express purpose of redisplaying. " + "It does not store user data which was not " + "provided through a command. " + "Users may remove their own content " + "without making a data removal request. " + "This cog does not support data requests, " + "but will respect deletion requests." +) + def setup(bot): bot.add_cog(Timezone(bot)) diff --git a/timezone/info.json b/timezone/info.json index 6049840..eb5fbff 100644 --- a/timezone/info.json +++ b/timezone/info.json @@ -12,5 +12,6 @@ "requirements": [ "pytz" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog stores data provided by users for the express purpose of redisplaying. It does not store user data which was not provided through a command. Users may remove their own content without making a data removal request. This cog does not support data requests, but will respect deletion requests." } \ No newline at end of file diff --git a/timezone/timezone.py b/timezone/timezone.py index c1c6cb2..9f9d3bf 100644 --- a/timezone/timezone.py +++ b/timezone/timezone.py @@ -3,13 +3,18 @@ import pytz from datetime import datetime from pytz import common_timezones from pytz import country_timezones -from typing import Optional +from typing import Optional, Literal from redbot.core import Config, commands, checks class Timezone(commands.Cog): """Gets times across the world...""" + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + await self.config.user_from_id(user_id).clear() + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 278049241001, force_registration=True) @@ -45,7 +50,7 @@ class Timezone(commands.Cog): "" ) else: - tz = tz.title() if '/' in tz else tz.upper() + tz = tz.title() if "/" in tz else tz.upper() if tz not in common_timezones: raise Exception(tz) fmt = "**%H:%M** %d-%B-%Y **%Z (UTC %z)**" @@ -139,8 +144,7 @@ class Timezone(commands.Cog): fmt = "**%H:%M** %d-%B-%Y **%Z (UTC %z)**" time = time.strftime(fmt) await ctx.send( - f"{user.name}'s current timezone is: **{usertime}**\n" - f"The current time is: {str(time)}" + f"{user.name}'s current timezone is: **{usertime}**\n" f"The current time is: {str(time)}" ) else: await ctx.send("That user hasn't set their timezone.") @@ -174,6 +178,4 @@ class Timezone(commands.Cog): position = "ahead of" if user_diff < other_diff else "behind" position_text = "" if time_diff == 0 else f" {position} you" - await ctx.send( - f"{user.display_name}'s time is {other_time} which is {time_amt}{position_text}." - ) + await ctx.send(f"{user.display_name}'s time is {other_time} which is {time_amt}{position_text}.") diff --git a/tools/__init__.py b/tools/__init__.py index 8e3bc8c..187591b 100644 --- a/tools/__init__.py +++ b/tools/__init__.py @@ -1,4 +1,7 @@ from .tools import Tools +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + + def setup(bot): - bot.add_cog(Tools(bot)) \ No newline at end of file + bot.add_cog(Tools(bot)) diff --git a/tools/converter.py b/tools/converter.py index bf544a9..bfc78b4 100644 --- a/tools/converter.py +++ b/tools/converter.py @@ -51,9 +51,7 @@ class GuildChannelConverter(converter.IDConverter, converter.Converter): else: result = converter._get_from_guilds(bot, "get_channel", channel_id) - if not isinstance( - result, (discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel) - ): + if not isinstance(result, (discord.TextChannel, discord.VoiceChannel, discord.CategoryChannel)): raise BadArgument('Channel "{}" not found.'.format(argument)) return result diff --git a/tools/info.json b/tools/info.json index 8629f42..47086cb 100644 --- a/tools/info.json +++ b/tools/info.json @@ -17,5 +17,6 @@ "tags": [ "tools" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } \ No newline at end of file diff --git a/tools/tools.py b/tools/tools.py index c1073f5..472f85d 100644 --- a/tools/tools.py +++ b/tools/tools.py @@ -3,11 +3,8 @@ import datetime import discord import inspect import logging -import random import re -import os -import time -from redbot.core import Config, checks, commands +from redbot.core import checks, commands from redbot.core.utils import chat_formatting as cf from redbot.core.utils.common_filters import filter_invites from redbot.core.utils.menus import menu, DEFAULT_CONTROLS, close_menu @@ -21,6 +18,10 @@ log = logging.getLogger("red.aikaterna.tools") class Tools(commands.Cog): """Mod and Admin tools.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot @@ -53,13 +54,9 @@ class Tools(commands.Cog): tcs = guild.text_channels vcs = guild.voice_channels except AttributeError: - return await ctx.send( - "User is not in that guild or I do not have access to that guild." - ) + return await ctx.send("User is not in that guild or I do not have access to that guild.") - author_text_channels = [ - c for c in tcs if c.permissions_for(ctx.author).read_messages is True - ] + author_text_channels = [c for c in tcs if c.permissions_for(ctx.author).read_messages is True] author_voice_channels = [c for c in vcs if c.permissions_for(ctx.author).connect is True] user_text_channels = [c for c in tcs if c.permissions_for(user).read_messages is True] @@ -72,9 +69,7 @@ class Tools(commands.Cog): user_voice_channels ) # voice channels only the author has access to - user_only_t = set(user_text_channels) - set( - author_text_channels - ) # text channels only the user has access to + user_only_t = set(user_text_channels) - set(author_text_channels) # text channels only the user has access to user_only_v = set(user_voice_channels) - set( author_voice_channels ) # voice channels only the user has access to @@ -87,18 +82,14 @@ class Tools(commands.Cog): ) # voice channels that author and user have in common msg = "```ini\n" - msg += "{} [TEXT CHANNELS IN COMMON]:\n\n{}\n\n".format( - len(common_t), ", ".join([c.name for c in common_t]) - ) + msg += "{} [TEXT CHANNELS IN COMMON]:\n\n{}\n\n".format(len(common_t), ", ".join([c.name for c in common_t])) msg += "{} [TEXT CHANNELS {} HAS EXCLUSIVE ACCESS TO]:\n\n{}\n\n".format( len(user_only_t), user.name.upper(), ", ".join([c.name for c in user_only_t]) ) msg += "{} [TEXT CHANNELS YOU HAVE EXCLUSIVE ACCESS TO]:\n\n{}\n\n\n".format( len(author_only_t), ", ".join([c.name for c in author_only_t]) ) - msg += "{} [VOICE CHANNELS IN COMMON]:\n\n{}\n\n".format( - len(common_v), ", ".join([c.name for c in common_v]) - ) + msg += "{} [VOICE CHANNELS IN COMMON]:\n\n{}\n\n".format(len(common_v), ", ".join([c.name for c in common_v])) msg += "{} [VOICE CHANNELS {} HAS EXCLUSIVE ACCESS TO]:\n\n{}\n\n".format( len(user_only_v), user.name.upper(), ", ".join([c.name for c in user_only_v]) ) @@ -119,16 +110,10 @@ class Tools(commands.Cog): guild = self.bot.get_guild(guild) try: - can_access = [ - c.name - for c in guild.text_channels - if c.permissions_for(user).read_messages == True - ] + can_access = [c.name for c in guild.text_channels if c.permissions_for(user).read_messages == True] text_channels = [c.name for c in guild.text_channels] except AttributeError: - return await ctx.send( - "User is not in that guild or I do not have access to that guild." - ) + return await ctx.send("User is not in that guild or I do not have access to that guild.") prefix = "You have" if user.id == ctx.author.id else user.name + " has" msg = "```ini\n[{} access to {} out of {} text channels]\n\n".format( @@ -136,9 +121,7 @@ class Tools(commands.Cog): ) msg += "[ACCESS]:\n{}\n\n".format(", ".join(can_access)) - msg += "[NO ACCESS]:\n{}\n```".format( - ", ".join(list(set(text_channels) - set(can_access))) - ) + msg += "[NO ACCESS]:\n{}\n```".format(", ".join(list(set(text_channels) - set(can_access)))) await ctx.send(msg) @access.command() @@ -152,14 +135,10 @@ class Tools(commands.Cog): guild = self.bot.get_guild(guild) try: - can_access = [ - c.name for c in guild.voice_channels if c.permissions_for(user).connect is True - ] + can_access = [c.name for c in guild.voice_channels if c.permissions_for(user).connect is True] voice_channels = [c.name for c in guild.voice_channels] except AttributeError: - return await ctx.send( - "User is not in that guild or I do not have access to that guild." - ) + return await ctx.send("User is not in that guild or I do not have access to that guild.") prefix = "You have" if user.id == ctx.author.id else user.name + " has" msg = "```ini\n[{} access to {} out of {} voice channels]\n\n".format( @@ -167,9 +146,7 @@ class Tools(commands.Cog): ) msg += "[ACCESS]:\n{}\n\n".format(", ".join(can_access)) - msg += "[NO ACCESS]:\n{}\n```".format( - ", ".join(list(set(voice_channels) - set(can_access))) - ) + msg += "[NO ACCESS]:\n{}\n```".format(", ".join(list(set(voice_channels) - set(can_access)))) await ctx.send(msg) @commands.guild_only() @@ -196,8 +173,7 @@ class Tools(commands.Cog): embed_list = [] for page in cf.pagify(msg, shorten_by=1400): embed = discord.Embed( - description="**Total bans:** {}\n\n{}".format(bancount, page), - colour=await ctx.embed_colour(), + description="**Total bans:** {}\n\n{}".format(bancount, page), colour=await ctx.embed_colour(), ) embed_list.append(embed) await menu(ctx, embed_list, DEFAULT_CONTROLS) @@ -332,17 +308,13 @@ class Tools(commands.Cog): role = roles[response - 1] awaiter = await ctx.send( - embed=discord.Embed( - description="Getting member names...", colour=await ctx.embed_colour() - ) + embed=discord.Embed(description="Getting member names...", colour=await ctx.embed_colour()) ) await asyncio.sleep(1.5) # taking time to retrieve the names - users_in_role = "\n".join( - sorted(m.display_name for m in guild.members if role in m.roles) - ) + users_in_role = "\n".join(sorted(m.display_name for m in guild.members if role in m.roles)) if len(users_in_role) == 0: embed = discord.Embed( - description=cf.bold(f"0 users found in the {role.name} role."), colour=await ctx.embed_colour() + description=cf.bold(f"0 users found in the {role.name} role."), colour=await ctx.embed_colour(), ) await awaiter.edit(embed=embed) return @@ -385,8 +357,7 @@ class Tools(commands.Cog): if ctx.channel.permissions_for(ctx.guild.me).embed_links: embed = discord.Embed( - description=f"{user.mention} joined this guild on {joined_on}.", - color=await ctx.embed_colour(), + description=f"{user.mention} joined this guild on {joined_on}.", color=await ctx.embed_colour(), ) await ctx.send(embed=embed) else: @@ -406,9 +377,9 @@ class Tools(commands.Cog): form = "{gid} :: {mems:0{zpadding}} :: {name}" all_forms = [ form.format( - gid=g.id, - mems=g.member_count, - name=filter_invites(cf.escape(g.name)), + gid=g.id, + mems=g.member_count, + name=filter_invites(cf.escape(g.name)), zpadding=max_zpadding ) for g in guilds @@ -438,9 +409,7 @@ class Tools(commands.Cog): topChannels_formed = "\n".join(self.channels_format(top_channels)) categories_formed = "\n\n".join([self.category_format(tup) for tup in category_channels]) - await ctx.send( - f"{ctx.guild.name} has {len(channels)} channel{'s' if len(channels) > 1 else ''}." - ) + await ctx.send(f"{ctx.guild.name} has {len(channels)} channel{'s' if len(channels) > 1 else ''}.") for page in cf.pagify(topChannels_formed, delims=["\n"], shorten_by=16): await ctx.send(asciidoc(page)) @@ -461,9 +430,7 @@ class Tools(commands.Cog): header = "{:>33}\n{}\n\n".format(head1, "-" * 57) user_body = ( - " {mem} ({memid})\n" - " {spcs}Joined Guild: {sp1}{join}\n" - " {spcs}Account Created: {sp2}{created}\n\n" + " {mem} ({memid})\n" " {spcs}Joined Guild: {sp1}{join}\n" " {spcs}Account Created: {sp2}{created}\n\n" ) disp = header @@ -526,11 +493,7 @@ class Tools(commands.Cog): else: role = self._role_from_string(ctx.guild, rolename) if role is None: - await ctx.send( - embed=discord.Embed( - description="Cannot find role.", colour=await ctx.embed_colour() - ) - ) + await ctx.send(embed=discord.Embed(description="Cannot find role.", colour=await ctx.embed_colour())) return await ctx.send(f"**{rolename} ID:** {role.id}") @@ -576,9 +539,7 @@ class Tools(commands.Cog): em.add_field(name="Server", value=role.guild.name) em.add_field(name="Role Name", value=role.name) em.add_field(name="Created", value=self._dynamic_time(role.created_at)) - em.add_field( - name="Users in Role", value=len([m for m in guild.members if role in m.roles]) - ) + em.add_field(name="Users in Role", value=len([m for m in guild.members if role in m.roles])) em.add_field(name="ID", value=role.id) em.add_field(name="Color", value=role.color) em.add_field(name="Position", value=role.position) @@ -624,9 +585,7 @@ class Tools(commands.Cog): form = "`{rpos:0{zpadding}}` - `{rid}` - `{rcolor}` - {rment} " max_zpadding = max([len(str(r.position)) for r in ctx.guild.roles]) rolelist = [ - form.format( - rpos=r.position, zpadding=max_zpadding, rid=r.id, rment=r.mention, rcolor=r.color - ) + form.format(rpos=r.position, zpadding=max_zpadding, rid=r.id, rment=r.mention, rcolor=r.color) for r in ctx.guild.roles ] @@ -635,8 +594,7 @@ class Tools(commands.Cog): embed_list = [] for page in cf.pagify(rolelist, shorten_by=1400): embed = discord.Embed( - description=f"**Total roles:** {len(ctx.guild.roles)}\n\n{page}", - colour=await ctx.embed_colour(), + description=f"**Total roles:** {len(ctx.guild.roles)}\n\n{page}", colour=await ctx.embed_colour(), ) embed_list.append(embed) await menu(ctx, embed_list, DEFAULT_CONTROLS) @@ -648,24 +606,8 @@ class Tools(commands.Cog): guild = ctx.guild if not user: user = author - seen = len( - set( - [ - member.guild.name - for member in self.bot.get_all_members() - if member.id == user.id - ] - ) - ) - sharedservers = str( - set( - [ - member.guild.name - for member in self.bot.get_all_members() - if member.id == user.id - ] - ) - ) + seen = len(set([member.guild.name for member in self.bot.get_all_members() if member.id == user.id])) + sharedservers = str(set([member.guild.name for member in self.bot.get_all_members() if member.id == user.id])) for shared in sharedservers: shared = "".strip("'").join(sharedservers).strip("'") shared = shared.strip("{").strip("}") @@ -693,15 +635,7 @@ class Tools(commands.Cog): guild = self.bot.get_guild(int(guild)) except TypeError: return await ctx.send("Not a valid guild id.") - online = str( - len( - [ - m.status - for m in guild.members - if str(m.status) == "online" or str(m.status) == "idle" - ] - ) - ) + online = str(len([m.status for m in guild.members if str(m.status) == "online" or str(m.status) == "idle"])) total_users = str(len(guild.members)) text_channels = [x for x in guild.channels if isinstance(x, discord.TextChannel)] voice_channels = [x for x in guild.channels if isinstance(x, discord.VoiceChannel)] @@ -739,17 +673,7 @@ class Tools(commands.Cog): roles = [x.name for x in user.roles if x.name != "@everyone"] if not roles: roles = ["None"] - seen = str( - len( - set( - [ - member.guild.name - for member in self.bot.get_all_members() - if member.id == user.id - ] - ) - ) - ) + seen = str(len(set([member.guild.name for member in self.bot.get_all_members() if member.id == user.id]))) load = "```\nLoading user info...```" waiting = await ctx.send(load) @@ -772,9 +696,7 @@ class Tools(commands.Cog): if actwatch := discord.utils.get(user.activities, type=discord.ActivityType.watching): data += "[Watching]: {}\n".format(cf.escape(str(actwatch.name))) if actstream := discord.utils.get(user.activities, type=discord.ActivityType.streaming): - data += "[Streaming]: [{}]({})\n".format( - cf.escape(str(actstream.name)), cf.escape(actstream.url) - ) + 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))) @@ -784,12 +706,8 @@ class Tools(commands.Cog): if caller != "invoke": data += "[Joined]: {}\n".format(self._dynamic_time(joined_at)) data += "[Roles]: {}\n".format(", ".join(roles)) - data += "[In Voice]: {}\n".format( - user.voice.channel if user.voice is not None else None - ) - data += "[AFK]: {}\n".format( - user.voice.afk if user.voice is not None else False - ) + data += "[In Voice]: {}\n".format(user.voice.channel if user.voice is not None else None) + data += "[AFK]: {}\n".format(user.voice.afk if user.voice is not None else False) data += "```" await asyncio.sleep(1) await waiting.edit(content=data) @@ -902,11 +820,7 @@ class Tools(commands.Cog): type_justify = max([len(type_name(c)) for c in channels]) return [ - channel_form.format( - name=c.name[:24].ljust(name_justify), - ctype=type_name(c).ljust(type_justify), - cid=c.id, - ) + channel_form.format(name=c.name[:24].ljust(name_justify), ctype=type_name(c).ljust(type_justify), cid=c.id,) for c in channels ] diff --git a/trickortreat/__init__.py b/trickortreat/__init__.py index c041200..ce5066c 100644 --- a/trickortreat/__init__.py +++ b/trickortreat/__init__.py @@ -1,5 +1,14 @@ from .trickortreat import TrickOrTreat +__red_end_user_data_statement__ = ( + "This cog does not persistently store end user data. " + "This cog does store discord IDs as needed for operation. " + "This cog does store user stats for the cog such as their score. " + "Users may remove their own content without making a data removal request." + "This cog does not support data requests, " + "but will respect deletion requests." +) + def setup(bot): bot.add_cog(TrickOrTreat(bot)) diff --git a/trickortreat/info.json b/trickortreat/info.json index 8949e26..644b3d8 100644 --- a/trickortreat/info.json +++ b/trickortreat/info.json @@ -11,5 +11,6 @@ "candy", "pick" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store end user data. This cog does store discord IDs as needed for operation. This cog does store user stats for the cog such as their score. Users may remove their own content without making a data removal request. This cog does not support data requests, but will respect deletion requests." } diff --git a/trickortreat/trickortreat.py b/trickortreat/trickortreat.py index 6b46f45..e785e10 100644 --- a/trickortreat/trickortreat.py +++ b/trickortreat/trickortreat.py @@ -1,5 +1,7 @@ import asyncio import datetime +from imaplib import Literal + import discord import random import math @@ -7,10 +9,15 @@ from redbot.core import commands, checks, Config, bank from redbot.core.utils.chat_formatting import box, pagify, humanize_number from redbot.core.utils.menus import menu, DEFAULT_CONTROLS -__version__ = "0.0.7" +__version__ = "0.0.8" class TrickOrTreat(commands.Cog): + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + await self.config.user_from_id(user_id).clear() + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2710311393, force_registration=True) @@ -51,9 +58,7 @@ class TrickOrTreat(commands.Cog): candy_type = "stars" candy_list = ["candies", "lollipops", "stars"] if candy_type not in candy_list: - return await ctx.send( - "That's not a candy type! Use the inventory command to see what you have." - ) + return await ctx.send("That's not a candy type! Use the inventory command to see what you have.") if userdata[candy_type] < number: return await ctx.send(f"You don't have that many {candy_type}.") if userdata[candy_type] == 0: @@ -76,9 +81,7 @@ class TrickOrTreat(commands.Cog): if yuck == 10: await self.config.user(ctx.author).sickness.set(userdata["sickness"] + 25) if yuck in range(1, 9): - await self.config.user(ctx.author).sickness.set( - userdata["sickness"] + (yuck * 2) - ) + await self.config.user(ctx.author).sickness.set(userdata["sickness"] + (yuck * 2)) if userdata["candies"] > 3 + number: lost_candy = userdata["candies"] - random.randint(1, 3) - number @@ -90,14 +93,10 @@ class TrickOrTreat(commands.Cog): await self.config.user(ctx.author).candies.set(0) await self.config.guild(ctx.guild).pick.set(pick_now + lost_candy) else: - await self.config.user(ctx.author).candies.set( - userdata["candies"] - lost_candy - ) + await self.config.user(ctx.author).candies.set(userdata["candies"] - lost_candy) await self.config.guild(ctx.guild).pick.set(pick_now + lost_candy) - await self.config.user(ctx.author).eaten.set( - userdata["eaten"] + (userdata["candies"] - lost_candy) - ) + await self.config.user(ctx.author).eaten.set(userdata["eaten"] + (userdata["candies"] - lost_candy)) return await ctx.send( f"You begin to think you don't need all this candy, maybe...\n*{lost_candy} candies are left behind*" @@ -117,9 +116,7 @@ class TrickOrTreat(commands.Cog): ) await self.config.guild(ctx.guild).pick.set(pick + lost_candy) await self.config.user(ctx.author).candies.set(0) - await self.config.user(ctx.author).eaten.set( - userdata["eaten"] + (userdata["candies"] - lost_candy) - ) + await self.config.user(ctx.author).eaten.set(userdata["eaten"] + (userdata["candies"] - lost_candy)) message = await ctx.send("...") await asyncio.sleep(2) await message.edit(content="..........") @@ -235,12 +232,9 @@ class TrickOrTreat(commands.Cog): for page in pagify(temp_msg, delims=["\n"], page_length=1000): embed = discord.Embed( colour=0xF4731C, - description=box(f"\N{CANDY} Global leaderboard \N{CANDY}", lang="prolog") - + (box(page, lang="md")), - ) - embed.set_footer( - text=f"Page {humanize_number(pages)}/{humanize_number(math.ceil(len(temp_msg) / 1500))}" + description=box(f"\N{CANDY} Global leaderboard \N{CANDY}", lang="prolog") + (box(page, lang="md")), ) + embed.set_footer(text=f"Page {humanize_number(pages)}/{humanize_number(math.ceil(len(temp_msg) / 1500))}") pages += 1 page_list.append(embed) return await menu(ctx, page_list, DEFAULT_CONTROLS) @@ -264,15 +258,11 @@ class TrickOrTreat(commands.Cog): elif sickness in range(56, 71): em.description += f"\n\n**Sickness is over 55/100**\n*You don't feel so good...*" elif sickness in range(71, 86): - em.description += ( - f"\n\n**Sickness is over 70/100**\n*You really don't feel so good...*" - ) + em.description += f"\n\n**Sickness is over 70/100**\n*You really don't feel so good...*" elif sickness in range(86, 101): em.description += f"\n\n**Sickness is over 85/100**\n*The thought of more sugar makes you feel awful...*" elif sickness > 100: - em.description += ( - f"\n\n**Sickness is over 100/100**\n*Better wait a while for more candy...*" - ) + em.description += f"\n\n**Sickness is over 100/100**\n*Better wait a while for more candy...*" await ctx.send(msg, embed=em) @commands.guild_only() @@ -351,9 +341,7 @@ class TrickOrTreat(commands.Cog): else: message = await ctx.send("You start looking around for a target...") await asyncio.sleep(random.randint(3, 6)) - return await message.edit( - content="You snuck around for a while but didn't find anything." - ) + return await message.edit(content="You snuck around for a while but didn't find anything.") user_candy_now = await self.config.user(ctx.author).candies() multip = random.randint(1, 100) / 100 if multip > 0.7: @@ -362,9 +350,7 @@ class TrickOrTreat(commands.Cog): if pieces <= 0: message = await ctx.send("You stealthily move over to an unsuspecting person...") await asyncio.sleep(4) - return await message.edit( - content="You found someone to pickpocket, but they had nothing but pocket lint." - ) + return await message.edit(content="You found someone to pickpocket, but they had nothing but pocket lint.") chance = random.randint(1, 25) sneak_phrases = [ "You look around furtively...", @@ -374,9 +360,7 @@ class TrickOrTreat(commands.Cog): if chance <= 10: message = await ctx.send("You creep closer to the target...") await asyncio.sleep(random.randint(3, 5)) - return await message.edit( - content="You snuck around for a while but didn't find anything." - ) + return await message.edit(content="You snuck around for a while but didn't find anything.") if chance > 18: await self.config.user(picked_user).candies.set(picked_candy_now - pieces) await self.config.user(ctx.author).candies.set(user_candy_now + pieces) @@ -412,9 +396,7 @@ class TrickOrTreat(commands.Cog): @commands.group() async def totchannel(self, ctx): """Channel management for Trick or Treat.""" - if ctx.invoked_subcommand is not None or isinstance( - ctx.invoked_subcommand, commands.Group - ): + if ctx.invoked_subcommand is not None or isinstance(ctx.invoked_subcommand, commands.Group): return channel_list = await self.config.guild(ctx.guild).channel() channel_msg = "Trick or Treat Channels:\n" @@ -501,10 +483,7 @@ class TrickOrTreat(commands.Cog): last_time = datetime.datetime.strptime(str(userdata["last_tot"]), "%Y-%m-%d %H:%M:%S.%f") now = datetime.datetime.now(datetime.timezone.utc) now = now.replace(tzinfo=None) - if ( - int((now - last_time).total_seconds()) - < await self.config.guild(message.guild).cooldown() - ): + if int((now - last_time).total_seconds()) < await self.config.guild(message.guild).cooldown(): messages = [ "The thought of candy right now doesn't really sound like a good idea.", "All the lights on this street are dark...", diff --git a/warcraftlogs/__init__.py b/warcraftlogs/__init__.py index 58e8cd7..7f46d5e 100644 --- a/warcraftlogs/__init__.py +++ b/warcraftlogs/__init__.py @@ -1,5 +1,16 @@ from .warcraftlogs import WarcraftLogs +__red_end_user_data_statement__ = ( + "This cog stores data provided by users " + "for the express purpose of redisplaying. " + "It does not store user data which was not " + "provided through a command. " + "Users may remove their own content " + "without making a data removal request. " + "This cog does not support data requests, " + "but will respect deletion requests." +) + def setup(bot): bot.add_cog(WarcraftLogs(bot)) diff --git a/warcraftlogs/info.json b/warcraftlogs/info.json index fc73a1d..43312d6 100644 --- a/warcraftlogs/info.json +++ b/warcraftlogs/info.json @@ -4,5 +4,7 @@ "install_msg": "Check out [p]help WarcraftLogs and set your WCL API key, available by signing into a WarcraftLogs account on their site and visiting the bottom of your settings page. ", "short": "WarcraftLogs data for World of Warcraft Classic players.", "tags": ["warcraft"], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog stores data provided by users for the express purpose of redisplaying. It does not store user data which was not provided through a command. Users may remove their own content without making a data removal request. This cog does not support data requests, but will respect deletion requests." + } \ No newline at end of file diff --git a/warcraftlogs/warcraftlogs.py b/warcraftlogs/warcraftlogs.py index e461254..facb019 100644 --- a/warcraftlogs/warcraftlogs.py +++ b/warcraftlogs/warcraftlogs.py @@ -1,18 +1,24 @@ +from imaplib import Literal + import aiohttp -import asyncio import datetime import discord import itertools import json from operator import itemgetter from redbot.core import Config, commands, checks -from redbot.core.utils.chat_formatting import box, humanize_list, pagify +from redbot.core.utils.chat_formatting import box from redbot.core.utils.menus import menu, DEFAULT_CONTROLS class WarcraftLogs(commands.Cog): """Access Warcraftlogs stats.""" + async def red_delete_data_for_user( + self, *, requester: Literal["discord", "owner", "user", "user_strict"], user_id: int, + ): + await self.config.user_from_id(user_id).clear() + def __init__(self, bot): self.bot = bot self.config = Config.get_conf(self, 2713931001, force_registration=True) @@ -89,9 +95,7 @@ class WarcraftLogs(commands.Cog): userdata = await self.config.user(ctx.author).all() apikey = await self.config.apikey() if not apikey: - return await ctx.send( - "The bot owner needs to set a WarcraftLogs API key before this can be used." - ) + return await ctx.send("The bot owner needs to set a WarcraftLogs API key before this can be used.") if not username: username = userdata["charname"] if not username: @@ -135,9 +139,7 @@ class WarcraftLogs(commands.Cog): log_data.append(log_info) # Logged Kill sorting - embed1 = discord.Embed( - title=f"{username.title()} - {realmname.title()} ({region.upper()})\nLogged Kills" - ) + embed1 = discord.Embed(title=f"{username.title()} - {realmname.title()} ({region.upper()})\nLogged Kills") for item in kill_data: zone_kills = "" for boss_info in list(item.values()): @@ -155,10 +157,7 @@ class WarcraftLogs(commands.Cog): for item in log_data: log_page = "" for id_data in list(item.values()): - sorted_item = { - k: v - for k, v in sorted(id_data.items(), key=lambda item: item[1], reverse=True) - } + sorted_item = {k: v for k, v in sorted(id_data.items(), key=lambda item: item[1], reverse=True)} short_list = dict(itertools.islice(sorted_item.items(), 5)) zone_name, phase_num = self.clean_name(list(item)) for log_id, info_list in short_list.items(): @@ -188,9 +187,7 @@ class WarcraftLogs(commands.Cog): userdata = await self.config.user(ctx.author).all() apikey = await self.config.apikey() if not apikey: - return await ctx.send( - "The bot owner needs to set a WarcraftLogs API key before this can be used." - ) + return await ctx.send("The bot owner needs to set a WarcraftLogs API key before this can be used.") if not username: username = userdata["charname"] if not username: @@ -328,7 +325,7 @@ class WarcraftLogs(commands.Cog): @staticmethod def get_recent_gear(data): - date_sorted_data = sorted(data, key=itemgetter('startTime'), reverse=True) + date_sorted_data = sorted(data, key=itemgetter("startTime"), reverse=True) for encounter in date_sorted_data: try: item_name = encounter["gear"][0]["name"] diff --git a/wolfram/__init__.py b/wolfram/__init__.py index 9842c14..de13601 100644 --- a/wolfram/__init__.py +++ b/wolfram/__init__.py @@ -1,5 +1,7 @@ from .wolfram import Wolfram +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(Wolfram(bot)) diff --git a/wolfram/info.json b/wolfram/info.json index 15d0643..a8e80c9 100644 --- a/wolfram/info.json +++ b/wolfram/info.json @@ -11,5 +11,6 @@ "requirements": [ "pillow" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/wolfram/wolfram.py b/wolfram/wolfram.py index 5fed4ce..48f681c 100644 --- a/wolfram/wolfram.py +++ b/wolfram/wolfram.py @@ -1,8 +1,6 @@ import aiohttp -import asyncio import discord from io import BytesIO -from PIL import Image import xml.etree.ElementTree as ET import urllib.parse @@ -13,6 +11,10 @@ from redbot.core.utils.chat_formatting import box, pagify class Wolfram(commands.Cog): """Ask Wolfram Alpha any question.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() @@ -27,9 +29,7 @@ class Wolfram(commands.Cog): """Ask Wolfram Alpha any question.""" api_key = await self.config.WOLFRAM_API_KEY() if not api_key: - return await ctx.send( - "No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/" - ) + return await ctx.send("No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/") url = "http://api.wolframalpha.com/v2/query?" query = " ".join(question) @@ -59,9 +59,7 @@ class Wolfram(commands.Cog): return await ctx.send_help() api_key = await self.config.WOLFRAM_API_KEY() if not api_key: - return await ctx.send( - "No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/" - ) + return await ctx.send("No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/") width = 800 font_size = 30 @@ -78,14 +76,10 @@ class Wolfram(commands.Cog): img = await r.content.read() if len(img) == 43: # img = b'Wolfram|Alpha did not understand your input' - return await ctx.send( - "There is as yet insufficient data for a meaningful answer." - ) + return await ctx.send("There is as yet insufficient data for a meaningful answer.") wolfram_img = BytesIO(img) try: - await ctx.channel.send( - file=discord.File(wolfram_img, f"wolfram{ctx.author.id}.png") - ) + await ctx.channel.send(file=discord.File(wolfram_img, f"wolfram{ctx.author.id}.png")) except Exception as e: await ctx.send(f"Oops, there was a problem: {e}") @@ -94,9 +88,7 @@ class Wolfram(commands.Cog): """Ask Wolfram Alpha any math question. Returns step by step answers.""" api_key = await self.config.WOLFRAM_API_KEY() if not api_key: - return await ctx.send( - "No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/" - ) + return await ctx.send("No API key set for Wolfram Alpha. Get one at http://products.wolframalpha.com/api/") url = f"http://api.wolframalpha.com/v2/query" params = { diff --git a/youtube/__init__.py b/youtube/__init__.py index 375cccc..3df02bc 100644 --- a/youtube/__init__.py +++ b/youtube/__init__.py @@ -1,5 +1,7 @@ from .youtube import YouTube +__red_end_user_data_statement__ = "This cog does not persistently store data or metadata about users." + def setup(bot): bot.add_cog(YouTube(bot)) diff --git a/youtube/info.json b/youtube/info.json index 47bae50..2d37e89 100644 --- a/youtube/info.json +++ b/youtube/info.json @@ -8,5 +8,6 @@ "tags": [ "youtube" ], - "type": "COG" + "type": "COG", + "end_user_data_statement": "This cog does not persistently store data or metadata about users." } diff --git a/youtube/youtube.py b/youtube/youtube.py index 09aa93c..7bacfe1 100644 --- a/youtube/youtube.py +++ b/youtube/youtube.py @@ -7,6 +7,10 @@ from redbot.core.utils.menus import menu, DEFAULT_CONTROLS class YouTube(commands.Cog): """Search YouTube for videos.""" + async def red_delete_data_for_user(self, **kwargs): + """ Nothing to delete """ + return + def __init__(self, bot): self.bot = bot self.session = aiohttp.ClientSession() @@ -14,7 +18,9 @@ class YouTube(commands.Cog): async def _youtube_results(self, query: str): try: headers = {"user-agent": "Red-cog/3.0"} - async with self.session.get("https://www.youtube.com/results", params={"search_query": query}, headers=headers) as r: + async with self.session.get( + "https://www.youtube.com/results", params={"search_query": query}, headers=headers + ) as r: result = await r.text() yt_find = re.findall(r"{\"videoId\":\"(.{11})", result) url_list = []