Пример #1
0
def reply_help(update: Update, context: CallbackContext):
    query = update.callback_query
    message = update.effective_message

    # Если функция вызвана из CallbackQueryHandler
    if query:
        query.answer()
        query_data = query.data
    else:
        query_data = None

    # По умолчанию, показываем общие команды
    # Если вызвано через CallbackQueryHandler, то проверяем по типу данных
    show_common_help = True
    if query_data:
        show_common_help = bool(PATTERN_HELP_COMMON.search(query_data))

    items_per_page = COMMANDS_PER_PAGE
    page = get_page(context)

    if show_common_help:
        pattern_help = PATTERN_HELP_COMMON
        all_items = COMMON_COMMANDS
        button_help_change_type_page = InlineKeyboardButton(
            '➡️ Команды админа',
            callback_data=fill_string_pattern(PATTERN_HELP_ADMIN, 1))
    else:
        pattern_help = PATTERN_HELP_ADMIN
        all_items = ADMIN_COMMANDS
        button_help_change_type_page = InlineKeyboardButton(
            '⬅️ Общие команды',
            callback_data=fill_string_pattern(PATTERN_HELP_COMMON, 1))

    # Элементы текущей страницы
    items = all_items[(page - 1) * items_per_page:page * items_per_page]

    username = update.effective_user.username
    is_admin = username == ADMIN_USERNAME[1:]
    if is_admin:
        after_inline_buttons = [button_help_change_type_page]
    else:
        after_inline_buttons = None

    text = '\n\n'.join(items)
    if show_common_help:
        text = HELP_TEXT + '\n\n' + text

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text,
        page_count=len(all_items),
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=fill_string_pattern(pattern_help, '{page}'),
        after_inline_buttons=after_inline_buttons,
    )
def on_get_users(update: Update, context: CallbackContext):
    r"""
    Получение пользователей:
     - /get_users
     - get[ _]users
    """

    message = update.effective_message

    query = update.callback_query
    if query:
        query.answer()

    page = get_page(context)
    total_users = db.User.select().count()
    items_per_page = 1

    user = db.User.get_by_page(page=page, items_per_page=items_per_page)[0]
    description = get_user_message_repr(user)
    text = f'Пользователь №{page}:\n{description}'

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text,
        page_count=total_users,
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=fill_string_pattern(PATTERN_GET_USER_BY_PAGE, '{page}'),
    )
def on_get_comics_stats(update: Update, context: CallbackContext):
    message = update.effective_message

    query = update.callback_query
    query.answer()

    quote_query = db.Quote.get_all_with_comics()
    quote_count = quote_query.count()

    year_by_number = {k: 0 for k in db.Quote.get_years()}
    for quote in quote_query.select(db.Quote.date):
        year = quote.date.year
        year_by_number[year] += 1

    text_year_by_counts = "\n".join(f'    <b>{year}</b>: {count}'
                                    for year, count in year_by_number.items())

    text = f'''\
<b>Статистика по комиксам.</b>

Всего <b>{quote_count}</b>:
{text_year_by_counts}
    '''

    reply_markup = InlineKeyboardMarkup.from_button(
        InlineKeyboardButton(
            '⬅️ Назад',
            callback_data=fill_string_pattern(PATTERN_QUERY_QUOTE_STATS)))

    message.edit_text(
        text,
        parse_mode=ParseMode.HTML,
        reply_markup=reply_markup,
    )
