Пример #1
0
def is_cmd_delayed(chat_id: int, cmd: str) -> bool:
    delayed_key = f'delayed:{cmd}:{chat_id}'
    delayed = cache.get(delayed_key)
    if delayed:
        return True
    cache.set(delayed_key, True, 5 * 60)  # 5 минут
    return False
Пример #2
0
def can_use(chat_id: int, from_uid: int) -> bool:
    key = f'8:{chat_id}:{from_uid}'
    count = cache.get(key, 0)
    if count < LIMIT:
        cache.set(key, count + 1, time=FEW_DAYS)
        return True
    return False
Пример #3
0
def call_cats_vision_api(bot: telegram.Bot,
                         update: telegram.Update,
                         key_media_group: str,
                         img_url=None):
    """
    Используется google vision api:
    * https://cloud.google.com/vision/
    * https://cloud.google.com/vision/docs/reference/libraries
    * https://googlecloudplatform.github.io/google-cloud-python/latest/vision/index.html
    """
    chat_id = update.message.chat_id

    # если урл картинки не задан, то сами берем самую большую фотку из сообщения
    # гугл апи, почему-то, перестал принимать ссылки телеги, нам приходится самим загружать ему фото
    if img_url is None:
        from google.cloud.vision import types as google_types
        file = bot.get_file(update.message.photo[-1].file_id)
        content = bytes(file.download_as_bytearray())
        # noinspection PyUnresolvedReferences
        image = google_types.Image(content=content)
    # но если передана ссылка, то и гуглу отдаем только ссылку
    # чтобы не заниматься самим вопросом загрузки каких-то непонятных ссылок
    # а если гугл не сможет ее открыть -- ну не судьба
    else:
        image = {'source': {'image_uri': img_url}}

    # noinspection PyPackageRequirements
    from google.cloud import vision
    # обращаемся к апи детектирования объектов на фото
    try:
        logger.debug(f"[google vision] parse img {img_url}")
        client = config.google_vision_client
        response = client.annotate_image({
            'image':
            image,
            'features': [{
                'type': vision.enums.Feature.Type.LABEL_DETECTION,
                'max_results': 30
            }],
        })
    except Exception as ex:
        logger.error(ex)
        return

    # если на фото кот
    cat = any(
        re.search(r"\bcats?\b", label.description, re.IGNORECASE)
        for label in response.label_annotations)
    if cat:
        logger.debug(f"[google vision] cat found")
        if update.message.media_group_id:
            if cache.get(key_media_group):
                return
            cache.set(key_media_group, True, time=TWO_DAYS)
        msg_id = update.message.message_id
        bot.sendMessage(chat_id,
                        CONFIG.get("cat_tag", "Это же кошак 🐈"),
                        reply_to_message_id=msg_id)
        return
    logger.debug(f"[google vision] cat not found")
Пример #4
0
    def on_mig_click(cls, bot: telegram.Bot, _: telegram.Message,
                     query: telegram.CallbackQuery, data):
        uid = query.from_user.id
        card: Card = cache.get(cls.__get_key(data['card_id']))
        if not card:
            bot.answer_callback_query(
                query.id,
                f"Ошибка. Не могу найти открытку #{data['card_id']}",
                show_alert=True)
            return

        if uid != card.to_uid:
            bot.answerCallbackQuery(
                query.id, 'Только адресат валентинки может подмигнуть 💔')
            return

        if uid in card.mig_uids:
            if User.get(uid).female:
                text = 'Подруга, ты уже подмигивала 💆'
            else:
                text = 'Дружище, ты уже подмигивал 💆‍♂️'
            bot.answerCallbackQuery(query.id, text)
            return

        card.mig_uids.append(uid)
        cache.set(cls.__get_key(card.card_id), card, time=USER_CACHE_EXPIRE)
        bot.answerCallbackQuery(query.id, 'Подмигивание прошло успешно')
        Stats.total_migs.incr()
        Stats.migs_users.add(uid)
        user = User.get(uid)
        username = user.get_username_or_link()
        ReactionNotification.send(bot, card.from_uid,
                                  f"{username} подмигивает тебе ❤", card)
        cls.__set_card_preview_as_done(bot, card)
