-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAnonBay.py
More file actions
657 lines (609 loc) · 29.6 KB
/
AnonBay.py
File metadata and controls
657 lines (609 loc) · 29.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AnonBay_Bot - Telegram Bot for Game Link Search with AI
Copyright (c) 2025 AnonBay_Bot
Licensed under the MIT License - see LICENSE file for details
"""
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, filters, ContextTypes, CallbackQueryHandler
import random
import google.generativeai as genai
import requests
import unicodedata
import difflib
import datetime
import json
import os
import telegram # Import necessário para capturar telegram.error.BadRequest
# Função utilitária para checar se usuário é admin
def is_admin(user_id):
return user_id in ADMINS
# /admin_atualizar_fontes - Força atualização das fontes de jogos
async def admin_atualizar_fontes(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if not is_admin(user_id):
await update.message.reply_text("❌ Você não tem permissão para este comando.")
return
try:
atualizar_jogos_multifonte()
await update.message.reply_text("✅ Fontes de jogos atualizadas com sucesso!")
except Exception as e:
await update.message.reply_text(f"❌ Erro ao atualizar fontes: {e}")
# /admin_limpar_cache - Limpa o cache de jogos (memória)
async def admin_limpar_cache(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if not is_admin(user_id):
await update.message.reply_text("❌ Você não tem permissão para este comando.")
return
global jogos_links_multifonte
jogos_links_multifonte = {}
await update.message.reply_text("🧹 Cache de jogos limpo! Use /admin_atualizar_fontes para recarregar.")
# /admin_stats - Mostra estatísticas básicas do bot
async def admin_stats(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if not is_admin(user_id):
await update.message.reply_text("❌ Você não tem permissão para este comando.")
return
num_jogos = len(jogos_links_multifonte)
num_usuarios = len(contextos_chat)
await update.message.reply_text(
f"📊 Estatísticas do Bot:\n"
f"Jogos indexados: {num_jogos}\n"
f"Usuários/chats com contexto: {num_usuarios}\n"
)
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
# --- Lista de IDs de administradores (carregada de variável de ambiente) ---
ID_ADMIN = os.getenv('ID_ADMIN')
ADMINS = [int(ID_ADMIN)] if ID_ADMIN else []
# Configure sua API key do Gemini aqui
GEMINI_API_KEY = os.environ['GEMINI_API_KEY']
TELEGRAM_TOKEN = os.environ['TELEGRAM_TOKEN']
genai.configure(api_key=GEMINI_API_KEY)
# Inicializar o modelo Gemini
model = genai.GenerativeModel('gemini-1.5-flash')
# Dicionários para armazenar dados dos usuários
jogos_ativos = {}
contextos_chat = {}
# Banco de dados de links de todas as fontes (cache em memória)
jogos_links_multifonte = {}
def atualizar_jogos_multifonte():
"""Atualiza o dicionário de jogos de todas as fontes com nome normalizado, links, data e tamanho."""
global jogos_links_multifonte
fontes = [
("EMPRESS", 'https://hydralinks.pages.dev/sources/empress.json'),
("GOG", 'https://hydralinks.pages.dev/sources/gog.json'),
("ATOP-GAMES", 'https://hydralinks.pages.dev/sources/atop-games.json'),
("FITGIRL", 'https://hydralinks.pages.dev/sources/fitgirl.json'),
("ONLINEFIX", 'https://hydralinks.pages.dev/sources/onlinefix.json'),
("STEAMRIP", 'https://hydralinks.pages.dev/sources/steamrip.json'),
("DODI", 'https://hydralinks.pages.dev/sources/dodi.json'),
("SHISUI", 'https://raw.githubusercontent.com/Shisuiicaro/source/refs/heads/main/shisuyssource.json'),
("IGG", 'https://wpnnt.github.io/sources/igg.json')
]
jogos_links_multifonte = {}
for nome_fonte, url in fontes:
try:
if url.endswith('.json') and not url.startswith('http'):
with open(url, 'r', encoding='utf-8') as f:
dados = json.load(f)
else:
response = requests.get(url, timeout=10)
if response.status_code == 200:
dados = response.json()
else:
continue
# Corrige: IGG online retorna lista, outras fontes retornam dict
if isinstance(dados, list):
jogos = dados
elif nome_fonte == "IGG" and 'downloads' in dados:
jogos = dados['downloads']
else:
jogos = dados.get('downloads', [])
for jogo in jogos:
nome_original = jogo.get('title', '').strip()
nome = nome_original.lower().replace('.', ' ').replace('-', ' ').replace('_', ' ').strip()
uris = jogo.get('uris', [])
data = jogo.get('uploadDate', '')
tamanho = jogo.get('fileSize', '')
# Novos campos para aliases e franquia
aliases = jogo.get('aliases', [])
if isinstance(aliases, str):
aliases = [aliases]
franchise = jogo.get('franchise', '')
# Indexação principal
if nome and uris:
keyset = set([nome])
# Indexa aliases
for alias in aliases:
alias_norm = alias.lower().replace('.', ' ').replace('-', ' ').replace('_', ' ').strip()
if alias_norm:
keyset.add(alias_norm)
# Indexa franquia
if franchise:
franchise_norm = franchise.lower().replace('.', ' ').replace('-', ' ').replace('_', ' ').strip()
keyset.add(franchise_norm)
# Indexa cada palavra do nome e dos aliases/franquia
for key in list(keyset):
for palavra in key.split():
if palavra:
keyset.add(palavra)
# Salva no dicionário, evitando sobrescrever entradas já existentes (prioriza nome completo)
for key in keyset:
if key not in jogos_links_multifonte:
jogos_links_multifonte[key] = {
'uris': uris,
'fonte': nome_fonte,
'data': data,
'tamanho': tamanho,
'title': nome_original,
'aliases': aliases,
'franchise': franchise
}
except Exception as e:
print(f"Erro ao atualizar jogos {nome_fonte}: {e}")
# Removido o auto-update ao iniciar. Agora a atualização das fontes só ocorre via /admin_atualizar_fontes
# Função para buscar link de jogo em todas as fontes
def normalizar_nome(nome):
# Remove acentos e caracteres especiais, deixa minúsculo e limpa espaços
nome = nome.lower().replace('.', ' ').replace('-', ' ').replace('_', ' ').strip()
nome = ''.join(c for c in unicodedata.normalize('NFD', nome) if unicodedata.category(c) != 'Mn')
return nome
def detectar_plataforma(title):
title_norm = title.lower()
if 'switch' in title_norm:
return 'Switch'
if 'ps3' in title_norm:
return 'PS3'
if 'ps4' in title_norm:
return 'PS4'
if 'xbox' in title_norm:
return 'Xbox'
if 'wii' in title_norm:
return 'Wii'
if 'pc' in title_norm or 'windows' in title_norm:
return 'PC'
# Se não especifica, assume PC
return 'PC'
# Função para buscar link de jogo em todas as fontes (fuzzy, aceita nomes parciais, palavras soltas e erros de digitação, prioriza PC)
# Nova versão: integração com textdistance para melhorar fuzzy matching
def buscar_link_multifonte(nome_jogo, fontes_escolhidas=None, max_resultados=30, plataforma_usuario=None):
try:
from rapidfuzz import fuzz, process
usar_rapidfuzz = True
except ImportError:
import difflib
usar_rapidfuzz = False
try:
import textdistance
usar_textdistance = True
except ImportError:
usar_textdistance = False
nome_jogo_norm = normalizar_nome(nome_jogo)
termos = set(nome_jogo_norm.split())
resultados = []
nomes_jogos = list(jogos_links_multifonte.keys())
nomes_filtrados = [n for n in nomes_jogos if not fontes_escolhidas or jogos_links_multifonte[n]['fonte'].upper() in fontes_escolhidas]
# Busca inteligente: avalia nome, aliases, franquia, e múltiplos termos
def score_jogo(n, dados):
score = 0
campos = [normalizar_nome(dados['title'])]
if 'aliases' in dados and dados['aliases']:
campos += [normalizar_nome(a) for a in dados['aliases'] if a]
if 'franchise' in dados and dados['franchise']:
campos.append(normalizar_nome(dados['franchise']))
# Exato
if nome_jogo_norm in campos:
score += 100
# Substring
elif any(nome_jogo_norm in campo for campo in campos):
score += 95
# Todas as palavras presentes em algum campo
elif any(all(t in campo for t in termos) for campo in campos):
score += 90
# Alguma palavra presente em algum campo
elif any(any(t in campo for t in termos) for campo in campos):
score += 80
# Fuzzy (token_sort + textdistance)
else:
best = 0
if usar_rapidfuzz:
best = max([fuzz.token_sort_ratio(nome_jogo_norm, campo) for campo in campos])
else:
best = max([difflib.SequenceMatcher(None, nome_jogo_norm, campo).ratio()*100 for campo in campos])
# Se textdistance disponível, faz média dos scores
if usar_textdistance:
best_td = max([textdistance.jaro_winkler.normalized_similarity(nome_jogo_norm, campo)*100 for campo in campos])
best = (best + best_td) / 2
score += best
# Bônus se plataforma for PC
if detectar_plataforma(dados['title']) == 'PC':
score += 2
return score
# Avalia todos os jogos únicos (por title+fonte)
vistos_titles = set()
for n in nomes_filtrados:
dados = jogos_links_multifonte[n]
title_key = (dados['title'].lower(), dados.get('fonte', ''))
if title_key in vistos_titles:
continue
s = score_jogo(n, dados)
if s > 40: # threshold para aparecer
resultados.append((s, n, dados))
vistos_titles.add(title_key)
# Ordena por score decrescente
resultados.sort(reverse=True, key=lambda x: x[0])
# Agrupa por plataforma
grupos = {'PC': [], 'Switch': [], 'PS3': [], 'PS4': [], 'Xbox': [], 'Wii': [], 'Outros': []}
for score, nome, dados in resultados:
plataforma = detectar_plataforma(dados['title'])
if plataforma in grupos:
grupos[plataforma].append((score, nome, dados))
else:
grupos['Outros'].append((score, nome, dados))
# Se o usuário pediu uma plataforma específica, priorize ela
if plataforma_usuario:
plat_cap = plataforma_usuario.capitalize()
ordem = [plat_cap] + [p for p in grupos if p != plat_cap]
else:
ordem = ['PC', 'Switch', 'PS4', 'PS3', 'Xbox', 'Wii', 'Outros']
resultados_ordenados = []
for p in ordem:
if p in grupos:
resultados_ordenados.extend(grupos[p])
# Remove duplicados mantendo ordem
vistos = set()
final = []
for item in resultados_ordenados:
if item[1] not in vistos:
final.append(item)
vistos.add(item[1])
return final
# Função para carregar contexto APENAS do arquivo ou variável de ambiente
def carregar_contexto_arquivo(fontes_escolhidas=None):
LIMITE_CARACTERES = 8000 # Ajuste conforme necessário
contexto_completo = ""
# Tenta carregar do arquivo primeiro
try:
with open('contexto.txt', 'r', encoding='utf-8') as arquivo:
contexto_completo = arquivo.read()
print(f"✅ Contexto carregado: {len(contexto_completo)} caracteres do arquivo")
except FileNotFoundError:
# Se não encontrar o arquivo, tenta carregar da variável de ambiente
contexto_env = os.getenv('BOT_CONTEXT')
if contexto_env:
contexto_completo = contexto_env
print(f"✅ Contexto carregado: {len(contexto_completo)} caracteres da variável de ambiente")
else:
# Contexto padrão mínimo como último recurso
contexto_completo = """Você é um assistente útil e amigável especializado em informações sobre jogos.
Sempre responda de forma educada e prestativa."""
print("⚠️ Usando contexto padrão - configure BOT_CONTEXT no ambiente ou crie contexto.txt")
# Limita o tamanho total do contexto
if len(contexto_completo) > LIMITE_CARACTERES:
contexto_completo = contexto_completo[:LIMITE_CARACTERES] + '\n... (contexto cortado para não exceder o limite da API)'
return contexto_completo
# Função para inicializar contexto do usuário
def inicializar_contexto(user_id):
if user_id not in contextos_chat:
contexto_do_arquivo = carregar_contexto_arquivo()
contextos_chat[user_id] = {
'personalidade': contexto_do_arquivo
}
# Função para construir prompt com contexto (sem histórico para privacidade)
def construir_prompt_com_contexto(user_id, nova_mensagem):
inicializar_contexto(user_id)
prompt = contextos_chat[user_id]['personalidade'] + "\n\n"
prompt += f"Usuário: {nova_mensagem}\nBot:"
return prompt
# /start
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
inicializar_contexto(user_id)
await update.message.reply_text("🤖 AnonBay_Bot iniciado!")
# /pergunta - IA principal (com contexto por chat/grupo)
async def pergunta(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
chat_id = update.effective_chat.id
pergunta = ' '.join(context.args)
if not pergunta:
await update.message.reply_text("Use: /pergunta <sua pergunta>")
return
try:
# Usa contexto do chat/grupo
if chat_id not in contextos_chat:
inicializar_contexto(chat_id)
prompt_completo = contextos_chat[chat_id]['personalidade'] + "\n\nUsuário: " + pergunta + "\nBot:"
response = model.generate_content(prompt_completo)
resposta = response.text
if len(resposta) > 4096:
resposta = resposta[:4093] + "..."
await update.message.reply_text(f"🤖 {resposta}")
except Exception as e:
await update.message.reply_text(f"❌ Erro: {str(e)}")
# /aprender - Ensina algo novo ao bot (qualquer usuário, contexto por chat/grupo)
async def aprender(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
chat_id = update.effective_chat.id
novo_conhecimento = ' '.join(context.args)
if not novo_conhecimento:
await update.message.reply_text(
"📚 Como ensinar algo novo ao bot:\n\n"
"Use: /aprender <novo conhecimento>\n\n"
"Exemplo: /aprender O Mario é o mascote da Nintendo."
)
return
try:
aprendizado_formatado = f"NOVO APRENDIZADO: {novo_conhecimento}"
if chat_id not in contextos_chat:
inicializar_contexto(chat_id)
contextos_chat[chat_id]['personalidade'] += f"\n{aprendizado_formatado}"
# Salva aprendizados por chat/grupo
if 'aprendizados' not in contextos_chat[chat_id]:
contextos_chat[chat_id]['aprendizados'] = []
contextos_chat[chat_id]['aprendizados'].append(novo_conhecimento)
# --- NOVO: recarrega contexto após aprender ---
contexto_do_arquivo = carregar_contexto_arquivo()
aprendizados = '\n'.join([f"NOVO APRENDIZADO: {a}" for a in contextos_chat[chat_id]['aprendizados']])
contextos_chat[chat_id]['personalidade'] = contexto_do_arquivo + ('\n' if contexto_do_arquivo else '') + aprendizados
await update.message.reply_text(
f"🧠 Aprendizado adicionado com sucesso!\n\n"
f"Novo conhecimento: {novo_conhecimento}\n\n"
f"O bot vai seguir exatamente o que foi ensinado neste chat!"
)
print(f"📚 Novo aprendizado adicionado no chat {chat_id}: {novo_conhecimento}")
except Exception as e:
await update.message.reply_text(f"❌ Erro ao aprender: {str(e)}")
# /ver_aprendizados - Mostra aprendizados do chat/grupo (qualquer usuário)
async def ver_aprendizados(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_chat.id
try:
if chat_id not in contextos_chat or 'aprendizados' not in contextos_chat[chat_id] or not contextos_chat[chat_id]['aprendizados']:
await update.message.reply_text("Nenhum aprendizado encontrado neste chat.")
return
aprendizados = contextos_chat[chat_id]['aprendizados']
ultimos_aprendizados = aprendizados[-5:]
mensagem = f"🧠 Últimos {len(ultimos_aprendizados)} aprendizados deste chat:\n\n"
for i, aprendizado in enumerate(ultimos_aprendizados, 1):
mensagem += f"{i}. {aprendizado}\n\n"
if len(aprendizados) > 5:
mensagem += f"... e mais {len(aprendizados) - 5} aprendizados anteriores"
await update.message.reply_text(mensagem)
except Exception as e:
await update.message.reply_text(f"❌ Erro ao ler aprendizados: {str(e)}")
# /link - Busca e envia todos os links do jogo informado, com botões paginados e detalhes
async def link(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_chat.id
if context.args:
nome_jogo = ' '.join(context.args)
else:
await update.message.reply_text("Envie o nome do jogo que você quer o link!\nExemplo: /link The Legend of Zelda")
return
# Não atualiza mais as fontes automaticamente
resultados = buscar_link_multifonte(nome_jogo, max_resultados=50)
if not resultados:
await update.message.reply_text("Nenhum link encontrado para esse jogo.")
return
await enviar_paginado(update, context, resultados, pagina=0)
# Função para enviar botões paginados com detalhes dos jogos encontrados (agora só mostra botão, link só ao clicar)
async def enviar_paginado(update, context, resultados, pagina=0):
itens_por_pagina = 10
total = len(resultados)
inicio = pagina * itens_por_pagina
fim = inicio + itens_por_pagina
pagina_resultados = resultados[inicio:fim]
keyboard = []
for idx, (score, nome, dados) in enumerate(pagina_resultados, start=inicio):
data_formatada = formatar_data(dados['data']) if dados.get('data') else ''
# Limita o título para caber mais informações no botão (ex: 28 caracteres)
titulo_curto = (dados['title'][:25] + '...') if len(dados['title']) > 28 else dados['title']
texto_botao = f"{titulo_curto} | {dados['fonte']} | {data_formatada} | {dados['tamanho']}"
keyboard.append([InlineKeyboardButton(texto_botao, callback_data=f"showlink_{idx}")])
# Botões de navegação
nav_buttons = []
if inicio > 0:
nav_buttons.append(InlineKeyboardButton("⬅️ Voltar", callback_data=f"pagina_{pagina-1}"))
if fim < total:
nav_buttons.append(InlineKeyboardButton("Avançar ➡️", callback_data=f"pagina_{pagina+1}"))
if nav_buttons:
keyboard.append(nav_buttons)
reply_markup = InlineKeyboardMarkup(keyboard) if keyboard else None
texto = f"Foram encontrados {total} resultados para sua busca. Toque em um botão para ver o link:"
# Salva resultados no contexto para paginação funcionar
if hasattr(context, 'chat_data'):
context.chat_data['ultimos_resultados'] = resultados
context.chat_data['pagina_atual'] = pagina
if update.callback_query:
await update.callback_query.edit_message_text(texto, reply_markup=reply_markup)
else:
await update.message.reply_text(texto, reply_markup=reply_markup)
# Função utilitária para formatar data
def formatar_data(data_str):
# Se vier no formato ISO com hora, corta só a data
if "T" in data_str:
return data_str.split("T")[0]
# Tenta vários formatos comuns
formatos = ["%Y-%m-%d", "%d/%m/%Y", "%Y/%m/%d", "%d-%m-%Y", "%Y.%m.%d", "%d.%m.%Y"]
for fmt in formatos:
try:
dt = datetime.datetime.strptime(data_str, fmt)
return dt.strftime("%d/%m/%Y")
except Exception:
continue
return data_str # Se não conseguir, retorna como veio
# Handler para paginação dos botões e exibição do link ao clicar
async def callback_buscar_link(update: Update, context: ContextTypes.DEFAULT_TYPE):
query = update.callback_query
data = query.data
try:
if data.startswith("pagina_"):
pagina = int(data.split("_")[1])
if hasattr(context, 'chat_data') and 'ultimos_resultados' in context.chat_data:
resultados = context.chat_data['ultimos_resultados']
await enviar_paginado(update, context, resultados, pagina)
else:
await query.answer("Não foi possível recuperar os resultados.", show_alert=True)
elif data.startswith("showlink_"):
idx = int(data.split("_")[1])
if hasattr(context, 'chat_data') and 'ultimos_resultados' in context.chat_data:
resultados = context.chat_data['ultimos_resultados']
if 0 <= idx < len(resultados):
score, nome, dados = resultados[idx]
data_upload = formatar_data(dados.get('data', '')) if dados.get('data') else ''
# IGG pode ter múltiplos links (uris)
if dados['fonte'] == "IGG" and isinstance(dados.get('uris'), list) and len(dados['uris']) > 1:
texto = (
f"<b>{dados['title']}</b>\n"
f"Fonte: <b>{dados['fonte']}</b>\n"
f"Data: <b>{data_upload}</b>\n"
f"Tamanho: <b>{dados['tamanho']}</b>\n\n"
f"<b>Links:</b>\n"
)
for i, uri in enumerate(dados['uris'], 1):
if isinstance(uri, dict) and 'name' in uri and 'url' in uri:
nome_link = uri['name']
# Troca MegaUp.net por MegaUp e Mega.nz por Mega
if nome_link.strip().lower().startswith('megaup.net'):
nome_link = 'MegaUp'
elif nome_link.strip().lower().startswith('mega.nz'):
nome_link = 'Mega'
texto += f"<b>{nome_link}:</b>\n<code>{uri['url']}</code>\n"
else:
texto += f"<b>Link {i}:</b>\n<code>{uri}</code>\n"
else:
url = dados['uris'][0] if dados['uris'] else None
texto = (
f"<b>{dados['title']}</b>\n"
f"Fonte: <b>{dados['fonte']}</b>\n"
f"Data: <b>{data_upload}</b>\n"
f"Tamanho: <b>{dados['tamanho']}</b>\n\n"
f"<b>Link:</b>\n<code>{url}</code>"
)
keyboard = [[InlineKeyboardButton("⬅️ Voltar para a lista", callback_data=f"pagina_{context.chat_data.get('pagina_atual', 0)}")]]
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text(texto, reply_markup=reply_markup, parse_mode='HTML')
return
await query.answer("Não foi possível exibir o link.", show_alert=True)
else:
await query.answer()
except telegram.error.BadRequest as e:
if 'query is too old' in str(e).lower() or 'query id is invalid' in str(e).lower():
pass
else:
print(f"Erro inesperado no callback: {e}")
except Exception as e:
print(f"Erro inesperado no callback: {e}")
# Atualiza processar_tentativa para usar busca paginada
async def processar_tentativa(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_chat.id
texto = update.message.text.strip().lower()
if chat_id in jogos_ativos:
try:
tentativa = int(update.message.text.strip())
numero = jogos_ativos[chat_id]
if tentativa == numero:
await update.message.reply_text("🎉 Parabéns! Você acertou o número!")
del jogos_ativos[chat_id]
elif tentativa < numero:
await update.message.reply_text("🔼 O número é maior!")
else:
await update.message.reply_text("🔽 O número é menor!")
except ValueError:
pass
elif 'link' in texto:
import re
padrao = r'link (?:do|de|da|d[oa]s)?\s*(.*)'
match = re.search(padrao, texto)
if match:
nome_jogo = match.group(1).strip()
if nome_jogo:
# Não atualiza mais as fontes automaticamente
resultados = buscar_link_multifonte(nome_jogo, max_resultados=50)
if resultados:
context.chat_data['ultimos_resultados'] = resultados
await enviar_paginado(update, context, resultados, pagina=0)
return
else:
await update.message.reply_text("Nenhum link encontrado para esse jogo.")
return
# Se não encontrou nome, ignora
# --- NOVO: resposta natural se mencionar o bot ---
elif any(x in texto for x in ['bot', 'anonbay_bot', 'anonbay bot', '@anonbay_bot', 'anonbay', 'AnonBay']):
user_id = update.effective_user.id
chat_id = update.effective_chat.id
mensagem = update.message.text
if chat_id not in contextos_chat:
inicializar_contexto(chat_id)
prompt_completo = contextos_chat[chat_id]['personalidade'] + "\n\nUsuário: " + mensagem + "\nBot:"
try:
response = model.generate_content(prompt_completo)
resposta = response.text
if len(resposta) > 4096:
resposta = resposta[:4093] + "..."
await update.message.reply_text(f"🤖 {resposta}")
except Exception as e:
await update.message.reply_text(f"❌ Erro: {str(e)}")
# /ajuda atualizado
async def ajuda(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"📋 **Comandos disponíveis:**\n\n"
"/pergunta <texto> - Converse com a IA do AnonBay_Bot!\n"
"/aprender <conhecimento> - Ensina algo novo\n"
"/ver_aprendizados - Mostra aprendizados do chat\n"
"/link - Peça um link de jogo\n"
"/jogar - Minigame de adivinhação\n"
"/parar - Para o jogo\n"
"/ajuda - Mostra esta mensagem\n"
"\n💡 Dica: Digite 'bot' ou 'AnonBay_Bot' em mensagens para chat natural!"
)
async def sobre(update: Update, context: ContextTypes.DEFAULT_TYPE):
await update.message.reply_text(
"🤖 AnonBay_Bot - Um bot de IA para busca de links!\n\n"
"Desenvolvido para responder perguntas, aprender, buscar jogos e muito mais.\n\n"
"Comandos principais: /ajuda"
)
# Minigame de adivinhação
async def jogar(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
chat_id = update.effective_chat.id
numero = random.randint(1, 100)
jogos_ativos[chat_id] = numero
await update.message.reply_text(
"🎲 Pensei em um número de 1 a 100. Tente adivinhar! Envie sua tentativa no chat.\nUse /parar para desistir."
)
async def parar_jogo(update: Update, context: ContextTypes.DEFAULT_TYPE):
chat_id = update.effective_chat.id
if chat_id in jogos_ativos:
del jogos_ativos[chat_id]
await update.message.reply_text("🛑 Jogo parado!")
else:
await update.message.reply_text("Nenhum jogo ativo neste chat.")
print("🔄 Atualizando fontes de jogos automaticamente...")
atualizar_jogos_multifonte()
print(f"✅ Fontes atualizadas! Total de jogos: {len(jogos_links_multifonte)}")
if __name__ == "__main__":
app = ApplicationBuilder().token(TELEGRAM_TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("ajuda", ajuda))
app.add_handler(CommandHandler("sobre", sobre))
app.add_handler(CommandHandler("jogar", jogar))
app.add_handler(CommandHandler("parar", parar_jogo))
app.add_handler(CommandHandler("pergunta", pergunta))
app.add_handler(CommandHandler("aprender", aprender))
app.add_handler(CommandHandler("ver_aprendizados", ver_aprendizados))
app.add_handler(CommandHandler("link", link))
# Comandos de administração
app.add_handler(CommandHandler("admin_atualizar_fontes", admin_atualizar_fontes))
app.add_handler(CommandHandler("admin_limpar_cache", admin_limpar_cache))
app.add_handler(CommandHandler("admin_stats", admin_stats))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, processar_tentativa))
app.add_handler(CallbackQueryHandler(callback_buscar_link))
try:
print("🤖 AnonBay_Bot iniciado - Contexto carregado do arquivo contexto.txt")
app.run_polling()
except Exception as e:
print(f"Erro fatal ao iniciar o bot: {e}")