def on_settings_filter(update: Update, context: CallbackContext):
    query = update.callback_query
    query.answer()

    settings = SettingState.FILTER
    user = db.User.get_from(update.effective_user)

    # Если значение было передано
    pattern = settings.get_pattern_with_params()
    m = pattern.search(query.data)
    if m:
        limit = int(m.group(1))
        if not limit:
            limit = None

        log.debug(f'    filter_quote_by_max_length_text = {limit}')
        user.set_filter_quote_by_max_length_text(limit)

        # После изменения фильтра нужно перегенерировать кэш
        years_of_quotes = user.get_years_of_quotes()
        update_cache(user, years_of_quotes, limit, log, update, context)
    else:
        limit = user.get_filter_quote_by_max_length_text()

    reply_markup = InlineKeyboardMarkup.from_column([
        # Пусть без ограничений будет 0, чтобы не переделывать логику с числами выше
        InlineKeyboardButton(
            (RADIOBUTTON if not limit else RADIOBUTTON_EMPTY) +
            ' Без ограничений',
            callback_data=fill_string_pattern(pattern, 0)),
        # Возможны будут другие варианты, но пока наличие значения - наличие флага
        InlineKeyboardButton(
            (RADIOBUTTON if limit else RADIOBUTTON_EMPTY) +
            ' Только маленькие',
            callback_data=fill_string_pattern(pattern,
                                              LENGTH_TEXT_OF_SMALL_QUOTE)),
        INLINE_KEYBOARD_BUTTON_BACK,
    ])

    # Fix error: "telegram.error.BadRequest: Message is not modified"
    if is_equal_inline_keyboards(reply_markup, query.message.reply_markup):
        return

    text = settings.description
    query.edit_message_text(text, reply_markup=reply_markup)
def on_get_group_chats_short(update: Update, context: CallbackContext):
    r"""
    Получение групповых чатов (короткая):
     - /get_group_chats_short
     - get group chats short
    """

    message = update.effective_message

    query = update.callback_query
    if query:
        query.answer()

    page = get_page(context)

    # Для получения только групповых чатов
    filters = [db.Chat.type != 'private']

    total_group_chats = db.Chat.select().where(*filters).count()
    items_per_page = ITEMS_PER_PAGE
    start = ((page - 1) * items_per_page) + 1

    chats = db.Chat.get_by_page(
        page=page,
        items_per_page=items_per_page,
        filters=filters,
    )

    items = []
    for i, chat in enumerate(chats, start):
        short_title = chat.get_short_title_for_group()
        short_title = f'{i}. {short_title}'
        items.append(short_title)

    text = f'Чаты ({total_group_chats}):\n' + '\n'.join(items)

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text,
        page_count=total_group_chats,
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=fill_string_pattern(PATTERN_GET_GROUP_CHATS_SHORT_BY_PAGE,
                                         '{page}'),
    )
def on_settings_year(update: Update, context: CallbackContext):
    query = update.callback_query
    query.answer()

    settings = SettingState.YEAR

    user = db.User.get_from(update.effective_user)
    years_of_quotes = user.get_years_of_quotes()

    pattern = settings.get_pattern_with_params()
    m = pattern.search(query.data)
    if m:
        year = int(m.group(1))
        if year not in years_of_quotes:
            return

        years_of_quotes[year] = not years_of_quotes[year]
        log.debug(f'    {year} = {years_of_quotes[year]}')

        filter_quote_by_max_length_text = user.get_filter_quote_by_max_length_text(
        )
        update_cache(user, years_of_quotes, filter_quote_by_max_length_text,
                     log, update, context)

    # Генерация матрицы кнопок
    items = [
        InlineKeyboardButton(
            (CHECKBOX if is_selected else CHECKBOX_EMPTY) + f' {year}',
            callback_data=fill_string_pattern(pattern, year))
        for year, is_selected in years_of_quotes.items()
    ]
    buttons = split_list(items, columns=4)
    buttons.append([INLINE_KEYBOARD_BUTTON_BACK])

    reply_markup = InlineKeyboardMarkup(buttons)

    # Fix error: "telegram.error.BadRequest: Message is not modified"
    if is_equal_inline_keyboards(reply_markup, query.message.reply_markup):
        return

    # Обновление базы данных должно быть в соответствии с тем, что видит пользователь
    user.set_years_of_quotes(years_of_quotes)

    text = settings.description
    query.edit_message_text(text, reply_markup=reply_markup)
