backup permission overwrites also
This commit is contained in:
parent
b2eccf90fd
commit
13296488f8
2 changed files with 114 additions and 5 deletions
|
|
@ -89,8 +89,82 @@ class BackupPermissions:
|
||||||
|
|
||||||
|
|
||||||
class BackupPermissionOverwrite:
|
class BackupPermissionOverwrite:
|
||||||
"""Minimal stand-in for discord.PermissionOverwrite."""
|
"""Minimal stand-in for discord.PermissionOverwrite.
|
||||||
pass
|
|
||||||
|
Supports:
|
||||||
|
- .pair() -> (BackupPermissions(allow), BackupPermissions(deny))
|
||||||
|
- iteration: yields (perm_name, True|False|None) tuples
|
||||||
|
- setattr(ow, perm_name, value) for merging
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Standard Discord permission flag names and their bit positions
|
||||||
|
VALID_NAMES = {
|
||||||
|
"create_instant_invite": 0, "kick_members": 1, "ban_members": 2,
|
||||||
|
"administrator": 3, "manage_channels": 4, "manage_guild": 5,
|
||||||
|
"add_reactions": 6, "view_audit_log": 7, "priority_speaker": 8,
|
||||||
|
"stream": 9, "view_channel": 10, "send_messages": 11,
|
||||||
|
"send_tts_messages": 12, "manage_messages": 13, "embed_links": 14,
|
||||||
|
"attach_files": 15, "read_message_history": 16, "mention_everyone": 17,
|
||||||
|
"use_external_emojis": 18, "view_guild_insights": 19, "connect": 20,
|
||||||
|
"speak": 21, "mute_members": 22, "deafen_members": 23,
|
||||||
|
"move_members": 24, "use_vad": 25, "change_nickname": 26,
|
||||||
|
"manage_nicknames": 27, "manage_roles": 28, "manage_webhooks": 29,
|
||||||
|
"manage_emojis_and_stickers": 30, "use_application_commands": 31,
|
||||||
|
"request_to_speak": 32, "manage_events": 33, "manage_threads": 34,
|
||||||
|
"create_public_threads": 35, "create_private_threads": 36,
|
||||||
|
"use_external_stickers": 37, "send_messages_in_threads": 38,
|
||||||
|
"use_embedded_activities": 39, "moderate_members": 40,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, allow: int = 0, deny: int = 0):
|
||||||
|
self._allow = allow
|
||||||
|
self._deny = deny
|
||||||
|
|
||||||
|
def pair(self):
|
||||||
|
"""Returns (Permissions(allow), Permissions(deny))."""
|
||||||
|
return BackupPermissions(self._allow), BackupPermissions(self._deny)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
"""Yields (perm_name, True|False|None) for each known permission."""
|
||||||
|
for name, bit in self.VALID_NAMES.items():
|
||||||
|
mask = 1 << bit
|
||||||
|
if self._allow & mask:
|
||||||
|
yield name, True
|
||||||
|
elif self._deny & mask:
|
||||||
|
yield name, False
|
||||||
|
else:
|
||||||
|
yield name, None
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
if name.startswith("_") or name not in self.VALID_NAMES:
|
||||||
|
super().__setattr__(name, value)
|
||||||
|
return
|
||||||
|
bit = self.VALID_NAMES[name]
|
||||||
|
mask = 1 << bit
|
||||||
|
# Clear both first
|
||||||
|
self._allow &= ~mask
|
||||||
|
self._deny &= ~mask
|
||||||
|
if value is True:
|
||||||
|
self._allow |= mask
|
||||||
|
elif value is False:
|
||||||
|
self._deny |= mask
|
||||||
|
# None = neutral, both cleared
|
||||||
|
|
||||||
|
|
||||||
|
class BackupOverwriteTarget:
|
||||||
|
"""Stand-in for the target (role) key in overwrites dict.
|
||||||
|
|
||||||
|
Satisfies: type(target).__name__ == 'Role' and target.id
|
||||||
|
"""
|
||||||
|
def __init__(self, role_id: int):
|
||||||
|
self.id = role_id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Role(id={self.id})"
|
||||||
|
|
||||||
|
# Allow `type(target).__name__` to return "Role"
|
||||||
|
BackupOverwriteTarget.__name__ = "Role"
|
||||||
|
BackupOverwriteTarget.__qualname__ = "Role"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -150,6 +224,17 @@ class BackupRole:
|
||||||
return f"BackupRole(id={self.id}, name='{self.name}')"
|
return f"BackupRole(id={self.id}, name='{self.name}')"
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_overwrites(raw_list: list) -> dict:
|
||||||
|
"""Parse overwrites JSON list into {BackupOverwriteTarget: BackupPermissionOverwrite} dict."""
|
||||||
|
result = {}
|
||||||
|
for entry in raw_list:
|
||||||
|
target = BackupOverwriteTarget(int(entry["id"]))
|
||||||
|
ow = BackupPermissionOverwrite(allow=int(entry.get("allow", 0)),
|
||||||
|
deny=int(entry.get("deny", 0)))
|
||||||
|
result[target] = ow
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class BackupCategory:
|
class BackupCategory:
|
||||||
"""Minimal stand-in for discord.CategoryChannel."""
|
"""Minimal stand-in for discord.CategoryChannel."""
|
||||||
|
|
||||||
|
|
@ -163,7 +248,7 @@ class BackupCategory:
|
||||||
self.name = data["name"]
|
self.name = data["name"]
|
||||||
self.position = data.get("position", 0)
|
self.position = data.get("position", 0)
|
||||||
self.type = ChannelType.category
|
self.type = ChannelType.category
|
||||||
self.overwrites = {}
|
self.overwrites = _parse_overwrites(data.get("overwrites", []))
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"BackupCategory(id={self.id}, name='{self.name}')"
|
return f"BackupCategory(id={self.id}, name='{self.name}')"
|
||||||
|
|
@ -194,7 +279,7 @@ class BackupChannel:
|
||||||
self.parent_id = category_id
|
self.parent_id = category_id
|
||||||
self.available_tags = data.get("available_tags", [])
|
self.available_tags = data.get("available_tags", [])
|
||||||
self.guild = guild
|
self.guild = guild
|
||||||
self.overwrites = {}
|
self.overwrites = _parse_overwrites(data.get("overwrites", []))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mention(self) -> str:
|
def mention(self) -> str:
|
||||||
|
|
|
||||||
|
|
@ -228,11 +228,23 @@ class DiscordExporter:
|
||||||
cat_channels = [c for c in channels if c.category_id == cat.id]
|
cat_channels = [c for c in channels if c.category_id == cat.id]
|
||||||
formatted_channels = await asyncio.gather(*[self._format_channel(c) for c in cat_channels])
|
formatted_channels = await asyncio.gather(*[self._format_channel(c) for c in cat_channels])
|
||||||
chan_count += len(formatted_channels)
|
chan_count += len(formatted_channels)
|
||||||
|
# Serialize role-only permission overwrites
|
||||||
|
cat_overwrites = []
|
||||||
|
for target, ow in cat.overwrites.items():
|
||||||
|
if isinstance(target, discord.Role):
|
||||||
|
allow, deny = ow.pair()
|
||||||
|
cat_overwrites.append({
|
||||||
|
"id": str(target.id),
|
||||||
|
"allow": allow.value,
|
||||||
|
"deny": deny.value
|
||||||
|
})
|
||||||
|
|
||||||
structure.append({
|
structure.append({
|
||||||
"type": "category",
|
"type": "category",
|
||||||
"id": str(cat.id),
|
"id": str(cat.id),
|
||||||
"name": cat.name,
|
"name": cat.name,
|
||||||
"position": cat.position,
|
"position": cat.position,
|
||||||
|
"overwrites": cat_overwrites,
|
||||||
"channels": list(formatted_channels)
|
"channels": list(formatted_channels)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -255,13 +267,25 @@ class DiscordExporter:
|
||||||
return structure, cat_count, chan_count
|
return structure, cat_count, chan_count
|
||||||
|
|
||||||
async def _format_channel(self, c):
|
async def _format_channel(self, c):
|
||||||
|
# Serialize role-only permission overwrites
|
||||||
|
ch_overwrites = []
|
||||||
|
for target, ow in c.overwrites.items():
|
||||||
|
if isinstance(target, discord.Role):
|
||||||
|
allow, deny = ow.pair()
|
||||||
|
ch_overwrites.append({
|
||||||
|
"id": str(target.id),
|
||||||
|
"allow": allow.value,
|
||||||
|
"deny": deny.value
|
||||||
|
})
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"id": str(c.id),
|
"id": str(c.id),
|
||||||
"name": c.name,
|
"name": c.name,
|
||||||
"type": str(c.type),
|
"type": str(c.type),
|
||||||
"position": c.position,
|
"position": c.position,
|
||||||
"topic": getattr(c, "topic", None),
|
"topic": getattr(c, "topic", None),
|
||||||
"nsfw": getattr(c, "nsfw", False)
|
"nsfw": getattr(c, "nsfw", False),
|
||||||
|
"overwrites": ch_overwrites
|
||||||
}
|
}
|
||||||
|
|
||||||
if isinstance(c, discord.ForumChannel):
|
if isinstance(c, discord.ForumChannel):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue