Upstream reaction roles changes
This commit is contained in:
parent
b4d95b5635
commit
ade0917985
1 changed files with 131 additions and 57 deletions
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_role_from_emoji(self, key):
|
||||||
|
value = self.emoji_map.get(key)
|
||||||
|
|
||||||
|
if value is not None and type(value) is not str:
|
||||||
|
return value.get("role")
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
async def generate_embed(self):
|
async def generate_embed(self):
|
||||||
emojis = list(self.emoji_map.keys())
|
last_descrption = []
|
||||||
description = "React to this message with the emojis given below to get your 'Looking for LDN game' roles. \n\n"
|
description = ["React to this message with the emojis given below to get your 'Looking for LDN game' roles. \n"]
|
||||||
|
|
||||||
for x in emojis:
|
for x in self.emoji_map:
|
||||||
if self.emoji_map.get(x) == "Testers":
|
value = self.emoji_map[x]
|
||||||
description += f'\nReact {x} to get the "{self.emoji_map.get(x)}" role.'
|
|
||||||
|
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,36 +173,53 @@ 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)
|
||||||
|
|
||||||
|
if should_handle_offline:
|
||||||
await self.handle_offline_reaction_add()
|
await self.handle_offline_reaction_add()
|
||||||
await self.handle_offline_reaction_remove()
|
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):
|
||||||
if payload.member.bot:
|
if payload.member.bot:
|
||||||
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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_role = self.get_role(emoji_name)
|
||||||
|
|
||||||
|
if target_role is not None:
|
||||||
await guild.get_member(payload.user_id).remove_roles(
|
await guild.get_member(payload.user_id).remove_roles(
|
||||||
self.get_role(payload.emoji.name)
|
self.get_role(emoji_name)
|
||||||
) # payload.member.remove_roles will throw error
|
) # payload.member.remove_roles will throw error
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue