Update main.py
Browse files
main.py
CHANGED
|
@@ -739,6 +739,11 @@ LEGENDA ORIGINAL:
|
|
| 739 |
except Exception as e:
|
| 740 |
print(f"⚠️ Erro ao gerar URL do meme: {e}")
|
| 741 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 742 |
# Atualizar no Supabase
|
| 743 |
update_url = f"{supabase_url}/rest/v1/posts?id=eq.{record_id}"
|
| 744 |
patch_payload = {
|
|
@@ -1033,6 +1038,256 @@ Reprovando (exemplo 2):
|
|
| 1033 |
os.unlink(cropped_file_path)
|
| 1034 |
|
| 1035 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1036 |
# ==========================================
|
| 1037 |
# GROQ ENDPOINTS / SUBTITLES
|
| 1038 |
# ==========================================
|
|
|
|
| 739 |
except Exception as e:
|
| 740 |
print(f"⚠️ Erro ao gerar URL do meme: {e}")
|
| 741 |
|
| 742 |
+
# Add generated subtitles to the result JSON for Supabase
|
| 743 |
+
if result_json and srt_for_export:
|
| 744 |
+
if isinstance(result_json[0], dict):
|
| 745 |
+
result_json[0]["subtitle_srt"] = srt_for_export
|
| 746 |
+
|
| 747 |
# Atualizar no Supabase
|
| 748 |
update_url = f"{supabase_url}/rest/v1/posts?id=eq.{record_id}"
|
| 749 |
patch_payload = {
|
|
|
|
| 1038 |
os.unlink(cropped_file_path)
|
| 1039 |
|
| 1040 |
|
| 1041 |
+
@app.api_route("/publish-girlsmoodaily", methods=["GET", "POST"])
|
| 1042 |
+
async def publish_girlsmoodaily_endpoint():
|
| 1043 |
+
if not client:
|
| 1044 |
+
raise HTTPException(status_code=500, detail="Gemini client is not initialized")
|
| 1045 |
+
temp_file = None
|
| 1046 |
+
try:
|
| 1047 |
+
supabase_url = os.getenv("SUPABASE_URL", "").rstrip("/")
|
| 1048 |
+
supabase_key = os.getenv("SUPABASE_KEY", "")
|
| 1049 |
+
if not supabase_url or not supabase_key:
|
| 1050 |
+
raise HTTPException(status_code=500, detail="Credenciais do Supabase não configuradas no ambiente.")
|
| 1051 |
+
|
| 1052 |
+
headers = {
|
| 1053 |
+
"apikey": supabase_key,
|
| 1054 |
+
"Authorization": f"Bearer {supabase_key}",
|
| 1055 |
+
"Content-Type": "application/json"
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
# Buscar 1 post pronto para publicação: tem result, tem final_content_url, published=false, superior_needs_verification IS NULL
|
| 1059 |
+
select_url = f"{supabase_url}/rest/v1/posts?select=*&result=not.is.null&final_content_url=not.is.null&published=eq.false&superior_needs_verification=is.null&limit=1"
|
| 1060 |
+
res_get = requests.get(select_url, headers=headers, timeout=10)
|
| 1061 |
+
if not res_get.ok:
|
| 1062 |
+
raise HTTPException(status_code=500, detail=f"Erro ao ler posts: {res_get.text}")
|
| 1063 |
+
|
| 1064 |
+
records = res_get.json()
|
| 1065 |
+
if not records:
|
| 1066 |
+
return {"status": "ok", "message": "Nenhuma postagem pendente para publicação."}
|
| 1067 |
+
|
| 1068 |
+
record = records[0]
|
| 1069 |
+
record_id = record.get("id")
|
| 1070 |
+
final_content_url = record.get("final_content_url", "")
|
| 1071 |
+
result_data = record.get("result", [])
|
| 1072 |
+
|
| 1073 |
+
if not final_content_url:
|
| 1074 |
+
raise HTTPException(status_code=400, detail=f"Registro ID {record_id} falhou: final_content_url inválida.")
|
| 1075 |
+
|
| 1076 |
+
discord_id = 3
|
| 1077 |
+
agent_name = "Amanda"
|
| 1078 |
+
|
| 1079 |
+
# Notificação de início no Discord
|
| 1080 |
+
try:
|
| 1081 |
+
import urllib.parse
|
| 1082 |
+
sys_msg = f"📦 **{agent_name}** começou a revisar uma postagem para publicação...\n\n📎 **Conteúdo:** {final_content_url}"
|
| 1083 |
+
sys_target_url = "https://discordmsg.arthurmribeiro51.workers.dev/?" + urllib.parse.urlencode({
|
| 1084 |
+
"mensagem": sys_msg,
|
| 1085 |
+
"id": 0
|
| 1086 |
+
})
|
| 1087 |
+
requests.get(
|
| 1088 |
+
"https://proxy.onrecurve.com/",
|
| 1089 |
+
params={"quest": sys_target_url},
|
| 1090 |
+
timeout=5
|
| 1091 |
+
)
|
| 1092 |
+
except Exception as e:
|
| 1093 |
+
print(f"⚠️ Erro ao enviar mensagem de sistema para o Discord: {e}")
|
| 1094 |
+
|
| 1095 |
+
# Baixar o conteúdo final para enviar ao Gemini
|
| 1096 |
+
print(f"📥 Baixando conteúdo final para revisão: {final_content_url}")
|
| 1097 |
+
response = download_file_with_retry(final_content_url, timeout=600)
|
| 1098 |
+
|
| 1099 |
+
content_type = response.headers.get('content-type', '').lower()
|
| 1100 |
+
if 'image' in content_type:
|
| 1101 |
+
if 'png' in content_type: ext = '.png'
|
| 1102 |
+
elif 'webp' in content_type: ext = '.webp'
|
| 1103 |
+
else: ext = '.jpg'
|
| 1104 |
+
else:
|
| 1105 |
+
ext = '.webm' if 'webm' in content_type else '.mp4'
|
| 1106 |
+
|
| 1107 |
+
temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=ext)
|
| 1108 |
+
for chunk in response.iter_content(chunk_size=1024*1024):
|
| 1109 |
+
if chunk: temp_file.write(chunk)
|
| 1110 |
+
temp_file.close()
|
| 1111 |
+
|
| 1112 |
+
# Montar contexto do que a Vicky produziu
|
| 1113 |
+
vicky_result = ""
|
| 1114 |
+
if result_data and isinstance(result_data, list) and len(result_data) > 0:
|
| 1115 |
+
r0 = result_data[0] if isinstance(result_data[0], dict) else {}
|
| 1116 |
+
vicky_result = f"""
|
| 1117 |
+
RESULTADO DA VICKY (o que foi produzido):
|
| 1118 |
+
- Título: {r0.get('title', 'N/A')}
|
| 1119 |
+
- Descrição: {r0.get('description', 'N/A')}
|
| 1120 |
+
- Legenda (subtítulos): {r0.get('legenda', False)}
|
| 1121 |
+
- Tipo: {r0.get('result_type', 'N/A')}
|
| 1122 |
+
"""
|
| 1123 |
+
|
| 1124 |
+
is_video = 'image' not in content_type
|
| 1125 |
+
|
| 1126 |
+
prompt = f"""Você é AMANDA, a Gestora de Distribuição da @girlsmoodaily no Instagram.
|
| 1127 |
+
|
| 1128 |
+
QUEM VOCÊ É
|
| 1129 |
+
|
| 1130 |
+
Você é a Amanda, organizada, detalhista e com olho clínico para qualidade. Você é a última pessoa que vê o conteúdo antes de ele ir ao ar. Nada sai sem a sua aprovação. Você fala de forma profissional mas acessível, como uma gerente de redes sociais que leva o trabalho muito a sério mas não é robótica. Você se comunica exclusivamente em português brasileiro.
|
| 1131 |
+
|
| 1132 |
+
Sua personalidade: responsável, metódica e confiante. Você não deixa passar nada. Se algo está errado, você percebe. Se está tudo certo, você aprova com segurança. Você é firme mas não arrogante, e sempre explica suas decisões com clareza.
|
| 1133 |
+
|
| 1134 |
+
|
| 1135 |
+
SUA MISSÃO
|
| 1136 |
+
|
| 1137 |
+
Você é o último filtro antes da publicação. Você recebe o conteúdo FINAL (já editado, com título aplicado, exportado e pronto) e verifica se ele está apto para ser publicado na @girlsmoodaily.
|
| 1138 |
+
|
| 1139 |
+
Sua verificação é sobre o PRODUTO FINAL, não sobre o conteúdo bruto original. A Diana já filtrou o conteúdo e a Vicky já criou o post. Você está olhando para o resultado final que vai pro feed/reels.
|
| 1140 |
+
|
| 1141 |
+
{vicky_result}
|
| 1142 |
+
|
| 1143 |
+
O QUE VOCÊ DEVE VERIFICAR
|
| 1144 |
+
|
| 1145 |
+
1. QUALIDADE VISUAL GERAL
|
| 1146 |
+
- O conteúdo final está visualmente limpo e profissional?
|
| 1147 |
+
- A resolução e qualidade são aceitáveis para publicação?
|
| 1148 |
+
- Se for vídeo: a imagem do título está bem posicionada? O vídeo está cortado corretamente?
|
| 1149 |
+
- Se for meme/imagem: o texto está legível? A composição visual está harmoniosa?
|
| 1150 |
+
|
| 1151 |
+
2. MARCAS D'ÁGUA E TEXTOS INDESEJADOS
|
| 1152 |
+
- Há marcas d'água visíveis de outras plataformas (TikTok, Instagram de terceiros, etc.)?
|
| 1153 |
+
- Há textos em idiomas estrangeiros que não deveriam estar ali (exceto se forem parte natural do conteúdo, como um meme em inglês que foi traduzido no título)?
|
| 1154 |
+
- Há logos ou arrobas de outras páginas visíveis no conteúdo final?
|
| 1155 |
+
|
| 1156 |
+
3. TÍTULO E TEXTO DA @GIRLSMOODAILY
|
| 1157 |
+
- O título aplicado pela Vicky está visível e legível?
|
| 1158 |
+
- O título faz sentido com o conteúdo visual?
|
| 1159 |
+
- Se for meme: o texto central está correto e bem formatado?
|
| 1160 |
+
- IMPORTANTE: Se houver texto no conteúdo que NÃO seja o título/texto central da @girlsmoodaily (como texto residual do post original que não foi removido), isso é um problema que merece atenção.
|
| 1161 |
+
|
| 1162 |
+
4. LEGENDAS (SUBTÍTULOS) - SOMENTE PARA VÍDEOS
|
| 1163 |
+
- Se o vídeo tem legendas incorporadas, elas estão em português?
|
| 1164 |
+
- As legendas são legíveis e bem posicionadas?
|
| 1165 |
+
- Se o vídeo deveria ter legendas mas não tem, isso é um problema.
|
| 1166 |
+
|
| 1167 |
+
5. ALINHAMENTO COM A IDENTIDADE DA PÁGINA
|
| 1168 |
+
- O produto final parece algo que a @girlsmoodaily publicaria?
|
| 1169 |
+
- A estética geral está alinhada com a vibe feminina, leve e alto-astral da página?
|
| 1170 |
+
|
| 1171 |
+
REGRAS DE DECISÃO
|
| 1172 |
+
|
| 1173 |
+
PUBLICAR (published: true, superior_needs_verification: false):
|
| 1174 |
+
- Tudo está perfeito. Conteúdo limpo, título legível, sem marcas d'água, qualidade boa, alinhado com a página. Pode ir ao ar.
|
| 1175 |
+
|
| 1176 |
+
PRECISA DE VERIFICAÇÃO SUPERIOR (published: false, superior_needs_verification: true):
|
| 1177 |
+
- O conteúdo provavelmente está ok, mas você tem alguma dúvida ou incerteza. Exemplos:
|
| 1178 |
+
- Há um texto residual no conteúdo que pode ou não ser problemático
|
| 1179 |
+
- A imagem do título parece levemente desalinhada ou cortada de forma estranha
|
| 1180 |
+
- Você não tem certeza se uma marca d'água sutil é realmente uma marca d'água
|
| 1181 |
+
- O conteúdo é bom mas algo te deixa com uma pulga atrás da orelha
|
| 1182 |
+
- Use isso quando você ACHA que deveria publicar mas não tem 100% de certeza
|
| 1183 |
+
|
| 1184 |
+
REJEITAR (published: false, superior_needs_verification: false):
|
| 1185 |
+
- O conteúdo tem problemas claros e óbvios que impedem a publicação. Exemplos:
|
| 1186 |
+
- Marca d'água enorme e visível
|
| 1187 |
+
- Texto completamente ilegível
|
| 1188 |
+
- Conteúdo claramente quebrado ou corrompido
|
| 1189 |
+
- Qualidade visual inaceitável
|
| 1190 |
+
- Problema grave que não tem como ser ignorado
|
| 1191 |
+
|
| 1192 |
+
FORMATO DE SAÍDA
|
| 1193 |
+
|
| 1194 |
+
Você deve retornar APENAS um objeto JSON puro, sem markdown, sem blocos de código, sem nenhum texto antes ou depois.
|
| 1195 |
+
|
| 1196 |
+
{{
|
| 1197 |
+
"publish_message": "<sua mensagem aqui>",
|
| 1198 |
+
"published": true ou false,
|
| 1199 |
+
"superior_needs_verification": true ou false
|
| 1200 |
+
}}
|
| 1201 |
+
|
| 1202 |
+
Regras para publish_message: escrita em português brasileiro, casual mas profissional, sem formatações, sem negrito, sem travessão, sem listas. Deve soar como uma gestora de redes sociais real falando. Explique sua decisão de forma clara e direta. Varie sempre a abertura. Nunca comece com "gente" ou "pessoal". Entre 2 e 4 frases.
|
| 1203 |
+
|
| 1204 |
+
EXEMPLOS DE TOM
|
| 1205 |
+
|
| 1206 |
+
Publicando:
|
| 1207 |
+
"Revisei o conteúdo final e tá tudo impecável. Título legível, sem marcas d'água, qualidade visual ótima e totalmente alinhado com a vibe da página. Pode ir ao ar sem nenhuma preocupação. ✅"
|
| 1208 |
+
|
| 1209 |
+
Pedindo verificação:
|
| 1210 |
+
"O conteúdo tá bom no geral, mas percebi um texto pequeno no canto inferior do vídeo que pode ser uma marca d'água ou pode ser parte do conteúdo original. Não consigo ter certeza, então prefiro que alguém dê uma segunda olhada antes de publicar. 🔍"
|
| 1211 |
+
|
| 1212 |
+
Rejeitando:
|
| 1213 |
+
"Infelizmente não posso aprovar essa aqui. Tem uma marca d'água do TikTok bem visível no meio do vídeo que compromete completamente a qualidade do post. Precisa ser reprocessado antes de publicar. ❌"
|
| 1214 |
+
"""
|
| 1215 |
+
|
| 1216 |
+
model_obj = get_gemini_model("flash")
|
| 1217 |
+
print(f"🧠 Enviando para Gemini (flash) para revisão de publicação...")
|
| 1218 |
+
|
| 1219 |
+
response_gemini = await client.generate_content(prompt, files=[temp_file.name], model=model_obj)
|
| 1220 |
+
|
| 1221 |
+
publish_data = extract_json_from_text(response_gemini.text)
|
| 1222 |
+
if publish_data is None:
|
| 1223 |
+
return JSONResponse(content={"raw_content": response_gemini.text, "error": "Failed to parse JSON"}, status_code=200)
|
| 1224 |
+
|
| 1225 |
+
# Enviar mensagens no Discord
|
| 1226 |
+
try:
|
| 1227 |
+
import urllib.parse
|
| 1228 |
+
# 1) Mensagem da Amanda para o canal dela (ID 3)
|
| 1229 |
+
target_url = "https://discordmsg.arthurmribeiro51.workers.dev/?" + urllib.parse.urlencode({
|
| 1230 |
+
"mensagem": publish_data.get("publish_message", ""),
|
| 1231 |
+
"id": discord_id
|
| 1232 |
+
})
|
| 1233 |
+
requests.get(
|
| 1234 |
+
"https://proxy.onrecurve.com/",
|
| 1235 |
+
params={"quest": target_url},
|
| 1236 |
+
timeout=5
|
| 1237 |
+
)
|
| 1238 |
+
|
| 1239 |
+
# 2) Aviso final pro Painel de Sistema (ID 0)
|
| 1240 |
+
is_published = publish_data.get("published", False)
|
| 1241 |
+
needs_verification = publish_data.get("superior_needs_verification", False)
|
| 1242 |
+
|
| 1243 |
+
if is_published:
|
| 1244 |
+
status_emoji = "✅"
|
| 1245 |
+
status_text = "APROVOU a publicação"
|
| 1246 |
+
elif needs_verification:
|
| 1247 |
+
status_emoji = "🔍"
|
| 1248 |
+
status_text = "SOLICITOU verificação de um superior"
|
| 1249 |
+
else:
|
| 1250 |
+
status_emoji = "❌"
|
| 1251 |
+
status_text = "REJEITOU a publicação"
|
| 1252 |
+
|
| 1253 |
+
sys_end_msg = f"{status_emoji} **{agent_name}** {status_text} da postagem #{record_id}."
|
| 1254 |
+
if final_content_url:
|
| 1255 |
+
sys_end_msg += f"\n\n📎 {final_content_url}"
|
| 1256 |
+
|
| 1257 |
+
sys_end_url = "https://discordmsg.arthurmribeiro51.workers.dev/?" + urllib.parse.urlencode({
|
| 1258 |
+
"mensagem": sys_end_msg,
|
| 1259 |
+
"id": 0
|
| 1260 |
+
})
|
| 1261 |
+
requests.get(
|
| 1262 |
+
"https://proxy.onrecurve.com/",
|
| 1263 |
+
params={"quest": sys_end_url},
|
| 1264 |
+
timeout=5
|
| 1265 |
+
)
|
| 1266 |
+
except Exception as e:
|
| 1267 |
+
print(f"⚠️ Erro ao enviar log para o Discord: {e}")
|
| 1268 |
+
|
| 1269 |
+
# Atualizar no Supabase
|
| 1270 |
+
update_url = f"{supabase_url}/rest/v1/posts?id=eq.{record_id}"
|
| 1271 |
+
patch_payload = {
|
| 1272 |
+
"publish_message": publish_data.get("publish_message"),
|
| 1273 |
+
"published": publish_data.get("published", False),
|
| 1274 |
+
"superior_needs_verification": publish_data.get("superior_needs_verification", False)
|
| 1275 |
+
}
|
| 1276 |
+
res_patch = requests.patch(update_url, headers=headers, json=patch_payload, timeout=10)
|
| 1277 |
+
if not res_patch.ok:
|
| 1278 |
+
print(f"⚠️ Erro ao atualizar Supabase: {res_patch.text}")
|
| 1279 |
+
|
| 1280 |
+
return {
|
| 1281 |
+
"success": True,
|
| 1282 |
+
"record_id": record_id,
|
| 1283 |
+
"publish_data": publish_data
|
| 1284 |
+
}
|
| 1285 |
+
except Exception as e:
|
| 1286 |
+
raise HTTPException(status_code=500, detail=f"Erro interno: {str(e)}")
|
| 1287 |
+
finally:
|
| 1288 |
+
if temp_file and os.path.exists(temp_file.name): os.unlink(temp_file.name)
|
| 1289 |
+
|
| 1290 |
+
|
| 1291 |
# ==========================================
|
| 1292 |
# GROQ ENDPOINTS / SUBTITLES
|
| 1293 |
# ==========================================
|