Beispiel #1
0
 def get_top_pair(uid: int) -> Optional[User]:
     replylove__dragon_lovers = CONFIG.get('replylove__dragon_lovers',
                                           [])
     if uid in replylove__dragon_lovers:
         return User(0, 0, 'drakon', '🐉')
     replylove__ignore = CONFIG.get('replylove__ignore', [])
     replylove__ignore_pairs = CONFIG.get('replylove__ignore_pairs',
                                          {}).get(str(chat_id),
                                                  {}).get(str(uid), [])
     pairs: List[Tuple[str, int]] = sort_dict(db['pair'])
     for pair, count in pairs:
         a_uid, b_uid = [get_int(x) for x in pair.split(',')]
         strast = None
         if a_uid is None or b_uid is None:
             continue
         if count < 5:
             continue
         if uid == a_uid and a_uid == b_uid:
             continue
         if any(x in replylove__dragon_lovers for x in (a_uid, b_uid)):
             continue
         if any(x in replylove__ignore for x in (uid, a_uid, b_uid)):
             continue
         if any(x in replylove__ignore_pairs for x in (a_uid, b_uid)):
             continue
         if uid == a_uid:
             strast = User.get(b_uid)
         if uid == b_uid:
             strast = User.get(a_uid)
         if strast:
             return strast
     return None
Beispiel #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)
Beispiel #3
0
 def get_top(type: str, uid: int) -> Optional[User]:
     if type not in db:
         return None
     if uid not in db[type]:
         return None
     replylove__ignore = CONFIG.get('replylove__ignore', [])
     if uid in replylove__ignore:
         return None
     replylove__dragon_lovers = CONFIG.get('replylove__dragon_lovers',
                                           [])
     if uid in replylove__dragon_lovers:
         return User(0, 0, 'drakon', '🐉')
     sorted: List[Tuple[int, int]] = sort_dict(db[type][uid])
     if len(sorted) == 0:
         return None
     replylove__ignore_pairs = CONFIG.get('replylove__ignore_pairs',
                                          {}).get(str(chat_id),
                                                  {}).get(str(uid), [])
     for result_uid, count in sorted:
         if count < 5:
             continue
         if uid == result_uid:
             continue
         if result_uid in replylove__dragon_lovers:
             continue
         if result_uid in replylove__ignore:
             continue
         if result_uid in replylove__ignore_pairs:
             continue
         return User.get(result_uid)
     return None
Beispiel #4
0
    def check_for_choosing_key(self, button: Button, label: str):
        if button.state == ButtonState.RELEASED:
            in_key_choosing = True

            while in_key_choosing:
                for event in pygame.event.get():
                    if event.type != pygame.KEYDOWN:
                        continue                        
                    if event.key == pygame.locals.K_ESCAPE:
                        in_key_choosing = False
                        break

                    button.set_label(pygame.key.name(KEYBOARD[event.key]), CONFIG.readable_font)
                    in_key_choosing = False

                    # Change key in configuration, too
                    if label == 'Up':
                        CONFIG.KEY_UP = KEYBOARD[event.key]
                    elif label == 'Down':
                        CONFIG.KEY_DOWN = KEYBOARD[event.key]
                    elif label == 'Right':
                        CONFIG.KEY_RIGHT = KEYBOARD[event.key]
                    elif label == 'Left':
                        CONFIG.KEY_LEFT = KEYBOARD[event.key]
                    CONFIG.save()
                    break
Beispiel #5
0
def set_default_logging_format():
    default_format = '[%(asctime)s][%(levelname)s][%(name)s] - %(message)s'
    logging.basicConfig(format=CONFIG.get('logging',
                                          {}).get('format', default_format),
                        level=logging.getLevelName(
                            CONFIG.get('logging', {}).get('level',
                                                          'INFO').upper()),
                        filename=CONFIG.get('logging', {}).get('file', None))
Beispiel #6
0
def forward_to_channel(bot: telegram.Bot, chat_id: int, message_id: int, user_id: int) -> None:
    """
    Форвардит сообщение в канал музкружка
    """
    channel_id = CONFIG.get('muzkruzhok_channel_id', None)
    if channel_id is None:
        return
    if chat_id in CONFIG.get('muzkruzhok_ban_chats', []):
        return
    bans = CONFIG.get('muzkruzhok_ban_ids', [])
    if user_id in bans:
        return
    bot.forward_message(channel_id, chat_id, message_id)
