diff --git a/daemons/bot.py b/daemons/bot.py index b8ec4b3..4947801 100644 --- a/daemons/bot.py +++ b/daemons/bot.py @@ -5,7 +5,7 @@ from telebot.types import Message import settings from helpers.mongo import mongo -from helpers.platform import platform +from helpers.sprint_platform import platform bot = telebot.TeleBot(os.getenv("TELEGRAM_TOKEN")) @@ -18,7 +18,7 @@ def on_start(message: Message): @bot.message_handler() def do_action(message: Message): - if settings.STAGE == 'development' and not platform.get_staff(message.chat.id): + if settings.STAGE == 'development' and not platform.is_staff(telegram_id=message.chat.id): return from helpers.answer import Answer Answer(message).process() diff --git a/helpers/answer.py b/helpers/answer.py index a3783ec..98f24c8 100644 --- a/helpers/answer.py +++ b/helpers/answer.py @@ -9,7 +9,7 @@ from helpers import get_next_daily_notify_time from helpers.keyboards import main_keyboard, notify_keyboard, yes_no_keyboard, again_keyboard, no_daily_notify, \ campus_keyboard, daily_notify_type, notify_type, first_lesson_notify from helpers.mongo import mongo -from helpers.platform import platform +from helpers.sprint_platform import platform from helpers.ruz import ruz diff --git a/helpers/keyboards.py b/helpers/keyboards.py index 315df33..b8191a1 100644 --- a/helpers/keyboards.py +++ b/helpers/keyboards.py @@ -1,6 +1,6 @@ import telebot -from helpers.platform import platform +from helpers.sprint_platform import platform def main_keyboard(telegram_id): @@ -9,7 +9,15 @@ def main_keyboard(telegram_id): kb.row("Расписание на неделю") kb.row("Напоминания о парах") kb.row("Ежедневные уведомления") - if platform.experiment_enabled_for_user('alice', telegram_id) or telegram_id in platform.get_config('alice_users'): + alice_exp = platform.get_experiment('alice') + try: + user = object() + user.platform_staff = lambda telegram_id: platform.is_staff(telegram_id=telegram_id) + alice_exp_enabled = eval(alice_exp['condition']) + except Exception as exc: + print(exc) + alice_exp_enabled = False + if alice_exp_enabled: kb.row("Подключение Алисы") kb.row("Сброс настроек") return kb diff --git a/helpers/platform.py b/helpers/platform.py deleted file mode 100644 index 96ab1c6..0000000 --- a/helpers/platform.py +++ /dev/null @@ -1,94 +0,0 @@ -import os - -from cachetools import TTLCache -from requests import get - - -class Platform: - def __init__(self): - self.staff_cache = TTLCache(1000, 60) - self.exp_cache = TTLCache(1000, 60) - self.configs = TTLCache(1000, 60) - self.stage = os.getenv("STAGE", "local") - self.token = os.getenv("PLATFORM_SECURITY_TOKEN") - self.project = "РУЗ Бот" - - def get_config(self, config_name): - config = self.configs.get(config_name) - if config is None: - config = get( - 'https://platform.sprinthub.ru/configs/get', - headers={'X-Security-Token': self.token}, - params={ - 'project': self.project, - 'stage': self.stage, - 'name': config_name - } - ) - if config.status_code != 200: - return {} - config = config.json() - self.configs[config_name] = config - return config - - def get_experiment(self, experiment_name): - exp = self.exp_cache.get(experiment_name) - if exp is None: - exp = get( - 'https://platform.sprinthub.ru/experiments/get', - headers={'X-Security-Token': self.token}, - params={ - 'project': self.project, - 'stage': self.stage, - 'name': experiment_name - } - ) - if exp.status_code != 200: - return { - 'enabled': False, - 'condition': False - } - exp = exp.json() - self.exp_cache[experiment_name] = exp - return exp - - def get_staff(self, telegram_id): - is_staff = self.staff_cache.get(telegram_id) - if is_staff is None: - exp = get( - 'https://platform.sprinthub.ru/is_staff', - headers={'X-Security-Token': self.token}, - params={ - 'telegram_id': telegram_id, - } - ) - if exp.status_code != 200: - return False - data = exp.json() - is_staff = data['is_staff'] - self.staff_cache[telegram_id] = is_staff - return is_staff - - def experiment_enabled_for_user(self, experiment_name, telegram_id): - exp_data = self.get_experiment(experiment_name) - if not exp_data['enabled']: - return False - - class User: - def __init__(self, platform, telegram_id): - self.platform = platform - self.telegram_id = telegram_id - - @property - def platform_staff(self): - return self.platform.get_staff(self.telegram_id) - - @property - def is_superuser(self): - return False - - user = User(self, telegram_id) - return bool(eval(exp_data['condition'])) - - -platform = Platform() diff --git a/helpers/sprint_platform.py b/helpers/sprint_platform.py new file mode 100644 index 0000000..c55a015 --- /dev/null +++ b/helpers/sprint_platform.py @@ -0,0 +1,116 @@ +import json +import os +import typing +import urllib.parse +from threading import Thread +from time import sleep + +from requests import get + + +class PlatformClient: + def __init__(self, platform_security_token: str, app_name: str, stage: str, configs: typing.List[str], experiments: typing.List[str], need_poll: bool = True): + self.platform_security_token = platform_security_token + self.app_name = app_name + self.stage = stage + self.configs = configs + self.experiments = experiments + self.endpoint = 'https://platform.sprinthub.ru/' + self.configs_url = urllib.parse.urljoin(self.endpoint, 'configs/get') + self.experiments_url = urllib.parse.urljoin(self.endpoint, 'experiments/get') + self.staff_url = urllib.parse.urljoin(self.endpoint, 'is_staff') + self.config_storage = {} + self.experiment_storage = {} + self.poll_data() + if need_poll: + self.poll_data_in_thread() + + def poll_data_in_thread(self): + def inner(): + while True: + sleep(30) + self.fetch_configs() + self.fetch_experiments() + + Thread(target=inner).start() + + def poll_data(self): + self.fetch_configs(with_exception=True) + self.fetch_experiments(with_exception=True) + + def request_with_retries(self, url, params, with_exception=False, retries_count=3): + exception_to_throw = None + for _ in range(retries_count): + try: + response = get( + url, + headers={'X-Security-Token': self.platform_security_token}, + params=params + ) + if response.status_code == 200: + return response.json() + print(f'Failed to request {url}, status_code={response.status_code}') + exception_to_throw = Exception('Not 200 status') + except Exception as exc: + print(exc) + exception_to_throw = exc + sleep(1) + print(f'Failed fetching with retries: {url}, {params}') + if with_exception: + raise exception_to_throw + + def fetch_configs(self, with_exception=False): + if self.stage == 'local': + local_platform = json.loads(open('local_platform.json', 'r').read()) + self.config_storage = local_platform['configs'] + return + for config in self.configs: + response_data = self.request_with_retries(self.configs_url, { + 'project': self.app_name, + 'stage': self.stage, + 'name': config + }, with_exception) + self.config_storage[config] = response_data + + def fetch_experiments(self, with_exception=False): + if self.stage == 'local': + local_platform = json.loads(open('local_platform.json', 'r').read()) + self.experiment_storage = local_platform['experiments'] + return + for experiment in self.experiments: + response_data = self.request_with_retries(self.experiments_url, { + 'project': self.app_name, + 'stage': self.stage, + 'name': experiment + }, with_exception) + self.experiment_storage[experiment] = response_data + + def is_staff(self, **kwargs): + if self.stage == 'local': + local_platform = json.loads(open('local_platform.json', 'r').read()) + local_staff = local_platform['staff'] + for element in local_staff: + for key, value in kwargs.items(): + if element[key] == value: + return True + return False + response_data = self.request_with_retries(self.staff_url, kwargs) + if response_data is None: + return False + return response_data['is_staff'] + + def get_config(self, name): + return self.config_storage[name] + + def get_experiment(self, name): + return self.experiment_storage[name] + + +platform = PlatformClient( + os.getenv('PLATFORM_SECURITY_TOKEN'), + 'РУЗ Бот', + os.getenv('STAGE'), + ['words'], + ['alice'], + need_poll=True, +)