role granting
This commit is contained in:
parent
54e890ab2c
commit
d6642549c4
@ -1,7 +1,12 @@
|
||||
import os
|
||||
|
||||
from v6d0auth.config import root
|
||||
|
||||
guild = int(os.getenv('v6guild', 541241763042689025))
|
||||
emoji = int(os.getenv('v6emoji', 586669134406877270))
|
||||
role = int(os.getenv('v6role', 643896112977018880))
|
||||
message = int(os.getenv('v6message', 825385619097518110))
|
||||
channel = int(os.getenv('v6channel', 876814972968116224))
|
||||
role_channel = int(os.getenv('v6channel', 964261739980025966))
|
||||
myroot = root / 'v6d3losyash'
|
||||
myroot.mkdir(exist_ok=True)
|
||||
|
@ -1,7 +1,9 @@
|
||||
import asyncio
|
||||
from io import StringIO
|
||||
from typing import Union, Optional
|
||||
|
||||
import discord
|
||||
from ptvp35 import KVJson, Db
|
||||
from v6d1tokens.client import request_token
|
||||
from v6d2ctx.serve import serve
|
||||
|
||||
@ -21,6 +23,7 @@ client = discord.Client(
|
||||
),
|
||||
)
|
||||
ESCAPED = '`_*\'"\\'
|
||||
nest_db = Db(config.myroot / 'nest.db', kvrequest_type=KVJson)
|
||||
|
||||
|
||||
def escape(s: str):
|
||||
@ -32,30 +35,220 @@ def escape(s: str):
|
||||
return res.getvalue()
|
||||
|
||||
|
||||
lock = asyncio.Lock()
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
print("ready")
|
||||
await client.change_presence(activity=discord.Game(
|
||||
name='феноменально',
|
||||
))
|
||||
async with lock:
|
||||
await state.reload()
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_add(payload: discord.RawReactionActionEvent):
|
||||
emoji: discord.Emoji = payload.emoji
|
||||
if emoji.id != config.emoji:
|
||||
return
|
||||
if payload.message_id != config.message:
|
||||
return
|
||||
class SimpleEmoji:
|
||||
def __init__(self, ref: Union[str, int]):
|
||||
self.ref = ref
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.ref)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, SimpleEmoji):
|
||||
return self.ref == other.ref
|
||||
return NotImplemented
|
||||
|
||||
@classmethod
|
||||
def of(cls, emoji: Union[str, discord.Emoji, discord.PartialEmoji]) -> 'SimpleEmoji':
|
||||
if isinstance(emoji, discord.Emoji):
|
||||
return cls(emoji.id)
|
||||
elif isinstance(emoji, str):
|
||||
return cls(emoji)
|
||||
elif isinstance(emoji, discord.PartialEmoji):
|
||||
if emoji.id is None:
|
||||
return cls(emoji.name)
|
||||
else:
|
||||
return cls(emoji.id)
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
def to(self) -> Union[str, discord.Emoji]:
|
||||
if isinstance(self.ref, str):
|
||||
return self.ref
|
||||
else:
|
||||
return client.get_emoji(self.ref)
|
||||
|
||||
|
||||
class RoleGrant:
|
||||
def __init__(self, role: int, emoji: SimpleEmoji):
|
||||
self.role = role
|
||||
self.emoji = emoji
|
||||
|
||||
def format(self) -> str:
|
||||
guild: discord.Guild = client.get_guild(config.guild)
|
||||
return f'{self.emoji.to()} {guild.get_role(self.role)}'
|
||||
|
||||
|
||||
class MessageDescription:
|
||||
def __init__(self, key: str, description: str, *roles: RoleGrant):
|
||||
self.key = key
|
||||
self._description = description
|
||||
self.roles = roles
|
||||
self.mapping: dict[SimpleEmoji, int] = {grant.emoji: grant.role for grant in roles}
|
||||
|
||||
def description(self):
|
||||
return self._description + '\n' + '\n'.join(map(RoleGrant.format, self.roles))
|
||||
|
||||
|
||||
class ChannelDescription:
|
||||
def __init__(self, *mds: MessageDescription):
|
||||
self.mds = mds
|
||||
self.mapping: dict[str, MessageDescription] = {md.key: md for md in mds}
|
||||
|
||||
|
||||
def role_channel() -> discord.TextChannel:
|
||||
guild: discord.Guild = client.get_guild(config.guild)
|
||||
member: discord.Member = guild.get_member(payload.user_id)
|
||||
channel: discord.TextChannel = guild.get_channel(config.role_channel)
|
||||
return channel
|
||||
|
||||
|
||||
class State:
|
||||
def __init__(self, cd: ChannelDescription):
|
||||
self.defmap: dict[str, int] = {}
|
||||
self.defrev: dict[int, str] = {}
|
||||
self.cd = cd
|
||||
|
||||
async def _load(self):
|
||||
self.defmap = nest_db.get('state-map', {})
|
||||
self.defrev.clear()
|
||||
for md in self.cd.mds:
|
||||
msg = await self._get(md.key, md.description())
|
||||
for reaction in msg.reactions:
|
||||
emoji = SimpleEmoji.of(reaction.emoji)
|
||||
if emoji not in md.mapping:
|
||||
await reaction.clear()
|
||||
for emoji in md.mapping.keys():
|
||||
await msg.add_reaction(emoji.to())
|
||||
|
||||
async def _get_raw(self, key: str, content: str) -> discord.Message:
|
||||
channel = role_channel()
|
||||
if key in self.defmap:
|
||||
try:
|
||||
msg = await channel.fetch_message(self.defmap[key])
|
||||
if msg.content != content:
|
||||
await msg.edit(content=content)
|
||||
return msg
|
||||
except discord.NotFound:
|
||||
pass
|
||||
return await channel.send(content)
|
||||
|
||||
async def _get(self, key: str, content: str) -> discord.Message:
|
||||
msg = await self._get_raw(key, content)
|
||||
self.defmap[key] = msg.id
|
||||
self.defrev[msg.id] = key
|
||||
return msg
|
||||
|
||||
def _reset_defmap(self):
|
||||
self.defmap = {key: msid for msid, key in self.defrev.items()}
|
||||
|
||||
async def _clear_channel(self):
|
||||
msg: discord.Message
|
||||
async for msg in role_channel().history(limit=100):
|
||||
if msg.author.id == client.user.id and msg.id not in self.defrev:
|
||||
await msg.delete()
|
||||
|
||||
async def _save(self):
|
||||
self._reset_defmap()
|
||||
await self._clear_channel()
|
||||
await nest_db.set('state-map', self.defmap)
|
||||
|
||||
async def reload(self):
|
||||
await self._load()
|
||||
await self._save()
|
||||
|
||||
async def _role_member(
|
||||
self, msid: int, member_id: int, emoji: SimpleEmoji
|
||||
) -> tuple[Optional[discord.Role], Optional[discord.Member]]:
|
||||
key = self.defrev[msid]
|
||||
md = self.cd.mapping[key]
|
||||
role_id = md.mapping.get(emoji)
|
||||
if role_id is None:
|
||||
channel = role_channel()
|
||||
message: discord.Message = await channel.fetch_message(msid)
|
||||
await message.clear_reaction(emoji.to())
|
||||
return None, None
|
||||
guild: discord.Guild = await client.fetch_guild(config.guild)
|
||||
role: discord.Role = guild.get_role(role_id)
|
||||
member: discord.Member = await guild.fetch_member(member_id)
|
||||
return role, member
|
||||
|
||||
async def role_grant(self, msid: int, member_id: int, emoji: SimpleEmoji):
|
||||
role, member = await self._role_member(msid, member_id, emoji)
|
||||
if role is None or member is None:
|
||||
return
|
||||
await member.add_roles(role, reason='emojis go yes')
|
||||
await log(f'emoji {emoji.to()} +role {role} member {member}')
|
||||
|
||||
async def role_revoke(self, msid: int, member_id: int, emoji: SimpleEmoji):
|
||||
role, member = await self._role_member(msid, member_id, emoji)
|
||||
if role is None or member is None:
|
||||
return
|
||||
await member.remove_roles(role, reason='emojis go no')
|
||||
await log(f'emoji {emoji.to()} -role {role} member {member}')
|
||||
|
||||
|
||||
state = State(
|
||||
ChannelDescription(
|
||||
MessageDescription(
|
||||
'games',
|
||||
'Игровые роли',
|
||||
RoleGrant(541263011822698506, SimpleEmoji(541257134407417864)),
|
||||
RoleGrant(542056704842661899, SimpleEmoji('🦆')),
|
||||
RoleGrant(541349294586724365, SimpleEmoji('⛏')),
|
||||
RoleGrant(541263005971644419, SimpleEmoji('🌈')),
|
||||
RoleGrant(934467294371938355, SimpleEmoji('🎩')),
|
||||
RoleGrant(933103254752088104, SimpleEmoji('🐟')),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def log(msg: str):
|
||||
guild: discord.Guild = client.get_guild(config.guild)
|
||||
channel: discord.TextChannel = guild.get_channel(config.channel)
|
||||
await channel.send(msg)
|
||||
|
||||
|
||||
async def grant_citizenship(member_id: int):
|
||||
guild: discord.Guild = client.get_guild(config.guild)
|
||||
member: discord.Member = guild.get_member(member_id)
|
||||
role = guild.get_role(config.role)
|
||||
if role in member.roles:
|
||||
return
|
||||
await member.add_roles(role, reason='феноменально')
|
||||
await log(f'{escape(str(member))} {member.id} <:Jesus:586669134406877270>')
|
||||
|
||||
channel: discord.TextChannel = guild.get_channel(config.channel)
|
||||
await channel.send(f'{escape(str(member))} {member.id} <:Jesus:586669134406877270>')
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_add(payload: discord.RawReactionActionEvent):
|
||||
if payload.user_id == client.user.id:
|
||||
return
|
||||
emoji: discord.PartialEmoji = payload.emoji
|
||||
if emoji.id == config.emoji and payload.message_id == config.message:
|
||||
await grant_citizenship(payload.user_id)
|
||||
if payload.message_id in state.defrev:
|
||||
await state.role_grant(payload.message_id, payload.user_id, SimpleEmoji.of(emoji))
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_remove(payload: discord.RawReactionActionEvent):
|
||||
if payload.user_id == client.user.id:
|
||||
return
|
||||
emoji: discord.PartialEmoji = payload.emoji
|
||||
if payload.message_id in state.defrev:
|
||||
await state.role_revoke(payload.message_id, payload.user_id, SimpleEmoji.of(emoji))
|
||||
|
||||
|
||||
@client.event
|
||||
@ -73,12 +266,23 @@ async def on_member_remove(member: discord.Member):
|
||||
if guild.id != config.guild:
|
||||
return
|
||||
channel: discord.TextChannel = guild.get_channel(config.channel)
|
||||
await channel.send(f'{escape(str(member))} {member.id} left (joined {member.joined_at})')
|
||||
message = await channel.send(f'{escape(str(member))} {member.id} left (joined {member.joined_at})')
|
||||
await asyncio.sleep(1)
|
||||
entry: discord.AuditLogEntry
|
||||
async for entry in guild.audit_logs(action=discord.AuditLogAction.kick):
|
||||
if entry.target.id == member.id:
|
||||
await message.reply(f'latest kick: {entry.created_at} {entry.reason}')
|
||||
break
|
||||
async for entry in guild.audit_logs(action=discord.AuditLogAction.ban):
|
||||
if entry.target.id == member.id:
|
||||
await message.reply(f'latest ban: {entry.created_at} {entry.reason}')
|
||||
break
|
||||
|
||||
|
||||
async def main():
|
||||
await client.login(token)
|
||||
await client.connect()
|
||||
async with nest_db:
|
||||
await client.login(token)
|
||||
await client.connect()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user