# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀ # █▀█ █ █ █ █▀█ █▀▄ █ # © Copyright 2022 # https://t.me/hikariatama # # 🔒 Licensed under the GNU GPLv3 # 🌐 https://www.gnu.org/licenses/agpl-3.0.html # meta pic: https://img.icons8.com/3d-plastilina/344/3d-plastilina-three-quarter-view-of-a-bitcoin-emblem.png # meta developer: @hikarimods # meta banner: https://mods.hikariatama.ru/badges/crypto.jpg import asyncio import difflib import logging import re import requests from lxml import etree from telethon.errors.rpcerrorlist import BotResponseTimeoutError from telethon.tl.types import Message from .. import loader, utils from ..inline.types import InlineCall logger = logging.getLogger(__name__) AMOUNT_REGEX = r"(?:Create check · |Создать чек · )(.*?)(?: ·|$)" INVOICE_AMOUNT_REGEX = ( r"(?:Invoice for |Счёт на)(.*?)(?:\.$| with description.| с описанием.)" ) RECEIVER_REGEX = r"(?:given to | для )(.*?)(?:\.| with| с описанием)" BALANCE_REGEX = r"(?:Available: |Доступно: )(.*)" EMOJI_MAP = { "USDT": "💵", "TON": "💵", "BTC": "💵", "ETH": "💵", "BNB": "💵", "BUSD": "💵", "USDC": "💵", } RATES_CONFIG = { "USD": "🇺🇸 USD: {} $", "RUB": "🇷🇺 RUB: {} ₽", "EUR": "🇪🇺 EUR: {} €", "UAH": "🇺🇦 UAH: {} ₴", "KZT": "🇰🇿 KZT: {} ₸", "PLN": "🇵🇱 PLN: {} zł", "UZS": "🇺🇿 UZS: {} сўм", "INR": "🇮🇳 INR: {} ₹", "TRY": "🇹🇷 TRY: {} ₺", } @loader.tds class Crypto(loader.Module): """Some basic stuff with cryptocurrencies and @CryptoBot""" strings = { "name": "Crypto", "no_args": ( "💀 You need to specify" " args" ), "incorrect_args": ( "💀 Incorrect args" ), "insufficient_funds": ( "👛 Insufficient" " funds" ), "empty_balance": ( "😭 You don't have any" " money" ), "confirm_check": ( "👛 Please, confirm that info below is valid:\n\n🪙 Amount:" " {amount}{receiver}{comment}\n\n{balance}" ), "confirm_invoice": ( "👛 Please, confirm that info below is valid:\n\n🪙 Amount:" " {amount}{comment}\n\n{balance}" ), "check": ( "{emoji} Check for {amount}{receiver}{comment}\n\n💸 Receive' " funds" ), "invoice": ( "{emoji} Invoice for {amount}{comment}\n\n💸 Proceed' " with payment" ), "comment": "\n💬 Comment: {}", "receiver": "\n👤 Receiver: {}", "available": "💰 Available: {}", "send_check": "👛 Send check", "send_invoice": "👛 Send invoice", "cancel": "🔻 Cancel", "wallet": ( "👛 Your CryptoBot wallet:\n\n{}' ), "multi-use_invoice": ( "👛 Multi-use invoice' ), "exchange_rates": "{emoji} {amount} {name} exchange rates:\n\n{rates}", "processing_rates": ( " Stealing some crypto" " from exchange..." ), } strings_ru = { "no_args": ( "💀 Ты должен указать" " аргументы" ), "incorrect_args": ( "💀 Неверные" " аргументы" ), "insufficient_funds": ( "👛 Недостаточно" " средств" ), "confirm_check": ( "👛 Пожалуйста, подтвердите, что информация ниже верна:\n\n🪙" " Сумма: {amount}{receiver}{comment}\n\n{balance}" ), "confirm_invoice": ( "👛 Пожалуйста, подтвердите, что информация ниже верна:\n\n🪙" " Сумма: {amount}{comment}\n\n{balance}" ), "check": ( "{emoji} Чек на {amount}{receiver}{comment}\n\n💸 Получить' " средства" ), "invoice": ( "{emoji} Счёт на {amount}{comment}\n\n💸 Оплатить' "" ), "comment": "\n💬 Комментарий: {}", "receiver": "\n👤 Получатель: {}", "available": "💰 Доступно: {}", "send_check": "👛 Отправить чек", "send_invoice": "👛 Отправить счёт", "cancel": "🔻 Отмена", "wallet": ( "👛 Твой CryptoBot кошелек:\n\n{}' ), "multi-use_invoice": ( "👛 Многоразовый счёт' ), "processing_rates": ( " Краду криптовалюту с" " биржи..." ), "exchange_rates": "{emoji} Курс {amount} {name}:\n\n{rates}", "empty_balance": ( "😭 На балансе ни" " гроша" ), } strings_de = { "no_args": ( "💀 Du musst Argumente" " angeben" ), "incorrect_args": ( "💀 Falsche Argumente" ), "insufficient_funds": ( "👛 Unzureichende" " Mittel" ), "confirm_check": ( "👛 Bitte bestätige, dass die folgenden Informationen korrekt sind:" "\n\n🪙 Betrag: {amount}{receiver}{comment}\n\n{balance}" ), "confirm_invoice": ( "👛 Bitte bestätige, dass die folgenden Informationen korrekt sind:" "\n\n🪙 Betrag: {amount}{comment}\n\n{balance}" ), "check": ( "{emoji} Check für {amount}{receiver}{comment}\n\n💸 Erhalte' " Zahlung" ), "invoice": ( "{emoji} Rechnung für {amount}{comment}\n\n💸 Bezahle' "" ), "comment": "\n💬 Kommentar: {}", "receiver": "\n👤 Empfänger: {}", "available": "💰 Verfügbar: {}", "send_check": "👛 Senden Sie den Scheck", "send_invoice": "👛 Senden Sie die Rechnung", "cancel": "🔻 Stornieren", "wallet": ( "👛 Deine CryptoBot Brieftasche:\n\n{}' ), "multi-use_invoice": ( "👛 Mehrfachrechnung' ), "processing_rates": ( " Ich stehle" " Kryptowährung von der Börse..." ), "exchange_rates": "{emoji} Kurs {amount} {name}:\n\n{rates}", "empty_balance": ( "😭 Nichts auf dem" " Konto" ), } strings_uz = { "no_args": ( "💀 Siz argumentlar" " berishingiz kerak" ), "incorrect_args": ( "💀 Noto'g'ri" " argumentlar" ), "insufficient_funds": ( "👛 Kifoya pul yo'q" ), "confirm_check": ( "👛 Iltimos, quyidagi ma'lumotlarning to'g'ri yoki yo'qligini" " tekshiring:\n\n🪙 Summa:" " {amount}{receiver}{comment}\n\n{balance}" ), "confirm_invoice": ( "👛 Iltimos, quyidagi ma'lumotlarning to'g'ri yoki yo'qligini" " tekshiring:\n\n🪙 Summa: {amount}{comment}\n\n{balance}" ), "check": ( "{emoji} {amount} uchun chex{receiver}{comment}\n\n💸 To\'lov' "" ), "invoice": ( "{emoji} {amount} uchun chalan{comment}\n\n💸 To\'lov' "" ), "comment": "\n💬 Izoh: {}", "receiver": "\n👤 Qabul qiluvchi: {}", "available": "💰 Mavjud: {}", "send_check": "👛 Chexni yuborish", "send_invoice": "👛 Chalan yuborish", "cancel": "🔻 Bekor qilish", "wallet": ( "👛 Sizning CryptoBot botingiz:\n\n{}' ), "multi-use_invoice": ( "👛 Bir qatorda ko\'p foydalanish uchun chalan' ), "processing_rates": ( " Mening bozoridan" " kriptoni chorayman..." ), "exchange_rates": "{emoji} {amount} {name} narxi:\n\n{rates}", "empty_balance": ( "😭 Hisob bo'sh" ), } strings_tr = { "no_args": ( "💀 Argümanlarınız" " gerekli" ), "incorrect_args": ( "💀 Yanlış argümanlar" ), "insufficient_funds": ( "👛 Yeterli bakiye" " yok" ), "confirm_check": ( "👛 Lütfen, aşağıdaki bilgilerin doğru veya yanlış olduğunu kontrol" " edin:\n\n🪙 Miktar: {amount}{receiver}{comment}\n\n{balance}" ), "confirm_invoice": ( "👛 Lütfen, aşağıdaki bilgilerin doğru veya yanlış olduğunu" " kontrol edin:\n\n🪙 Miktar: {amount}{comment}\n\n{balance}" ), "check": ( "{emoji} {amount} için çek{receiver}{comment}\n\n💸 Ödeme' "" ), "invoice": ( "{emoji} {amount} için fatura{comment}\n\n💸 Ödeme' "" ), "comment": "\n💬 Yorum: {}", "receiver": "\n👤 Alıcı: {}", "available": "💰 Mevcut: {}", "send_check": "👛 Çeki yolla", "send_invoice": "👛 Faturayı yolla", "cancel": "🔻 İptal et", "wallet": ( "👛 CryptoBot cüzdanınız:\n\n{}' ), "multi-use_invoice": ( "👛 Tek kullanımlık fatura' ), "processing_rates": ( " Kripto para" " değiştiriyorum..." ), "exchange_rates": "{emoji} {amount} {name} fiyatı:\n\n{rates}", "empty_balance": ( "😭 Bakiye boş" ), } def __init__(self): self.bot = "@CryptoBot" self.config = loader.ModuleConfig( loader.ConfigValue( "spoiler_balance", True, "Hide balance under spoiler", validator=loader.validators.Boolean(), ), loader.ConfigValue( "hide_balance", False, "Do not show balance at all", validator=loader.validators.Boolean(), ), loader.ConfigValue( "valutes", list(RATES_CONFIG), "Valutes to show in exchange rates", validator=loader.validators.Series( loader.validators.Choice(list(RATES_CONFIG)) ), ), loader.ConfigValue( "use_testnet", False, "Use testnet version of CryptoBot", validator=loader.validators.Boolean(), on_change=lambda: asyncio.ensure_future(self._process_config()), ), ) async def _process_config(self): await asyncio.sleep(0.5) self.bot = "@CryptoBot" if not self.config["debug"] else "@CryptoTestnetBot" async def _form_action( self, call: InlineCall, args: str, message: Message, formatting: dict, name: str, index: int, ): query = await self._client.inline_query(self.bot, args) result = await query[index].click("me") await result.delete() await self._client.send_message( message.peer_id, self.strings(name).format( **formatting, link=result.reply_markup.rows[0].buttons[0].url, emoji=next( ( emoji for trigger, emoji in EMOJI_MAP.items() if trigger in query[0].description ), "💎", ), ), reply_to=message.reply_to_msg_id, link_preview=False, ) await call.delete() @loader.command( ru_doc="<сумма> [человек] [комментарий] - Выписать чек", de_doc=" [Person] [Kommentar] - Ausstellen eines Schecks", tr_doc=" [kişi] [yorum] - Çek çıkar", uz_doc=" [odam] [izoh] - Chiqarish chiqoni", hi_doc="<राशि> [व्यक्ति] [टिप्पणी] - चेक बनाएं", ) async def check(self, message: Message): """ [person] [comment] - Send check""" args = utils.get_args_raw(message) if not args: await utils.answer(message, self.strings("no_args")) return if args.split()[0] == "0": receiver = ( args.split()[1] if len(args.split()) > 1 and args.split()[1].startswith("@") else "" ) if receiver: receiver = self.strings("receiver").format(receiver) comment = ( ( args.split(maxsplit=2)[2] if len(args.split()) > 2 and args.split()[1].startswith("@") else args.split(maxsplit=1)[1] ) if len(args.split()) > 1 else "" ) if comment: comment = self.strings("comment").format(comment) await utils.answer( message, self.strings("check").format( amount="1.205487 BTC (25621.80$)", comment=comment, receiver=receiver, link="https://www.youtube.com/watch?v=hGA6MGBuaCs", emoji=EMOJI_MAP["BTC"], ), ) return try: query = await asyncio.wait_for( self._client.inline_query(self.bot, args), timeout=3000, ) except (BotResponseTimeoutError, asyncio.TimeoutError): await utils.answer(message, self.strings("incorrect_args")) return article = query[0].description.strip() article_t = query[0].title.strip() if not article.startswith("Check") and not article.startswith("Чек"): await utils.answer(message, self.strings("insufficient_funds")) return amount = re.search(AMOUNT_REGEX, article_t)[1] if re.search(RECEIVER_REGEX, article): receiver = self.strings("receiver").format( utils.escape_html(re.search(RECEIVER_REGEX, article)[1]) ) else: receiver = "" if re.search(BALANCE_REGEX, article) and not self.config["hide_balance"]: balance = self.strings("available").format( ( "{}" if self.config["spoiler_balance"] else "{}" ).format(utils.escape_html(re.search(BALANCE_REGEX, article)[1])) ) else: balance = "" comment = args.split(maxsplit=1)[1] if len(args.split()) > 1 else "" if receiver: comment = comment.split(maxsplit=1)[1] if len(comment.split()) > 1 else "" if comment: comment = self.strings("comment").format(utils.escape_html(comment)) await self.inline.form( message=message, text=self.strings("confirm_check").format( amount=amount, comment=comment, receiver=receiver, balance=balance, ), reply_markup=[ { "text": self.strings("send_check"), "callback": self._form_action, "args": ( args, message, {"amount": amount, "comment": comment, "receiver": receiver}, "check", 0, ), }, {"text": self.strings("cancel"), "action": "close"}, ], ) @loader.command( ru_doc="Показать баланс криптокошелька", de_doc="Zeige den Kryptowährungsbetrag", tr_doc="Kripto cüzdanınızın bakiyesini göster", uz_doc="Kriptovalyuta portfelingizdagi balansni ko'rsatish", hi_doc="क्रिप्टो वॉलेट की शेष राशि दिखाएं", ) async def wallet(self, message: Message): """Show wallet balance""" async with self._client.conversation(self.bot) as conv: m = await conv.send_message("/wallet") r = await conv.get_response() await m.delete() buttons = utils.array_sum([row.buttons for row in r.reply_markup.rows]) button = next( (btn for btn in buttons if btn.text == "Show Small Balances"), None ) if button: await r.click(data=button.data) r = (await self._client.get_messages(r.peer_id, ids=[r.id]))[0] await r.delete() info = "\n\n".join( f"{next((emoji for trigger, emoji in EMOJI_MAP.items() if trigger in line), '💎')} {line.split(maxsplit=1)[1]}" for line in r.raw_text.splitlines() if line.startswith("·") and ": 0 " not in line ) await utils.answer( message, ( self.strings("wallet").format( f"https://t.me/{self.bot.strip('@')}", info ) if info else self.strings("empty_balance") ), ) @loader.command( ru_doc="[-o - не создавать новый] - Отправить мультисчёт", de_doc="[-o - erstelle keine neue] - Sende eine Mehrfachzahlung", tr_doc="[-o - yeni oluşturma] - Çoklu ödeme gönder", uz_doc="[-o - yangi yaratmaslik] - Ko'p mablag'li to'lovni yuborish", hi_doc="[-o - नया नहीं बनाएं] - एकाधिक भुगतान भेजें", ) async def muinvoice(self, message: Message): """[-o - don't create new one] Send multi-use invoice""" if "-o" in utils.get_args_raw(message) and self.get("muinvoice_url"): url = self.get("muinvoice_url") else: query = await self._client.inline_query(self.bot, "") m = await query[0].click("me") await m.delete() url = m.reply_markup.rows[0].buttons[0].url self.set("muinvoice_url", url) await utils.answer( message, self.strings("multi-use_invoice").format(url=url), ) @loader.command( ru_doc="<сумма> [комментарий] - Выставить счет", de_doc=" [Kommentar] - Stelle eine Rechnung aus", tr_doc=" [yorum] - Fatura çıkar", uz_doc=" [izoh] - Hisobni chiqarish", hi_doc="<राशि> [टिप्पणी] - चालान बनाएं", ) async def invoice(self, message: Message): """ [comment] - Send invoice""" args = utils.get_args_raw(message) if not args: await utils.answer(message, self.strings("no_args")) return try: query = await asyncio.wait_for( self._client.inline_query(self.bot, args), timeout=3000, ) except (BotResponseTimeoutError, asyncio.TimeoutError): await utils.answer(message, self.strings("incorrect_args")) return article = query[-1].description.strip() if not article.startswith("Invoice") and not article.startswith("Счёт"): await utils.answer(message, self.strings("insufficient_funds")) return amount = re.search(INVOICE_AMOUNT_REGEX, article)[1] if re.search(BALANCE_REGEX, article) and not self.config["hide_balance"]: balance = self.strings("available").format( ( "{}" if self.config["spoiler_balance"] else "{}" ).format(utils.escape_html(re.search(BALANCE_REGEX, article)[1])) ) else: balance = "" comment = args.split(maxsplit=1)[1] if len(args.split()) > 1 else "" if comment: comment = self.strings("comment").format(utils.escape_html(comment)) await self.inline.form( message=message, text=self.strings("confirm_invoice").format( amount=amount, comment=comment, balance=balance, ), reply_markup=[ { "text": self.strings("send_invoice"), "callback": self._form_action, "args": ( args, message, {"amount": amount, "comment": comment}, "invoice", -1, ), }, {"text": self.strings("cancel"), "action": "close"}, ], ) @loader.command( ru_doc="[amount] - Показать курс криптовалюты", de_doc="[Betrag] - Zeige den Kurs der Kryptowährung", tr_doc="[miktar] - Kripto para biriminin kurunu göster", uz_doc="[miqdor] - Kriptovalyutaning kursini ko'rsatish", hi_doc="[राशि] <नाम> - क्रिप्टोकरेंसी की दर दिखाएं", ) async def rates(self, message: Message): """[amount] - Show cryptocurrency exchange rates""" args = utils.get_args_raw(message) if not args: await utils.answer(message, self.strings("no_args")) return if len(args.split()) > 1 and args[0].isdigit(): amount = float(args.split(maxsplit=1)[0]) args = args.split(maxsplit=1)[1] else: amount = 1 message = await utils.answer(message, self.strings("processing_rates")) valutes = { valute.getchildren()[1].text: float( valute.getchildren()[4].text.replace(",", ".") ) / int(valute.getchildren()[2].text) for valute in etree.fromstring( ( await utils.run_sync( requests.get, "https://www.cbr.ru/scripts/XML_daily.asp" ) ).content ).getchildren() } def to_RUB(price_usd: float) -> float: return price_usd * valutes["USD"] def to_XXX(price_usd: float, name: str) -> float: return to_RUB(price_usd) / valutes[name] crypto = { crypto["symbol"]: { "rates": { "USD": float(crypto["priceUsd"]), "RUB": to_RUB(float(crypto["priceUsd"])), **{ name: to_XXX(float(crypto["priceUsd"]), name) for name in self.config["valutes"] if name not in {"USD", "RUB"} }, }, "name": crypto["name"], } for crypto in ( await utils.run_sync(requests.get, "https://api.coincap.io/v2/assets") ).json()["data"] } closest_crypto = difflib.get_close_matches( args.upper(), crypto.keys(), n=1, ) if not closest_crypto: await utils.answer(message, self.strings("incorrect_args")) return exchange_rates = crypto[closest_crypto[0]]["rates"] await utils.answer( message, self.strings("exchange_rates").format( emoji=next( ( emoji for name, emoji in EMOJI_MAP.items() if name in closest_crypto[0] or closest_crypto[0] in name ), "💎", ), name=crypto[closest_crypto[0]]["name"], rates="\n".join( RATES_CONFIG[valute].format( f"{exchange_rates[valute] * amount:_.2f}".replace("_", " ") ) for valute in self.config["valutes"] ), amount=amount, ), )