Spaces:
Paused
Paused
Update routers/instagram.py
Browse files- routers/instagram.py +75 -23
routers/instagram.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
import os
|
| 2 |
import httpx
|
|
|
|
| 3 |
from typing import Dict, Optional, Tuple
|
| 4 |
from fastapi import APIRouter, HTTPException
|
| 5 |
from pydantic import BaseModel
|
|
@@ -7,7 +8,7 @@ from pydantic import BaseModel
|
|
| 7 |
router = APIRouter()
|
| 8 |
|
| 9 |
# 📱 Instagram API Config
|
| 10 |
-
INSTAGRAM_API_BASE = "https://graph.instagram.com
|
| 11 |
INSTAGRAM_PAGE_ID = "17841464166934843" # Seu Page ID
|
| 12 |
INSTAGRAM_TOKEN = os.getenv("INSTAGRAM_ACCESS_TOKEN")
|
| 13 |
|
|
@@ -28,6 +29,28 @@ class PublishResponse(BaseModel):
|
|
| 28 |
comment_posted: bool = False
|
| 29 |
comment_id: Optional[str] = None
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
def clean_html_tags(text: str) -> str:
|
| 32 |
"""
|
| 33 |
Remove todas as tags HTML.
|
|
@@ -118,12 +141,12 @@ async def post_comment(client: httpx.AsyncClient, post_id: str, comment_text: st
|
|
| 118 |
try:
|
| 119 |
comment_url = f"{INSTAGRAM_API_BASE}/{post_id}/comments"
|
| 120 |
headers = {
|
| 121 |
-
"Content-Type": "application/json"
|
| 122 |
-
"Authorization": f"Bearer {INSTAGRAM_TOKEN}"
|
| 123 |
}
|
| 124 |
|
| 125 |
comment_payload = {
|
| 126 |
-
"message": comment_text
|
|
|
|
| 127 |
}
|
| 128 |
|
| 129 |
print(f"💬 Postando comentário no post {post_id}")
|
|
@@ -157,36 +180,49 @@ async def publish_instagram_post(post: InstagramPost) -> PublishResponse:
|
|
| 157 |
3. Se necessário, posta o resto do texto como comentário
|
| 158 |
"""
|
| 159 |
|
| 160 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
try:
|
| 162 |
# 📝 Processa o texto da caption
|
| 163 |
main_caption, remaining_text = format_text_for_instagram(post.caption) if post.caption else ("", None)
|
| 164 |
|
| 165 |
# 🎯 ETAPA 1: Criar o media container
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
| 168 |
}
|
| 169 |
|
| 170 |
# Adiciona caption processada se fornecida
|
| 171 |
if main_caption:
|
| 172 |
-
|
| 173 |
|
| 174 |
media_url = f"{INSTAGRAM_API_BASE}/{INSTAGRAM_PAGE_ID}/media"
|
| 175 |
-
headers = {
|
| 176 |
-
"Content-Type": "application/json",
|
| 177 |
-
"Authorization": f"Bearer {INSTAGRAM_TOKEN}"
|
| 178 |
-
}
|
| 179 |
|
| 180 |
print(f"📤 Criando media container para: {post.image_url}")
|
|
|
|
| 181 |
if remaining_text:
|
| 182 |
print(f"✂️ Texto cortado - será postado comentário com {len(remaining_text)} caracteres")
|
| 183 |
|
|
|
|
| 184 |
media_response = await client.post(
|
| 185 |
-
media_url,
|
| 186 |
-
|
| 187 |
-
json=media_payload
|
| 188 |
)
|
| 189 |
|
|
|
|
|
|
|
|
|
|
| 190 |
if media_response.status_code != 200:
|
| 191 |
error_detail = media_response.text
|
| 192 |
print(f"❌ Erro ao criar media container: {error_detail}")
|
|
@@ -207,17 +243,17 @@ async def publish_instagram_post(post: InstagramPost) -> PublishResponse:
|
|
| 207 |
print(f"✅ Media container criado com ID: {media_id}")
|
| 208 |
|
| 209 |
# 🎯 ETAPA 2: Publicar o post
|
| 210 |
-
|
| 211 |
-
"creation_id": media_id
|
|
|
|
| 212 |
}
|
| 213 |
|
| 214 |
publish_url = f"{INSTAGRAM_API_BASE}/{INSTAGRAM_PAGE_ID}/media_publish"
|
| 215 |
|
| 216 |
print(f"📤 Publicando post com creation_id: {media_id}")
|
| 217 |
publish_response = await client.post(
|
| 218 |
-
publish_url,
|
| 219 |
-
|
| 220 |
-
json=publish_payload
|
| 221 |
)
|
| 222 |
|
| 223 |
if publish_response.status_code != 200:
|
|
@@ -236,8 +272,12 @@ async def publish_instagram_post(post: InstagramPost) -> PublishResponse:
|
|
| 236 |
if post_id:
|
| 237 |
try:
|
| 238 |
# Query para obter o permalink do post
|
| 239 |
-
|
| 240 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
if details_response.status_code == 200:
|
| 243 |
details_data = details_response.json()
|
|
@@ -291,4 +331,16 @@ async def publish_instagram_post(post: InstagramPost) -> PublishResponse:
|
|
| 291 |
raise HTTPException(
|
| 292 |
status_code=500,
|
| 293 |
detail=f"Erro interno do servidor: {str(e)}"
|
| 294 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import os
|
| 2 |
import httpx
|
| 3 |
+
import socket
|
| 4 |
from typing import Dict, Optional, Tuple
|
| 5 |
from fastapi import APIRouter, HTTPException
|
| 6 |
from pydantic import BaseModel
|
|
|
|
| 8 |
router = APIRouter()
|
| 9 |
|
| 10 |
# 📱 Instagram API Config
|
| 11 |
+
INSTAGRAM_API_BASE = "https://graph.instagram.com" # Removido v23.0 para testar
|
| 12 |
INSTAGRAM_PAGE_ID = "17841464166934843" # Seu Page ID
|
| 13 |
INSTAGRAM_TOKEN = os.getenv("INSTAGRAM_ACCESS_TOKEN")
|
| 14 |
|
|
|
|
| 29 |
comment_posted: bool = False
|
| 30 |
comment_id: Optional[str] = None
|
| 31 |
|
| 32 |
+
# 🔍 Função para testar conectividade
|
| 33 |
+
async def test_connectivity():
|
| 34 |
+
"""Testa a conectividade básica com a API do Instagram"""
|
| 35 |
+
try:
|
| 36 |
+
# Teste 1: Resolução DNS
|
| 37 |
+
print("🔍 Testando resolução DNS...")
|
| 38 |
+
socket.gethostbyname("graph.instagram.com")
|
| 39 |
+
print("✅ DNS resolvido com sucesso")
|
| 40 |
+
|
| 41 |
+
# Teste 2: Conexão HTTP
|
| 42 |
+
print("🔍 Testando conectividade HTTP...")
|
| 43 |
+
async with httpx.AsyncClient(timeout=10.0) as client:
|
| 44 |
+
response = await client.get("https://graph.instagram.com")
|
| 45 |
+
print(f"✅ Conectividade OK - Status: {response.status_code}")
|
| 46 |
+
|
| 47 |
+
except socket.gaierror as e:
|
| 48 |
+
print(f"❌ Erro de DNS: {e}")
|
| 49 |
+
raise HTTPException(status_code=502, detail=f"Erro de resolução DNS: {e}")
|
| 50 |
+
except Exception as e:
|
| 51 |
+
print(f"❌ Erro de conectividade: {e}")
|
| 52 |
+
raise HTTPException(status_code=502, detail=f"Erro de conectividade: {e}")
|
| 53 |
+
|
| 54 |
def clean_html_tags(text: str) -> str:
|
| 55 |
"""
|
| 56 |
Remove todas as tags HTML.
|
|
|
|
| 141 |
try:
|
| 142 |
comment_url = f"{INSTAGRAM_API_BASE}/{post_id}/comments"
|
| 143 |
headers = {
|
| 144 |
+
"Content-Type": "application/json"
|
|
|
|
| 145 |
}
|
| 146 |
|
| 147 |
comment_payload = {
|
| 148 |
+
"message": comment_text,
|
| 149 |
+
"access_token": INSTAGRAM_TOKEN
|
| 150 |
}
|
| 151 |
|
| 152 |
print(f"💬 Postando comentário no post {post_id}")
|
|
|
|
| 180 |
3. Se necessário, posta o resto do texto como comentário
|
| 181 |
"""
|
| 182 |
|
| 183 |
+
# 🔍 Testa conectividade antes de prosseguir
|
| 184 |
+
await test_connectivity()
|
| 185 |
+
|
| 186 |
+
# Configuração do cliente HTTP com mais opções
|
| 187 |
+
client_config = httpx.AsyncClient(
|
| 188 |
+
timeout=httpx.Timeout(30.0, connect=10.0),
|
| 189 |
+
follow_redirects=True,
|
| 190 |
+
verify=True, # Verifica certificados SSL
|
| 191 |
+
limits=httpx.Limits(max_connections=10, max_keepalive_connections=5)
|
| 192 |
+
)
|
| 193 |
+
|
| 194 |
+
async with client_config as client:
|
| 195 |
try:
|
| 196 |
# 📝 Processa o texto da caption
|
| 197 |
main_caption, remaining_text = format_text_for_instagram(post.caption) if post.caption else ("", None)
|
| 198 |
|
| 199 |
# 🎯 ETAPA 1: Criar o media container
|
| 200 |
+
# Usando parâmetros de query ao invés de JSON body (como no curl que funcionou)
|
| 201 |
+
media_params = {
|
| 202 |
+
"image_url": post.image_url,
|
| 203 |
+
"access_token": INSTAGRAM_TOKEN
|
| 204 |
}
|
| 205 |
|
| 206 |
# Adiciona caption processada se fornecida
|
| 207 |
if main_caption:
|
| 208 |
+
media_params["caption"] = main_caption
|
| 209 |
|
| 210 |
media_url = f"{INSTAGRAM_API_BASE}/{INSTAGRAM_PAGE_ID}/media"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
|
| 212 |
print(f"📤 Criando media container para: {post.image_url}")
|
| 213 |
+
print(f"🔗 URL: {media_url}")
|
| 214 |
if remaining_text:
|
| 215 |
print(f"✂️ Texto cortado - será postado comentário com {len(remaining_text)} caracteres")
|
| 216 |
|
| 217 |
+
# Usando POST com parâmetros de query (igual ao curl)
|
| 218 |
media_response = await client.post(
|
| 219 |
+
media_url,
|
| 220 |
+
params=media_params # Mudança aqui: usando params ao invés de json
|
|
|
|
| 221 |
)
|
| 222 |
|
| 223 |
+
print(f"📊 Status da resposta: {media_response.status_code}")
|
| 224 |
+
print(f"📊 Headers da resposta: {dict(media_response.headers)}")
|
| 225 |
+
|
| 226 |
if media_response.status_code != 200:
|
| 227 |
error_detail = media_response.text
|
| 228 |
print(f"❌ Erro ao criar media container: {error_detail}")
|
|
|
|
| 243 |
print(f"✅ Media container criado com ID: {media_id}")
|
| 244 |
|
| 245 |
# 🎯 ETAPA 2: Publicar o post
|
| 246 |
+
publish_params = {
|
| 247 |
+
"creation_id": media_id,
|
| 248 |
+
"access_token": INSTAGRAM_TOKEN
|
| 249 |
}
|
| 250 |
|
| 251 |
publish_url = f"{INSTAGRAM_API_BASE}/{INSTAGRAM_PAGE_ID}/media_publish"
|
| 252 |
|
| 253 |
print(f"📤 Publicando post com creation_id: {media_id}")
|
| 254 |
publish_response = await client.post(
|
| 255 |
+
publish_url,
|
| 256 |
+
params=publish_params # Usando params também aqui
|
|
|
|
| 257 |
)
|
| 258 |
|
| 259 |
if publish_response.status_code != 200:
|
|
|
|
| 272 |
if post_id:
|
| 273 |
try:
|
| 274 |
# Query para obter o permalink do post
|
| 275 |
+
post_details_params = {
|
| 276 |
+
"fields": "permalink",
|
| 277 |
+
"access_token": INSTAGRAM_TOKEN
|
| 278 |
+
}
|
| 279 |
+
post_details_url = f"{INSTAGRAM_API_BASE}/{post_id}"
|
| 280 |
+
details_response = await client.get(post_details_url, params=post_details_params)
|
| 281 |
|
| 282 |
if details_response.status_code == 200:
|
| 283 |
details_data = details_response.json()
|
|
|
|
| 331 |
raise HTTPException(
|
| 332 |
status_code=500,
|
| 333 |
detail=f"Erro interno do servidor: {str(e)}"
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
# 🔍 Endpoint para testar conectividade
|
| 337 |
+
@router.get("/test-connection")
|
| 338 |
+
async def test_connection():
|
| 339 |
+
"""Endpoint para testar a conectividade com a API do Instagram"""
|
| 340 |
+
try:
|
| 341 |
+
await test_connectivity()
|
| 342 |
+
return {"status": "success", "message": "Conectividade OK"}
|
| 343 |
+
except HTTPException as e:
|
| 344 |
+
raise e
|
| 345 |
+
except Exception as e:
|
| 346 |
+
raise HTTPException(status_code=500, detail=f"Erro no teste: {str(e)}")
|