Пример #5
0
    def message_handler(cls, bot: telegram.Bot,
                        update: telegram.Update) -> None:
        chat_id = update.message.chat_id
        msg_id = update.message.message_id
        user_id = update.message.from_user.id
        img_url = get_photo_url(bot, update.message)
        photo = cls.__check(img_url, chat_id, msg_id, user_id)

        if not photo:
            return
        if user_id == photo.user_id:
            logger.debug(
                f'same user bayan: {chat_id}:{msg_id}={photo.message_id}')
            return

        if update.message.media_group_id:
            key_media_group = f'{KEY_PREFIX}:media_group_reacted:{chat_id}:{update.message.media_group_id}'
            if cache.get(key_media_group):
                return
            cache.set(key_media_group, True, time=TWO_DAYS)

        data = {
            "name": BAYANOMETER_SHOW_ORIG,
            "type": cls.data_type,
            "orig_photo": photo,
            "url": img_url
        }
        cls.__send(bot, chat_id, msg_id, photo.date, data)
Пример #6
0
 def edit_message(cls,
                  bot: telegram.Bot,
                  message_id: int,
                  text: str,
                  chat_id: int = chat_id,
                  buttons=None) -> None:
     if chat_id == 0:
         return
     reply_markup = cls.get_reply_markup(buttons)
     try:
         bot.edit_message_text(text,
                               chat_id,
                               message_id,
                               reply_markup=reply_markup,
                               parse_mode=telegram.ParseMode.HTML,
                               disable_web_page_preview=True)
         cache.set(f'{CACHE_PREFIX}:messages:{chat_id}:{message_id}:text',
                   text,
                   time=USER_CACHE_EXPIRE)
         cache.set(
             f'{CACHE_PREFIX}:messages:{chat_id}:{message_id}:buttons',
             buttons,
             time=USER_CACHE_EXPIRE)
     except Exception as e:
         logger.error(
             f"[{MODULE_NAME}] Can't edit message from {chat_id}. Exception: {e}"
         )
Пример #7
0
    def get(cls, uid) -> typing.Optional['User']:
        if not uid:
            return None
        if isinstance(uid, ChatUser):
            uid = uid.uid
        if isinstance(uid, str):
            uid = get_int(uid)
            if uid is None:
                return None

        cached = cache.get(cls.__get_cache_key(uid))
        if cached:
            return cached

        logger.debug(f'get_lock {uid}')
        # лок, чтобы в редис попало то, что в бд
        with cls.get_lock:
            try:
                user = UserDB.get(uid)
                if user:
                    cache.set(cls.__get_cache_key(uid),
                              user,
                              time=USER_CACHE_EXPIRE)
                    return user
            except Exception as e:
                logger.error(e)
        return None
Пример #8
0
def add_stickerset_to_used(stickerset):
    """
    Добавляем пак в список использованных.
    """
    used_stickersets = set(cache.get('pipinder:used_stickersets', []))
    used_stickersets.add(stickerset.name)
    cache.set('pipinder:used_stickersets', used_stickersets, time=YEAR)
Пример #9
0
 def send_message(cls,
                  bot: telegram.Bot,
                  text: str,
                  chat_id: int = chat_id,
                  buttons=None,
                  reply_to_message_id=None) -> Optional[int]:
     if chat_id == 0:
         return
     reply_markup = cls.get_reply_markup(buttons)
     try:
         message = bot.send_message(chat_id,
                                    text,
                                    reply_markup=reply_markup,
                                    reply_to_message_id=reply_to_message_id,
                                    parse_mode=telegram.ParseMode.HTML,
                                    disable_web_page_preview=True,
                                    timeout=20)
         cache.set(
             f'{CACHE_PREFIX}:messages:{chat_id}:{message.message_id}:text',
             message.text_html,
             time=USER_CACHE_EXPIRE)
         cache.set(
             f'{CACHE_PREFIX}:messages:{chat_id}:{message.message_id}:buttons',
             buttons,
             time=USER_CACHE_EXPIRE)
         return message.message_id
     except Exception as e:
         logger.error(
             f"[{MODULE_NAME}] Can't send message to {chat_id}. Exception: {e}"
         )
         if str(e) == 'Timed out':
             raise Exception(e)
         return None
