role granting

This commit is contained in:
AF 2022-04-15 16:08:01 +03:00
parent 54e890ab2c
commit d6642549c4
2 changed files with 222 additions and 13 deletions

View File

@ -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)

View File

@ -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__':