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, )