list_subscriptions() was called before the get_dialogs() await.
During that await the event loop could process a !reload and replace
self.__config; subsequent set_name calls would then update the orphaned
old config object while !sub list reads from the new one.
Move list_subscriptions() after get_dialogs() so both the entity cache
and the config snapshot are taken from the same post-reload state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_entity fails silently for chats not yet cached in current session.
Call get_dialogs() first to populate the cache, then resolve all titles.
Show each updated name so it's clear what was resolved (and what wasn't).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New format: [contain/regexp] 'rule' = [id_from] name_from -> [id_dest] name_dest
!sub names resolves chat titles for all existing rules and updates their names
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
!sub list now shows numbered lines; del/on/off operate by line number
instead of internal rule id — simpler to use from chat.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
get_entity ensures Telethon has the peer cached before forwarding.
Wrap in try/except so a bad destination logs an error instead of
crashing the entire message handler.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
os.replace (rename) changes the inode which Docker bind-mounts prohibit
(EBUSY). Write directly to the file instead; a single-process writer
with RLock is safe without atomic rename.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>