[Python] Ajuda com Telebot em Python - paginação com condição [fechada]

Discussão em 'Python' iniciado por Stack, Novembro 9, 2024.

  1. Stack

    Stack Membro Participativo

    Sou novata na área de programação e estou montando um telebot que se baseia no sistema gacha. Existe um comando nele chamado /matilha que mostra uma subcategoria em específico com dados de um banco sqlite. Estou tentando incluir paginação pra subcategorias com mais de 20 cards listados, mas não está dando certo.

    Como faço para montar a paginação com a condição de +20 cards listados por subcategoria e como deixo o código mais condensado? Porque como as funcionalidades foram sendo acrescentadas aos poucos, ele tá todo remendado/redundante e não estou gostando nem um pouco mas não tenho conhecimento pra arrumar sem acabar perdendo uma funcionalidade ou outra.

    # ----------------------------------------------- COMANDO MATILHA ---------------------------------------------
    #dicionario global
    page_data = {}
    user_selections = {}
    filtros_ativos = {}
    estados_filtros = {}

    #diminuição da sensibilidade da busca
    subcategoria_associacoes = {
    'Stray Kids': ['stray kids', 'skz', '143'],
    '(G)I-DLE': ['gidle', '2018'],
    'O Castelo Animado': ['castelo', 'castelo animado', 'o castelo animado', '24'],
    "That '70s Show": ['70s', "70's", 'that 70s show', "that 70's show", '70'],
    'Princesa Mononoke': ['mononoke', 'princesa mononoke', '25'],
    'Jujutsu Kaisen': ['jjk', 'jujutsu', 'jujutsu kaisen', '26'],
    'TOMORROW X TOGETHER': ['TXT', 'txt', '101'],
    'The Rose': ['The Rose', 'the rose', 'tr', '303'],
    'Pentagon': ['Pentagon', 'pentagon', 'ptg', '305'],
    'Monsta X': ['mx', '707'],
    'Twice': ['TWICE', 'twice', '212'],
    'Artistas Pop': ['pop', 'artistas pop', '10', 'apop'],
    'WayV': ['wayv', '211'],
    'A.C.E': ['ace', 'a.c.e', '304']

    }

    #buscar subcategoria na tabela acima
    def buscar_subcategoria(nome):
    nome = nome.lower()
    for subcategoria, alias in subcategoria_associacoes.items():
    if nome in alias or nome == subcategoria.lower():
    return subcategoria
    return None

    #construção de texto com cards filtrados
    def construir_texto(cards):
    if not cards:
    return "Nenhum card encontrado com os filtros aplicados."

    lista_cards = []
    for card_id, nome, raridade, quantidade_possuida in cards:
    icon = '' if raridade == 'Ouro' else '' if raridade == 'Prata' else ''
    quantidade_texto = f' ({quantidade_possuida}x)' if quantidade_possuida > 0 else ' '
    lista_cards.append(f"{icon} {card_id}. {nome}{quantidade_texto}")
    return "\n".join(lista_cards)

    #atualizar mensagem matilha (com paginação)
    def atualizar_mensagem_matilha(message, user_id, subcategoria_id, subcategoria_nome, categoria_nome, subcategoria_imagem, cards, quantidade_total, mostrar_todos=False, pagina_atual=1, limite_por_pagina=20):
    try:
    #filtra os cards
    lista_filtrada = aplicar_filtros(cards, user_id, mostrar_todos)

    #calcula a paginação
    total_cards = len(lista_filtrada)
    ultima_pagina = (total_cards + limite_por_pagina - 1) // limite_por_pagina # Calcula o número de páginas
    inicio = (pagina_atual - 1) * limite_por_pagina
    fim = inicio + limite_por_pagina
    cards_paginados = lista_filtrada[inicio:fim]

    #construção de texto da pagina atual
    texto_atualizado = construir_texto(cards_paginados)
    markup = gerar_markup_filtros(subcategoria_id, user_id, pagina_atual, ultima_pagina)

    #legenda
    caption = (
    f"A matilha ouviu seu chamado e se fez presente!\n\n"
    f"{subcategoria_id}. {subcategoria_nome} — {categoria_nome}\n"
    f"{total_cards} lobos disponíveis, {quantidade_total} em sua matilha.\n"
    f"Página {pagina_atual}/{ultima_pagina}\n\n"
    f"{texto_atualizado}\n\n"
    f"| Para visualizar o card individualmente, utilize o comando /wolf [id]."
    )

    #edita a mensagem para mostrar os filtros aplicados/pagina passada
    if 'message_id' in page_data.get(user_id, {}):
    bot.edit_message_caption(
    chat_id=message.chat.id,
    message_id=page_data[user_id]['message_id'],
    caption=caption,
    reply_markup=markup
    )
    else:
    with open(subcategoria_imagem, 'rb') as photo:
    sent_message = bot.send_photo(
    chat_id=message.chat.id,
    photo=photo,
    caption=caption,
    reply_markup=markup
    )
    page_data[user_id] = {'message_id': sent_message.message_id}

    except Exception as e:
    print(f"Erro ao atualizar a mensagem da matilha: {e}")


    #aplicar filtros aos cards
    def aplicar_filtros(cards, user_id, mostrar_todos):
    if mostrar_todos:
    return cards

    filtros = filtros_ativos.get(user_id, {})

    #filtra os cards a partir do filtro ativo
    cards_filtrados = []
    for card in cards:
    card_id, nome, raridade, quantidade_possuida = card
    possui = quantidade_possuida > 0

    if (filtros.get('possuo') and not possui) or (filtros.get('naopossuo') and possui):
    continue #ignora o card se não corresponde ao filtro possuo

    if filtros.get('ouro') and raridade != 'Ouro':
    continue #ignora o card se não for ouro quando o filtro ouro tá ativo
    if filtros.get('prata') and raridade != 'Prata':
    continue #ignora o card se não for prata quando o filtro prata tá ativo
    if filtros.get('bronze') and raridade != 'Bronze':
    continue #ignora o card se não for bronze quando o filtro bronze tá ativo

    #filtra o card que passou no filtro
    cards_filtrados.append(card)

    return cards_filtrados


    #teclado de filtros e botao de paginação
    def gerar_markup_filtros(subcategoria_id, user_id, pagina_atual, ultima_pagina):
    markup = InlineKeyboardMarkup(row_width=5)

    #botoes de filtros
    markup.add(
    InlineKeyboardButton("", callback_data=f"filtro_possuo_{subcategoria_id}_{user_id}"),
    InlineKeyboardButton("", callback_data=f"filtro_naopossuo_{subcategoria_id}_{user_id}"),
    InlineKeyboardButton("", callback_data=f"filtro_ouro_{subcategoria_id}_{user_id}"),
    InlineKeyboardButton("", callback_data=f"filtro_prata_{subcategoria_id}_{user_id}"),
    InlineKeyboardButton("", callback_data=f"filtro_bronze_{subcategoria_id}_{user_id}")
    )

    #paginacao
    botoes_paginacao = []
    if pagina_atual > 1:
    botoes_paginacao.append(InlineKeyboardButton("⬅️ Anterior", callback_data=f"pagina_{pagina_atual - 1}_{subcategoria_id}_{user_id}"))
    if pagina_atual < ultima_pagina:
    botoes_paginacao.append(InlineKeyboardButton("Próximo ➡️", callback_data=f"pagina_{pagina_atual + 1}_{subcategoria_id}_{user_id}"))
    if botoes_paginacao:
    markup.add(*botoes_paginacao)

    return markup


    #recuperar cards com filtros aplicados
    def recuperar_cards(subcategoria_id, filtro, estado_filtro, user_id, limite=20):
    query = '''
    SELECT cards.card_id, cards.nome, cards.raridade,
    COALESCE(inventario.quantidade, 0) AS quantidade_possuida
    FROM cards
    LEFT JOIN inventario ON cards.card_id = inventario.card_id AND inventario.user_id = ?
    WHERE cards.subcategoria_id = ?
    '''
    parametros = [user_id, subcategoria_id]

    if filtro == "possuo" and estado_filtro:
    query += " AND inventario.quantidade > 0"
    elif filtro == "naopossuo" and estado_filtro: # Alterado para naopossuo
    query += " AND (inventario.quantidade IS NULL OR inventario.quantidade = 0)"
    elif filtro in ["ouro", "prata", "bronze"] and estado_filtro:
    query += " AND cards.raridade = ?"
    parametros.append(filtro.capitalize())

    query += '''
    ORDER BY
    CASE cards.raridade
    WHEN 'Ouro' THEN 1
    WHEN 'Prata' THEN 2
    WHEN 'Bronze' THEN 3
    END,
    cards.card_id ASC
    LIMIT ?
    '''
    parametros.append(limite)

    cursor.execute(query, parametros)
    return cursor.fetchall()

    #extrair o subcategoria_id do callback data
    def obter_subcategoria_id_do_callback(callback_data):
    partes = callback_data.split('_')
    if len(partes) >= 3:
    return partes[2]
    return None

    #processar filtros em uma thread separada
    def processar_filtro_em_thread(filtro, user_id, subcategoria_id, message_id):
    try:
    #chama a função de recuperação de cards com os filtros
    cards = recuperar_cards(subcategoria_id=subcategoria_id, filtro=filtro, estado_filtro=True, user_id=user_id)

    #texto atualizado da mensagem com os cards filtrados
    texto_atualizado = construir_texto(cards)

    #verifica se `texto_atualizado` não está vazio
    if texto_atualizado:
    #edita a mensagem original com o novo texto
    bot.edit_message_text(chat_id=user_id, message_id=message_id, text=texto_atualizado)
    else:
    #caso o texto estiver vazio, retorna uma mensagem apropriada
    bot.edit_message_text(chat_id=user_id, message_id=message_id, text="Nenhum card encontrado com os filtros aplicados.")
    except Exception as e:
    print(f"Erro ao processar o callback_filtros: {e}")

    #verificar se o conteúdo da mensagem foi realmente alterado
    def conteudo_modificado(caption_novo, caption_atual, markup_novo, markup_atual):
    #verifica se a legenda e a marcação são diferentes daquelas já aplicadas
    return caption_novo.strip() != caption_atual.strip() or markup_novo != markup_atual

    #callback para manipular botões de paginação
    @bot.callback_query_handler(func=lambda call: call.data.startswith('pagina_'))
    def callback_paginacao(call):
    try:
    user_id = call.from_user.id
    dados_callback = call.data.split('_')

    #extrai a página, subcategoria_id e user_id dos dados do callback
    pagina_atual = int(dados_callback[1])
    subcategoria_id = dados_callback[2]

    #obter subcategoria e categoria
    cursor.execute("SELECT nome, categoria_id, imagem FROM subcategorias WHERE id = ?", (subcategoria_id,))
    subcategoria = cursor.fetchone()
    if not subcategoria:
    print(f"Subcategoria {subcategoria_id} não encontrada.")
    return
    subcategoria_nome, categoria_id, subcategoria_imagem = subcategoria

    cursor.execute("SELECT nome FROM categorias WHERE id = ?", (categoria_id,))
    categoria_nome = cursor.fetchone()
    categoria_nome = categoria_nome[0] if categoria_nome else "Categoria desconhecida"

    #obter todos os cards da subcategoria e aplicar filtros
    cursor.execute('''
    SELECT cards.card_id, cards.nome, cards.raridade,
    COALESCE(inventario.quantidade, 0) AS quantidade_possuida
    FROM cards
    LEFT JOIN inventario ON cards.card_id = inventario.card_id AND inventario.user_id = ?
    WHERE cards.subcategoria_id = ?
    ORDER BY
    CASE cards.raridade
    WHEN 'Ouro' THEN 1
    WHEN 'Prata' THEN 2
    WHEN 'Bronze' THEN 3
    END,
    cards.card_id ASC
    ''', (user_id, subcategoria_id))
    cards = cursor.fetchall()

    #atualizar a mensagem com a nova página e filtros
    atualizar_mensagem_matilha(
    call.message, user_id, subcategoria_id, subcategoria_nome, categoria_nome,
    subcategoria_imagem, cards, quantidade_total=len(cards),
    mostrar_todos=False, pagina_atual=pagina_atual
    )

    bot.answer_callback_query(call.id, f"Página {pagina_atual} carregada.")

    except Exception as e:
    print(f"Erro no callback de paginação: {e}")


    #CALLBACK FILTRO
    @bot.callback_query_handler(func=lambda call: call.data.startswith('filtro_'))
    def callback_filtros(call):
    try:
    bot.answer_callback_query(call.id, "Processando filtro...")

    user_id = call.from_user.id
    dados_callback = call.data.split('_')

    filtro_nome = dados_callback[1] #deve capturar 'possuo', 'naopossuo', 'ouro', 'prata' ou 'bronze'
    subcategoria_id = dados_callback[2] #identifica o subcategoria_id
    filtro_ativo = filtros_ativos.get(user_id, {})

    #alterna o estado do filtro e verifica se é válido
    if filtro_nome in ["possuo", "naopossuo", "ouro", "prata", "bronze"]:
    #alterna o filtro (ativa/desativa)
    filtro_ativo[filtro_nome] = not filtro_ativo.get(filtro_nome, False)
    filtros_ativos[user_id] = filtro_ativo
    else:
    print(f"Filtro '{filtro_nome}' não reconhecido.")
    return

    #recuperar informações da subcategoria
    cursor.execute("SELECT nome, categoria_id, imagem FROM subcategorias WHERE id = ?", (subcategoria_id,))
    subcategoria = cursor.fetchone()

    if not subcategoria:
    print(f"Subcategoria {subcategoria_id} não encontrada.")
    return

    subcategoria_nome, categoria_id, subcategoria_imagem = subcategoria

    #obter o nome da categoria
    cursor.execute("SELECT nome FROM categorias WHERE id = ?", (categoria_id,))
    categoria_nome = cursor.fetchone()
    if categoria_nome:
    categoria_nome = categoria_nome[0]
    else:
    print(f"Categoria {categoria_id} não encontrada.")
    return

    #obter todos os cards da subcategoria
    cursor.execute('''
    SELECT cards.card_id, cards.nome, cards.raridade,
    COALESCE(inventario.quantidade, 0) AS quantidade_possuida
    FROM cards
    LEFT JOIN inventario ON cards.card_id = inventario.card_id AND inventario.user_id = ?
    WHERE cards.subcategoria_id = ?
    ORDER BY
    CASE cards.raridade
    WHEN 'Ouro' THEN 1
    WHEN 'Prata' THEN 2
    WHEN 'Bronze' THEN 3
    END,
    cards.card_id ASC
    ''', (user_id, subcategoria_id))

    cards = cursor.fetchall()

    #aplicar filtros se estiverem ativos
    if filtro_ativo.get("possuo"):
    cards = [card for card in cards if card[3] > 0] # Filtra apenas cards que o usuário possui
    if filtro_ativo.get("naopossuo"):
    cards = [card for card in cards if card[3] == 0] # Filtra apenas cards que o usuário não possui
    if filtro_ativo.get("ouro"):
    cards = [card for card in cards if card[2] == "Ouro"] # Filtra apenas cards ouro
    if filtro_ativo.get("prata"):
    cards = [card for card in cards if card[2] == "Prata"] # Filtra apenas cards prata
    if filtro_ativo.get("bronze"):
    cards = [card for card in cards if card[2] == "Bronze"] # Filtra apenas cards bronze

    #preparar o texto da legenda com os detalhes atualizados
    if cards:
    quantidade_total = sum(quantidade for _, _, _, quantidade in cards)
    caption_novo = (
    f"A matilha ouviu seu chamado e se fez presente!\n\n"
    f"{subcategoria_id}. {subcategoria_nome} — {categoria_nome}\n"
    f"{len(cards)} lobos disponíveis, {quantidade_total} em sua matilha.\n\n"
    f"{construir_texto(cards)}\n\n"
    f"| Para visualizar o card individualmente, utilize o comando /wolf [id]."
    )
    else:
    caption_novo = (
    f"{subcategoria_nome} — {categoria_nome}\n"
    "Nenhum card encontrado com os filtros aplicados."
    )

    #obter o markup atual e gerar o novo markup
    markup_atual = call.message.reply_markup
    markup_novo = gerar_markup_filtros(subcategoria_id, user_id)

    #verificar se a legenda realmente mudou antes de editar
    if conteudo_modificado(caption_novo, call.message.caption, markup_novo, markup_atual):
    bot.edit_message_caption(
    chat_id=call.message.chat.id,
    message_id=call.message.message_id,
    caption=caption_novo,
    reply_markup=markup_novo
    )
    else:
    bot.answer_callback_query(call.id, "Nenhuma mudança detectada. Filtro já aplicado.")

    except Exception as e:
    print(f"Erro ao processar o callback_filtros: {e}")

    #FUNÇÃO MATILHA - essa que é puxada quando o usuario manda o comando /matilha no bot
    @bot.message_handler(commands=['matilha'])
    def comando_matilha(message):
    try:
    #pega o nome da subcategoria a partir da mensagem
    nome_subcategoria = message.text.split(' ', 1)[1] if len(message.text.split()) > 1 else ''

    #busca a subcategoria usando as associações
    subcategoria_nome = buscar_subcategoria(nome_subcategoria)
    if not subcategoria_nome:
    bot.send_message(message.chat.id, "Subcategoria não encontrada.")
    return

    #buscando o id da subcategoria no banco de dados
    cursor.execute("SELECT id, categoria_id, imagem FROM subcategorias WHERE nome = ?", (subcategoria_nome,))
    subcategoria = cursor.fetchone()

    if not subcategoria:
    bot.send_message(message.chat.id, "Subcategoria não encontrada.")
    return

    #se a subcategoria for encontrada, continue com o restante do código
    subcategoria_id, categoria_id, subcategoria_imagem = subcategoria

    #obter o nome da categoria
    cursor.execute("SELECT nome FROM categorias WHERE id = ?", (categoria_id,))
    categoria_nome = cursor.fetchone()

    if categoria_nome:
    categoria_nome = categoria_nome[0]
    else:
    bot.send_message(message.chat.id, "Categoria não encontrada.")
    return

    #recuperar o user_id
    user_id = message.from_user.id

    #recuperar todos os cards da subcategoria
    cards = recuperar_cards(subcategoria_id, None, False, user_id)

    #construir texto inicial
    quantidade_total = sum(quantidade for _, _, _, quantidade in cards)
    caption = (
    f"A matilha ouviu seu chamado e se fez presente!\n\n"
    f"{subcategoria_id}. {subcategoria_nome} — {categoria_nome}\n"
    f"{len(cards)} lobos disponíveis, {quantidade_total} em sua matilha.\n\n"
    f"{construir_texto(cards)}\n\n"
    f"| Para visualizar o card individualmente, utilize o comando /wolf [id]."
    )

    #enviar a nova mensagem com os cards
    with open(subcategoria_imagem, 'rb') as photo:
    bot.send_photo(
    chat_id=message.chat.id,
    photo=photo,
    caption=caption,
    reply_markup=gerar_markup_filtros(subcategoria_id, user_id)
    )
    except Exception as e:
    print(f"Erro ao processar o comando /matilha: {e}")
    bot.send_message(message.chat.id, "Ocorreu um erro ao processar seu comando.")

    Continue reading...

Compartilhe esta Página