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.
1926 lines
70 KiB
Python
Executable File
1926 lines
70 KiB
Python
Executable File
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)
|