Beispiel #7
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")
Beispiel #8
0
def request_wu(city_code: str):
    """
    Получает у апи погоду по указанному городу через WU.

    :param str city_code: код города в формате 'Country/City'
    """
    api_key = CONFIG.get('weather_wunderground_api_key')
    if not api_key:
        return
    features = 'conditions/astronomy/forecast/hourly/almanac'
    url_template = 'http://api.wunderground.com/api/{}/{}/lang:RU/q/{}.json'
    url = url_template.format(api_key, features, city_code.replace(' ', '%20'))

    response = requests.get(url)
    FileUtils.dump_tmp_city(
        'wu', city_code, response.text)  # сохраняем ответ во временную папку

    # если в ответе ошибка
    if response.status_code != requests.codes.ok:
        return {
            'error': True,
            'error_msg':
            "Ошибка какая-то:\n\n{}".format(str(response.status_code))
        }

    # если все ок
    return {'error': False, 'json': response.json()}
Beispiel #9
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 'Никто'
Beispiel #10
0
def request(city_code: str):
    """
    Получает у апи погоду по указанному городу

    :param str city_code: gps координаты города (55.7507,37.6177)
    """
    api_key = CONFIG.get('weather_yandex_api_key')
    if not api_key:
        return
    # url = f'https://api.darksky.net/forecast/{api_key}/{city_code}?lang=ru&units=si&exclude=minutely,alerts,flags'
    lat, lon = city_code.split(',')
    url = f'https://api.weather.yandex.ru/v1/informers?lang=ru_RU&lat={lat}&lon={lon}'
    headers = {'X-Yandex-API-Key': api_key}

    response = requests.get(url, headers=headers)
    FileUtils.dump_tmp_city(
        'ya', city_code, response.text)  # сохраняем ответ во временную папку

    # если в ответе ошибка
    if response.status_code != requests.codes.ok:
        return {
            'error': True,
            'error_msg': f"Ошибка какая-то:\n\n{str(response.status_code)}"
        }

    # если все ок
    return {'error': False, 'json': response.json()}
Beispiel #11
0
def run_weekly_stats(bot: telegram.Bot, update: telegram.Update) -> None:
    uid = update.message.chat_id
    logger.info(f'id {uid} /weekly_stats')
    if uid != CONFIG.get('debug_uid', None):
        return

    from src.modules.weeklystat import weekly_stats
    weekly_stats(bot, None)
 def is_today_ending() -> bool:
     """
     Сегодня 15-е фев?
     """
     # TODO: убрать
     if CONFIG.get('feb14_debug_end', False):
         return True
     return datetime.today().strftime("%m-%d") == '02-15'
 def is_day_active() -> bool:
     """
     Сегодня 14-е фев?
     """
     # TODO: убрать
     if CONFIG.get('feb14_debug_begin', False):
         return True
     return datetime.today().strftime(
         "%m-%d") == '02-14'  # месяц-день. Первое января будет: 01-01
Beispiel #14
0
def send_to_all_chats_handler(bot: telegram.Bot, update: telegram.Update) -> None:
    uid = update.message.chat_id
    logger.info(f'id {uid} /send_to_all_chats')
    if uid != CONFIG.get('debug_uid', None):
        return

    text = f"""

""".strip()
Beispiel #15
0
def changelog(bot: telegram.Bot, update):
    text = CONFIG.get('changelog', '')
    if len(text) == 0:
        return
    chat_id = update.message.chat_id
    bot.send_message(chat_id,
                     text,
                     parse_mode=telegram.ParseMode.HTML,
                     disable_web_page_preview=True)
Beispiel #16
0
def can_use_repinder(_: telegram.Bot, __: int, user_id: int) -> bool:
    """
    Этот пользователь может использовать репиндер?
    """
    if user_id in CONFIG.get('repinder_users', []):
        return True
    # if check_admin(bot, chat_id, user_id):
    #     return True
    return False
