예제 #1
0
 def execute(self, bot: telegram.Bot):
     user = User.get(self.uid)
     old_text = cache.get(
         f'{CACHE_PREFIX}__message_text_{self.message_id}')
     if old_text:
         new_text = re.sub(r"^Подписано\s+[█ ]+$",
                           f'Подписано {user.fullname}', old_text, 0,
                           re.IGNORECASE | re.MULTILINE)
         buttons = cache.get(
             f'{CACHE_PREFIX}__message_buttons_{self.message_id}')
         reply_markup = self.get_reply_markup(buttons)
         female = 'а' if user.female else ''
         bot.send_message(
             FSBDayTelegram.chat_id,
             f'Какой ужас. Это был{female} {user.get_username_or_link()}',
             reply_to_message_id=self.message_id,
             parse_mode=telegram.ParseMode.HTML)
         return bot.edit_message_text(
             new_text,
             FSBDayTelegram.chat_id,
             self.message_id,
             parse_mode=telegram.ParseMode.HTML,
             reply_markup=reply_markup)
     bot.send_message(
         FSBDayTelegram.chat_id,
         f'Не могу исправить само сообщение. Но оно подписано {user.get_username_or_link()}',
         reply_to_message_id=self.message_id,
         parse_mode=telegram.ParseMode.HTML)
예제 #2
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)
예제 #3
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)
예제 #4
0
def check_command_is_off(chat_id, cmd_name):
    """
    Проверяет, отключена ли эта команда в чате.
    """
    all_disabled = cache.get(f'all_cmd_disabled:{chat_id}')
    if all_disabled:
        return True

    disabled = cache.get(f'cmd_disabled:{chat_id}:{cmd_name}')
    if disabled:
        return True
    return False
예제 #5
0
 def __super_stukach_alert(
         cls,
         uid: int) -> typing.Union[None, FSBDayTelegram.TelegramExecute]:
     recent_stucks = cache.get(f'{CACHE_PREFIX}__{uid}_recent_stucks')
     key_super_stukach_alert = f'{CACHE_PREFIX}__super_stukach_alert'
     if recent_stucks and recent_stucks >= 3 and not cache.get(
             key_super_stukach_alert):
         user = User.get(uid)
         cache.set(key_super_stukach_alert, True, time=30 * 60)
         return FSBDayTelegram.SendToChat(
             f'{user.get_username_or_link()} стучите помедленнее. Я не успеваю записывать.'
         )
     return None
예제 #6
0
def draft_chat_button_click_handler(bot: telegram.Bot, _: telegram.Update,
                                    query: telegram.CallbackQuery,
                                    data) -> None:
    """
    Обработчик кнопки выбора чата
    """
    user_id = query.from_user.id

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

    key_delayed = f'{CACHE_PREFIX}:delayed:{user_id}'
    if cache.get(key_delayed):
        query.answer(text='Отправляй раз в минуту. Жди 👆')
        return
    cache.set(key_delayed, True, time=60)

    chat_id: int = data['chat_id']
    card = draft.select_chat(chat_id)

    card_in_chat_msg = bot.send_message(chat_id,
                                        card.get_message_text(),
                                        reply_markup=get_reply_markup(
                                            card.get_message_buttons()),
                                        parse_mode=HTML,
                                        disable_web_page_preview=True)
    card.message_id = card_in_chat_msg.message_id

    with StatsRedis.lock:
        with StatsRedis() as stats:
            stats.add_card(card)

    query.message.delete()

    status_message: telegram.Message = bot.send_message(
        user_id,
        'Валентинка отправлена!',
        reply_to_message_id=draft.original_draft_message_id)
    card.original_draft_message_id = draft.original_draft_message_id
    card.status_message_id = status_message.message_id
    cache.set(f'{CACHE_PREFIX}:card:{chat_id}:{card.message_id}',
              card,
              time=TWO_DAYS)

    clear_random_hearts(user_id)
    query.answer()
예제 #7
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)
예제 #8
0
def callback_last_word(bot: telegram.Bot, _: telegram.Update, query, data):
    uid = query.from_user.id
    cid = query.message.chat_id
    msg_ids = [
        result[0] for result in (cache.get(get_last_word_cache_key(cid, _uid))
                                 for _uid in data['leaves_uid'])
        if result is not None and isinstance(result, tuple)
    ]
    if len(msg_ids) == 0:
        try:
            bot.sendMessage(
                uid,
                'Увы, у меня не сохранились последние слова этих человеков 😢')
        except Exception:
            pass
        return

    try:
        bot.sendMessage(uid, 'Последние слова убывших:')
    except Exception:
        pass
    for msg_id in msg_ids:
        try:
            bot.forwardMessage(uid, cid, message_id=msg_id)
        except Exception:
            pass