Пример #10
0
    def add_user(cls, user: telegram.User) -> None:
        if user.is_bot:
            return
        username = user.username
        fullname = ' '.join([user.first_name or '', user.last_name
                             or '']).strip()

        uid = user.id
        old_user = cls.get(uid)
        old_user_female = False if old_user is None else old_user.female
        new_user = User(uid=uid,
                        username=username,
                        fullname=fullname,
                        female=old_user_female)

        # пользователя нужно всегда обновлять в редисе (продлевать кэш, так сказать)
        # но в базе он меняется редко. поэтому сразу обновляем редис
        cache.set(cls.__get_cache_key(uid), new_user, time=USER_CACHE_EXPIRE)

        # и только потом проверяем, нужно ли обновить в базе
        # если нужно, то __add вызовет блокировку потока
        if old_user is not None:
            update = {}
            if old_user.username != username:
                update['username'] = username
            if old_user.fullname != fullname:
                update['fullname'] = fullname
            if update:
                cls.__add(new_user, update)
            return
        cls.__add(new_user)
Пример #11
0
def private_text_handler(bot: telegram.Bot, update: telegram.Update) -> None:
    """
    Обработчик текста в личке бота
    """
    message: telegram.Message = update.message
    user_id = message.from_user.id
    from_user = get_vuser(user_id)
    entities = message.parse_entities().items()
    mentions = get_mentions(entities)
    text_html = replace_text_mentions(message.text, entities)

    answer = command_val(text_html, mentions, from_user,
                         get_random_hearts(user_id))

    if isinstance(answer, str):
        bot.send_message(user_id, answer, parse_mode=HTML)
        return

    if isinstance(answer, CardDraftSelectHeart):
        answer.original_draft_message_id = message.message_id
        cache.set(f'{CACHE_PREFIX}:draft:card:{user_id}',
                  answer,
                  time=TWO_DAYS)
        bot.send_message(user_id,
                         answer.get_message_text(),
                         reply_markup=get_reply_markup(
                             answer.get_message_buttons()),
                         parse_mode=HTML)
Пример #12
0
def draft_heart_button_click_handler(bot: telegram.Bot, _: telegram.Update,
                                     query: telegram.CallbackQuery,
                                     data) -> None:
    """
    Обработчик кнопки выбора сердечка
    """
    user_id = query.from_user.id

    draft: Optional[CardDraftSelectHeart] = cache.get(
        f'{CACHE_PREFIX}:draft:card:{user_id}')
    if draft is None or not isinstance(draft, CardDraftSelectHeart):
        query.answer(text='Черновик не найден')
        query.message.delete()
        return

    heart: str = data['heart']
    chat_names = {
        chat.chat_id: get_chat_title(bot, chat.chat_id)
        for chat in draft.from_user.chats
    }
    answer = draft.select_heart(heart, chat_names)
    answer.original_draft_message_id = draft.original_draft_message_id

    cache.set(f'{CACHE_PREFIX}:draft:card:{user_id}', answer, time=TWO_DAYS)
    query.edit_message_text(text=answer.get_message_text(), parse_mode=HTML)
    query.edit_message_reply_markup(
        reply_markup=get_reply_markup(answer.get_message_buttons()))
    query.answer()