Beispiel #17
0
    def get_all_love(cls,
                     chat_id: int,
                     date=None,
                     header='Вся страсть') -> str:
        def get_no_love_str(no_love_: List) -> str:
            length = len(no_love_)
            if length == 0:
                return ''
            if length <= 10:
                return '\n\nБеcстрастные:\n' + '\n'.join(
                    (cls.__format_pair(a) for a in no_love_))
            return f'\n\nИ еще {pytils.numeral.get_plural(length, "беcстрастный, беcстрастных, беcстрастных")}'

        def get_narcissist(narcissist_: List[User]) -> str:
            if len(narcissist_) == 0:
                return ''
            return f'\n\nНарциссы:\n' + '\n'.join(
                (cls.__format_pair(a) for a in narcissist_))

        all_chat_users = ChatUser.get_all(chat_id)
        all_users = (User.get(chatuser.uid) for chatuser in all_chat_users)
        all_users = sorted(all_users, key=lambda x: x.fullname)
        all_love = [(user,
                     ReplyTop.get_user_top_strast(chat_id, user.uid, date)[0])
                    for user in all_users if user]

        in_love = [(a, b, ReplyTop.get_user_top_strast(chat_id, b.uid,
                                                       date)[0])
                   for a, b in all_love if b]
        narcissist = [
            a for a, _ in all_love
            if a.uid in CONFIG.get('replylove__narcissist', [])
        ]
        no_love = [
            a for a, b in all_love
            if not b and a.uid not in CONFIG.get('replylove__narcissist', [])
        ]

        in_love_str = '\n'.join(
            cls.__format_pair(a, b, b_pair) for a, b, b_pair in in_love)
        no_love_str = get_no_love_str(no_love)
        narcissist_str = get_narcissist(narcissist)

        return f'{header}:\n\n{in_love_str}{narcissist_str}{no_love_str}'
Beispiel #18
0
 def __format_pair(cls,
                   a: User,
                   b: Optional[User] = None,
                   b_pair: Optional[User] = None) -> str:
     if not b:
         return f'<b>{cls.get_fullname_or_username(a)}</b>'
     if a.uid in CONFIG.get('replylove__dragon_lovers', []):
         return f'<b>{cls.get_fullname_or_username(a)}</b> ⟷ 🐉'
     love = ' ❤' if b_pair and b_pair.uid == a.uid else ''
     return f'<b>{cls.get_fullname_or_username(a)}</b> ⟷ {cls.get_fullname_or_username(b)}{love}'
Beispiel #19
0
def prepare():
    """
    Подготовительный этап
    """
    set_default_logging_format()
    if 'google_vision_client_json_file' in CONFIG:
        config.google_vision_client = auth_google_vision(
            CONFIG['google_vision_client_json_file'])
    cache.set('pipinder:fav_stickersets_names',
              set(CONFIG.get("sasha_rebinder_stickersets_names", [])),
              time=YEAR)
Beispiel #20
0
def year(bot: telegram.Bot, update: telegram.Update) -> None:
    uid = update.message.chat_id
    logger.info(f'id {uid} /year')
    if uid != CONFIG.get('debug_uid', None):
        return

    from src.models.user_stat import UserStat
    from src.modules.weeklystat import send_long
    bot.send_chat_action(uid, telegram.chataction.ChatAction.TYPING)

    cid = CONFIG.get('anon_chat_id')
    # cid = -48952907
    year = 2017
    info = UserStat.get_chat_year(cid, year)

    msg = f'<b>Rapture {year}</b>\n' \
          f'Нас: {info["users_count"]}\n' \
          f'Сообщений: {info["msg_count"]}\n'
    msg += '\n'
    msg += info['top_chart'].replace('<b>', '').replace('</b>', '')
    send_long(bot, CONFIG.get('anon_chat_id'), msg)
