Files
marcocosta10000 594f1a5725 feat: initial commit of Sistema do Mestre application
Add complete Flask web application with Telegram bot integration,
multiple AI provider support (llama.cpp, OpenRouter, z.ai, StepFun),
extensive order banks for BDSM-themed gameplay, automated cron
scheduling, and responsive web UI with inference parameter controls.
2026-10-17 19:08:54 -03:00

1926 lines
70 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)