Пример #13
0
def generate_next_name(key: str) -> str:
    """
    Возвращает следующее неиспользованное имя
    """
    # особые дни
    if key == 'orzik' and is_space_day():
        return 'Гагарин'

    # получаем непустые имена без пробелов
    stripped_names = [
        x for x in (x.strip() for x in CONFIG.get(key, 'Никто').split(','))
        if x
    ]

    # удаляем дубликаты, сохраняя порядок имен
    uniq_names = list(OrderedDict((x, True) for x in stripped_names).keys())

    # мы отдельно храним последние выбранные имена, чтобы они не повторялись
    recent_names = cache.get(f'{key}_recent', [])

    # очередь deq используется, чтобы хранить половину от возможного числа имен
    half_len = round(len(uniq_names) / 2)
    deq: deque = deque(maxlen=half_len)
    deq.extend(recent_names)

    # типа бесконечный цикл, в течение которого выбирается случайное имя, которое еще не постили
    for _ in range(1, 1000):
        name = random.choice(uniq_names)
        if name not in recent_names:
            deq.append(name)
            cache.set(f'{key}_recent', list(deq), time=MONTH)
            return name
    return 'Никто'
Пример #14
0
def orzik_correction(bot: telegram.Bot, update: telegram.Update) -> None:
    """
    Реакция на упоминание орзика
    """
    chat_id = update.message.chat_id
    today = datetime.today().replace(hour=0, minute=0, second=0,
                                     microsecond=0).strftime("%Y%m%d")
    delayed_key = f'orzik_correction:{today}:{chat_id}'

    # уже постили. нужно ждать
    delayed = cache.get(delayed_key)
    if delayed:
        return
    name = get_base_name('orzik')

    # помимо имен бот еще иногда дает орзику указания ("НЕ постишь селфи")
    # затруднительно автоматически преобразовывать это к "сегодня он не постит селфи", да и не нужно
    # зато нужно отличать имена от таких указаний и игнорировать их
    # обычно имена состоят из одного слова
    # но даже если имя из двух слов, то обычно оба слова начинаются с больших букв - это и проверяем
    if len(name.split(' ')) > 1 and not name.istitle():
        # если это не имя, то сегодняшний день пропускаем
        cache.set(delayed_key, True, time=(2 * 24 * 60 * 60))
        return

    cache.set(delayed_key, True, time=(4 * 60 * 60))  # и теперь ждем 4 часа
    bot.send_message(chat_id,
                     f'Сегодня он {name}',
                     reply_to_message_id=update.message.message_id)
Пример #15
0
def send_weather_now(bot: telegram.Bot, update: telegram.Update) -> None:
    chat_id = update.message.chat_id

    if cache.get(f'weather:{chat_id}:now:start_collect'):
        return
    cache.set(f'weather:{chat_id}:now:start_collect', True, 90)

    cached_key = f'weather:{chat_id}:now:result'
    cached_result = cache.get(cached_key)
    if cached_result is not None:
        bot.send_message(chat_id,
                         cached_result,
                         parse_mode=telegram.ParseMode.HTML,
                         disable_web_page_preview=True)
        return

    debug = CONFIG.get('weather_debug')
    weather_cities = CONFIG.get('weather_cities', {}).get(str(chat_id), [])
    if len(weather_cities) == 0:
        bot.send_message(chat_id, 'Забыли города добавить в конфиг')
        return

    bot.send_chat_action(chat_id, telegram.ChatAction.TYPING)
    jsons = make_requests(chat_id, weather_cities, debug=debug)
    cities = parse_jsons(jsons)

    poweredby = f"\n<a href='https://yandex.ru/pogoda'>По данным Яндекс.Погоды</a>"
    cities_joined = "\n".join(cities)
    result = f"Погода сейчас:\n\n{cities_joined}{poweredby}"
    cache.set(cached_key, result, 30 * 60)  # хранится в кэше 30 минут

    bot.send_message(chat_id,
                     result,
                     parse_mode=telegram.ParseMode.HTML,
                     disable_web_page_preview=True)