Beispiel #21
0
def send_mylove(bot: telegram.Bot, update: telegram.Update, send_to_cid: int,
                find_in_cid: int) -> None:
    def format_love(type: str, b: User, _: bool) -> typing.Optional[str]:
        if not b:
            return None
        b_pair, b_inbound, b_outbound = ReplyTop.get_user_top_strast(find_in_cid, b.uid)

        mutual_sign = ' ❤'
        if type == 'pair' and b_pair:
            mutual = mutual_sign if b_pair.uid == user_id else ''
            return f'Парная: {ReplyLove.get_fullname_or_username(b)}{mutual}'
        if type == 'inbound' and b_inbound:
            mutual = mutual_sign if b_inbound and b_inbound.uid == user_id else ''
            return f'Входящая: {ReplyLove.get_fullname_or_username(b)}{mutual}'
        if type == 'outbound' and b_outbound:
            mutual = mutual_sign if b_outbound and b_outbound.uid == user_id else ''
            return f'Исходящая: {ReplyLove.get_fullname_or_username(b)}{mutual}'
        return None

    bot.sendChatAction(send_to_cid, ChatAction.TYPING)

    reply_to_msg = update.message.reply_to_message
    if reply_to_msg:
        user_id = reply_to_msg.from_user.id
    else:
        splitted = update.message.text.split()
        if len(splitted) == 2:
            user_id = User.get_id_by_name(splitted[1])
        else:
            user_id = update.message.from_user.id
    user = User.get(user_id)
    if not user:
        bot.send_message(send_to_cid, 'А кто это? Таких не знаю.',
                         reply_to_message_id=update.message.message_id)
        return

    pair, inbound, outbound = ReplyTop.get_user_top_strast(find_in_cid, user_id)

    formats = (format_love('pair', pair, user.female), format_love('inbound', inbound, user.female),
               format_love('outbound', outbound, user.female))
    love_list = [s for s in formats if s]
    if len(love_list) == 0:
        result = '🤷‍♀️🤷‍♂️ А нет никакой страсти'
    else:
        result = '\n'.join(love_list)

    if user_id in CONFIG.get('replylove__dragon_lovers', []):
        result = '🐉'

    bot.send_message(send_to_cid, f'Страсть {user.get_username_or_link()}:\n\n{result}',
                     reply_to_message_id=update.message.message_id, parse_mode=ParseMode.HTML)
def send_gdeleha(bot, chat_id, msg_id, user_id):
    if user_id in CONFIG.get('leha_ids', []) or user_id in CONFIG.get(
            'leha_anya', []):
        bot.sendMessage(chat_id, "Леха — это ты!", reply_to_message_id=msg_id)
        return
    send_random_sticker(bot, chat_id, [
        'BQADAgADXgIAAolibATmbw713OR4OAI',
        'BQADAgADYwIAAolibATGN2HOX9g1wgI',
        'BQADAgADZQIAAolibAS0oUsHQK3DeQI',
        'BQADAgADdAIAAolibATvy9YzL3EJ_AI',
        'BQADAgADcwIAAolibATLRcR2Y1U5LQI',
        'BQADAgADdgIAAolibAQD0bDVAip6bwI',
        'BQADAgADeAIAAolibAT4u54Y18S13gI',
        'BQADAgADfQIAAolibAQzRBdOwpQL_gI',
        'BQADAgADfgIAAolibASJFncLc9lxdgI',
        'BQADAgADfwIAAolibATLieQe0J2MxwI',
        'BQADAgADgAIAAolibATcQ-VMJoDQ-QI',
        'BQADAgADggIAAolibAR_Wqvo57gCPwI',
        'BQADAgADhAIAAolibATcTIr_YdowgwI',
        'BQADAgADiAIAAolibARZHNSejUISQAI',
        'BQADAgADigIAAolibAS_n7DVTejNhAI',
        'BQADAgADnQIAAolibAQE8V7GaofXLgI',
    ])
Beispiel #23
0
def anon(bot: telegram.Bot, update: telegram.Update) -> None:
    if not CONFIG.get('anon', False):
        return
    text = update.message.text
    if not text:
        return
    uid = update.message.from_user.id
    cid = CONFIG['anon_chat_id']

    text = re.sub(r"\s*/\w+", '', text)
    text = text.strip()
    if len(text) == 0:
        return
    logger.info(f'[anon] from {uid}. text: {text}')
    bot.send_message(cid, text, disable_web_page_preview=True)
Beispiel #24
0
 def cmd_mats(cls, bot: telegram.Bot, update: telegram.Update) -> None:
     uid = update.message.from_user.id
     # только админ бота может использовать команду
     if uid != CONFIG.get('debug_uid', None):
         return
     # получаем параметры команды (текст после "/mats ")
     text = update.message.text.partition(' ')[2].strip()
     if not text:
         return
     # получаем мат
     mat_words = list(word.lower() for word in Antimat.bad_words(text))
     if len(mat_words) == 0:
         return
     # отправляем мат
     matshowtime.send(bot, mat_words)
