implement emoji sync
This commit is contained in:
parent
75f7b8ecf9
commit
17d18bdb1e
7 changed files with 215 additions and 38 deletions
|
|
@ -15,7 +15,10 @@ class MigrationContext:
|
||||||
def __init__(self, config: AppConfig, target_platform: str = "fluxer"):
|
def __init__(self, config: AppConfig, target_platform: str = "fluxer"):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.target_platform = target_platform
|
self.target_platform = target_platform
|
||||||
self.state = MigrationState()
|
self.state = MigrationState(
|
||||||
|
state_file=f"{target_platform}.state.json",
|
||||||
|
messages_file=f"{target_platform}.messages.json"
|
||||||
|
)
|
||||||
|
|
||||||
self.discord_reader = DiscordReader(
|
self.discord_reader = DiscordReader(
|
||||||
token=config.discord_bot_token,
|
token=config.discord_bot_token,
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,44 @@ class MigrationState:
|
||||||
self.sticker_map.pop(str(discord_id), None)
|
self.sticker_map.pop(str(discord_id), None)
|
||||||
self.save_state()
|
self.save_state()
|
||||||
|
|
||||||
|
# --- Generic Aliases for target platform migration ---
|
||||||
|
|
||||||
|
get_target_channel_id = get_fluxer_channel_id
|
||||||
|
set_channel_mapping = set_channel_mapping # already generic enough in name if we ignore the 'fluxer' in implementation
|
||||||
|
|
||||||
|
def set_target_channel_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_channel_mapping(discord_id, target_id)
|
||||||
|
|
||||||
|
def get_target_category_id(self, discord_id: str) -> str | None:
|
||||||
|
return self.get_fluxer_category_id(discord_id)
|
||||||
|
|
||||||
|
def set_target_category_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_category_mapping(discord_id, target_id)
|
||||||
|
|
||||||
|
def get_target_role_id(self, discord_id: str) -> str | None:
|
||||||
|
return self.get_fluxer_role_id(discord_id)
|
||||||
|
|
||||||
|
def set_target_role_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_role_mapping(discord_id, target_id)
|
||||||
|
|
||||||
|
def get_target_emoji_id(self, discord_id: str) -> str | None:
|
||||||
|
return self.get_fluxer_emoji_id(discord_id)
|
||||||
|
|
||||||
|
def set_target_emoji_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_emoji_mapping(discord_id, target_id)
|
||||||
|
|
||||||
|
def get_target_sticker_id(self, discord_id: str) -> str | None:
|
||||||
|
return self.get_fluxer_sticker_id(discord_id)
|
||||||
|
|
||||||
|
def set_target_sticker_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_sticker_mapping(discord_id, target_id)
|
||||||
|
|
||||||
|
def get_target_message_id(self, discord_id: str) -> str | None:
|
||||||
|
return self.get_fluxer_message_id(discord_id)
|
||||||
|
|
||||||
|
def set_target_message_mapping(self, discord_id: str, target_id: str):
|
||||||
|
self.set_message_mapping(discord_id, target_id)
|
||||||
|
|
||||||
# --- Message Management ---
|
# --- Message Management ---
|
||||||
|
|
||||||
def set_message_mapping(self, discord_id: str, fluxer_id: str):
|
def set_message_mapping(self, discord_id: str, fluxer_id: str):
|
||||||
|
|
|
||||||
|
|
@ -6,28 +6,28 @@ from src.core.base import MigrationContext
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
async def danger_remove_logo_and_banner(context: MigrationContext) -> dict:
|
async def danger_remove_logo_and_banner(context: MigrationContext) -> dict:
|
||||||
"""Removes the community logo and banner image. Returns per-field status."""
|
"""Removes the target community's logo and banner image. Returns per-field status."""
|
||||||
return await context.fluxer_writer.remove_community_logo_and_banner()
|
return await context.writer.remove_community_logo_and_banner()
|
||||||
|
|
||||||
async def danger_delete_all_channels(context: MigrationContext, progress_callback=None) -> int:
|
async def danger_delete_all_channels(context: MigrationContext, progress_callback=None) -> int:
|
||||||
"""Deletes every channel and category in the Fluxer community."""
|
"""Deletes every channel and category in the target community."""
|
||||||
count = await context.fluxer_writer.delete_all_channels(progress_callback=progress_callback)
|
count = await context.writer.delete_all_channels(progress_callback=progress_callback)
|
||||||
context.state.clear_channel_mappings()
|
context.state.clear_channel_mappings()
|
||||||
context.state.clear_message_history()
|
context.state.clear_message_history()
|
||||||
return count
|
return count
|
||||||
|
|
||||||
async def danger_reset_channel_permissions(context: MigrationContext, progress_callback=None) -> int:
|
async def danger_reset_channel_permissions(context: MigrationContext, progress_callback=None) -> int:
|
||||||
"""Resets all permission overwrites on every channel and category."""
|
"""Resets all permission overwrites on every channel and category in the target community."""
|
||||||
return await context.fluxer_writer.reset_channel_permissions(progress_callback=progress_callback)
|
return await context.writer.reset_channel_permissions(progress_callback=progress_callback)
|
||||||
|
|
||||||
async def danger_delete_all_roles(context: MigrationContext, progress_callback=None) -> int:
|
async def danger_delete_all_roles(context: MigrationContext, progress_callback=None) -> int:
|
||||||
"""Deletes all deletable roles (skips managed/bot roles and @everyone)."""
|
"""Deletes all deletable roles in the target community."""
|
||||||
count = await context.fluxer_writer.delete_all_roles(progress_callback=progress_callback)
|
count = await context.writer.delete_all_roles(progress_callback=progress_callback)
|
||||||
context.state.clear_role_mappings()
|
context.state.clear_role_mappings()
|
||||||
return count
|
return count
|
||||||
|
|
||||||
async def danger_delete_all_emojis_and_stickers(context: MigrationContext, progress_callback=None) -> dict:
|
async def danger_delete_all_emojis_and_stickers(context: MigrationContext, progress_callback=None) -> dict:
|
||||||
"""Deletes all custom emojis and stickers. Returns {"emojis": int, "stickers": int}."""
|
"""Deletes all custom emojis and stickers from the target community. Returns {"emojis": int, "stickers": int}."""
|
||||||
counts = await context.fluxer_writer.delete_all_emojis_and_stickers(progress_callback=progress_callback)
|
counts = await context.writer.delete_all_emojis_and_stickers(progress_callback=progress_callback)
|
||||||
context.state.clear_asset_mappings()
|
context.state.clear_asset_mappings()
|
||||||
return counts
|
return counts
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ async def sync_server_metadata(context: MigrationContext, progress_callback: Cal
|
||||||
if "name" in components:
|
if "name" in components:
|
||||||
try:
|
try:
|
||||||
name = metadata.get("name")
|
name = metadata.get("name")
|
||||||
await context.fluxer_writer.update_guild_metadata(name=name)
|
await context.writer.update_guild_metadata(name=name)
|
||||||
cloned_data["name"] = name
|
cloned_data["name"] = name
|
||||||
await progress_callback("Server Name", "DONE")
|
await progress_callback("Server Name", "DONE")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -28,7 +28,7 @@ async def sync_server_metadata(context: MigrationContext, progress_callback: Cal
|
||||||
icon_bytes = await context.discord_reader.download_asset(context.discord_reader.guild.icon)
|
icon_bytes = await context.discord_reader.download_asset(context.discord_reader.guild.icon)
|
||||||
|
|
||||||
if icon_bytes:
|
if icon_bytes:
|
||||||
await context.fluxer_writer.update_guild_metadata(icon=icon_bytes)
|
await context.writer.update_guild_metadata(icon=icon_bytes)
|
||||||
cloned_data["icon"] = icon_bytes
|
cloned_data["icon"] = icon_bytes
|
||||||
await progress_callback("Server Icon", "DONE")
|
await progress_callback("Server Icon", "DONE")
|
||||||
else:
|
else:
|
||||||
|
|
@ -44,7 +44,7 @@ async def sync_server_metadata(context: MigrationContext, progress_callback: Cal
|
||||||
banner_bytes = await context.discord_reader.download_asset(context.discord_reader.guild.banner)
|
banner_bytes = await context.discord_reader.download_asset(context.discord_reader.guild.banner)
|
||||||
|
|
||||||
if banner_bytes:
|
if banner_bytes:
|
||||||
await context.fluxer_writer.update_guild_metadata(banner=banner_bytes)
|
await context.writer.update_guild_metadata(banner=banner_bytes)
|
||||||
cloned_data["banner"] = banner_bytes
|
cloned_data["banner"] = banner_bytes
|
||||||
await progress_callback("Server Banner", "DONE")
|
await progress_callback("Server Banner", "DONE")
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
99
src/stoat/emoji_stickers.py
Normal file
99
src/stoat/emoji_stickers.py
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Callable, Awaitable, List
|
||||||
|
|
||||||
|
from src.core.base import MigrationContext
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
async def sync_assets_state(context: MigrationContext):
|
||||||
|
"""
|
||||||
|
Scans Stoat for emojis matching Discord names and updates stoat.state.json mappings.
|
||||||
|
"""
|
||||||
|
discord_emojis = await context.discord_reader.get_emojis()
|
||||||
|
# Stickers not supported on Stoat based on current library investigation
|
||||||
|
|
||||||
|
# StoatWriter should have a way to fetch emojis
|
||||||
|
# For now we use the client directly if it starts
|
||||||
|
if not hasattr(context.writer, 'client'):
|
||||||
|
await context.writer.start()
|
||||||
|
|
||||||
|
server = await context.writer.client.fetch_server(context.writer.community_id)
|
||||||
|
stoat_emojis = await server.fetch_emojis()
|
||||||
|
|
||||||
|
# Build name -> id maps and ID sets for Stoat for fast lookup
|
||||||
|
stoat_emoji_map = {e.name: str(e.id) for e in stoat_emojis if e.name}
|
||||||
|
stoat_emoji_ids = {str(e.id) for e in stoat_emojis}
|
||||||
|
|
||||||
|
updates = 0
|
||||||
|
removals = 0
|
||||||
|
|
||||||
|
# 1. Verify and Sync Emojis
|
||||||
|
for emoji in discord_emojis:
|
||||||
|
discord_id = str(emoji.id)
|
||||||
|
# Using target mapping in state
|
||||||
|
stoat_id = context.state.get_target_emoji_id(discord_id)
|
||||||
|
|
||||||
|
if stoat_id:
|
||||||
|
if stoat_id not in stoat_emoji_ids:
|
||||||
|
context.state.emoji_map.pop(discord_id, None)
|
||||||
|
context.state.save_state()
|
||||||
|
removals += 1
|
||||||
|
elif emoji.name in stoat_emoji_map:
|
||||||
|
context.state.set_target_emoji_mapping(discord_id, stoat_emoji_map[emoji.name])
|
||||||
|
updates += 1
|
||||||
|
|
||||||
|
if updates > 0 or removals > 0:
|
||||||
|
logger.info(f"Stoat Asset sync: {updates} mapped, {removals} stale mappings removed")
|
||||||
|
|
||||||
|
|
||||||
|
async def migrate_emojis(context: MigrationContext, progress_callback: Callable[[str, str, int, int], Awaitable[None]] | None = None, types_to_include: List[str] = ["Emoji", "Sticker"], force: bool = False) -> dict[str, dict[str, str]]:
|
||||||
|
"""Copies custom emojis.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force: If True, skip state cache and re-copy even if already migrated.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A dictionary containing dicts of cloned asset names to their new IDs by type: {"Emoji": {"name": "id", ...}, "Sticker": {}}
|
||||||
|
"""
|
||||||
|
objs = []
|
||||||
|
if "Emoji" in types_to_include:
|
||||||
|
emojis = await context.discord_reader.get_emojis()
|
||||||
|
objs.extend([(e, "Emoji") for e in emojis])
|
||||||
|
|
||||||
|
# Stickers are skipped for Stoat
|
||||||
|
|
||||||
|
if not force:
|
||||||
|
objs = [(obj, obj_type) for obj, obj_type in objs if not (
|
||||||
|
context.state.get_target_emoji_id(str(obj.id)) if obj_type == "Emoji" else None
|
||||||
|
)]
|
||||||
|
|
||||||
|
total = len(objs)
|
||||||
|
cloned_assets: dict[str, dict[str, str]] = {"Emoji": {}, "Sticker": {}}
|
||||||
|
|
||||||
|
if total == 0:
|
||||||
|
return cloned_assets
|
||||||
|
|
||||||
|
for idx, (obj, obj_type) in enumerate(objs):
|
||||||
|
if not context.is_running: break
|
||||||
|
|
||||||
|
try:
|
||||||
|
if obj_type == "Emoji":
|
||||||
|
img_data = await context.discord_reader.download_emoji(obj)
|
||||||
|
stoat_id = await context.writer.create_emoji(
|
||||||
|
name=obj.name,
|
||||||
|
image_bytes=img_data
|
||||||
|
)
|
||||||
|
if stoat_id:
|
||||||
|
context.state.set_target_emoji_mapping(str(obj.id), stoat_id)
|
||||||
|
cloned_assets["Emoji"][obj.name] = stoat_id
|
||||||
|
else:
|
||||||
|
# Sticker not supported
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error downloading/uploading {obj_type.lower()} {obj.name}: {e}")
|
||||||
|
|
||||||
|
if progress_callback: await progress_callback(obj.name, obj_type, idx + 1, total)
|
||||||
|
await asyncio.sleep(context.config.migration.rate_limit_delay_seconds)
|
||||||
|
|
||||||
|
return cloned_assets
|
||||||
|
|
@ -11,6 +11,14 @@ class StoatWriter:
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
self.client = stoat.Client(token=self.token, bot=True)
|
self.client = stoat.Client(token=self.token, bot=True)
|
||||||
|
# We don't fetch the server here to avoid slowing down startup if not needed
|
||||||
|
# but we might want a helper to get it
|
||||||
|
self._server = None
|
||||||
|
|
||||||
|
async def _get_server(self):
|
||||||
|
if not self._server:
|
||||||
|
self._server = await self.client.fetch_server(self.community_id)
|
||||||
|
return self._server
|
||||||
|
|
||||||
async def validate(self) -> dict:
|
async def validate(self) -> dict:
|
||||||
results = {
|
results = {
|
||||||
|
|
@ -103,14 +111,28 @@ class StoatWriter:
|
||||||
async def create_role(self, **kwargs) -> str:
|
async def create_role(self, **kwargs) -> str:
|
||||||
return "dummy_stoat_role_id"
|
return "dummy_stoat_role_id"
|
||||||
|
|
||||||
async def create_emoji(self, **kwargs) -> str:
|
async def create_emoji(self, name: str, image_bytes: bytes, **kwargs) -> str:
|
||||||
return "dummy_stoat_emoji_id"
|
server = await self._get_server()
|
||||||
|
try:
|
||||||
|
emoji = await server.create_server_emoji(name=name, image=image_bytes)
|
||||||
|
return str(emoji.id)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create Stoat emoji {name}: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
async def create_sticker(self, **kwargs) -> str:
|
async def create_sticker(self, **kwargs) -> str:
|
||||||
return "dummy_stoat_sticker_id"
|
return "dummy_stoat_sticker_id"
|
||||||
|
|
||||||
async def update_guild_metadata(self, **kwargs) -> None:
|
async def update_guild_metadata(self, name: Optional[str] = None, icon: Optional[bytes] = None, banner: Optional[bytes] = None, **kwargs) -> None:
|
||||||
pass
|
server = await self._get_server()
|
||||||
|
try:
|
||||||
|
await server.edit(
|
||||||
|
name=name if name is not None else stoat.UNDEFINED,
|
||||||
|
icon=icon if icon is not None else stoat.UNDEFINED,
|
||||||
|
banner=banner if banner is not None else stoat.UNDEFINED
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to update Stoat guild metadata: {e}")
|
||||||
|
|
||||||
async def remove_community_logo_and_banner(self) -> dict:
|
async def remove_community_logo_and_banner(self) -> dict:
|
||||||
return {"icon": "SKIP", "banner": "SKIP"}
|
return {"icon": "SKIP", "banner": "SKIP"}
|
||||||
|
|
@ -125,7 +147,17 @@ class StoatWriter:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
async def delete_all_emojis_and_stickers(self, **kwargs) -> dict:
|
async def delete_all_emojis_and_stickers(self, **kwargs) -> dict:
|
||||||
return {"emojis": 0, "stickers": 0}
|
server = await self._get_server()
|
||||||
|
emojis = await server.fetch_emojis()
|
||||||
|
count = 0
|
||||||
|
for emoji in emojis:
|
||||||
|
try:
|
||||||
|
await emoji.delete()
|
||||||
|
count += 1
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to delete Stoat emoji {emoji.name}: {e}")
|
||||||
|
|
||||||
|
return {"emojis": count, "stickers": 0}
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ from src.core.configuration import load_config, save_config
|
||||||
from src.core.base import MigrationContext
|
from src.core.base import MigrationContext
|
||||||
from src.fluxer.clone_server import sync_channel_state, migrate_channels
|
from src.fluxer.clone_server import sync_channel_state, migrate_channels
|
||||||
from src.fluxer.roles_permissions import sync_roles_state, sync_permissions, migrate_roles
|
from src.fluxer.roles_permissions import sync_roles_state, sync_permissions, migrate_roles
|
||||||
from src.fluxer.emoji_stickers import sync_assets_state, migrate_emojis
|
import src.fluxer.emoji_stickers as fluxer_emoji_stickers
|
||||||
|
import src.stoat.emoji_stickers as stoat_emoji_stickers
|
||||||
from src.fluxer.server_metadata import sync_server_metadata
|
from src.fluxer.server_metadata import sync_server_metadata
|
||||||
from src.fluxer.migrate_message import analyze_migration, migrate_messages
|
from src.fluxer.migrate_message import analyze_migration, migrate_messages
|
||||||
from src.fluxer.danger_zone import danger_remove_logo_and_banner, danger_delete_all_channels, danger_reset_channel_permissions, danger_delete_all_roles, danger_delete_all_emojis_and_stickers
|
from src.fluxer.danger_zone import danger_remove_logo_and_banner, danger_delete_all_channels, danger_reset_channel_permissions, danger_delete_all_roles, danger_delete_all_emojis_and_stickers
|
||||||
|
|
@ -368,7 +369,7 @@ class MigrationCLI:
|
||||||
console.print(f"Stoat Bot Token {get_status_str(self.validation_results.get('stoat_token', False), self.validation_results.get('stoat_bot_name'))}")
|
console.print(f"Stoat Bot Token {get_status_str(self.validation_results.get('stoat_token', False), self.validation_results.get('stoat_bot_name'))}")
|
||||||
console.print(f"Stoat Server ID {get_status_str(self.validation_results.get('stoat_server', False), self.validation_results.get('stoat_server_name'))}")
|
console.print(f"Stoat Server ID {get_status_str(self.validation_results.get('stoat_server', False), self.validation_results.get('stoat_server_name'))}")
|
||||||
|
|
||||||
console.print("\n(1) Edit tokens")
|
console.print("\n(1) Edit Bot tokens & Community IDs")
|
||||||
console.print("(2) Edit API url (for self hosted instances)")
|
console.print("(2) Edit API url (for self hosted instances)")
|
||||||
console.print("(B) Back")
|
console.print("(B) Back")
|
||||||
|
|
||||||
|
|
@ -721,11 +722,15 @@ class MigrationCLI:
|
||||||
|
|
||||||
async def copy_emojis(self):
|
async def copy_emojis(self):
|
||||||
console.print("\n[yellow]Fetching emojis and stickers...[/yellow]")
|
console.print("\n[yellow]Fetching emojis and stickers...[/yellow]")
|
||||||
|
|
||||||
|
# Select module based on platform
|
||||||
|
asset_mod = stoat_emoji_stickers if self.target_platform == "stoat" else fluxer_emoji_stickers
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.engine.start_connections()
|
await self.engine.start_connections()
|
||||||
|
|
||||||
with console.status("[yellow]Checking Fluxer for existing emojis and stickers...[/yellow]"):
|
with console.status(f"[yellow]Checking {self.target_platform.capitalize()} for existing emojis and stickers...[/yellow]"):
|
||||||
await sync_assets_state(self.engine)
|
await asset_mod.sync_assets_state(self.engine)
|
||||||
|
|
||||||
emojis = await self.engine.discord_reader.get_emojis()
|
emojis = await self.engine.discord_reader.get_emojis()
|
||||||
stickers = await self.engine.discord_reader.get_stickers()
|
stickers = await self.engine.discord_reader.get_stickers()
|
||||||
|
|
@ -737,14 +742,14 @@ class MigrationCLI:
|
||||||
|
|
||||||
cached_emojis = 0
|
cached_emojis = 0
|
||||||
for e in emojis:
|
for e in emojis:
|
||||||
already = self.engine.state.get_fluxer_emoji_id(str(e.id))
|
already = self.engine.state.get_target_emoji_id(str(e.id))
|
||||||
status = "[dim]already copied[/dim]" if already else "[bold green]NEW[/bold green]"
|
status = "[dim]already copied[/dim]" if already else "[bold green]NEW[/bold green]"
|
||||||
if already: cached_emojis += 1
|
if already: cached_emojis += 1
|
||||||
table.add_row("Emoji", e.name, status)
|
table.add_row("Emoji", e.name, status)
|
||||||
|
|
||||||
cached_stickers = 0
|
cached_stickers = 0
|
||||||
for s in stickers:
|
for s in stickers:
|
||||||
already = self.engine.state.get_fluxer_sticker_id(str(s.id))
|
already = self.engine.state.get_target_sticker_id(str(s.id))
|
||||||
status = "[dim]already copied[/dim]" if already else "[bold green]NEW[/bold green]"
|
status = "[dim]already copied[/dim]" if already else "[bold green]NEW[/bold green]"
|
||||||
if already: cached_stickers += 1
|
if already: cached_stickers += 1
|
||||||
table.add_row("Sticker", s.name, status)
|
table.add_row("Sticker", s.name, status)
|
||||||
|
|
@ -779,9 +784,9 @@ class MigrationCLI:
|
||||||
# Ask about force re-copy only if there are cached items in scope
|
# Ask about force re-copy only if there are cached items in scope
|
||||||
cached_in_scope = 0
|
cached_in_scope = 0
|
||||||
if "Emoji" in types_to_include:
|
if "Emoji" in types_to_include:
|
||||||
cached_in_scope += sum(1 for e in emojis if self.engine.state.get_fluxer_emoji_id(str(e.id)))
|
cached_in_scope += sum(1 for e in emojis if self.engine.state.get_target_emoji_id(str(e.id)))
|
||||||
if "Sticker" in types_to_include:
|
if "Sticker" in types_to_include:
|
||||||
cached_in_scope += sum(1 for s in stickers if self.engine.state.get_fluxer_sticker_id(str(s.id)))
|
cached_in_scope += sum(1 for s in stickers if self.engine.state.get_target_sticker_id(str(s.id)))
|
||||||
|
|
||||||
force = False
|
force = False
|
||||||
if cached_in_scope > 0:
|
if cached_in_scope > 0:
|
||||||
|
|
@ -813,7 +818,7 @@ class MigrationCLI:
|
||||||
progress.update(emoji_task, total=total, completed=current, description=f"[cyan]Copying {item_type}: {item_name}")
|
progress.update(emoji_task, total=total, completed=current, description=f"[cyan]Copying {item_type}: {item_name}")
|
||||||
|
|
||||||
self.engine.is_running = True
|
self.engine.is_running = True
|
||||||
cloned_assets = await migrate_emojis(
|
cloned_assets = await asset_mod.migrate_emojis(
|
||||||
self.engine,
|
self.engine,
|
||||||
progress_callback=update_progress,
|
progress_callback=update_progress,
|
||||||
types_to_include=types_to_include,
|
types_to_include=types_to_include,
|
||||||
|
|
@ -1245,11 +1250,11 @@ class MigrationCLI:
|
||||||
await self.engine.close_connections()
|
await self.engine.close_connections()
|
||||||
|
|
||||||
async def danger_zone(self):
|
async def danger_zone(self):
|
||||||
"""Danger Zone – irreversible destructive operations on the Fluxer community."""
|
"""Danger Zone – irreversible destructive operations on the target community."""
|
||||||
console.print("")
|
console.print("")
|
||||||
console.print(Panel.fit(
|
console.print(Panel.fit(
|
||||||
"[bold red]\u26a0 DANGER ZONE \u26a0[/bold red]\n"
|
"[bold red]\u26a0 DANGER ZONE \u26a0[/bold red]\n"
|
||||||
"[yellow]These actions are PERMANENT and IRREVERSIBLE on your Fluxer community.[/yellow]\n"
|
f"[yellow]These actions are PERMANENT and IRREVERSIBLE on your {self.target_platform.capitalize()} community.[/yellow]\n"
|
||||||
"[yellow]Always double-check before confirming.[/yellow]",
|
"[yellow]Always double-check before confirming.[/yellow]",
|
||||||
style="bold red"
|
style="bold red"
|
||||||
))
|
))
|
||||||
|
|
@ -1272,8 +1277,8 @@ class MigrationCLI:
|
||||||
# ---- (1) Delete all Channels & Categories ----
|
# ---- (1) Delete all Channels & Categories ----
|
||||||
if choice == "1":
|
if choice == "1":
|
||||||
console.print("")
|
console.print("")
|
||||||
community_name = self.validation_results.get("fluxer_community_name", "Unknown")
|
community_name = self.validation_results.get(f"{self.target_platform}_community_name", "Unknown")
|
||||||
console.print(f"[bold red]This will DELETE every channel and category in the Fluxer community: \"{community_name}\"[/bold red]")
|
console.print(f"[bold red]This will DELETE every channel and category in the {self.target_platform.capitalize()} community: \"{community_name}\"[/bold red]")
|
||||||
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
||||||
return
|
return
|
||||||
if not Confirm.ask("[bold red]Last chance \u2013 this cannot be undone. Continue?[/bold red]"):
|
if not Confirm.ask("[bold red]Last chance \u2013 this cannot be undone. Continue?[/bold red]"):
|
||||||
|
|
@ -1305,8 +1310,8 @@ class MigrationCLI:
|
||||||
# ---- (2) Reset Channel & Category Permissions ----
|
# ---- (2) Reset Channel & Category Permissions ----
|
||||||
elif choice == "2":
|
elif choice == "2":
|
||||||
console.print("")
|
console.print("")
|
||||||
community_name = self.validation_results.get("fluxer_community_name", "Unknown")
|
community_name = self.validation_results.get(f"{self.target_platform}_community_name", "Unknown")
|
||||||
console.print(f"[bold red]This will RESET all permission overwrites on every channel and category in the Fluxer community: \"{community_name}\"[/bold red]")
|
console.print(f"[bold red]This will RESET all permission overwrites on every channel and category in the {self.target_platform.capitalize()} community: \"{community_name}\"[/bold red]")
|
||||||
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
||||||
return
|
return
|
||||||
if not Confirm.ask("[bold red]Last chance \u2013 all custom permission overrides will be wiped. Continue?[/bold red]"):
|
if not Confirm.ask("[bold red]Last chance \u2013 all custom permission overrides will be wiped. Continue?[/bold red]"):
|
||||||
|
|
@ -1338,8 +1343,8 @@ class MigrationCLI:
|
||||||
# ---- (3) Delete all Roles ----
|
# ---- (3) Delete all Roles ----
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
console.print("")
|
console.print("")
|
||||||
community_name = self.validation_results.get("fluxer_community_name", "Unknown")
|
community_name = self.validation_results.get(f"{self.target_platform}_community_name", "Unknown")
|
||||||
console.print(f"[bold red]This will DELETE all roles in the Fluxer community: \"{community_name}\"[/bold red]")
|
console.print(f"[bold red]This will DELETE all roles in the {self.target_platform.capitalize()} community: \"{community_name}\"[/bold red]")
|
||||||
console.print("[dim]Managed roles (including the bot's own role) and @everyone are automatically protected.[/dim]")
|
console.print("[dim]Managed roles (including the bot's own role) and @everyone are automatically protected.[/dim]")
|
||||||
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
||||||
return
|
return
|
||||||
|
|
@ -1372,8 +1377,8 @@ class MigrationCLI:
|
||||||
# ---- (4) Delete all Emojis & Stickers ----
|
# ---- (4) Delete all Emojis & Stickers ----
|
||||||
elif choice == "4":
|
elif choice == "4":
|
||||||
console.print("")
|
console.print("")
|
||||||
community_name = self.validation_results.get("fluxer_community_name", "Unknown")
|
community_name = self.validation_results.get(f"{self.target_platform}_community_name", "Unknown")
|
||||||
console.print(f"[bold red]This will DELETE all custom emojis and stickers in the Fluxer community: \"{community_name}\"[/bold red]")
|
console.print(f"[bold red]This will DELETE all custom emojis and stickers in the {self.target_platform.capitalize()} community: \"{community_name}\"[/bold red]")
|
||||||
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
if not Confirm.ask("[bold red]Are you absolutely sure?[/bold red]"):
|
||||||
return
|
return
|
||||||
if not Confirm.ask("[bold red]Last chance \u2013 this cannot be undone. Continue?[/bold red]"):
|
if not Confirm.ask("[bold red]Last chance \u2013 this cannot be undone. Continue?[/bold red]"):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue