__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)