Shirino/main.py
2023-05-30 14:33:41 +04:00

173 lines
4 KiB
Python

import json
import hashlib
import logging
from typing import Any, Dict, List, Optional
import requests
from pydantic import BaseSettings
from aiogram import Bot
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.types import InlineQuery
from aiogram.types import InlineQueryResultArticle
from aiogram.types import InputTextMessageContent
# Constants
DDG_URL = 'https://duckduckgo.com/js/spice/currency'
COINAPI_URL = 'https://rest.coinapi.io/v1/exchangerate'
# ---
# Config from .env
class Settings(BaseSettings):
debug: bool
coinapi_key: str
telegram_token: str
class Config:
env_file = '.env'
env_file_encoding = 'utf-8'
settings = Settings()
# ---
# Logging
log = logging.getLogger('shirino')
handler = logging.StreamHandler()
fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(fmt)
log.addHandler(handler)
if settings.debug:
handler.setLevel(logging.DEBUG)
log.setLevel(logging.DEBUG)
# ---
bot = Bot(token=settings.telegram_token)
dp = Dispatcher(bot)
class CurrencyConverter:
def __init__(self) -> None:
self.amount = 1.0
self.conv_amount = 0.0
self.from_currency = ''
self.conv_currency = ''
def convert(self) -> None:
"""Currency conversion"""
if not self.ddgapi():
self.coinapi()
def ddgapi(self) -> bool:
"""Get data from DuckDuckGo's currency API
Returns:
`False` if the currency does not exist,
`True` on successful conversion
"""
# API request
resp = requests.get(
f'{DDG_URL}/{self.amount}/{self.from_currency}'
f'/{self.conv_currency}'
)
log.debug(resp.text)
# Parsing JSON data
data: Dict[str, Any] = json.loads(
resp.text \
.replace('ddg_spice_currency(', '') \
.replace(');', '')
)
log.debug(data)
# If the currency does not exist
descr = data.get('headers', {}).get('description', '')
if descr.find('ERROR') != -1:
return False
# Otherwise
conv: Dict[str, str] = data.get('conversion', {})
self.conv_amount = float(conv.get('converted-amount', 0))
log.debug(conv)
return True
def coinapi(self) -> None:
"""Get data from CoinAPI (for cryptocurrencies)"""
resp = requests.get(
(
f'{COINAPI_URL}/{self.from_currency}'
f'/{self.conv_currency}'
),
headers={
'X-CoinAPI-Key': settings.coinapi_key,
},
)
data: Dict[str, Any] = resp.json()
self.conv_amount = float(data.get('rate', 0))
@dp.inline_handler()
async def currency(inline_query: InlineQuery) -> None:
query = inline_query.query
article: List[Optional[InlineQueryResultArticle]] = [None]
text = query.split()
len_ = len(text)
result_id = hashlib.md5(query.encode()).hexdigest()
conv = CurrencyConverter()
try:
if len_ == 3:
conv.amount = float(text[0])
conv.from_currency = text[1].upper()
conv.conv_currency = text[2].upper()
conv.convert()
elif len_ == 2:
conv.from_currency = text[0].upper()
conv.conv_currency = text[1].upper()
conv.convert()
else:
raise ValueError('Надо 2 или 3 аргумента')
result = (
f'{conv.amount} {conv.from_currency} = '
f'{conv.conv_amount} {conv.conv_currency}'
)
except Exception as ex:
result = f'{type(ex)}: {ex}'
article[0] = InlineQueryResultArticle(
id=result_id,
title=result,
input_message_content=InputTextMessageContent(
message_text=result,
),
)
await inline_query.answer(
article,
cache_time=1,
is_personal=True,
)
executor.start_polling(dp, skip_updates=True)