Beispiel #25
0
 def get_user_chats(cls, uid: int, cids: typing.Optional[typing.List[int]] = None) -> \
 typing.List[int]:
     config_cids = cids if cids else [int(c) for c in CONFIG.get('chats', [])]
     try:
         with session_scope() as db:
             # noinspection PyUnresolvedReferences
             user_in_chats = db.query(ChatUserDB) \
                 .filter(ChatUserDB.cid.in_(config_cids)) \
                 .filter(ChatUserDB.uid == uid) \
                 .filter(ChatUserDB.left == 0) \
                 .all()
             return [chatuser.cid for chatuser in user_in_chats]
     except Exception as e:
         logger.error(e)
         raise Exception(f"Can't get user chats {uid} from DB")
Beispiel #26
0
 def __ignore_pairs(chat_id, pairs):
     copy = pairs.copy()
     replylove__ignore_pairs = CONFIG.get('replylove__ignore_pairs',
                                          {}).get(str(chat_id), {})
     for uid_str, ignore_uids in replylove__ignore_pairs.items():
         str_uids = [str(uid) for uid in ignore_uids]
         for pair in pairs.keys():
             pair_uids = pair.split(',')
             if uid_str not in pair_uids:
                 continue
             pair_uids.remove(uid_str)
             b = pair_uids[0]
             if b in str_uids:
                 copy.pop(pair, None)
     return copy
Beispiel #27
0
 def get_stats(cls, cid, date=None):
     monday = get_current_monday() if date is None else get_date_monday(
         date)
     db = cls.db_helper.get_db(monday, cid)
     ignore = CONFIG.get('replylove__ignore', [])
     return {
         'to':
         sort_dict(cls.__remove_uids(db['to'], ignore))[:3],
         'from':
         sort_dict(cls.__remove_uids(db['from'], ignore))[:3],
         'pair':
         sort_dict(
             cls.__ignore_pairs(cid, cls.__remove_uids(db['pair'],
                                                       ignore)))[:10]
     }
Beispiel #28
0
def send_end(bot: telegram.Bot) -> None:
    """
    Отправка во все чаты подводящих итог сообщений
    """
    def _get_text(chat_id: int) -> str:
        stats_text = StatsHumanReporter(stats).get_text(chat_id)
        return f'Отгремело шампанское, отзвенели бубенцы. Замолили ли мы нашего двукратного дракона великого зеленокожесластного? Такс:\n\n{stats_text}'

    with StatsRedis() as stats:
        # отправка админу статы по всем чатам
        admin_key = f'{CACHE_PREFIX}:end:admin'
        if not cache.get(admin_key, False):
            text = StatsHumanReporter(stats).get_text(None)
            dsp(send_html, bot, CONFIG.get('debug_uid', None), text)
            cache.set(admin_key, True, time=TWO_DAYS)

        send_to_all_chats(bot, 'end', _get_text)
Beispiel #29
0
def repair_bot(logger=None):
    """
    Запускаем скрипт перезапуска бота
    """
    import time as time_time
    import sys
    time_time.sleep(5)
    if logger:
        logger.critical('Need bot repair')
        logger.critical('--')
    else:
        print('[CRITICAL] Need bot repair', file=sys.stderr)
    time_time.sleep(1)
    if 'pm2_bot_repair' in CONFIG:
        import subprocess
        # запускам скрипт перезапуска бота
        subprocess.Popen([CONFIG.get('pm2_bot_repair')], shell=True)
    exit()
Beispiel #30
0
 def callback_handler(cls, bot: telegram.Bot, _, query: telegram.CallbackQuery, data) -> None:
     uid = query.from_user.id
     cid = query.message.chat_id
     button_msg_id = query.message.message_id
     if uid == CONFIG.get('debug_uid', None):
         try:
             reply_msg_id = None
             if query.message.reply_to_message:
                 reply_msg_id = query.message.reply_to_message.message_id
             bot.forward_message(uid, cid, message_id=reply_msg_id)
         except Exception:
             pass
     if 'type' not in data or data['type'] == Photo.data_type:
         Photo.callback_handler(bot, uid, cid, button_msg_id, data, query)
         return
     elif data['type'] == URL.data_type:
         URL.callback_handler(bot, uid, cid, button_msg_id, data, query)
         return
     bot.answer_callback_query(query.id)