예제 #9
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
예제 #10
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()
예제 #11
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 'Никто'
예제 #12
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)
예제 #13
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
예제 #14
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)
예제 #15
0
def callback_handler(bot: telegram.Bot, update: telegram.Update) -> None:
    query = update.callback_query
    data = cache.get(f'callback:{query.data}')
    if not data:
        return
    if data['name'] == '/off':
        bot.answerCallbackQuery(query.id)
        callback_off(bot, update, query, data)
        return
    if data['name'] == 'last_word':
        bot.answerCallbackQuery(query.id,
                                url=f"t.me/{bot.username}?start={query.data}")
        callback_last_word(bot, update, query, data)
        return
    if data['name'] == 'dayof':
        DayOfManager.callback_handler(bot, update, query, data)
        return
    if data['name'] == 'bayanometer_show_orig':
        Bayanometer.callback_handler(bot, update, query, data)
        return
    if data['name'] == 'spoiler':
        SpoilerHandlers.callback_handler(bot, update, query, data)
        return
    if data['name'] == 'matshowtime':
        MatshowtimeHandlers.callback_handler(bot, update, query, data)
        return
    if data['name'] == 'i_stat':
        istat_callback_handler(bot, update, query, data)
        return
예제 #16
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")
예제 #17
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
예제 #18
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)
예제 #19
0
 def __get_count(key: str, plural_forms: str) -> typing.Union[None, str]:
     count = cache.get(key)
     if not count:
         count = 0
     if isinstance(count, (set, list, dict)):
         count = len(count)
     return get_plural(count, plural_forms)
예제 #20
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
예제 #21
0
    def on_show_click(cls, bot: telegram.Bot, _: telegram.Update,
                      query: telegram.CallbackQuery, data) -> None:
        spoiler: Spoiler = cache.get(cls.__get_key(data['spoiler_id']))
        if not spoiler:
            bot.answer_callback_query(
                query.id,
                f"Ошибка. Не могу найти спойлер {data['spoiler_id']}",
                show_alert=True)
            return

        uid = query.from_user.id
        if len(spoiler.body) <= 200:
            bot.answer_callback_query(query.id, spoiler.body, show_alert=True)
            logger.info(
                f'[spoiler] {uid} show popup spoiler {spoiler.spoiler_id}')
            return

        bot.answerCallbackQuery(query.id,
                                url=f"t.me/{bot.username}?start={query.data}")
        if not spoiler.show(bot, uid):
            cls.__cant_send(bot, query.message.chat_id, uid,
                            spoiler.spoiler_id)
            return
        logger.info(
            f'[spoiler] {uid} show private spoiler {spoiler.spoiler_id}')
예제 #22
0
 def __get_engage_users_count(cls):
     users = cache.get(cls.key_engage_users)
     if not users:
         return None
     return get_plural(
         len(users),
         'человек принял участие, человека приняло участие, человек приняло участие'
     )
예제 #23
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
예제 #24
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)
예제 #25
0
 def get_user_domain_count(cls, monday, uid, cid, domain):
     user_domains = cache.get(
         cls.__get_user_domain_cache_key(monday, uid, cid))
     if user_domains is None:
         return 0
     if domain in user_domains:
         return user_domains[domain]
     return 0
예제 #26
0
def check_user_is_plohish(update):
    chat_id = update.message.chat_id
    user_id = update.message.from_user.id
    cmd_name = get_command_name(update.message.text)
    disabled = cache.get(f'plohish_cmd:{chat_id}:{user_id}:{cmd_name}')
    if disabled:
        return True
    return False
예제 #27
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))
예제 #28
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
예제 #29
0
def startup_time(bot: telegram.Bot, update: telegram.Update) -> None:
    uid = update.message.chat_id
    logger.info(f'id {uid} /startup_time')
    cached = cache.get('bot_startup_time')
    if cached:
        bot.send_message(uid, cached.strftime('%Y-%m-%d %H:%M'))
        return
    bot.send_message(uid, 'В кеше ничего нет (но должно быть)')
예제 #30
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)