def on_get_quote_stats(update: Update, context: CallbackContext):
    """
    Получение статистики по цитатам:
     - /quote_stats
     - quote stats
     - статистика цитат
    """

    message = update.effective_message

    query = update.callback_query
    if query:
        query.answer()

    quote_count = db.Quote.select().count()
    quote_with_comics_count = db.Quote.get_all_with_comics().count()

    text_year_by_counts = "\n".join(
        f'    <b>{year}</b>: {count}'
        for year, count in db.Quote.get_year_by_counts())

    text = f'''\
<b>Статистика по цитатам.</b>

Всего <b>{quote_count}</b>, с комиксами <b>{quote_with_comics_count}</b>:
{text_year_by_counts}
    '''

    reply_markup = InlineKeyboardMarkup.from_button(
        InlineKeyboardButton(
            '➡️ Комиксы',
            callback_data=fill_string_pattern(PATTERN_COMICS_STATS)))

    is_new = not message.edit_date
    if is_new:
        message.reply_html(text, reply_markup=reply_markup)
    else:
        message.edit_text(
            text,
            parse_mode=ParseMode.HTML,
            reply_markup=reply_markup,
        )
def on_get_users_short(update: Update, context: CallbackContext):
    r"""
    Получение пользователей (короткая):
     - /get_users_short
     - get[ _]users[ _]short
    """

    message = update.effective_message

    query = update.callback_query
    if query:
        query.answer()

    page = get_page(context)

    total_users = db.User.select().count()
    items_per_page = ITEMS_PER_PAGE
    start = ((page - 1) * items_per_page) + 1

    users = db.User.get_by_page(page=page, items_per_page=items_per_page)

    items = []
    for i, user in enumerate(users, start):
        short_title = user.get_short_title()
        short_title = f'{i}. {short_title}'
        items.append(short_title)

    text = f'Пользователи ({total_users}):\n' + '\n'.join(items)

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text,
        page_count=total_users,
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=fill_string_pattern(PATTERN_GET_USERS_SHORT_BY_PAGE,
                                         '{page}'),
    )
def on_get_errors_short(update: Update, context: CallbackContext):
    r"""
    Получение ошибок (короткая):
     - /get_errors_short
     - get[ _]errors[ _]short
    """

    message = update.effective_message

    query = update.callback_query
    if query:
        query.answer()

    page = get_page(context)

    total = db.Error.select().count()
    items_per_page = ERRORS_PER_PAGE
    start = ((page - 1) * items_per_page) + 1

    errors = db.Error.get_by_page(page=page, items_per_page=items_per_page)

    items = []
    for i, error in enumerate(errors, start):
        short_title = error.get_short_title()
        short_title = f'{i}. {short_title}'
        items.append(short_title)

    text = 'Ошибки:\n' + '\n'.join(items)

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text,
        page_count=total,
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=fill_string_pattern(PATTERN_GET_ERRORS_SHORT_BY_PAGE,
                                         '{page}'),
    )
