clean up
This commit is contained in:
parent
97e22a7c40
commit
185afb1ee0
9 changed files with 96 additions and 121 deletions
|
|
@ -8,6 +8,7 @@ async def log_audit_event(context: MigrationContext, title: str, description: st
|
||||||
Logs an event by sending a summary to the `#reaper-logs` audit channel.
|
Logs an event by sending a summary to the `#reaper-logs` audit channel.
|
||||||
If the channel does not exist, it will dynamically create it.
|
If the channel does not exist, it will dynamically create it.
|
||||||
"""
|
"""
|
||||||
|
async with context._audit_lock:
|
||||||
# 1. Initialize or Validate channel
|
# 1. Initialize or Validate channel
|
||||||
channel_id = context.state.audit_log_channel
|
channel_id = context.state.audit_log_channel
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ async def log_audit_event(context: MigrationContext, title: str, description: st
|
||||||
logger.error(f"Failed to setup audit log channel: {e}")
|
logger.error(f"Failed to setup audit log channel: {e}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. Format and send the message
|
# 2. Format and send the message (outside the lock since setup is done)
|
||||||
content = f"**[{title}]**\n{description}"
|
content = f"**[{title}]**\n{description}"
|
||||||
try:
|
try:
|
||||||
await context.writer.send_marker(context.state.audit_log_channel, content, files=files)
|
await context.writer.send_marker(context.state.audit_log_channel, content, files=files)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
@ -20,6 +21,7 @@ class MigrationContext:
|
||||||
# If caller didn't specify, fall back to config value
|
# If caller didn't specify, fall back to config value
|
||||||
self.target_platform = target_platform or config.target_platform or "fluxer"
|
self.target_platform = target_platform or config.target_platform or "fluxer"
|
||||||
self.state = MigrationState()
|
self.state = MigrationState()
|
||||||
|
self._audit_lock = asyncio.Lock()
|
||||||
|
|
||||||
# Select the appropriate source reader
|
# Select the appropriate source reader
|
||||||
if source_mode == "backup":
|
if source_mode == "backup":
|
||||||
|
|
@ -35,15 +37,17 @@ class MigrationContext:
|
||||||
logger.info("Source mode: LIVE — using Discord API")
|
logger.info("Source mode: LIVE — using Discord API")
|
||||||
|
|
||||||
# Build the writer for the active target platform only
|
# Build the writer for the active target platform only
|
||||||
token = config.target_bot_token or ""
|
|
||||||
community_id = config.target_server_id or ""
|
|
||||||
api_url = config.target_api_url or "default"
|
|
||||||
|
|
||||||
if self.target_platform == "stoat":
|
if self.target_platform == "stoat":
|
||||||
|
token = config.stoat_bot_token or ""
|
||||||
|
community_id = config.stoat_server_id or ""
|
||||||
|
api_url = config.stoat_api_url or "default"
|
||||||
self.writer = StoatWriter(token=token, community_id=community_id, api_url=api_url)
|
self.writer = StoatWriter(token=token, community_id=community_id, api_url=api_url)
|
||||||
self.stoat_writer = self.writer
|
self.stoat_writer = self.writer
|
||||||
self.fluxer_writer = FluxerWriter(token="", community_id="", api_url="default")
|
self.fluxer_writer = FluxerWriter(token="", community_id="", api_url="default")
|
||||||
else:
|
else:
|
||||||
|
token = config.fluxer_bot_token or ""
|
||||||
|
community_id = config.fluxer_server_id or ""
|
||||||
|
api_url = config.fluxer_api_url or "default"
|
||||||
self.writer = FluxerWriter(token=token, community_id=community_id, api_url=api_url)
|
self.writer = FluxerWriter(token=token, community_id=community_id, api_url=api_url)
|
||||||
self.fluxer_writer = self.writer
|
self.fluxer_writer = self.writer
|
||||||
self.stoat_writer = StoatWriter(token="", community_id="", api_url="default")
|
self.stoat_writer = StoatWriter(token="", community_id="", api_url="default")
|
||||||
|
|
@ -104,8 +108,9 @@ class MigrationContext:
|
||||||
|
|
||||||
# CONSISTENCY: Once target metadata is known, initialize the flat SQLite DB.
|
# CONSISTENCY: Once target metadata is known, initialize the flat SQLite DB.
|
||||||
if results["target_community"] and results["target_community_name"]:
|
if results["target_community"] and results["target_community_name"]:
|
||||||
|
tid = self.config.fluxer_server_id if self.target_platform == "fluxer" else self.config.stoat_server_id
|
||||||
self.ensure_state_initialized(
|
self.ensure_state_initialized(
|
||||||
str(self.config.target_server_id or ""),
|
str(tid or ""),
|
||||||
results["target_community_name"]
|
results["target_community_name"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,34 +17,6 @@ class AppConfig(BaseModel):
|
||||||
anonymize_users: bool = Field(default=False)
|
anonymize_users: bool = Field(default=False)
|
||||||
log_level: str = Field(default="INFO")
|
log_level: str = Field(default="INFO")
|
||||||
|
|
||||||
# ── backward‑compat shims (read‑only) ────────────────────────────────
|
|
||||||
# The rest of the codebase (fluxer/stoat modules) still reads these.
|
|
||||||
# They all delegate to the unified target_* fields.
|
|
||||||
|
|
||||||
@property
|
|
||||||
def use_fluxer(self) -> bool:
|
|
||||||
return self.target_platform == "fluxer"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def use_stoat(self) -> bool:
|
|
||||||
return self.target_platform == "stoat"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_bot_token(self) -> Optional[str]:
|
|
||||||
return self.fluxer_bot_token if self.target_platform == "fluxer" else self.stoat_bot_token
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_server_id(self) -> Optional[str]:
|
|
||||||
return self.fluxer_server_id if self.target_platform == "fluxer" else self.stoat_server_id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_api_url(self) -> Optional[str]:
|
|
||||||
return self.fluxer_api_url if self.target_platform == "fluxer" else self.stoat_api_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def fluxer_community_id(self) -> Optional[str]:
|
|
||||||
return self.fluxer_server_id
|
|
||||||
|
|
||||||
def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing: bool = True) -> AppConfig:
|
def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing: bool = True) -> AppConfig:
|
||||||
path = Path(config_path)
|
path = Path(config_path)
|
||||||
if not path.exists():
|
if not path.exists():
|
||||||
|
|
@ -62,24 +34,6 @@ def load_config(config_path: Union[str, Path] = "config.yaml", create_if_missing
|
||||||
if not data:
|
if not data:
|
||||||
raise ValueError("Configuration file is empty or invalid YAML.")
|
raise ValueError("Configuration file is empty or invalid YAML.")
|
||||||
|
|
||||||
# ── migrate legacy configs that used single target fields ──
|
|
||||||
if "fluxer_community_id" in data:
|
|
||||||
data.setdefault("fluxer_server_id", data.pop("fluxer_community_id"))
|
|
||||||
|
|
||||||
if "target_bot_token" in data or "target_server_id" in data:
|
|
||||||
platform = data.get("target_platform", "fluxer")
|
|
||||||
if platform == "fluxer":
|
|
||||||
data.setdefault("fluxer_bot_token", data.get("target_bot_token"))
|
|
||||||
data.setdefault("fluxer_server_id", data.get("target_server_id"))
|
|
||||||
data.setdefault("fluxer_api_url", data.get("target_api_url"))
|
|
||||||
elif platform == "stoat":
|
|
||||||
data.setdefault("stoat_bot_token", data.get("target_bot_token"))
|
|
||||||
data.setdefault("stoat_server_id", data.get("target_server_id"))
|
|
||||||
data.setdefault("stoat_api_url", data.get("target_api_url"))
|
|
||||||
|
|
||||||
for key in ("target_bot_token", "target_server_id", "target_api_url", "use_fluxer", "use_stoat"):
|
|
||||||
data.pop(key, None)
|
|
||||||
|
|
||||||
return AppConfig(**data)
|
return AppConfig(**data)
|
||||||
|
|
||||||
def save_config(config: AppConfig, config_path: Union[str, Path] = "config.yaml"):
|
def save_config(config: AppConfig, config_path: Union[str, Path] = "config.yaml"):
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ async def sync_assets_state(context: MigrationContext):
|
||||||
discord_emojis = await context.discord_reader.get_emojis()
|
discord_emojis = await context.discord_reader.get_emojis()
|
||||||
discord_stickers = await context.discord_reader.get_stickers()
|
discord_stickers = await context.discord_reader.get_stickers()
|
||||||
|
|
||||||
fluxer_emojis = await context.fluxer_writer.client.get_guild_emojis(context.config.fluxer_community_id)
|
fluxer_emojis = await context.fluxer_writer.client.get_guild_emojis(context.config.fluxer_server_id)
|
||||||
fluxer_stickers = await context.fluxer_writer.client.get_guild_stickers(context.config.fluxer_community_id)
|
fluxer_stickers = await context.fluxer_writer.client.get_guild_stickers(context.config.fluxer_server_id)
|
||||||
|
|
||||||
# Build name -> id maps and ID sets for Fluxer for fast lookup
|
# Build name -> id maps and ID sets for Fluxer for fast lookup
|
||||||
fluxer_emoji_map = {e.get("name"): str(e.get("id")) for e in fluxer_emojis if e.get("name")}
|
fluxer_emoji_map = {e.get("name"): str(e.get("id")) for e in fluxer_emojis if e.get("name")}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ async def sync_roles_state(context: MigrationContext):
|
||||||
"""
|
"""
|
||||||
logger.info("Synchronizing role mappings with Fluxer...")
|
logger.info("Synchronizing role mappings with Fluxer...")
|
||||||
discord_roles = await context.discord_reader.get_roles()
|
discord_roles = await context.discord_reader.get_roles()
|
||||||
fluxer_roles = await context.fluxer_writer.client.get_guild_roles(context.config.fluxer_community_id)
|
fluxer_roles = await context.fluxer_writer.client.get_guild_roles(context.config.fluxer_server_id)
|
||||||
|
|
||||||
# Build name -> id maps and ID sets for Fluxer for fast lookup
|
# Build name -> id maps and ID sets for Fluxer for fast lookup
|
||||||
fluxer_role_map = {r.get("name"): str(r.get("id")) for r in fluxer_roles if r.get("name")}
|
fluxer_role_map = {r.get("name"): str(r.get("id")) for r in fluxer_roles if r.get("name")}
|
||||||
|
|
@ -70,7 +70,7 @@ async def sync_permissions(context: MigrationContext, progress_callback: Callabl
|
||||||
discord_role_id = str(target.id)
|
discord_role_id = str(target.id)
|
||||||
# Handle @everyone role special case
|
# Handle @everyone role special case
|
||||||
if discord_role_id == context.config.discord_server_id:
|
if discord_role_id == context.config.discord_server_id:
|
||||||
fluxer_role_id = context.config.fluxer_community_id
|
fluxer_role_id = context.config.fluxer_server_id
|
||||||
else:
|
else:
|
||||||
fluxer_role_id = context.state.get_fluxer_role_id(discord_role_id)
|
fluxer_role_id = context.state.get_fluxer_role_id(discord_role_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ class FluxerWriter:
|
||||||
rate_limit_per_user=slowmode_delay,
|
rate_limit_per_user=slowmode_delay,
|
||||||
position=position
|
position=position
|
||||||
)
|
)
|
||||||
|
self._channels_cache = None
|
||||||
return str(guild_channel["id"])
|
return str(guild_channel["id"])
|
||||||
|
|
||||||
async def modify_channel(self, channel_id: str, parent_id: Optional[str] = None, name: Optional[str] = None, topic: Optional[str] = None, nsfw: Optional[bool] = None, slowmode_delay: Optional[int] = None, position: Optional[int] = None) -> bool:
|
async def modify_channel(self, channel_id: str, parent_id: Optional[str] = None, name: Optional[str] = None, topic: Optional[str] = None, nsfw: Optional[bool] = None, slowmode_delay: Optional[int] = None, position: Optional[int] = None) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ class StoatWriter:
|
||||||
else: # Text Channel
|
else: # Text Channel
|
||||||
ch = await server.create_text_channel(name=name, description=topic)
|
ch = await server.create_text_channel(name=name, description=topic)
|
||||||
# We no longer parent here, clone_server.py will do it in bulk
|
# We no longer parent here, clone_server.py will do it in bulk
|
||||||
|
self._server = None # Clear cache
|
||||||
return str(ch.id)
|
return str(ch.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to create Stoat channel {name}: {e}")
|
logger.error(f"Failed to create Stoat channel {name}: {e}")
|
||||||
|
|
|
||||||
|
|
@ -327,8 +327,10 @@ class ConfigScreen(Screen):
|
||||||
)
|
)
|
||||||
yield Label("Bot Token:", classes="field_label")
|
yield Label("Bot Token:", classes="field_label")
|
||||||
with Horizontal(classes="fetch_row"):
|
with Horizontal(classes="fetch_row"):
|
||||||
|
cur_plat = self.config.target_platform or "fluxer"
|
||||||
|
t_token = self.config.stoat_bot_token if cur_plat == "stoat" else self.config.fluxer_bot_token
|
||||||
yield Input(
|
yield Input(
|
||||||
value=self.config.target_bot_token or "",
|
value=t_token or "",
|
||||||
id="inp_target_token",
|
id="inp_target_token",
|
||||||
password=True,
|
password=True,
|
||||||
placeholder="Paste Target Bot Token",
|
placeholder="Paste Target Bot Token",
|
||||||
|
|
@ -344,8 +346,9 @@ class ConfigScreen(Screen):
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Label("Target API URL:", classes="field_label")
|
yield Label("Target API URL:", classes="field_label")
|
||||||
|
t_api = self.config.stoat_api_url if cur_plat == "stoat" else self.config.fluxer_api_url
|
||||||
yield Input(
|
yield Input(
|
||||||
value=self.config.target_api_url if (self.config.target_api_url and self.config.target_api_url != "default") else "",
|
value=t_api if (t_api and t_api != "default") else "",
|
||||||
id="inp_target_api",
|
id="inp_target_api",
|
||||||
placeholder="Leave this Empty for official instance",
|
placeholder="Leave this Empty for official instance",
|
||||||
tooltip="Enter the custom API url\nfor self hosted instances"
|
tooltip="Enter the custom API url\nfor self hosted instances"
|
||||||
|
|
@ -373,12 +376,14 @@ class ConfigScreen(Screen):
|
||||||
|
|
||||||
# Also auto-fetch target servers if mode is not backup_only
|
# Also auto-fetch target servers if mode is not backup_only
|
||||||
if self._get_selected_mode() != "backup_only":
|
if self._get_selected_mode() != "backup_only":
|
||||||
if self.config.target_bot_token:
|
|
||||||
platform = self.config.target_platform
|
platform = self.config.target_platform
|
||||||
|
t_token = self.config.stoat_bot_token if platform == "stoat" else self.config.fluxer_bot_token
|
||||||
|
if t_token:
|
||||||
if platform != "none":
|
if platform != "none":
|
||||||
|
t_api = self.config.stoat_api_url if platform == "stoat" else self.config.fluxer_api_url
|
||||||
self.run_worker(self._do_fetch_target_servers(
|
self.run_worker(self._do_fetch_target_servers(
|
||||||
token=self.config.target_bot_token,
|
token=t_token,
|
||||||
api_url=self.config.target_api_url,
|
api_url=t_api,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
initial=True
|
initial=True
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ class OperationPane(Container):
|
||||||
|
|
||||||
enabled = (v.get("discord_token") and v.get("discord_server") and not d_missing)
|
enabled = (v.get("discord_token") and v.get("discord_server") and not d_missing)
|
||||||
for bid in ("#op_backup_msgs", "#op_backup_sync"):
|
for bid in ("#op_backup_msgs", "#op_backup_sync"):
|
||||||
self.query_one(bid, Button).disabled = not enabled
|
for btn in self.query(bid): btn.disabled = not enabled
|
||||||
|
|
||||||
for btn in self.query("#op_backup_stats"):
|
for btn in self.query("#op_backup_stats"):
|
||||||
btn.display = self.has_backup
|
btn.display = self.has_backup
|
||||||
|
|
@ -389,7 +389,7 @@ class OperationPane(Container):
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
for bid in ("#op_clone", "#op_sync", "#op_messages", "#op_danger"):
|
for bid in ("#op_clone", "#op_sync", "#op_messages", "#op_danger"):
|
||||||
self.query_one(bid, Button).disabled = not self.tokens_valid
|
for btn in self.query(bid): btn.disabled = not self.tokens_valid
|
||||||
|
|
||||||
# ── validation ────────────────────────────────────────────────────────
|
# ── validation ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -438,8 +438,13 @@ class OperationPane(Container):
|
||||||
# Check what we have
|
# Check what we have
|
||||||
has_d_token = bool(self.config.discord_bot_token)
|
has_d_token = bool(self.config.discord_bot_token)
|
||||||
has_d_server = bool(self.config.discord_server_id)
|
has_d_server = bool(self.config.discord_server_id)
|
||||||
has_t_token = bool(self.config.target_bot_token)
|
|
||||||
has_t_server = bool(self.config.target_server_id)
|
if self.target_platform == "stoat":
|
||||||
|
has_t_token = bool(self.config.stoat_bot_token)
|
||||||
|
has_t_server = bool(self.config.stoat_server_id)
|
||||||
|
else:
|
||||||
|
has_t_token = bool(self.config.fluxer_bot_token)
|
||||||
|
has_t_server = bool(self.config.fluxer_server_id)
|
||||||
|
|
||||||
# Flag which operations are being validated
|
# Flag which operations are being validated
|
||||||
validating_discord = False
|
validating_discord = False
|
||||||
|
|
@ -1105,7 +1110,8 @@ class OperationPane(Container):
|
||||||
tgt_server_name = tgt_server_info.get("community_name", "target community")
|
tgt_server_name = tgt_server_info.get("community_name", "target community")
|
||||||
|
|
||||||
# ENSURE INITIALIZED for mapping lookup in analyze/migrate
|
# ENSURE INITIALIZED for mapping lookup in analyze/migrate
|
||||||
self.engine.ensure_state_initialized(str(self.engine.config.target_server_id), tgt_server_name)
|
tid = self.engine.config.fluxer_server_id if self.target_platform == "fluxer" else self.engine.config.stoat_server_id
|
||||||
|
self.engine.ensure_state_initialized(str(tid or ""), tgt_server_name)
|
||||||
|
|
||||||
if src_server:
|
if src_server:
|
||||||
modal.write(f"[bold cyan]Source Server Profile:[/bold cyan]")
|
modal.write(f"[bold cyan]Source Server Profile:[/bold cyan]")
|
||||||
|
|
@ -1533,7 +1539,7 @@ class OperationPane(Container):
|
||||||
try:
|
try:
|
||||||
if "dz_del_roles" in selections:
|
if "dz_del_roles" in selections:
|
||||||
if is_fluxer:
|
if is_fluxer:
|
||||||
community_id = self.engine.config.target_server_id
|
community_id = self.engine.config.fluxer_server_id
|
||||||
roles_raw = await writer.client.get_guild_roles(community_id)
|
roles_raw = await writer.client.get_guild_roles(community_id)
|
||||||
role_names = [
|
role_names = [
|
||||||
r.get("name", "Unknown") for r in roles_raw
|
r.get("name", "Unknown") for r in roles_raw
|
||||||
|
|
@ -1554,7 +1560,7 @@ class OperationPane(Container):
|
||||||
if "dz_del_assets" in selections:
|
if "dz_del_assets" in selections:
|
||||||
asset_names = []
|
asset_names = []
|
||||||
if is_fluxer:
|
if is_fluxer:
|
||||||
community_id = self.engine.config.target_server_id
|
community_id = self.engine.config.fluxer_server_id
|
||||||
emojis = await writer.client.get_guild_emojis(community_id)
|
emojis = await writer.client.get_guild_emojis(community_id)
|
||||||
asset_names += [f"{e.get('name', '?')} (emoji)" for e in emojis]
|
asset_names += [f"{e.get('name', '?')} (emoji)" for e in emojis]
|
||||||
try:
|
try:
|
||||||
|
|
@ -1592,23 +1598,24 @@ class OperationPane(Container):
|
||||||
target_stickers_map = {}
|
target_stickers_map = {}
|
||||||
try:
|
try:
|
||||||
if is_fluxer:
|
if is_fluxer:
|
||||||
target_roles_raw = await writer.client.get_guild_roles(self.engine.config.target_server_id)
|
tid = self.engine.config.fluxer_server_id
|
||||||
|
target_roles_raw = await writer.client.get_guild_roles(tid)
|
||||||
target_roles_map = {r.get("name", "").lower(): str(r.get("id")) for r in target_roles_raw}
|
target_roles_map = {r.get("name", "").lower(): str(r.get("id")) for r in target_roles_raw}
|
||||||
|
|
||||||
target_emojis_raw = await writer.client.get_guild_emojis(self.engine.config.target_server_id)
|
target_emojis_raw = await writer.client.get_guild_emojis(tid)
|
||||||
target_emojis_map = {e.get("name", "").lower(): str(e.get("id")) for e in target_emojis_raw}
|
target_emojis_map = {e.get("name", "").lower(): str(e.get("id")) for e in target_emojis_raw}
|
||||||
|
|
||||||
# RE-INITIALIZE STATE if we found community info
|
# RE-INITIALIZE STATE if we found community info
|
||||||
# This ensures mapping persistence even if validate_all was skipped
|
# This ensures mapping persistence even if validate_all was skipped
|
||||||
try:
|
try:
|
||||||
community_info = await writer.client.get_guild(self.engine.config.target_server_id)
|
community_info = await writer.client.get_guild(tid)
|
||||||
if community_info:
|
if community_info:
|
||||||
self.engine.ensure_state_initialized(str(self.engine.config.target_server_id), community_info.get("name", "Target"))
|
self.engine.ensure_state_initialized(str(tid or ""), community_info.get("name", "Target"))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target_stickers_raw = await writer.client.get_guild_stickers(self.engine.config.target_server_id)
|
target_stickers_raw = await writer.client.get_guild_stickers(tid)
|
||||||
target_stickers_map = {s.get("name", "").lower(): str(s.get("id")) for s in target_stickers_raw}
|
target_stickers_map = {s.get("name", "").lower(): str(s.get("id")) for s in target_stickers_raw}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
@ -1620,7 +1627,8 @@ class OperationPane(Container):
|
||||||
target_emojis_map = {e.name.lower(): str(e.id) for e in target_emojis_raw}
|
target_emojis_map = {e.name.lower(): str(e.id) for e in target_emojis_raw}
|
||||||
|
|
||||||
# RE-INITIALIZE STATE
|
# RE-INITIALIZE STATE
|
||||||
self.engine.ensure_state_initialized(str(self.engine.config.target_server_id), server.name)
|
tid = self.engine.config.stoat_server_id
|
||||||
|
self.engine.ensure_state_initialized(str(tid or ""), server.name)
|
||||||
|
|
||||||
target_chans_raw = await writer.get_channels()
|
target_chans_raw = await writer.get_channels()
|
||||||
target_chans_map = {c.get("name", "").lower(): str(c.get("id")) for c in target_chans_raw if c.get("type") != 4}
|
target_chans_map = {c.get("name", "").lower(): str(c.get("id")) for c in target_chans_raw if c.get("type") != 4}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue