__version__ = (2, 0, 1) # █ █ ▀ █▄▀ ▄▀█ █▀█ ▀ # █▀█ █ █ █ █▀█ █▀▄ █ # © Copyright 2022 # https://t.me/hikariatama # # 🔒 Licensed under the GNU AGPLv3 # 🌐 https://www.gnu.org/licenses/agpl-3.0.html # meta pic: https://static.dan.tatar/account_switcher_icon.png # meta banner: https://mods.hikariatama.ru/badges/account_switcher.jpg # meta developer: @hikarimods # scope: hikka_only # scope: hikka_min 1.2.10 import io import logging import re import typing from aiogram.utils.exceptions import ChatNotFound from telethon.tl.functions.account import UpdateProfileRequest from telethon.tl.functions.channels import InviteToChannelRequest from telethon.tl.functions.photos import UploadProfilePhotoRequest from telethon.tl.functions.users import GetFullUserRequest from telethon.tl.types import Message as TelethonMessage from .. import loader, utils from ..inline.types import InlineCall, InlineMessage logger = logging.getLogger(__name__) @loader.tds class AccountSwitcherMod(loader.Module): """Allows you to easily switch between different profiles""" strings = { "name": "AccountSwitcher", "account_saved": ( "🌚 Account saved!' ), "restore_btn": "👆 Restore", "desc": "This chat will handle your saved profiles", "first_name_restored": ( " First name restored\n" ), "first_name_unsaved": ( "⚠️ First name not saved\n" ), "last_name_restored": ( " First name restored\n" ), "last_name_unsaved": ( "⚠️ First name not saved\n" ), "bio_restored": ( " Bio restored\n" ), "bio_unsaved": ( "⚠️ Bio not saved\n" ), "data_not_restored": ( "🚫 First name not" " restored\n🚫 Last name not" " restored\n🚫 Bio not" " restored\n" ), "pfp_restored": ( " Profile photo restored" ), "pfp_unsaved": ( "⚠️ Profile photo not saved" ), } strings_ru = { "account_saved": ( "🌚 Аккаунт сохранен!' ), "restore_btn": "👆 Восстановить", "desc": "Тут будут появляться сохраненные профили", "first_name_restored": ( " Имя восстановлено\n" ), "first_name_unsaved": ( "⚠️ Имя не сохранялось\n" ), "last_name_restored": ( " Фамилия восстановлена\n" ), "last_name_unsaved": ( "⚠️ Фамилия не сохранялась\n" ), "bio_restored": ( " Био восстановлено\n" ), "bio_unsaved": ( "⚠️ Био не сохранялось\n" ), "data_not_restored": ( "🚫 Имя не" " восстановлено\n🚫 Фамилия" " не восстановлена\n🚫" " Био не" " восстановлено\n" ), "pfp_restored": ( " Аватарка восстановлена" ), "pfp_unsaved": ( "⚠️ Аватарка не сохранялась" ), "_cmd_doc_accsave": "Сохранить аккаунт для последующего использования", "_cls_doc": "Позволяет быстро переключаться между разными аккаунтами", } strings_de = { "account_saved": ( "🌚 Konto gespeichert!' ), "restore_btn": "👆 Wiederherstellen", "desc": "Dieser Chat wird deine gespeicherten Profile verwalten", "first_name_restored": ( " Vorname" " wiederhergestellt.\n" ), "first_name_unsaved": ( "⚠️ Vorname nicht" " gespeichert.\n" ), "last_name_restored": ( " Nachname" " wiederhergestellt.\n" ), "last_name_unsaved": ( "⚠️ Nachname nicht" " gespeichert.\n" ), "bio_restored": ( " Bio wiederhergestellt.\n" ), "bio_unsaved": ( "⚠️ Bio nicht gespeichert.\n" ), "data_not_restored": ( "🚫 Vorname nicht" " wiederhergestellt.\n🚫" " Nachname nicht wiederhergestellt.\n🚫 Bio nicht wiederhergestellt.\n" ), "pfp_restored": ( " Profilbild" " wiederhergestellt." ), "pfp_unsaved": ( "⚠️ Profilbild nicht" " gespeichert." ), "_cmd_doc_accsave": "Speichert das Konto für spätere Verwendung", "_cls_doc": "Ermöglicht es, schnell zwischen verschiedenen Konten zu wechseln", } strings_hi = { "account_saved": ( "🌚 खाता सहेजा गया!' ), "restore_btn": "👆 पुनर्स्थापित करें", "desc": "यह चैट आपके सहेजे गए प्रोफाइल का प्रबंधन करेगा", "first_name_restored": ( " पहला नाम पुनर्स्थापित" " किया गया।\n" ), "first_name_unsaved": ( "⚠️ पहला नाम सहेजा नहीं गया।\n" ), "last_name_restored": ( " अंतिम नाम पुनर्स्थापित" " किया गया।\n" ), "last_name_unsaved": ( "⚠️ अंतिम नाम सहेजा नहीं गया।\n" ), "bio_restored": ( " बायो पुनर्स्थापित किया" " गया।\n" ), "bio_unsaved": ( "⚠️ बायो सहेजा नहीं गया।\n" ), "data_not_restored": ( "🚫 पहला नाम पुनर्स्थापित" " नहीं किया गया।\n🚫 अंतिम" " नाम पुनर्स्थापित नहीं किया गया।\n🚫 बायो पुनर्स्थापित नहीं किया" " गया।\n" ), "pfp_restored": ( " प्रोफ़ाइल चित्र" " पुनर्स्थापित किया गया।" ), "pfp_unsaved": ( "⚠️ प्रोफ़ाइल चित्र सहेजा" " नहीं गया।" ), "_cmd_doc_accsave": "भविष्य के उपयोग के लिए खाता सहेजें", "_cls_doc": "विभिन्न खातों के बीच जल्दी से जल्दी बदलने की अनुमति देता है", } strings_uz = { "account_saved": ( "🌚 Hisob saqlandi!' ), "restore_btn": "👆 Qayta tiklash", "desc": "Bu chat sizning saqlangan profilni boshqaradi", "first_name_restored": ( " Nomi qayta tiklandi.\n" ), "first_name_unsaved": ( "⚠️ Nomi saqlanmadi.\n" ), "last_name_restored": ( " Familiya qayta" " tiklandi.\n" ), "last_name_unsaved": ( "⚠️ Familiya saqlanmadi.\n" ), "bio_restored": ( " Bio qayta tiklandi.\n" ), "bio_unsaved": ( "⚠️ Bio saqlanmadi.\n" ), "data_not_restored": ( "🚫 Nomi qayta" " tiklanmadi.\n🚫 Familiya" " qayta tiklanmadi.\n🚫 Bio" " qayta tiklanmadi.\n" ), "pfp_restored": ( " Profil rasmi qayta" " tiklandi." ), "pfp_unsaved": ( "⚠️ Profil rasmi saqlanmadi." ), "_cmd_doc_accsave": "Keyingi ishlatish uchun hisobni saqlash", "_cls_doc": "Tez-tez turli hisoblarga o'tishga imkon beradi", } async def client_ready(self, client, db): self._accs_db, is_new = await utils.asset_channel( self._client, "hikka-acc-switcher", self.strings("desc"), silent=True, archive=True, avatar="https://raw.githubusercontent.com/hikariatama/assets/master/hikka-acc-switcher.png", _folder="hikka", ) self._accs_db_id = int(f"-100{self._accs_db.id}") if not is_new: return try: await self._client( InviteToChannelRequest(self._accs_db, [self.inline.bot_username]) ) except Exception: logger.warning("Unable to invite logger to chat. Maybe he's already there?") async def _save_acc( self, photo: typing.Optional[bytes], first_name: str, last_name: str, bio: str, no_retry: bool = False, ) -> int: info = ( f"{utils.escape_html(first_name)} " f"{utils.escape_html(last_name)}\n\n" f"Bio: {utils.escape_html(bio)}\n" ) try: if photo is not None: photo_io = io.BytesIO(photo) photo_io.name = "pfp.jpg" return ( await self.inline.bot.send_document( self._accs_db_id, photo_io, caption=info, parse_mode="HTML", reply_markup=self.inline.generate_markup( {"text": self.strings("restore_btn"), "data": "accrest"} ), ) ).message_id else: return ( await self.inline.bot.send_message( self._accs_db_id, info, parse_mode="HTML", reply_markup=self.inline.generate_markup( {"text": self.strings("restore_btn"), "data": "accrest"} ), ) ).message_id except ChatNotFound: if no_retry: logger.exception("Can't restore account") return await self._client( InviteToChannelRequest(self._accs_db, [self.inline.bot_username]) ) return await self._save_acc( photo, first_name, last_name, bio, no_retry=True, ) async def accrest_callback_handler(self, call: InlineCall): if call.data != "accrest": return await call.answer(await self._restore(call.message), show_alert=True) async def accsavecmd(self, message: TelethonMessage): """Save account for future restoring""" full = await self._client(GetFullUserRequest("me")) acc = await self._client.force_get_entity("me") message_id = await self._save_acc( ( (await self._client.download_profile_photo(acc, bytes)) if full.full_user.profile_photo else None ), getattr(acc, "first_name", "None"), getattr(acc, "last_name", "None"), (getattr(full.full_user, "about", "None")), ) await utils.answer( message, self.strings("account_saved").format(self._accs_db.id, message_id) ) async def _restore( self, reply: typing.Union[TelethonMessage, InlineMessage], ) -> str: log = "" first_name, last_name, bio = list( map( lambda x: x.replace(">", ">") .replace("<", "<") .replace(""", '"') .replace("&", "&"), re.findall( r"(.*?)", getattr(reply, "html_text", reply.text), flags=re.S, ), ) ) if first_name == "None": first_name = None if last_name == "None": last_name = None if bio == "None": bio = None try: await self._client(UpdateProfileRequest(first_name, last_name, bio)) log += ( self.strings("first_name_restored") if first_name else self.strings("first_name_unsaved") ) log += ( self.strings("last_name_restored") if last_name else self.strings("last_name_unsaved") ) log += self.strings("bio_restored") if bio else self.strings("bio_unsaved") except Exception: logger.exception("Can't restore account due to") log += self.strings("data_not_restored") try: upload = await self._client.upload_file( await self._client.download_file(reply.media, bytes) ) await self._client(UploadProfilePhotoRequest(upload)) log += self.strings("pfp_restored") except Exception: try: file = io.BytesIO() await reply.document.download(destination_file=file) await self._client( UploadProfilePhotoRequest( await self._client.upload_file(file), ) ) log += self.strings("pfp_restored") except Exception: log += self.strings("pfp_unsaved") return re.sub(r"\n{2,}", "\n", log)