import logging import os import random import sys import time import signal import subprocess import requests from flask import Flask, render_template, jsonify, request, Response, session from dados_piru_sem_gozar import banco_piru_sem_gozar from dados_bunda_cu import gerar_ordem_exibir_bunda_cu from dados_exposicao_publica import ( banco_exposicao_publica, gerar_ordem_exposicao_publica, ) from dados_exposicao_extrema import banco_exposicao_extrema from dados_piru import banco_piru from dados_sua_vez_mestre import banco_sua_vez_mestre from dados_plug import banco_plug from dados_apenas_pelado import banco_apenas_pelado from dados_plug_fixo_medio import banco_plug_fixo_medio from dados_picantes import banco_picantes from dados_plug_prostata import banco_plug_prostata from dados_edging import banco_edging from dados_plug_super_grosso import banco_plug_super_grosso from dados_masturbador_ovo import banco_masturbador_ovo from dados_plug_parede import banco_plug_parede from dados_plug_super_grosso_festinha import banco_plug_super_grosso_festinha from dados_esposa_amigas_amigos import banco_esposa_amigas_amigos from dados_gozo_controlado import banco_gozo_controlado from dados_ruined_orgasm import banco_ruined_orgasm from dados_gozar_imaginacao import banco_gozar_imaginacao from dados_aleatorio2 import banco_aleatorio2 from dados_dupla_ativo_passivo_gold import ( banco_dupla_ativo_passivo_gold, gerar_ordem_dupla_ativo_passivo_gold, ) from dados_mulher_ativa import ( banco_mulher_ativa, gerar_ordem_mulher_ativa, ) from dados_esposa_brinca_marido import ( banco_esposa_brinca_marido, gerar_ordem_esposa_brinca_marido, ) from dados_desafio_rapido import banco_desafio_rapido from dados_desafio_tempo import banco_desafio_tempo from dados_teste_obediencia import banco_teste_obediencia from dados_jogo_verdade import banco_jogo_verdade from dados_missao_secreta import banco_missao_secreta from dados_castigo import banco_castigo, gerar_ordem_castigo from dados_alongamento_ginastica import ( banco_alongamento_ginastica, gerar_ordem_alongamento_ginastica, ) from dados_alongamento_relaxamento import ( banco_alongamento_relaxamento, gerar_ordem_alongamento_relaxamento, ) from dados_consulta import banco_consulta, gerar_ordem_consulta from dados_consulta_exposicao import ( banco_consulta_exposicao, gerar_ordem_consulta_exposicao, contos_consulta_exposicao, ) from dados_esposa_visita import ( banco_esposa_visita, gerar_ordem_esposa_visita, contos_esposa_visita, ) from dados_sozinho_cama import banco_sozinho_cama, gerar_ordem_sozinho_cama from dados_fantasias import banco_fantasias, gerar_ordem_fantasias from dados_sensorial import banco_sensorial, gerar_ordem_sensorial from dados_sentado_cadeira import banco_sentado_cadeira from dados_cenario_completo import gerar_cenario_completo from ia_llamacpp import ( servidor_disponivel, gerar_ordem_ia, gerar_ordem_sentado_trabalhando, gerar_ordem_plug_fixo_medio, gerar_ordem_cenario_completo_ia, gerar_ordem_ia_3, gerar_ordem_ia_4, conversar_ia, revisar_ordem_ia, LlamaCppError, configurar_provedor, obter_config_publica, estado_revisao_cron_ativo, definir_revisao_cron, provedor_atual, ) from dados import ( banco_dois_homens_mulher, banco_rotina_domestica, banco_rotina_diaria_inteira, banco_estudos_auto_ditadas, comodos_casa, locais_corpo, locais_corpo_dupla, plugs_disponiveis, banco_sozinho_masturbacao, banco_sozinho_vibracao, banco_sozinho_exposicao, banco_sozinho_humilhacao, banco_sozinho_plug, banco_sozinho_edging, banco_banho, banco_anal_dedos, banco_gozo_anal, banco_toma_gozo, banco_vibracao, banco_controle_gozo, banco_exibicionismo, banco_sexo_oral, banco_penetracao, banco_masturbacao_parceiro, banco_masturbacao_individual, banco_trabalhos_domesticos, banco_sendo_passivo, banco_sendo_ativo, banco_casal_homem_mulher, banco_trabalho_home_office, banco_tomando_gozo_individual, banco_aleatorio, gerar_ordem_definitiva, gerar_ordem_sozinho_completa, gerar_ordem_dupla_completa, gerar_ordem_masturbacao_gozo, gerar_ordem_faca_sem_gozar, gerar_ordem_tomando_gozo_individual, gerar_ordem_aleatoria ) app = Flask(__name__) # Secret key for signed session cookies. Override FLASK_SECRET_KEY in production. app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'dev-secret-change-me') logger = logging.getLogger(__name__) # --------------------------------------------------------------------------- # Disable browser cache for HTML pages (forces fresh JS every time) # --------------------------------------------------------------------------- @app.after_request def add_no_cache_headers(response): if response.content_type and 'text/html' in response.content_type: response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' response.headers['Pragma'] = 'no-cache' response.headers['Expires'] = '0' return response # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- # Decorative box-drawing borders (40 dashes wide) reused by every header. _BORDA_CIMA = "\u2554" + "\u2550" * 40 + "\u2557\n" _BORDA_BAIXO = "\u255a" + "\u2550" * 40 + "\u255d\n\n" def gerar_cabecalho(titulo, subtitulo=""): """Build the standardized decorative header used by all order routes.""" partes = [_BORDA_CIMA, f" {titulo}\n"] if subtitulo: partes.append(f" {subtitulo}\n") partes.append(_BORDA_BAIXO) return "".join(partes) def responder_ordem(ordem, timestamp=None): """Return the standard JSON response payload for an order.""" return jsonify({ 'ordem': ordem, 'timestamp': timestamp or time.strftime('%H:%M:%S') }) def gerar_resposta(titulo, subtitulo, gerador, *args, **kwargs): """Build header, call *gerador* safely and return the JSON response. Wraps the generator call in try/except so a failure produces a clean user-facing message instead of an unhandled 500. """ cabecalho = gerar_cabecalho(titulo, subtitulo) try: ordem = gerador(*args, **kwargs) except Exception as exc: # noqa: BLE001 - surface any generator failure logger.exception("Erro ao gerar ordem para '%s'", titulo) ordem = f"\u26a0\ufe0f Erro ao gerar a ordem. Tente novamente.\n({exc})" return responder_ordem(cabecalho + ordem) def responder_banco(titulo, subtitulo, banco): """Pick a random entry from *banco* and return the standard response.""" cabecalho = gerar_cabecalho(titulo, subtitulo) try: ordem = random.choice(banco) except (IndexError, TypeError): logger.exception("Banco vazio ou inválido para '%s'", titulo) ordem = "\u26a0\ufe0f Nenhuma ordem disponível neste banco." return responder_ordem(cabecalho + ordem) def enviar_telegram_amigos(texto, chat_ids=None): """Send *texto* to one or more friends on Telegram. *chat_ids* is an explicit list of Telegram chat ids to deliver to. When omitted it defaults to ``TELEGRAM_CHAT_ID_AMIGO1`` and ``TELEGRAM_CHAT_ID_AMIGO2`` (the two-friends button). Returns a dict with the list of chat ids that received the message (``enviados``) and any per-recipient errors (``erros``). When the bot token or no chat id is configured, ``erro`` describes the problem and nothing is sent. """ bot_token = os.environ.get("TELEGRAM_BOT_TOKEN", "").strip() if chat_ids is None: chat_ids = [ os.environ.get("TELEGRAM_CHAT_ID_AMIGO1", "").strip(), os.environ.get("TELEGRAM_CHAT_ID_AMIGO2", "").strip(), ] amigos = [str(cid).strip() for cid in chat_ids if str(cid).strip()] if not bot_token: return {'enviados': [], 'erros': [], 'erro': 'TELEGRAM_BOT_TOKEN não configurado.'} if not amigos: return {'enviados': [], 'erros': [], 'erro': 'Nenhum TELEGRAM_CHAT_ID_AMIGO configurado.'} api_url = f"https://api.telegram.org/bot{bot_token}/sendMessage" agora = time.strftime('%d/%m/%Y %H:%M') texto_completo = f"{texto}\n\n📅 {agora}" enviados = [] erros = [] for chat_id in amigos: try: resp = requests.post(api_url, json={ "chat_id": chat_id, "text": texto_completo, "parse_mode": "HTML", }, timeout=15) if resp.status_code == 200: enviados.append(chat_id) else: erros.append(f"{chat_id}: HTTP {resp.status_code}") logger.warning("Telegram (amigo %s) retornou %s: %s", chat_id, resp.status_code, resp.text[:200]) except requests.RequestException as exc: erros.append(f"{chat_id}: {exc}") logger.error("Falha ao enviar Telegram (amigo %s): %s", chat_id, exc) return {'enviados': enviados, 'erros': erros} def enviar_telegram_principal(texto): """Send *texto* to the main Telegram chat (TELEGRAM_CHAT_ID). Returns a dict mirroring :func:`enviar_telegram_amigos`: ``enviados`` lists the chat ids that received the message and ``erros`` any per-recipient errors. When the bot token or chat id is not configured, ``erro`` describes the problem and nothing is sent. """ return enviar_telegram_amigos( texto, chat_ids=[os.environ.get("TELEGRAM_CHAT_ID", "").strip()], ) # --------------------------------------------------------------------------- # Order generation logic # --------------------------------------------------------------------------- def gerar_ordem_sozinho(): """Gera ordem para modo sozinho - 50% detalhada, 50% dos bancos""" if random.random() < 0.50: return gerar_ordem_sozinho_completa() vibe = random.random() if vibe < 0.30: return random.choice(banco_sozinho_masturbacao) elif vibe < 0.50: return random.choice(banco_sozinho_vibracao) elif vibe < 0.80: return random.choice(banco_sozinho_humilhacao) elif vibe < 0.93: return random.choice(banco_sozinho_plug) else: return random.choice(banco_sozinho_edging) # --------------------------------------------------------------------------- # Routes - pages # --------------------------------------------------------------------------- @app.route('/') def index(): return render_template('index.html') @app.route('/contos') def contos(): return render_template('contos.html') # --------------------------------------------------------------------------- # Routes - session management # --------------------------------------------------------------------------- @app.route('/iniciar_sessao', methods=['POST']) def iniciar_sessao(): dados = request.get_json() or {} modo = dados.get('modo', 'sozinho') if modo not in ('sozinho',): modo = 'sozinho' session['modo'] = modo return jsonify({'status': 'ok', 'modo': modo}) @app.route('/encerrar_sessao', methods=['POST']) def encerrar_sessao(): session.pop('modo', None) return jsonify({'status': 'ok'}) # --------------------------------------------------------------------------- # Routes - main order generation # --------------------------------------------------------------------------- @app.route('/proxima_ordem') def proxima_ordem(): cabecalho = gerar_cabecalho( "\u26a0\ufe0f DECRETO: VOC\u00ca \u00e9 o \u00daNICO SUJEITO DESTE CASTIGO" ) try: ordem = gerar_ordem_sozinho() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem sozinho") ordem = f"\u26a0\ufe0f Erro ao gerar a ordem. Tente novamente.\n({exc})" return responder_ordem(cabecalho + ordem) @app.route('/ordem_masturbacao_gozo') def ordem_masturbacao_gozo(): """Gera ordem de masturbação com gozo obrigatório""" return gerar_resposta( "\U0001f51e ORDEM DE MASTURBA\u00c7\u00c3O E GOZO \u00daNICO", "\u26a0\ufe0f GOZAR \u00c9 OBRIGAT\u00d3RIO. N\u00c3O PARE ANTES DE GOZAR.", gerar_ordem_masturbacao_gozo, ) @app.route('/ordem_faca_sem_gozar') def ordem_faca_sem_gozar(): """Gera ordem para fazer algo sem gozar""" return gerar_resposta( "\u26d4 ORDEM: FA\u00c7A SEM GOZAR", "\u26a0\ufe0f GOZAR \u00c9 PROIBIDO. CONTROLE-SE.", gerar_ordem_faca_sem_gozar, ) @app.route('/ordem_piru_sem_gozar') def ordem_piru_sem_gozar(): """Gera ordem de estímulo peniano sem gozar""" return responder_banco( "\U0001f4a5 PIRU SEM GOZAR", "\u26a0\ufe0f GOZAR \u00c9 PROIBIDO. CONTROLE O PIRU.", banco_piru_sem_gozar, ) @app.route('/ordem_tomando_gozo_individual') def ordem_tomando_gozo_individual(): """Gera ordem para masturbar e tomar o próprio gozo""" return gerar_resposta( "\U0001f4a6 TOMANDO O GOZO INDIVIDUAL", "\u26a0\ufe0f GOZE E TOME O PR\u00d3PRIO GOZO. N\u00c3O DESPERDI\u00c7E UMA GOTA.", gerar_ordem_tomando_gozo_individual, ) @app.route('/ordem_aleatoria') def ordem_aleatoria(): """Gera ordem aleatória diversa do Mestre""" return gerar_resposta( "\U0001f3b2 ALEAT\u00d3RIO DO MESTRE", "\u26a0\ufe0f O MESTRE DECIDIU. OBEDE\u00c7A SEM QUESTINAR.", gerar_ordem_aleatoria, ) # --------------------------------------------------------------------------- # Routes - simple bank-backed orders # --------------------------------------------------------------------------- @app.route('/ordem_rotina_completa') def ordem_rotina_completa(): return responder_banco("\U0001f4cb ROTINA COMPLETA", "", banco_trabalhos_domesticos) @app.route('/ordem_rotina') def ordem_rotina(): return responder_banco("\U0001f305 ROTINA DOM\u00c9STICA", "", banco_trabalhos_domesticos) @app.route('/ordem_estudos') def ordem_estudos(): """Gera ordem para estudos auto-ditadas eróticos""" return responder_banco("\U0001f4da ESTUDOS AUTO-DITADAS", "", banco_estudos_auto_ditadas) @app.route('/ordem_home_office') def ordem_home_office(): """Gera ordem para trabalho home office erótico""" return responder_banco("\U0001f4bb HOME OFFICE", "", banco_estudos_auto_ditadas) @app.route('/ordem_trio_bi') def ordem_trio_bi(): return responder_banco("3\ufe0f\u20e3 TRIO BI", "", banco_dois_homens_mulher) @app.route('/ordem_casal_homem_mulher') def ordem_casal_homem_mulher(): """Gera ordem para casal homem e mulher""" return responder_banco("\U0001f46b CASAL HOMEM & MULHER", "", banco_casal_homem_mulher) @app.route('/ordem_sendo_ativo') def ordem_sendo_ativo(): """Gera ordem para o ativo em cena de dois homens""" return responder_banco("\u2694\ufe0f SENDO ATIVO", "", banco_sendo_ativo) @app.route('/ordem_sendo_passivo') def ordem_sendo_passivo(): """Gera ordem para o passivo em cena de dois homens""" return responder_banco("\U0001f52e SENDO PASSIVO", "", banco_sendo_passivo) @app.route('/ordem_trabalho_domestico') def ordem_trabalho_domestico(): """Gera ordem de trabalho doméstico picante""" return responder_banco("\U0001f3e0 TRABALHOS DOM\u00c9STICOS", "", banco_trabalhos_domesticos) # --------------------------------------------------------------------------- # Routes - themed orders with custom generators # --------------------------------------------------------------------------- @app.route('/ordem_exibir_bunda_cu') def ordem_exibir_bunda_cu(): """Gera ordem para exibir bunda e cú""" return gerar_resposta( "\U0001f354 EXIBIR BUNDA E C\u00da", "\u26a0\ufe0f A PORTA DA COZINHA. EXIBA-SE.", gerar_ordem_exibir_bunda_cu, ) @app.route('/ordem_exposicao_extrema') def ordem_exposicao_extrema(): """Gera ordem aleatória de Exposição Extrema.""" return responder_banco( "📸 EXPOSIÇÃO EXTREMA", "\u26a0\ufe0f MOSTRE-SE INTEIRO. SEM PUDOR. SEM ESCONDERIJO. OBEDEÇA.", banco_exposicao_extrema, ) @app.route('/ordem_exposicao_publica') def ordem_exposicao_publica(): """Gera ordem aleatória de Exposição Pública.""" return responder_banco( "\U0001f4f7 EXPOSIÇÃO PÚBLICA", "\u26a0\ufe0f RUA, PRAÇA, MERCADO, SHOPPING. MOSTRE TUDO. SEM PUDOR. OBEDEÇA.", banco_exposicao_publica, ) @app.route('/ordem_plug') def ordem_plug(): """Gera ordem aleatória de plug anal""" return responder_banco( "\U0001f511 ORDEM DE PLUG", "\u26a0\ufe0f PLUG NO CU. SINTA CADA MOVIMENTO. OBEDE\u00c7A \u00c0 ORDEM.", banco_plug, ) @app.route('/ordem_apenas_pelado') def ordem_apenas_pelado(): """Gera ordem aleatória para ficar apenas pelado""" return responder_banco( "\U0001f4aa APENAS PELADO", "\u26a0\ufe0f TIRE TUDO. FIQUE PELADO. SEM ROUPA, SEM VERGONHA. SUA PELE \u00c9 SUA VERDADE.", banco_apenas_pelado, ) @app.route('/ordem_plug_fixo_medio') def ordem_plug_fixo_medio(): """Gera ordem aleatória do Plug Fixo Médio""" return responder_banco( "\U0001f4a5 PLUG FIXO M\u00c9DIO", "\u26a0\ufe0f ENFIOU N\u00c3O SAI. O PLUG FIXO M\u00c9DIO \u00c9 SEU COMPANHEIRO. OBEDE\u00c7A.", banco_plug_fixo_medio, ) @app.route('/logs_ordens') def logs_ordens(): """Exibe as últimas linhas do log de ordens do Telegram.""" try: with open('logs_ordens.log', 'r') as f: linhas = f.readlines() ultimas = linhas[-80:] if len(linhas) > 80 else linhas conteudo = ''.join(ultimas) except FileNotFoundError: conteudo = "Arquivo logs_ordens.log não encontrado." except Exception as exc: conteudo = f"Erro ao ler log: {exc}" return Response(conteudo, mimetype='text/plain; charset=utf-8') @app.route('/logs_contos') def logs_contos(): """Exibe as últimas linhas do log de contos do Telegram.""" try: with open('logs_contos.log', 'r') as f: linhas = f.readlines() ultimas = linhas[-80:] if len(linhas) > 80 else linhas conteudo = ''.join(ultimas) except FileNotFoundError: conteudo = "Arquivo logs_contos.log não encontrado." except Exception as exc: conteudo = f"Erro ao ler log: {exc}" return Response(conteudo, mimetype='text/plain; charset=utf-8') @app.route('/ordem_picante') def ordem_picante(): """Gera ordem aleatória picante""" return responder_banco( "\U0001f525 ORDEM PICANTE", "\u26a0\ufe0f TEOR DE PERVERSIDADE ELEVADO. OBEDE\u00c7A SEM HESITAR.", banco_picantes, ) @app.route('/ordem_plug_prostata') def ordem_plug_prostata(): """Gera ordem aleatória com plug comprido para próstata""" return responder_banco( "\U0001f4a5 PLUG COMPRIDO PARA PR\u00d3STATA", "\u26a0\ufe0f PLUG COMPRIDO MASSAGEANDO A PR\u00d3STATA. SINTA O PRAZER PROFUNDO, OBEDE\u00c7A.", banco_plug_prostata, ) @app.route('/ordem_edging') def ordem_edging(): """Gera ordem aleatória de edging""" return responder_banco( "\U0001f3af EDGING", "\u26a0\ufe0f BEIRA DO ORGASMO. CONTROLE O GOZO. OBEDE\u00c7A.", banco_edging, ) @app.route('/ordem_ruined_orgasm') def ordem_ruined_orgasm(): """Gera ordem aleatória de ruined orgasm""" return responder_banco( "\U0001f486 RUINED ORGASM", "\u26a0\ufe0f O GOZO É NEGADO. A FRUSTRAÇÃO É SUA. OBEDE\u00c7A.", banco_ruined_orgasm, ) @app.route('/ordem_gozar_imaginacao') def ordem_gozar_imaginacao(): """Gera ordem aleatória de gozar com imaginação""" return responder_banco( "💦 GOZAR COM IMAGINAÇÃO", "🔥 GOZE AGORA. USE A IMAGINAÇÃO. SEM PUDOR.", banco_gozar_imaginacao, ) @app.route('/ordem_aleatorio2') def ordem_aleatorio2(): """Gera ordem aleatória 2""" return responder_banco( "🎲 ALEATÓRIO 2", "🔥 ORDEM ALEATÓRIA. SEM PUDOR. MUITO PRAZER.", banco_aleatorio2, ) # ── Mega Aleatório ────────────────────────────────────────────────────────── # All banco lists aggregated so a single endpoint can pick from ANY category. _BANCOS_MEGA = [ ("🔥 ALEATÓRIO", banco_aleatorio), ("🎲 ALEATÓRIO 2", banco_aleatorio2), ("🔌 PLUG", banco_plug), ("🔌 PLUG FIXO MÉDIO", banco_plug_fixo_medio), ("🔌 PLUG PRÓSTATA", banco_plug_prostata), ("👤 APENAS PELADO", banco_apenas_pelado), ("🔥 PICANTES", banco_picantes), ("🎯 EDGING", banco_edging), ("💋 RUINED ORGASM", banco_ruined_orgasm), ("💦 GOZAR IMAGINAÇÃO", banco_gozar_imaginacao), ("📸 EXPOSIÇÃO PÚBLICA", banco_exposicao_publica), ("🌙 CASTIGO", banco_castigo), ("🎭 FANTASIAS", banco_fantasias), ("🌡️ SENSORIAL", banco_sensorial), ("🤸 ALONGAMENTO/GINÁSTICA", banco_alongamento_ginastica), ("🧘 ALONGAMENTO/RELAXAMENTO", banco_alongamento_relaxamento), ("🩺 CONSULTA", banco_consulta), ("🩺 CONSULTA/EXPOSIÇÃO", banco_consulta_exposicao), ("💕 ESPOSA VISITA", banco_esposa_visita), ("💕 ESPOSA BRINCA MARIDO", banco_esposa_brinca_marido), ("🪑 SENTADO CADEIRA", banco_sentado_cadeira), ("🔮 SENDO PASSIVO", banco_sendo_passivo), ("⚔️ SENDO ATIVO", banco_sendo_ativo), ("👫 CASAL HOMEM/MULHER", banco_casal_homem_mulher), ("3️⃣ TRIO BI", banco_dois_homens_mulher), ("👑 DUPLA ATIVO/PASSIVO GOLD", banco_dupla_ativo_passivo_gold), ("💋 MULHER ATIVA", banco_mulher_ativa), ("🛏️ SOZINHO CAMA", banco_sozinho_cama), ("🏠 TRAB. DOMÉSTICOS", banco_trabalhos_domesticos), ("🌅 ROTINA DOMÉSTICA", banco_rotina_domestica), ("📋 ROTINA COMPLETA", banco_rotina_diaria_inteira), ("📚 ESTUDOS", banco_estudos_auto_ditadas), ("💻 HOME OFFICE", banco_trabalho_home_office), ("🍆 PIRU", banco_piru), ("💋 SUA VEZ MESTRE", banco_sua_vez_mestre), ("💦 MAST. INDIVIDUAL", banco_masturbacao_individual), ("💦 MAST. PARCEIRO", banco_masturbacao_parceiro), ("🚿 BANHO", banco_banho), ("🖐️ ANAL DEDOS", banco_anal_dedos), ("💦 GOZO ANAL", banco_gozo_anal), ("💦 TOMA GOZO", banco_toma_gozo), ("📳 VIBRAÇÃO", banco_vibracao), ("🎯 CONTROLE GOZO", banco_controle_gozo), ("👀 EXIBICIONISMO", banco_exibicionismo), ("👄 SEXO ORAL", banco_sexo_oral), ("🔫 PENETRAÇÃO", banco_penetracao), ("💦 SOZINHO MAST.", banco_sozinho_masturbacao), ("📳 SOZINHO VIBRAÇÃO", banco_sozinho_vibracao), ("👀 SOZINHO EXPOSIÇÃO", banco_sozinho_exposicao), ("😵 SOZINHO HUMILHAÇÃO", banco_sozinho_humilhacao), ("🔌 SOZINHO PLUG", banco_sozinho_plug), ("🎯 SOZINHO EDGING", banco_sozinho_edging), ("🔌 PLUG SUPER GROSSO (4.5cm)", banco_plug_super_grosso), ("🥚 MASTURBADOR OVO", banco_masturbador_ovo), ("🔥 PLUG NA PAREDE", banco_plug_parede), ("🔥 PLUG SUPER GROSSO FESTINHA", banco_plug_super_grosso_festinha), ("🎪 ESPOSA, AMIGAS & AMIGOS", banco_esposa_amigas_amigos), ("🎯 GOZO CONTROLADO", banco_gozo_controlado), ] @app.route('/ordem_mega_aleatorio') def ordem_mega_aleatorio(): """Gera ordem de qualquer categoria, sorteada aleatoriamente.""" nome, banco = random.choice(_BANCOS_MEGA) return responder_banco( f"🎲 MEGA ALEATÓRIO — {nome}", "🔥 QUALQUER CATEGORIA. QUALQUER ORDEM. OBEDEÇA.", banco, ) @app.route('/ordem_cenario_completo') def ordem_cenario_completo(): """Gera um cenário completo combinando local, posição, acessório, duração, ação e regra.""" return gerar_resposta( "\U0001f3aa CEN\u00c1RIO COMPLETO", "\u26a0\ufe0f LOCAL, POSI\u00c7\u00c3O, ACESS\u00d3RIO, DURA\u00c7\u00c3O, A\u00c7\u00c3O E REGRA. CUMPRA TUDO.", gerar_cenario_completo, ) @app.route('/ia_ordem_cenario_completo') def ia_ordem_cenario_completo(): """Ask the AI to generate an immersive order based on a random Cenário Completo. The AI receives 6 random components (local, posição, acessório, duração, ação, regra) and weaves them into a single vivid order. """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f3aa CEN\u00c1RIO COMPLETO (IA)", "\u26a0\ufe0f CEN\u00c1RIO SORTEADO E INTERPRETADO PELA IA. CUMPRA TUDO.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_cenario_completo_ia(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para cenario completo: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar cenario completo via IA") return jsonify({'erro': f"Erro ao gerar o cenário. ({exc})"}), 500 return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ordem_dupla_ativo_passivo_gold') def ordem_dupla_ativo_passivo_gold(): """Gera ordem aleatória de Dupla Ativo/Passivo Gold.""" return gerar_resposta( "\U0001f451 DUPLA ATIVO/PASSIVO GOLD", "\u26a0\ufe0f O MESTRE COMANDA O ATIVO E O PASSIVO. OBEDE\u00c7AM.", gerar_ordem_dupla_ativo_passivo_gold, ) @app.route('/ordem_mulher_ativa') def ordem_mulher_ativa(): """Gera ordem aleatória de Mulher Ativa.""" return gerar_resposta( "\U0001f484 MULHER ATIVA", "\u26a0\ufe0f ELA COMANDA. ELE OBEDECE. SEM LIMITES.", gerar_ordem_mulher_ativa, ) @app.route('/ordem_esposa_brinca_marido') def ordem_esposa_brinca_marido(): """Gera ordem aleatória de Esposa Brinca com o Marido.""" return gerar_resposta( "\U0001f495 ESPOSA BRINCA COM O MARIDO", "\u26a0\ufe0f ELA ADORA A BUNDA, O C\u00da E O PAU DELE. N\u00c3O IMPORTA ONDE ESTEJA, NEM QUEM EST\u00c1 VISITANDO. ELA BRINCA.", gerar_ordem_esposa_brinca_marido, ) @app.route('/ordem_desafio_rapido') def ordem_desafio_rapido(): """Gera ordem aleatória de Desafio Rápido.""" return responder_banco( "⚡ DESAFIO RÁPIDO", "\u26a0\ufe0f 30 DESAFIOS CURTOS E INTENSOS. CUMPRA TUDO SEM RECLAMAR.", banco_desafio_rapido, ) @app.route('/ordem_jogo_verdade') def ordem_jogo_verdade(): """Gera ordem de Verdade ou Desafio.""" return responder_banco( "🎭 JOGO DA VERDADE", "\u26a0\ufe0f VOCÊ ESCOLHE: VERDADE OU DESAFIO. SE ERRAR, PUNIÇÃO EXTRA.", banco_jogo_verdade, ) @app.route('/ordem_teste_obediencia') def ordem_teste_obediencia(): """Gera Teste de Obediência com 3 ordens sequenciais.""" try: ordens = random.sample(banco_teste_obediencia, 3) cabecalho = gerar_cabecalho( "🧪 TESTE DE OBEDIÊNCIA", "\u26a0\ufe0f 3 ORDENS SEQUENCIAIS. NÃO PULE NENHUMA. CUMPRA TODAS.", ) texto = cabecalho + "\n\n".join(ordens) return jsonify({ 'ordem': texto, 'timestamp': time.strftime('%H:%M:%S') }) except Exception as exc: return jsonify({'erro': f"Erro ao gerar teste: {exc}"}), 500 @app.route('/ordem_roleta_castigo') def ordem_roleta_castigo(): """Gera ordem da Roleta do Castigo - categoria aleatória.""" try: opcoes = [ ("🌙 CASTIGO & DISCIPLINA", banco_castigo), ("🔥 EXPOSIÇÃO PÚBLICA", banco_exposicao_publica), ("👀 SOZINHO HUMILHAÇÃO", banco_sozinho_humilhacao), ("📸 EXPOSIÇÃO EXTREMA", banco_exposicao_extrema), ("🔌 PLUG", banco_plug), ("🔌 PLUG FIXO MÉDIO", banco_plug_fixo_medio), ("⚡ PLUG DE PRÓSTATA", banco_plug_prostata), ("🔴 PLUG SUPER GROSSO (4.5cm)", banco_plug_super_grosso), ("🎯 EDGING", banco_edging), ("💋 RUINED ORGASM", banco_ruined_orgasm), ("💦 GOZAR IMAGINAÇÃO", banco_gozar_imaginacao), ("🎲 ALEATÓRIO 2", banco_aleatorio2), ("🔥 PICANTES", banco_picantes), ("🌙 CASTIGO", banco_castigo), ("🎭 FANTASIAS & ROLEPLAY", banco_fantasias), ("🌡️ BRINCADEIRAS SENSORIAIS", banco_sensorial), ("🩺 FANTASIA DA CONSULTA", banco_consulta), ("🩺 CONSULTA & EXPOSIÇÃO", banco_consulta_exposicao), ("💕 MINHA ESPOSA RECEBE VISITA", banco_esposa_visita), ("🪑 SENTADO NA CADEIRA", banco_sentado_cadeira), ("🔮 SENDO PASSIVO", banco_sendo_passivo), ("⚔️ SENDO ATIVO", banco_sendo_ativo), ("👫 CASAL HOMEM & MULHER", banco_casal_homem_mulher), ("3️⃣ TRIO BI", banco_dois_homens_mulher), ("👑 DUPLA ATIVO/PASSIVO GOLD", banco_dupla_ativo_passivo_gold), ("💋 MULHER ATIVA", banco_mulher_ativa), ("🛏️ SOZINHO CAMA", banco_sozinho_cama), ("🏠 TRAB. DOMÉSTICOS", banco_trabalhos_domesticos), ("📚 ESTUDOS AUTO-DITADAS", banco_estudos_auto_ditadas), ("💻 HOME OFFICE", banco_trabalho_home_office), ] nome_categoria, banco = random.choice(opcoes) try: ordem = random.choice(banco) except Exception: ordem = "Nenhuma ordem disponível. Tente novamente." cabecalho = gerar_cabecalho( "🎰 ROLETA DO CASTIGO", f"\u26a0\ufe0f CATEGORIA SORTEADA: {nome_categoria}. OBEDEÇA.", ) return jsonify({ 'ordem': cabecalho + ordem, 'timestamp': time.strftime('%H:%M:%S') }) except Exception as exc: return jsonify({'erro': f"Erro ao girar roleta: {exc}"}), 500 @app.route('/ordem_missao_secreta') def ordem_missao_secreta(): """Gera ordem de Missão Secreta.""" return responder_banco( "🕵️ MISSÃO SECRETA", "\u26a0\ufe0f SAIA DE CASA OU FAÇA ALGO EM PÚBLICO SEM SER PERCEBIDO. CUMPRA TUDO.", banco_missao_secreta, ) @app.route('/ordem_desafio_tempo') def ordem_desafio_tempo(): """Gera ordem de Desafio do Tempo.""" return responder_banco( "⏱️ DESAFIO DO TEMPO", "\u26a0\ufe0f TEMPO LIMITE ESPECÍFICO. CUMPRA NO TEMPO DADO. SEM DESCULPAS.", banco_desafio_tempo, ) @app.route('/ordem_castigo') def ordem_castigo(): """Gera ordem aleatória de Castigo & Disciplina.""" return responder_banco( "\U0001f319 CASTIGO & DISCIPLINA", "\u26a0\ufe0f O SUBMISSO FOI DESOBEDIENTE. ACEITE A PUNI\u00c7\u00c3O SEM RECLAMAR.", banco_castigo, ) @app.route('/ordem_fantasias') def ordem_fantasias(): """Gera ordem aleatória de Fantasiais & Roleplay.""" return responder_banco( "\U0001f3ad FANTASIAS & ROLEPLAY", "\U0001f525 ENTRE NO CENARIO. VIVA A FANTASIA E OBEDE\u00c7A O PAPEL.", banco_fantasias, ) @app.route('/ordem_sensorial') def ordem_sensorial(): """Gera ordem aleatória de Brincadeiras Sensoriais.""" return responder_banco( "\U0001f321\ufe0f BRINCADEIRAS SENSORIAIS", "\u2744\ufe0f GELO, VELAS, PENAS E VENDAGEM. SINTA CADA ESTIMULO.", banco_sensorial, ) @app.route('/ordem_alongamento_ginastica') def ordem_alongamento_ginastica(): """Gera ordem aleatória de Alongamento & Ginástica.""" return responder_banco( "\U0001f3cb\ufe0f ALONGAMENTO & GIN\u00c1STICA", "\u26a0\ufe0f O MESTRE COMANDA O CORPO. ALONGUE, SUJE E OBEDE\u00c7A SEM RECLAMAR.", banco_alongamento_ginastica, ) @app.route('/ordem_consulta') def ordem_consulta(): """Gera ordem aleatória de Fantasia da Consulta.""" return responder_banco( "\U0001fa7a FANTASIA DA CONSULTA", "\u26a0\ufe0f DEITE-SE NA MACA. OBEDE\u00c7A A MEDICA, O MEDICO, AS ENFERMEIRAS E AS ESTAGIARIAS.", banco_consulta, ) @app.route('/ordem_consulta_exposicao') def ordem_consulta_exposicao(): """Gera ordem aleatória de Fantasia da Consulta e Exposição.""" return responder_banco( "\U0001fa7a FANTASIA DA CONSULTA E EXPOSIÇÃO", "\u26a0\ufe0f NU PELOS CORREDORES. PLUGS, GOZOS SEGUIDOS, ELASTICIDADE E EXPOSIÇÃO NO BALCÃO. OBEDEÇA SEM PUDOR.", banco_consulta_exposicao, ) @app.route('/ordem_esposa_visita') def ordem_esposa_visita(): """Gera ordem aleatória de Minha Esposa Recebe Visita.""" return responder_banco( "\U0001f495 MINHA ESPOSA RECEBE VISITA", "\u26a0\ufe0f A ESPOSA MANDA. O AMIGO VEM, ELA TRANSAR NA SUA FRENTE E VOCÊ OBEDECE TUDO. ATIVO OU PASSIVO.", banco_esposa_visita, ) @app.route('/dupla_ativo_passivo_gold_telegram', methods=['POST']) def dupla_ativo_passivo_gold_telegram(): """Generate a Dupla Ativo/Passivo Gold order and send it to Telegram. Triggered by the frontend every 5 minutes when the "👑 DUPLA ATIVO/PASSIVO GOLD" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "\U0001f451 DUPLA ATIVO/PASSIVO GOLD", "\u26a0\ufe0f O MESTRE COMANDA O ATIVO E O PASSIVO.", ) try: ordem = cabecalho + gerar_ordem_dupla_ativo_passivo_gold() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Dupla Ativo/Passivo Gold") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/mulher_ativa_telegram', methods=['POST']) def mulher_ativa_telegram(): """Generate a Mulher Ativa order and send it to Telegram. Triggered by the frontend every 5 minutes when the "💋 MULHER ATIVA" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "\U0001f484 MULHER ATIVA", "\u26a0\ufe0f ELA COMANDA. ELE OBEDECE.", ) try: ordem = cabecalho + gerar_ordem_mulher_ativa() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Mulher Ativa") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/sozinho_cama_telegram', methods=['POST']) def sozinho_cama_telegram(): """Generate a Sozinho Deitado na Cama order and send it to Telegram. Triggered by the frontend every 5 minutes when the "🛏️ SOZINHO DEITADO NA CAMA" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "\U0001f6cf\ufe0f SOZINHO DEITADO NA CAMA", "\U0001f525 VOCE ESTA PELADO, DEITADO NA CAMA. OBEDECA SOZINHO.", ) try: ordem = cabecalho + gerar_ordem_sozinho_cama() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Sozinho Deitado na Cama") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/alongamento_relaxamento_telegram', methods=['POST']) def alongamento_relaxamento_telegram(): """Generate a Alongamento & Relaxamento order and send it to Telegram. Triggered by the frontend every 55 minutes when the "🧘 ALONGAMENTO & RELAXAMENTO" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). Designed as a spicy break for someone who programs many hours straight. """ cabecalho = gerar_cabecalho( "\U0001f9d8 ALONGAMENTO & RELAXAMENTO", "\u23f1 PAUSA DO PROGRAMADOR. LEVANTE-SE E ALONGUE. OBEDECA.", ) try: ordem = cabecalho + gerar_ordem_alongamento_relaxamento() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Alongamento & Relaxamento") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) # --------------------------------------------------------------------------- # Routes - IA (llama.cpp local) # --------------------------------------------------------------------------- # Keep only the most recent turns so the session cookie does not blow up. IA_HISTORICO_MAX = 20 @app.route('/ia_status') def ia_status(): """Report whether the active AI provider is reachable and its settings.""" config = obter_config_publica() prov = provedor_atual() prov_label = { "llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun", }.get(prov, prov) return jsonify({ 'disponivel': servidor_disponivel(), 'provedor': config['provedor'], 'modelo': config['openrouter_model'], 'api_key_definida': config['openrouter_api_key_definida'], 'openrouter_base_url': config['openrouter_base_url'], 'zai_modelo': config['zai_model'], 'zai_api_key_definida': config['zai_api_key_definida'], 'zai_base_url': config['zai_base_url'], 'stepfun_modelo': config['stepfun_model'], 'stepfun_api_key_definida': config['stepfun_api_key_definida'], 'stepfun_base_url': config['stepfun_base_url'], 'llamacpp_base_url': config['llamacpp_base_url'], }) @app.route('/ia_ordem') def ia_ordem(): """Ask the AI to generate a single Order (Ordem).""" prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( f"\U0001f916 ORDEM DA IA ({prov_label})", "\u26a0\ufe0f ORDEM GERADA PELO MODELO. OBEDE\u00c7A.", ) params = session.get('ia_params', {}) try: ordem = gerar_ordem_ia(session.get('modo', 'sozinho'), params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem: %s", exc) ordem = f"\u26a0\ufe0f {exc}" except Exception as exc: # noqa: BLE001 logger.exception("Erro inesperado ao gerar ordem via IA") ordem = f"\u26a0\ufe0f Erro ao gerar a ordem com a IA.\n({exc})" return responder_ordem(cabecalho + ordem) @app.route('/ia_ordem_sentado_trabalhando') def ia_ordem_sentado_trabalhando(): """Ask the AI to generate a 'Sentado Trabalhando' order. The user remains seated at the computer. The order is also sent to the main Telegram chat (TELEGRAM_CHAT_ID). """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f4bb SENTADO TRABALHANDO", "\u26a0\ufe0f ORDEM PARA FAZER SENTADO NA CADEIRA, SEM SAIR DO LUGAR.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_sentado_trabalhando(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem sentado trabalhando: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar ordem sentado trabalhando") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/ia_ordem_plug_fixo_medio') def ia_ordem_plug_fixo_medio(): """Ask the AI to generate a 'Plug Fixo Médio' order. The submissive already has the medium fixed plug inserted. The order revolves around the plug: movements, sensations, tasks, etc. """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f4a5 PLUG FIXO M\u00c9DIO (IA)", "\u26a0\ufe0f ORDEM GERADA PELA IA COM O PLUG FIXO M\u00c9DIO ENFIADO NO C\u00da.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_plug_fixo_medio(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem plug fixo medio: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar ordem plug fixo medio via IA") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ia_ordem_3') def ia_ordem_3(): """Ask the AI to generate an Ordens da IA 3 order. Focused on pau, cabeça do pau, bunda, cú, dedo(s), plug, pelado, edging, ruined orgasm. No gozar, no chão, no cama. """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f916 ORDENS DA IA 3", "\u26a0\ufe0f FOCO: PAU, CABEÇA DO PAU, BUNDA, CÚ. GOZAR PROIBIDO.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_ia_3(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem 3: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar ordem IA 3") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ia_ordem_4') def ia_ordem_4(): """Ask the AI to generate an immersive extreme order. Focused on exposure, humiliation, forbidden pleasure, risk of being seen, and the sensation of being naked in public or semi-public. """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f916 ORDEM EXTREMA (IA 4)", "\u26a0\ufe0f EXPOSIÇÃO, HUMILHAÇÃO, RISCO. CUMPRA TUDO.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_ia_4(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem 4: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar ordem IA 4") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ia_ordem_3_telegram', methods=['POST']) def ia_ordem_3_telegram(): """Generate an Ordens da IA 3 order and send it to Telegram. Triggered by the frontend every 10 minutes when the "🤖 ORDENS DA IA 3 (10min)" toggle button is active. """ prov = provedor_atual() prov_label = {"llamacpp": "llama.cpp (local)", "openrouter": "OpenRouter", "zai": "z.ai", "stepfun": "StepFun"}[prov] cabecalho = gerar_cabecalho( "\U0001f916 ORDENS DA IA 3", "\u26a0\ufe0f FOCO: PAU, CABEÇA DO PAU, BUNDA, CÚ. GOZAR PROIBIDO.", ) params = session.get('ia_params', {}) try: ordem = cabecalho + gerar_ordem_ia_3(params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem 3 telegram: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao gerar ordem IA 3 telegram") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/sentado_cadeira_cron_telegram', methods=['POST']) def sentado_cadeira_cron_telegram(): """Generate a Sentado na Cadeira order and send it to Telegram. Triggered by the frontend every 15 minutes when the "🪑 CRON SENTADO (15min)" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "\U0001faa9 SENTADO NA CADEIRA", "\u26a0\ufe0f ORDEM PARA FAZER SENTADO NA CADEIRA. VARIADOS ESTADOS.", ) try: ordem = cabecalho + random.choice(banco_sentado_cadeira) except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Sentado na Cadeira") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/ordem_sentado_cadeira') def ordem_sentado_cadeira(): """Gera ordem aleatória para fazer sentado na cadeira""" return responder_banco( "\U0001faa9 SENTADO NA CADEIRA", "\u26a0\ufe0f ORDEM PARA FAZER SENTADO NA CADEIRA. VARIADOS ESTADOS.", banco_sentado_cadeira, ) @app.route('/ordem_plug_super_grosso') def ordem_plug_super_grosso(): """Gera ordem aleatória com o Plug Super Grosso de 4.5cm""" return responder_banco( "\U0001f525 PLUG SUPER GROSSO (4.5cm)", "\u26a0\ufe0f 4.5cm DE PURO SILICONE ARREGAÇANDO SEU CU. OBEDEÇA CADA PALAVRA.", banco_plug_super_grosso, ) @app.route('/ordem_masturbador_ovo') def ordem_masturbador_ovo(): """Gera ordem aleatória com o Masturbador Ovo para cabeça do pau""" return responder_banco( "\U0001f95a MASTURBADOR OVO", "\u26a0\ufe0f OVO VIBRADOR NA CABEÇA DO PAU. CABECINHA VAI PULSAR. OBEDEÇA.", banco_masturbador_ovo, ) @app.route('/ordem_plug_parede') def ordem_plug_parede(): """Gera ordem aleatória com plug pressionado contra a parede""" return responder_banco( "\U0001f525 PLUG NA PAREDE", "\u26a0\ufe0f JOELHOS NO CHÃO, PLUG PRESSIONADO CONTRA A PAREDE, CU EXPOSTO. OBEDEÇA.", banco_plug_parede, ) @app.route('/ordem_plug_super_grosso_festinha') def ordem_plug_super_grosso_festinha(): """Gera ordem para festinha privada com plug super grosso""" return responder_banco( "\U0001f525 FESTINHA PLUG SUPER GROSSO", "\u26a0\ufe0f VOCÊ É O CENTRO DAS ATENÇÕES. SUA ESPOSA, AMIGAS E AMIGOS. OBEDEÇA.", banco_plug_super_grosso_festinha, ) @app.route('/ordem_esposa_amigas_amigos') def ordem_esposa_amigas_amigos(): """Gera ordem para putaria geral com esposa, amigas e amigos""" return responder_banco( "\U0001f3aa ESPOSA, AMIGAS & AMIGOS", "\u26a0\ufe0f PUTARIA GERAL — ELES FAZEM ABSOLUTAMENTE TUDO COM VOCÊ. OBEDEÇA.", banco_esposa_amigas_amigos, ) @app.route('/ordem_gozo_controlado') def ordem_gozo_controlado(): """Gera ordem de gozo controlado por esposa, amigas e amigos""" return responder_banco( "\U0001f3af GOZO CONTROLADO", "\u26a0\ufe0f ELES DECIDEM QUANTAS VEZES VOCÊ GOZA. EDGING, RUINED, FORÇADO. OBEDEÇA.", banco_gozo_controlado, ) @app.route('/plug_fixo_medio_telegram', methods=['POST']) def plug_fixo_medio_telegram(): """Generate a Plug Fixo Médio order and send it to Telegram. Triggered by the frontend every 5 minutes when the "🔌 PLUG FIXO MÉDIO (5min)" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "\U0001f4a5 PLUG FIXO M\u00c9DIO", "\u26a0\ufe0f ENFIOU N\u00c3O SAI. O PLUG FIXO M\u00c9DIO \u00c9 SEU COMPANHEIRO.", ) try: ordem = cabecalho + random.choice(banco_plug_fixo_medio) except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Plug Fixo Médio") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/piru_telegram', methods=['POST']) def piru_telegram(): """Generate a Piru order and send it to Telegram. Triggered by the frontend every 5 minutes when the "🍆 PIRU (5min)" toggle button is active. The order is sent to the main chat (TELEGRAM_CHAT_ID). """ cabecalho = gerar_cabecalho( "🍆 PIRU", "\u26a0\ufe0f TRABALHE O PAU. SEM PUDOR. OBEDEÇA.", ) try: ordem = cabecalho + random.choice(banco_piru) except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Piru") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/piru_sem_gozar_telegram', methods=['POST']) def piru_sem_gozar_telegram(): """Generate a Piru Sem Gozar order and send to Telegram. Triggered by the frontend every 5 minutes when the "🍆 PIRU SEM GOZAR (5min)" toggle button is active. """ cabecalho = gerar_cabecalho( "\U0001f4a5 PIRU SEM GOZAR", "\u26a0\ufe0f GOZAR \u00c9 PROIBIDO. CONTROLE O PIRU.", ) try: ordem = cabecalho + random.choice(banco_piru_sem_gozar) except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Piru Sem Gozar") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/sua_vez_mestre', methods=['POST']) def sua_vez_mestre(): """Generate a role-reversal order and send it to Telegram. The "SUA VEZ MESTRE" toggle sends orders where the submissive gives commands back to the Mestre. Sent to the main chat. """ cabecalho = gerar_cabecalho( "🍑 SUA VEZ MESTRE", "🔁 O escravo comanda. Você se entrega agora.", ) try: ordem = cabecalho + random.choice(banco_sua_vez_mestre) except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar ordem Sua Vez Mestre") return jsonify({'erro': f"Erro ao gerar a ordem. ({exc})"}), 500 resultado = enviar_telegram_principal(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/ia_chat', methods=['POST']) def ia_chat(): """Send a user message to the AI and return the assistant reply. Conversation history is kept in the session (capped to IA_HISTORICO_MAX turns) so the chat has context across messages. """ dados = request.get_json() or {} mensagem = (dados.get('mensagem') or '').strip() if not mensagem: return jsonify({'erro': 'Mensagem vazia.'}), 400 historico = session.get('ia_historico', []) params = session.get('ia_params', {}) persona = dados.get('persona') or 'mestre' try: resposta = conversar_ia(historico, mensagem, params=params, persona=persona) except LlamaCppError as exc: logger.warning("IA indisponivel para chat: %s", exc) return jsonify({ 'erro': str(exc), 'resposta': None, }), 503 except Exception as exc: # noqa: BLE001 logger.exception("Erro inesperado no chat da IA") return jsonify({'erro': f"Erro ao conversar com a IA. ({exc})"}), 500 # Persist the exchange so the next message has context. historico.append({'role': 'user', 'content': mensagem}) historico.append({'role': 'assistant', 'content': resposta}) session['ia_historico'] = historico[-IA_HISTORICO_MAX:] return jsonify({ 'resposta': resposta, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ia_limpar_chat', methods=['POST']) def ia_limpar_chat(): """Clear the stored IA conversation history.""" session.pop('ia_historico', None) return jsonify({'status': 'ok'}) @app.route('/ia_conversar_inicio', methods=['POST']) def ia_conversar_inicio(): """Make the AI open a free-flowing conversation (companion persona). The AI speaks first, using the ``"conversa"`` persona — a warm, instigating companion (namorada/mestre/amigo) that knows the game data and never lets the dialogue die. Triggered by the "Conversar" button so the chat starts with the AI provoking the user. """ params = session.get('ia_params', {}) historico = session.get('ia_historico', []) try: resposta = conversar_ia( historico, "(O usuário acabou de chegar e quer conversar. Comece você: " "apresente-se no papel que preferir, seja caloroso(a) e já faça " "uma pergunta instigante para ele responder.)", params=params, persona="conversa", ) except LlamaCppError as exc: logger.warning("IA indisponivel para iniciar conversa: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: # noqa: BLE001 logger.exception("Erro inesperado ao iniciar conversa") return jsonify({'erro': f"Erro ao iniciar a conversa. ({exc})"}), 500 # Seed the history with the AI's opening line so the next reply has context. historico.append({'role': 'assistant', 'content': resposta}) session['ia_historico'] = historico[-IA_HISTORICO_MAX:] return jsonify({ 'resposta': resposta, 'timestamp': time.strftime('%H:%M:%S'), }) @app.route('/ia_conversar_cenario', methods=['POST']) def ia_conversar_cenario(): """Generate a random Cenário Completo and start a chat with the AI using it as context. The AI receives the 6 scenario components and roleplays as the Master, incorporating the scenario into the conversation. """ from dados_cenario_completo import ( LOCAIS, POSICOES, ACESSORIOS, DURACOES, ACOES, REGRAS, ) local = random.choice(LOCAIS) posicao = random.choice(POSICOES) acessorio = random.choice(ACESSORIOS) duracao = random.choice(DURACOES) acao = random.choice(ACOES) regra = random.choice(REGRAS) cenario_texto = ( f"CENÁRIO COMPLETO SORTEADO:\n" f"• LOCAL: {local}\n" f"• POSIÇÃO: {posicao}\n" f"• ACESSÓRIO: {acessorio}\n" f"• DURAÇÃO: {duracao}\n" f"• AÇÃO: {acao}\n" f"• REGRA: {regra}\n" ) session['cenario_ativo'] = { 'local': local, 'posicao': posicao, 'acessorio': acessorio, 'duracao': duracao, 'acao': acao, 'regra': regra, } params = session.get('ia_params', {}) historico = session.get('ia_historico', []) try: resposta = conversar_ia( historico, f"(Um cenário completo foi sorteado para a sessão de hoje.\n\n" f"{cenario_texto}\n\n" f"Apresente-se no papel que preferir, seja caloroso(a) e já faça " f"uma pergunta instigante para ele responder, usando este cenário " f"como contexto da conversa.)", params=params, persona="conversa", ) except LlamaCppError as exc: logger.warning("IA indisponivel para conversa com cenario: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado ao iniciar conversa com cenario") return jsonify({'erro': f"Erro ao iniciar a conversa. ({exc})"}), 500 historico.append({'role': 'assistant', 'content': resposta}) session['ia_historico'] = historico[-100:] return jsonify({ 'resposta': resposta, 'cenario': cenario_texto, 'timestamp': time.strftime('%H:%M:%S'), }) # Rotação de mensagens para o auto-chat da IA. _AUTO_CHAT_PROMPTS = [ "(Voce eh O MESTRE real, presente, dominador. De uma ordem sexual explicita e direta " "para o submisso. Mande ele lamber seu cu, chupar seu pau, enfiar o dedo no cu dele, " "arregar a bunda. Seja criativo e use linguagem de verdade, como se estivesse ali ao vivo.)", "(O MESTRE esta de olho no submisso. Mande ele se ajoelhar, abrir a boca e se preparar " "para receber seu pau. Ordene que ele chupe com vontade, sem parar, ate voce mandar " "parar. Seja explicito e dominador.)", "(O MESTRE quer usar o submisso agora. De uma ordem envolvendo o cu do submisso: mande " "ele enfiar o dedo no proprio cu, arregar a bunda, se esfregar, se preparar para ser " "penetrado. Use linguagem crua e direta.)", "(O MESTRE esta com o pau duro e o submisso vai servir. Mande ele lamber, chupar, " "engolir. Diga que voce vai gozar na boca dele e ele vai engolir cada gota. Seja vivido e real.)", "(O MESTRE quer humilhar e dominar o submisso. Mande ele se despir, ficar de quatro, " "arregar a bunda e mostrar o cu. Depois de uma ordem sexual explicita. Seja criativo.)", "(O MESTRE esta excitado e quer usar o corpo do submisso. Mande ele deitar de brucos, " "abrir as pernas, enfiar o dedo no cu e se preparar. Diga que voce vai enfiar seu pau " "no cu dele e comer com forca. Seja direto e agressivo.)", "(O MESTRE quer ouvir o submisso gemer. Mande ele se masturbar enquanto chupa um pau " "imaginario. Diga para ele lamber os labios, babar, e implorar por mais. Seja provocante e dominador.)", "(O MESTRE vai gozar. Mande o submisso abrir a boca, esticar a lingua e esperar. " "Diga que voce vai jorrar toda a porra quente na boca dele e ele vai engolir sem " "reclamar. Depois mande ele lamber o pau para limpar.)", "(O MESTRE acordou com vontade. Mande o submisso chupar seu pau agora, bem devagar, " "sentindo cada centimetro. Depois acelera e enfia goela abaixo. Seja explicito e mandao.)", "(O MESTRE quer ver o submisso se tocando. Mande ele passar a mao no proprio corpo, " "beliscar os mamilos, descer a mao ate o pau e se masturbar enquanto voce observa. " "De ordens curtas e imperativas.)", "(O MESTRE esta de mau humor e o submisso vai pagar. Mande ele se ajoelhar, abrir a " "boca e chupar seu pau sem usar as maos. Se ele reclamar, a ordem dobra. Seja agressivo.)", "(O MESTRE quer provar a obediencia do submisso. Mande ele lamber o chao, depois " "lamber seus pes, depois chupar seu pau. Escale a humilhacao. Seja criativo e cruel.)", ] @app.route('/ia_auto_chat', methods=['POST']) def ia_auto_chat(): """Trigger the AI to send an automatic message to the chat. Called by the frontend every 5 minutes when auto-chat is active. Uses the 'mestre' persona so the AI speaks as the dominant Master, giving explicit sexual orders. """ import random as _random mensagem_auto = _random.choice(_AUTO_CHAT_PROMPTS) historico = session.get('ia_historico', []) params = session.get('ia_params', {}) try: resposta = conversar_ia( historico, mensagem_auto, params=params, persona="mestre", ) except LlamaCppError as exc: logger.warning("IA indisponivel para auto-chat: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: logger.exception("Erro inesperado no auto-chat da IA") return jsonify({'erro': f"Erro no auto-chat. ({exc})"}), 500 # Store the exchange so context is preserved. historico.append({'role': 'user', 'content': mensagem_auto}) historico.append({'role': 'assistant', 'content': resposta}) session['ia_historico'] = historico[-IA_HISTORICO_MAX:] return jsonify({ 'resposta': resposta, 'timestamp': time.strftime('%H:%M:%S'), 'mensagem_auto': mensagem_auto, }) @app.route('/ia_ordem_amigos', methods=['POST']) def ia_ordem_amigos(): """Generate an AI order and send it to the two friends on Telegram. Triggered by the frontend every 5 minutes when the "📲 ORDENS P/ AMIGOS" button is active in the IA (Modelo) panel. """ params = session.get('ia_params', {}) try: ordem = gerar_ordem_ia(session.get('modo', 'sozinho'), params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem aos amigos: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: # noqa: BLE001 logger.exception("Erro inesperado ao gerar ordem para amigos") return jsonify({'erro': f"Erro ao gerar a ordem com a IA. ({exc})"}), 500 resultado = enviar_telegram_amigos(ordem) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/ia_ordem_amigos3', methods=['POST']) def ia_ordem_amigos3(): """Generate an AI order and send it to three friends on Telegram. Triggered by the frontend every 5 minutes when the "📲 ORDENS P/ 3 AMIGOS" button is active in the IA (Modelo) panel. Sends to AMIGO1 + AMIGO2 + AMIGO3. """ params = session.get('ia_params', {}) try: ordem = gerar_ordem_ia(session.get('modo', 'sozinho'), params=params) except LlamaCppError as exc: logger.warning("IA indisponivel para ordem aos 3 amigos: %s", exc) return jsonify({'erro': str(exc)}), 503 except Exception as exc: # noqa: BLE001 logger.exception("Erro inesperado ao gerar ordem para 3 amigos") return jsonify({'erro': f"Erro ao gerar a ordem com a IA. ({exc})"}), 500 chat_ids = [ os.environ.get("TELEGRAM_CHAT_ID_AMIGO1", "").strip(), os.environ.get("TELEGRAM_CHAT_ID_AMIGO2", "").strip(), os.environ.get("TELEGRAM_CHAT_ID_AMIGO3", "").strip(), ] resultado = enviar_telegram_amigos(ordem, chat_ids=chat_ids) return jsonify({ 'ordem': ordem, 'timestamp': time.strftime('%H:%M:%S'), 'enviados': resultado['enviados'], 'erros': resultado.get('erros', []), 'erro': resultado.get('erro'), }) @app.route('/ia_configurar', methods=['POST']) def ia_configurar(): """Update AI provider, credentials and inference parameters. Inference parameters are stored in the session; the provider and cloud credentials (OpenRouter / z.ai) are applied live via :func:`configurar_provedor`. Credentials are only updated when a non-empty value is sent, so the UI can omit the API key on subsequent saves without wiping it. """ dados = request.get_json() or {} params = { 'temperature': float(dados.get('temperature', 0.7)), 'max_tokens': int(dados.get('max_tokens', 1024)), 'top_p': float(dados.get('top_p', 0.9)), 'repeat_penalty': float(dados.get('repeat_penalty', 1.1)), } session['ia_params'] = params kwargs = {} if dados.get('provedor'): kwargs['provedor'] = dados['provedor'] if dados.get('openrouter_api_key'): kwargs['openrouter_api_key'] = dados['openrouter_api_key'] if dados.get('openrouter_model') is not None: kwargs['openrouter_model'] = dados['openrouter_model'] if dados.get('openrouter_base_url') is not None: kwargs['openrouter_base_url'] = dados['openrouter_base_url'] if dados.get('zai_api_key'): kwargs['zai_api_key'] = dados['zai_api_key'] if dados.get('zai_model') is not None: kwargs['zai_model'] = dados['zai_model'] if dados.get('zai_base_url') is not None: kwargs['zai_base_url'] = dados['zai_base_url'] if dados.get('stepfun_api_key'): kwargs['stepfun_api_key'] = dados['stepfun_api_key'] if dados.get('stepfun_model') is not None: kwargs['stepfun_model'] = dados['stepfun_model'] if dados.get('stepfun_base_url') is not None: kwargs['stepfun_base_url'] = dados['stepfun_base_url'] if dados.get('llamacpp_base_url') is not None: kwargs['llamacpp_base_url'] = dados['llamacpp_base_url'] if kwargs: configurar_provedor(**kwargs) return jsonify({ 'status': 'ok', 'params': params, 'config': obter_config_publica(), }) @app.route('/ia_revisar_cron', methods=['GET', 'POST']) def ia_revisar_cron(): """Get or toggle the "AI rewrites cron orders before sending" feature. GET -> ``{"ativo": bool}`` with the current state. POST -> ``{"ativo": bool}`` (optional) toggles/persists the flag and returns the new state. With no body it just flips the current value. """ if request.method == 'POST': dados = request.get_json(silent=True) or {} ativo = bool(dados.get('ativo', not estado_revisao_cron_ativo())) definir_revisao_cron(ativo) logger.info("Revisão de cron pela IA %s.", 'ativada' if ativo else 'desativada') return jsonify({'ativo': estado_revisao_cron_ativo()}) # --------------------------------------------------------------------------- # Routes - file download # --------------------------------------------------------------------------- @app.route('/gerar_prompts_imagens') def gerar_prompts(): """Gera arquivo .txt com 100 prompts de imagens para SeaArt.ai""" try: conteudo = gerar_prompts_imagens() except Exception as exc: # noqa: BLE001 logger.exception("Erro ao gerar prompts de imagens") conteudo = f"Erro ao gerar prompts: {exc}" return Response( conteudo, mimetype='text/plain', headers={'Content-Disposition': 'attachment; filename=prompts_seaart.txt'} ) # --------------------------------------------------------------------------- # Cron Jobs Control # --------------------------------------------------------------------------- PID_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cron_pids.json') # Cron Jobs do sistema Jogo: (script, arquivo de log). CRON_SCRIPTS = [ ('enviar_ordem_mestre.py', 'logs_ordens.log'), ('enviar_conto_erotico.py', 'logs_contos.log'), ] def _carregar_pids(): """Load process IDs from the PID file.""" try: with open(PID_FILE, 'r') as f: return __import__('json').load(f) except (FileNotFoundError, __import__('json').JSONDecodeError): return {} def _salvar_pids(pids): """Save process IDs to the PID file.""" with open(PID_FILE, 'w') as f: __import__('json').dump(pids, f) def _parar_processo(pid): """Stop a process by PID.""" try: os.kill(pid, signal.SIGTERM) return True except (ProcessLookupError, PermissionError): return False def _encontrar_pids_por_nome(nome_script): """Find all running PIDs for a given cron script name. Uses ``pgrep -f`` so it catches processes started manually (e.g. via ``iniciar.sh`` with ``nohup``) that are NOT tracked in the PID file. """ try: saida = subprocess.check_output( ['pgrep', '-f', nome_script], stderr=subprocess.DEVNULL, ) return [int(pid) for pid in saida.decode().split() if pid.strip()] except (subprocess.CalledProcessError, FileNotFoundError): # pgrep returns exit code 1 when no match is found. return [] @app.route('/cron_parar_todos') def cron_parar_todos(): """Stop all cron jobs. Stops every Cron Job of the Jogo system: both the ones tracked in the PID file and any orphaned instance started outside the web UI (for example via ``iniciar.sh``). """ pid_proprio = os.getpid() resultados = [] pids_parados = set() # 1) Processos rastreados no arquivo de PIDs. for nome, pid in _carregar_pids().items(): if _parar_processo(pid): resultados.append(f"✅ {nome} (PID {pid}) parado") pids_parados.add(pid) else: resultados.append(f"⚠️ {nome} (PID {pid}) não encontrado") # 2) Processos órfãos encontrados pelo nome do script. for nome_script, _ in CRON_SCRIPTS: for pid in _encontrar_pids_por_nome(nome_script): if pid == pid_proprio or pid in pids_parados: continue if _parar_processo(pid): resultados.append(f"✅ {nome_script} (PID {pid}) parado") pids_parados.add(pid) _salvar_pids({}) if not resultados: resultados.append("ℹ️ Nenhum Cron Job estava em execução.") return jsonify({"status": "success", "message": " | ".join(resultados)}) @app.route('/cron_iniciar_todos') def cron_iniciar_todos(): """Start all cron jobs.""" base_dir = os.path.dirname(os.path.abspath(__file__)) pids = _carregar_pids() resultados = [] for script, log_file in CRON_SCRIPTS: script_path = os.path.join(base_dir, script) log_path = os.path.join(base_dir, log_file) try: with open(log_path, 'a') as log: proc = subprocess.Popen( [sys.executable, script_path], stdout=log, stderr=log, start_new_session=True ) pids[script] = proc.pid resultados.append(f"✅ {script} iniciado (PID {proc.pid})") except Exception as e: resultados.append(f"❌ {script} falhou: {e}") _salvar_pids(pids) return jsonify({"status": "success", "message": " | ".join(resultados)}) if __name__ == '__main__': # Debug mode is opt-in via FLASK_DEBUG env var (avoids exposing the # Werkzeug debugger / RCE risk in any non-local deployment). debug = os.environ.get('FLASK_DEBUG', '0') == '1' port = int(os.environ.get('FLASK_PORT', '5000')) app.run(debug=debug, port=port)