Upstream reaction roles changes

This commit is contained in:
Mary 2022-09-05 19:30:16 +02:00 committed by TSR Berry
parent b4d95b5635
commit ade0917985
No known key found for this signature in database
GPG key ID: 52353C0A4CCA15E2

View file

@ -1,10 +1,12 @@
import collections
import json import json
import config import config
import os import os
import discord import discord
from discord.ext import commands
from discord.ext.commands import Cog from discord.ext.commands import Cog
from helpers.checks import check_if_staff
class RyujinxReactionRoles(Cog): class RyujinxReactionRoles(Cog):
def __init__(self, bot): def __init__(self, bot):
@ -13,46 +15,86 @@ class RyujinxReactionRoles(Cog):
config.reaction_roles_channel_id config.reaction_roles_channel_id
) # The channel to send the reaction role message. (self-roles channel) ) # The channel to send the reaction role message. (self-roles channel)
self.emoji_map = {
"🦑": "Looking for LDN game (Splatoon 2)",
"👹": "Looking for LDN game (Monster Hunter Generations Ultimate)",
"👺": "Looking for LDN game (Monster Hunter Rise)",
"🧩": "Looking for LDN game (Mario Party Superstars)",
"🐉": "Looking for LDN game (Pokémon Sword/Shield)",
"⚔️": "Looking for LDN game (Super Smash Bros. Ultimate)",
"🏎️": "Looking for LDN game (Mario Kart 8)",
"🪨": "Looking for LDN game (Pokémon Brilliant Diamond/Shining Pearl)",
"🍃": "Looking for LDN game (Animal Crossing: New Horizons)",
"": "Looking for LDN game (Others)",
"🚩": "Testers",
# LDN roles should be placed *before* testers role, because of embed generating.
# LDN roles ought to be in the format "Looking for LDN game (<game>)".
} # The mapping of emoji ids to the role.
self.file = "data/reactionroles.json" # the file to store the required reaction role data. (message id of the RR message.) self.file = "data/reactionroles.json" # the file to store the required reaction role data. (message id of the RR message.)
self.msg_id = None self.msg_id = None
self.m = None # the msg object self.m = None # the msg object
self.get_role = lambda emoji_name: discord.utils.get( @commands.guild_only()
@commands.check(check_if_staff)
@commands.command()
async def register_reaction_role(self, ctx, target_role_id: int, emoji_name: str):
"""Register a reaction role, staff only."""
if emoji_name[0] == '<':
emoji_name = emoji_name[1:-1]
if target_role_id in config.staff_role_ids:
return await ctx.send("Error: Dangerous role found!")
target_role = ctx.guild.get_role(target_role_id)
if target_role is None:
return await ctx.send("Error: Role not found!")
target_role_name = target_role.name
for key in self.reaction_config["reaction_roles_emoji_map"]:
value = self.reaction_config["reaction_roles_emoji_map"][key]
if type(value) is str and target_role_name == value:
return await ctx.send(f"Error: {target_role_name}: already registered.")
self.reaction_config["reaction_roles_emoji_map"][emoji_name] = target_role_name
self.save_reaction_config(self.reaction_config)
await self.reload_reaction_message(False)
await ctx.send(f"{target_role_name}: registered.")
def get_emoji_full_name(self, emoji):
emoji_name = emoji.name
if emoji_name is not None and emoji.id is not None:
emoji_name = f":{emoji_name}:{emoji.id}"
return emoji_name
def get_role(self, key):
return discord.utils.get(
self.bot.guilds[0].roles, self.bot.guilds[0].roles,
name=self.emoji_map.get(str(emoji_name)), name=self.get_role_from_emoji(key),
) )
async def generate_embed(self): def get_role_from_emoji(self, key):
emojis = list(self.emoji_map.keys()) value = self.emoji_map.get(key)
description = "React to this message with the emojis given below to get your 'Looking for LDN game' roles. \n\n"
for x in emojis: if value is not None and type(value) is not str:
if self.emoji_map.get(x) == "Testers": return value.get("role")
description += f'\nReact {x} to get the "{self.emoji_map.get(x)}" role.'
return value
async def generate_embed(self):
last_descrption = []
description = ["React to this message with the emojis given below to get your 'Looking for LDN game' roles. \n"]
for x in self.emoji_map:
value = self.emoji_map[x]
emoji = x
if len(emoji.split(':')) == 3:
emoji = f"<{emoji}>"
if type(value) is str:
description.append(f"{emoji} for __{self.emoji_map.get(x).split('(')[1].split(')')[0]}__")
else: else:
description += ( role_name = value["role"]
f"{x} for __{self.emoji_map.get(x).split('(')[1].split(')')[0]}__ \n" line_fmt = value["fmt"]
) if value.get("should_be_last", False):
last_descrption.append(line_fmt.format(emoji, role_name))
else:
description.append(line_fmt.format(emoji, role_name))
embed = discord.Embed( embed = discord.Embed(
title="**Select your roles**", description=description, color=420420 title="**Select your roles**", description='\n'.join(description) + '\n' + '\n'.join(last_descrption), color=420420
) )
embed.set_footer( embed.set_footer(
text="To remove a role, simply remove the corresponding reaction." text="To remove a role, simply remove the corresponding reaction."
@ -63,8 +105,12 @@ class RyujinxReactionRoles(Cog):
async def handle_offline_reaction_add(self): async def handle_offline_reaction_add(self):
for reaction in self.m.reactions: for reaction in self.m.reactions:
for user in await reaction.users().flatten(): for user in await reaction.users().flatten():
if self.emoji_map.get(reaction.emoji) is not None: emoji_name = str(reaction.emoji)
role = self.get_role(reaction.emoji) if emoji_name[0] == '<':
emoji_name = emoji_name[1:-1]
if self.get_role_from_emoji(emoji_name) is not None:
role = self.get_role(emoji_name)
if not user in role.members and not user.bot: if not user in role.members and not user.bot:
await user.add_roles(role) await user.add_roles(role)
else: else:
@ -73,27 +119,38 @@ class RyujinxReactionRoles(Cog):
async def handle_offline_reaction_remove(self): async def handle_offline_reaction_remove(self):
for emoji in self.emoji_map: for emoji in self.emoji_map:
for reaction in self.m.reactions: for reaction in self.m.reactions:
role = self.get_role(reaction.emoji) emoji_name = str(reaction.emoji)
if emoji_name[0] == '<':
emoji_name = emoji_name[1:-1]
role = self.get_role(emoji_name)
for user in role.members: for user in role.members:
if user not in await reaction.users().flatten(): if user not in await reaction.users().flatten():
await self.m.guild.get_member(user.id).remove_roles(role) await self.m.guild.get_member(user.id).remove_roles(role)
@Cog.listener() def load_reaction_config(self):
async def on_ready(self): if not os.path.exists(self.file):
self.bot.log.error("HERE?!")
with open(self.file, "w") as f:
json.dump({}, f)
with open(self.file, "r") as f:
return json.load(f)
def save_reaction_config(self, value):
with open(self.file, "w") as f:
json.dump(value, f)
async def reload_reaction_message(self, should_handle_offline = True):
self.emoji_map = collections.OrderedDict(sorted(self.reaction_config["reaction_roles_emoji_map"].items(), key=lambda x: str(x[1])))
guild = self.bot.guilds[0] # The ryu guild in which the bot is. guild = self.bot.guilds[0] # The ryu guild in which the bot is.
channel = guild.get_channel(self.channel_id) channel = guild.get_channel(self.channel_id)
if not os.path.exists(self.file): m = discord.utils.get(await channel.history().flatten(), id=self.reaction_config["id"])
with open(self.file, "w") as f:
f.write("{}")
with open(self.file, "r") as f:
id = json.load(f).get("id")
m = discord.utils.get(await channel.history().flatten(), id=id)
if m is None: if m is None:
os.remove(self.file) self.reaction_config["id"] = None
embed = await self.generate_embed() embed = await self.generate_embed()
self.m = await channel.send(embed=embed) self.m = await channel.send(embed=embed)
@ -102,13 +159,13 @@ class RyujinxReactionRoles(Cog):
for x in self.emoji_map: for x in self.emoji_map:
await self.m.add_reaction(x) await self.m.add_reaction(x)
with open(self.file, "w") as f: self.reaction_config["id"] = self.m.id
json.dump({"id": self.m.id}, f) self.save_reaction_config(self.reaction_config)
await self.handle_offline_reaction_remove() await self.handle_offline_reaction_remove()
else: else:
self.m = discord.utils.get(await channel.history().flatten(), id=id) self.m = discord.utils.get(await channel.history().flatten(), id=self.reaction_config["id"])
self.msg_id = self.m.id self.msg_id = self.m.id
await self.m.edit(embed=await self.generate_embed()) await self.m.edit(embed=await self.generate_embed())
@ -116,8 +173,15 @@ class RyujinxReactionRoles(Cog):
if not x in self.m.reactions: if not x in self.m.reactions:
await self.m.add_reaction(x) await self.m.add_reaction(x)
await self.handle_offline_reaction_add() if should_handle_offline:
await self.handle_offline_reaction_remove() await self.handle_offline_reaction_add()
await self.handle_offline_reaction_remove()
@Cog.listener()
async def on_ready(self):
self.reaction_config = self.load_reaction_config()
await self.reload_reaction_message()
@Cog.listener() @Cog.listener()
async def on_raw_reaction_add(self, payload): async def on_raw_reaction_add(self, payload):
@ -125,28 +189,38 @@ class RyujinxReactionRoles(Cog):
pass pass
else: else:
if payload.message_id == self.msg_id: if payload.message_id == self.msg_id:
if self.emoji_map.get(payload.emoji.name) is not None: emoji_name = self.get_emoji_full_name(payload.emoji)
if self.get_role(payload.emoji.name) is not None:
if self.get_role_from_emoji(emoji_name) is not None:
target_role = self.get_role(emoji_name)
if target_role is not None:
await payload.member.add_roles( await payload.member.add_roles(
self.get_role(payload.emoji.name) target_role
) )
else: else:
print(f"Role {self.emoji_map[payload.emoji.name]} not found.") self.bot.log.error(f"Role {self.emoji_map[emoji_name]} not found.")
await self.m.clear_reaction(payload.emoji)
else: else:
await self.m.clear_reaction(payload.emoji.name) await self.m.clear_reaction(payload.emoji)
@Cog.listener() @Cog.listener()
async def on_raw_reaction_remove(self, payload): async def on_raw_reaction_remove(self, payload):
if payload.message_id == self.msg_id: if payload.message_id == self.msg_id:
if self.emoji_map.get(str(payload.emoji.name)) is not None: emoji_name = self.get_emoji_full_name(payload.emoji)
if self.get_role_from_emoji(emoji_name) is not None:
guild = discord.utils.find( guild = discord.utils.find(
lambda guild: guild.id == payload.guild_id, self.bot.guilds lambda guild: guild.id == payload.guild_id, self.bot.guilds
) )
await guild.get_member(payload.user_id).remove_roles( target_role = self.get_role(emoji_name)
self.get_role(payload.emoji.name)
) # payload.member.remove_roles will throw error if target_role is not None:
await guild.get_member(payload.user_id).remove_roles(
self.get_role(emoji_name)
) # payload.member.remove_roles will throw error
async def setup(bot): async def setup(bot):