aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Johannes Christ <[email protected]>2019-07-28 18:54:48 +0200
committerGravatar Johannes Christ <[email protected]>2019-07-28 18:54:48 +0200
commit3e8690b6ac580ff49fac7022b74ba1b8f505ae83 (patch)
treefb6cfedcef4281da6dde95dad287b7c8e585d8e2
parentMerge pull request #386 from python-discord/django-merge (diff)
Revert 4d35f8f7137edb97e1124fa9087bd86399398047.
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/events.py285
2 files changed, 286 insertions, 0 deletions
diff --git a/bot/__main__.py b/bot/__main__.py
index b3f80ef55..9bfd99098 100644
--- a/bot/__main__.py
+++ b/bot/__main__.py
@@ -31,6 +31,7 @@ bot.http_session = ClientSession(
bot.api_client = APIClient(loop=asyncio.get_event_loop())
# Internal/debug
+bot.load_extension("bot.cogs.events")
bot.load_extension("bot.cogs.filtering")
bot.load_extension("bot.cogs.logging")
bot.load_extension("bot.cogs.modlog")
diff --git a/bot/cogs/events.py b/bot/cogs/events.py
new file mode 100644
index 000000000..160791fb0
--- /dev/null
+++ b/bot/cogs/events.py
@@ -0,0 +1,285 @@
+import logging
+
+from aiohttp import ClientResponseError
+from discord import Colour, Embed, Member, Object
+from discord.ext.commands import (
+ BadArgument, Bot, BotMissingPermissions,
+ CommandError, CommandInvokeError, CommandNotFound,
+ Context, NoPrivateMessage, UserInputError
+)
+
+from bot.cogs.modlog import ModLog
+from bot.constants import (
+ Channels, Colours, DEBUG_MODE,
+ Guild, Icons, Keys,
+ Roles, URLs
+)
+from bot.utils import chunks
+
+log = logging.getLogger(__name__)
+
+RESTORE_ROLES = (str(Roles.muted), str(Roles.announcements))
+
+
+class Events:
+ """No commands, just event handlers."""
+
+ def __init__(self, bot: Bot):
+ self.bot = bot
+
+ @property
+ def mod_log(self) -> ModLog:
+ return self.bot.get_cog("ModLog")
+
+ async def send_updated_users(self, *users, replace_all=False):
+ users = list(filter(lambda user: str(Roles.verified) in user["roles"], users))
+
+ for chunk in chunks(users, 1000):
+ response = None
+
+ try:
+ if replace_all:
+ response = await self.bot.http_session.post(
+ url=URLs.site_user_api,
+ json=chunk,
+ headers={"X-API-Key": Keys.site_api}
+ )
+ else:
+ response = await self.bot.http_session.put(
+ url=URLs.site_user_api,
+ json=chunk,
+ headers={"X-API-Key": Keys.site_api}
+ )
+
+ await response.json() # We do this to ensure we got a proper response from the site
+ except Exception:
+ if not response:
+ log.exception(f"Failed to send {len(chunk)} users")
+ else:
+ text = await response.text()
+ log.exception(f"Failed to send {len(chunk)} users", extra={"body": text})
+ break # Stop right now, thank you very much
+
+ result = {}
+
+ if replace_all:
+ response = None
+
+ try:
+ response = await self.bot.http_session.post(
+ url=URLs.site_user_complete_api,
+ headers={"X-API-Key": Keys.site_api}
+ )
+
+ result = await response.json()
+ except Exception:
+ if not response:
+ log.exception(f"Failed to send {len(chunk)} users")
+ else:
+ text = await response.text()
+ log.exception(f"Failed to send {len(chunk)} users", extra={"body": text})
+
+ return result
+
+ async def send_delete_users(self, *users):
+ try:
+ response = await self.bot.http_session.delete(
+ url=URLs.site_user_api,
+ json=list(users),
+ headers={"X-API-Key": Keys.site_api}
+ )
+
+ return await response.json()
+ except Exception:
+ log.exception(f"Failed to delete {len(users)} users")
+ return {}
+
+ async def get_user(self, user_id):
+ response = await self.bot.http_session.get(
+ url=URLs.site_user_api,
+ params={"user_id": user_id},
+ headers={"X-API-Key": Keys.site_api}
+ )
+
+ resp = await response.json()
+ return resp["data"]
+
+ async def on_command_error(self, ctx: Context, e: CommandError):
+ command = ctx.command
+ parent = None
+
+ if command is not None:
+ parent = command.parent
+
+ if parent and command:
+ help_command = (self.bot.get_command("help"), parent.name, command.name)
+ elif command:
+ help_command = (self.bot.get_command("help"), command.name)
+ else:
+ help_command = (self.bot.get_command("help"),)
+
+ if hasattr(command, "on_error"):
+ log.debug(f"Command {command} has a local error handler, ignoring.")
+ return
+
+ if isinstance(e, CommandNotFound) and not hasattr(ctx, "invoked_from_error_handler"):
+ tags_get_command = self.bot.get_command("tags get")
+ ctx.invoked_from_error_handler = True
+
+ # Return to not raise the exception
+ return await ctx.invoke(tags_get_command, tag_name=ctx.invoked_with)
+ elif isinstance(e, BadArgument):
+ await ctx.send(f"Bad argument: {e}\n")
+ await ctx.invoke(*help_command)
+ elif isinstance(e, UserInputError):
+ await ctx.invoke(*help_command)
+ elif isinstance(e, NoPrivateMessage):
+ await ctx.send("Sorry, this command can't be used in a private message!")
+ elif isinstance(e, BotMissingPermissions):
+ await ctx.send(
+ f"Sorry, it looks like I don't have the permissions I need to do that.\n\n"
+ f"Here's what I'm missing: **{e.missing_perms}**"
+ )
+ elif isinstance(e, CommandInvokeError):
+ if isinstance(e.original, ClientResponseError):
+ if e.original.code == 404:
+ await ctx.send("There does not seem to be anything matching your query.")
+ else:
+ await ctx.send("BEEP BEEP UNKNOWN API ERROR!=?!??!?!?!?")
+
+ else:
+ await ctx.send(
+ f"Sorry, an unexpected error occurred. Please let us know!\n\n```{e}```"
+ )
+ raise e.original
+ raise e
+
+ async def on_ready(self):
+ users = []
+
+ for member in self.bot.get_guild(Guild.id).members: # type: Member
+ roles = [str(r.id) for r in member.roles] # type: List[int]
+
+ users.append({
+ "avatar": member.avatar_url_as(format="png"),
+ "user_id": str(member.id),
+ "roles": roles,
+ "username": member.name,
+ "discriminator": member.discriminator
+ })
+
+ if users:
+ log.info(f"{len(users)} user roles to be updated")
+
+ done = await self.send_updated_users(*users, replace_all=True)
+
+ if any(done.values()):
+ embed = Embed(
+ title="Users updated"
+ )
+
+ for key, value in done.items():
+ if value:
+ if key == "deleted_oauth":
+ key = "Deleted (OAuth)"
+ elif key == "deleted_jam_profiles":
+ key = "Deleted (Jammer Profiles)"
+ elif key == "deleted_responses":
+ key = "Deleted (Jam Form Responses)"
+ elif key == "jam_bans":
+ key = "Ex-Jammer Bans"
+ else:
+ key = key.title()
+
+ embed.add_field(
+ name=key, value=str(value)
+ )
+
+ if not DEBUG_MODE:
+ await self.bot.get_channel(Channels.devlog).send(
+ embed=embed
+ )
+
+ async def on_member_update(self, before: Member, after: Member):
+ if (
+ before.roles == after.roles
+ and before.name == after.name
+ and before.discriminator == after.discriminator
+ and before.avatar == after.avatar):
+ return
+
+ before_role_names = [role.name for role in before.roles] # type: List[str]
+ after_role_names = [role.name for role in after.roles] # type: List[str]
+ role_ids = [str(r.id) for r in after.roles] # type: List[str]
+
+ log.debug(f"{before.display_name} roles changing from {before_role_names} to {after_role_names}")
+
+ changes = await self.send_updated_users({
+ "avatar": after.avatar_url_as(format="png"),
+ "user_id": str(after.id),
+ "roles": role_ids,
+ "username": after.name,
+ "discriminator": after.discriminator
+ })
+
+ log.debug(f"User {after.id} updated; changes: {changes}")
+
+ async def on_member_join(self, member: Member):
+ role_ids = [str(r.id) for r in member.roles] # type: List[str]
+ new_roles = []
+
+ try:
+ user_objs = await self.get_user(str(member.id))
+ except Exception as e:
+ log.exception("Failed to persist roles")
+
+ await self.mod_log.send_log_message(
+ Icons.crown_red, Colour(Colours.soft_red), "Failed to persist roles",
+ f"```py\n{e}\n```",
+ member.avatar_url_as(static_format="png")
+ )
+ else:
+ if user_objs:
+ old_roles = user_objs[0].get("roles", [])
+
+ for role in RESTORE_ROLES:
+ if role in old_roles:
+ new_roles.append(Object(int(role)))
+
+ for role in new_roles:
+ if str(role) not in role_ids:
+ role_ids.append(str(role.id))
+
+ changes = await self.send_updated_users({
+ "avatar": member.avatar_url_as(format="png"),
+ "user_id": str(member.id),
+ "roles": role_ids,
+ "username": member.name,
+ "discriminator": member.discriminator
+ })
+
+ log.debug(f"User {member.id} joined; changes: {changes}")
+
+ if new_roles:
+ await member.add_roles(
+ *new_roles,
+ reason="Roles restored"
+ )
+
+ await self.mod_log.send_log_message(
+ Icons.crown_blurple, Colour.blurple(), "Roles restored",
+ f"Restored {len(new_roles)} roles",
+ member.avatar_url_as(static_format="png")
+ )
+
+ async def on_member_remove(self, member: Member):
+ changes = await self.send_delete_users({
+ "user_id": str(member.id)
+ })
+
+ log.debug(f"User {member.id} left; changes: {changes}")
+
+
+def setup(bot):
+ bot.add_cog(Events(bot))
+ log.info("Cog loaded: Events")