Пример #16
0
def make_requests(chat_id, weather_cities, debug=False):
    def make_request(city, debug=False):
        city_name, city_code, timezone, wu_city_code = city
        if debug:
            val = FileUtils.load_json(city_code)
        else:
            response = request(city_code)
            val = response['error_msg'] if response['error'] else response[
                'json']
        return city_name, timezone, val

    if debug:
        return [make_request(city, debug=True) for city in weather_cities]

    cached_key = f'weather:{chat_id}:requests'
    cached = cache.get(cached_key)
    if cached:
        return cached

    num_of_workers = 3
    pool = ThreadPool(num_of_workers)
    results = pool.map(make_request, weather_cities)
    pool.close()
    pool.join()

    cache.set(cached_key, results, 30 * 60)  # хранится в кэше 30 минут
    return results
Пример #17
0
 def __check(cls, url, chat_id, message_id, user_id: int) -> Optional['URL']:
     hash_value = cls.__hash(url)
     key = f'{KEY_PREFIX}:url:{chat_id}:{hash_value}'
     cached = cache.get(key)
     if cached:
         return cached
     bayan = URL(message_id, datetime.datetime.now(), user_id)
     cache.set(key, bayan, time=YEAR)
Пример #18
0
def get_random_hearts(user_id: int) -> List[str]:
    key = f'{CACHE_PREFIX}:draft:hearts:{user_id}'
    cached = cache.get(key)
    if cached:
        return cached
    hearts = random.choices(all_hearts, k=3)
    cache.set(key, hearts, time=TWO_DAYS)
    return hearts
Пример #19
0
 def __incr(self, type: str, uid: int) -> bool:
     key = f'{self.key_prefix}:{type}'
     uids: List[int] = cache.get(key, [])
     if uid in uids:
         return False
     uids.append(uid)
     cache.set(key, uids, time=USER_CACHE_EXPIRE)
     return True
Пример #20
0
def set_top_mater(stats_from_cid, users_msg_stats) -> None:
    try:
        top_mater = [row['uid'] for row in users_msg_stats[0:3]]
        cache.set(f'weekgoal:{stats_from_cid}:top_mater_uids',
                  top_mater,
                  time=MONTH)
    except Exception:
        pass
Пример #21
0
 def execute(self, bot):
     reply_markup = self.get_reply_markup(self.buttons)
     bot.edit_message_reply_markup(FSBDayTelegram.chat_id,
                                   self.message_id,
                                   reply_markup=reply_markup)
     cache.set(f'{CACHE_PREFIX}__message_buttons_{self.message_id}',
               self.buttons,
               time=USER_CACHE_EXPIRE)
Пример #22
0
 def increase_khaleesi_time(cls, chat_id: int) -> None:
     key = cls.__get_count_cache_key(chat_id)
     count = cache.get(key)
     if not count:
         count = 0
     cache.set(key, count + 1, time=(2 * 24 * 60 * 60))
     # и теперь ждем 4 часа
     cache.set(cls.__get_limited_cache_key(chat_id), True, time=(4 * 60 * 60))
Пример #23
0
def send_random_sticker_from_stickerset(bot: telegram.Bot, chat_id: int,
                                        stickerset_name: str) -> None:
    key = f'stickerset:{stickerset_name}'
    stickerset = cache.get(key)
    if not stickerset:
        stickerset = bot.get_sticker_set(stickerset_name)
        cache.set(key, stickerset, time=50)
    sticker = random.choice(stickerset.stickers)
    bot.send_sticker(chat_id, sticker)
Пример #24
0
def cheats_found(chat_id: int, user_id: int, sum_count: int) -> bool:
    key = cheats_key(chat_id, user_id)
    sums = cache.get(key, 0)
    sums += sum_count
    if sums > 50:
        return True

    cache.set(key, sums, time=10 * 60)  # 10m
    return False
Пример #25
0
def off_all_cmds(bot, update):
    """
    Отключает все команды в указанном чате.
    """
    chat_id = update.message.chat_id
    cache.set(f'all_cmd_disabled:{chat_id}', True, time=CONFIG['off_delay'])
    bot.sendMessage(
        chat_id,
        'Все команды выключены на 5 минут.\nСтатистика собирается в школу.')