def on_get_quote_by_date(update: Update,
                         context: CallbackContext) -> Optional[db.Quote]:
    """
    Получение цитаты по её дате:
     - <день>.<месяц>.<год>, например 13.10.2006
    """

    query = update.callback_query
    message = update.effective_message

    default_page = 1

    # Если функция вызвана из CallbackQueryHandler
    if query:
        query.answer()
        page = int(context.match.group(1))
        date_str = context.match.group(2)
    else:
        page = default_page
        date_str = message.text

    date = DT.datetime.strptime(date_str, db.DATE_FORMAT_QUOTE).date()

    # Показываем по одной цитате
    items_per_page = 1

    items = db.Quote.paginating_by_date(
        page=
        1,  # Всегда страница первая, т.к. значение items_per_page запредельное
        items_per_page=
        999,  # Просто очень большое число, чтобы получить все цитаты за дату
        date=date,
    )
    if not items:
        nearest_date_before, nearest_date_after = db.Quote.get_nearest_dates(
            date)
        buttons = []

        if nearest_date_before:
            date_before_str = nearest_date_before.strftime(
                db.DATE_FORMAT_QUOTE)
            buttons.append(
                InlineKeyboardButton(f'⬅️ {date_before_str}',
                                     callback_data=fill_string_pattern(
                                         PATTERN_PAGE_GET_BY_DATE,
                                         default_page, date_before_str)))

        if nearest_date_after:
            date_after_str = nearest_date_after.strftime(db.DATE_FORMAT_QUOTE)
            buttons.append(
                InlineKeyboardButton(f'➡️ {date_after_str}',
                                     callback_data=fill_string_pattern(
                                         PATTERN_PAGE_GET_BY_DATE,
                                         default_page, date_after_str)))

        text = f'Цитаты за <b>{date_str}</b> не существуют. Как насчет посмотреть за ближайшие даты?'
        reply_markup = InlineKeyboardMarkup.from_row(buttons)

        message.reply_html(
            text,
            reply_markup=reply_markup,
            quote=True,
        )
        return

    quote_obj = items[page - 1]
    text = get_html_message(quote_obj)

    data_pattern = fill_string_pattern(PATTERN_PAGE_GET_BY_DATE, '{page}',
                                       date.strftime(db.DATE_FORMAT_QUOTE))

    reply_text_or_edit_with_keyboard_paginator(
        message,
        query,
        text=text,
        page_count=len(items),
        items_per_page=items_per_page,
        current_page=page,
        data_pattern=data_pattern,
        parse_mode=ParseMode.HTML,
        disable_web_page_preview=True,
        quote=True,
    )

    return quote_obj
def reply_quote_ids(items: List[int], update: Update,
                    context: CallbackContext):
    sep = ', '

    def _get_search_result(items: list) -> str:
        result = sep.join(
            get_deep_linking(quote_id, update) for quote_id in items)
        return f'Найдено {len(items)}:\n{result}'

    def _get_result(items: list, post_fix='...') -> str:
        text = _get_search_result(items)
        if len(text) <= MAX_MESSAGE_LENGTH:
            return text

        # Результат может быть слишком большим, а нужно вместить сообщение в MAX_MESSAGE_LENGTH
        # Поэтому, если при составлении текста результата длина вышла больше нужно уменьшить
        prev = 0
        while True:
            i = text.find(sep, prev + 1)
            if i == -1:
                break

            # Если на этой итерации кусочек текста превысил максимум, значит нужно остановиться,
            # а предыдущий кусочек текста сохранить -- его размер как раз подходит
            if len(text[:i]) + len(post_fix) > MAX_MESSAGE_LENGTH:
                text = text[:prev] + post_fix
                break

            prev = i

        return text

    if items:
        text = _get_result(items)
    else:
        text = 'Не найдено!'

    from_message_id = update.effective_message.message_id

    # Первые 50 результатов
    max_results = 50

    # Результат будет разделен по группам: 1-5, 6-10, ...
    parts = 5

    buttons = []
    for i in range(0, len(items[:max_results]), parts):
        sub_items = items[i:i + parts]
        start = i + 1
        end = i + len(sub_items)
        text_btn = f'{start}' if start == end else f'{start}-{end}'

        data = fill_string_pattern(PATTERN_GET_QUOTES, from_message_id,
                                   ",".join(map(str, sub_items)))

        buttons.append(InlineKeyboardButton(text_btn, callback_data=data))

    buttons = split_list(buttons, columns=5)
    reply_markup = InlineKeyboardMarkup(buttons)

    reply_info(
        text,
        update,
        context,
        parse_mode=ParseMode.MARKDOWN,
        disable_web_page_preview=True,
        quote=True,
        reply_markup=reply_markup,
    )