Пример #26
0
def send_to_all_chats(bot: telegram.Bot, key_name: str,
                      get_text: Callable[[int], str]) -> None:
    for chat in get_config_chats():
        chat_id = chat.chat_id
        chat_key = f'{CACHE_PREFIX}:{key_name}:{chat_id}'
        if cache.get(chat_key, False):
            continue
        dsp(send_html, bot, chat_id, get_text(chat_id))
        cache.set(chat_key, True, time=TWO_DAYS)
Пример #27
0
    def preview_done_click(cls, bot: telegram.Bot, _: telegram.Message,
                           query: telegram.CallbackQuery, __):
        uid = query.from_user.id
        key = cls.__get_key(uid)

        key_delayed = f'{key}:delayed'
        if cache.get(key_delayed):
            bot.answer_callback_query(query.id, 'Ждите…')
            return
        cache.set(key_delayed, True, time=60)

        preview_data = cache.get(key)
        if not preview_data:
            bot.answer_callback_query(
                query.id,
                'Произошла ошибка. Отправьте текст валентинки повторно',
                show_alert=True)
            cache.delete(key_delayed)
            return

        # если валентинка уже отправлена, а пользователь жмет на кнопки прошлых предпросмотров
        if preview_data['done']:
            bot.answer_callback_query(query.id)
            cache.delete(key_delayed)
            return

        # пробуем отправить валентинку в чат
        text = cls.__get_text(preview_data['text'],
                              preview_data['heart_index'],
                              with_header=False,
                              title='')
        card = Card(bot,
                    preview_data['chat_id'],
                    preview_data['from_uid'],
                    preview_data['to_uid'],
                    text,
                    preview_data['text'],
                    preview_data['preview_message_id'],
                    heart_index=preview_data['heart_index'])
        if not CardCreator.send_card(bot, card):
            bot.answer_callback_query(
                query.id,
                'Произошла ошибка. Напишите текст валентинки повторно',
                show_alert=True)
            cache.delete(key_delayed)
            return

        # если отправилась, то нужно все подчистить
        bot.answer_callback_query(query.id, 'Успешно отправилось!')
        cls.__change_preview_title(bot, preview_data, SENT_TITLE)
        if not preview_data['done']:
            preview_data['done'] = True
            cache.set(cls.__get_key(preview_data['from_uid']),
                      preview_data,
                      time=USER_CACHE_EXPIRE)
        cache.delete(key_delayed)
Пример #28
0
def update_stickers(_: telegram.Bot, update: telegram.Update) -> None:
    """
    Добавление стикера в использованные
    """
    if not update.message.sticker:
        return
    cache_key = f'pipinder:monday_stickersets:{get_current_monday_str()}'
    monday_stickersets = set(cache.get(cache_key, set()))
    monday_stickersets.add(update.message.sticker.set_name)
    cache.set(cache_key, monday_stickersets, time=USER_CACHE_EXPIRE)
Пример #29
0
 def __is_plagiat(text: str) -> bool:
     text = text.strip()
     key = f'{CACHE_PREFIX}__texts'
     texts = cache.get(key)
     texts = set() if not texts else set(texts)
     if text in texts:
         return True
     texts.add(text)
     cache.set(key, texts, time=USER_CACHE_EXPIRE)
     return False
Пример #30
0
 def __edit_preview(cls, bot: telegram.Bot, preview_data) -> None:
     msg = cls.__get_text(preview_data['text'], preview_data['heart_index'])
     TelegramWrapper.edit_message(bot,
                                  preview_data['preview_message_id'],
                                  msg,
                                  chat_id=preview_data['from_uid'],
                                  buttons=cls.__get_buttons())
     cache.set(cls.__get_key(preview_data['from_uid']),
               preview_data,
               time=USER_CACHE_EXPIRE)