Saltar al contenido principal
SocialCutter

IA y agentes

Sube imágenes a Slack con files.uploadV2

Sube imágenes a Slack con files.uploadV2 o compártelas por URL, y encadena SocialCutter para responder con los formatos listos.

  • Slack
  • files.uploadV2
  • files:write
  • Block Kit
  • bot
  • SocialCutter
  • Python

Qué resuelve

Slack es donde el equipo comparte imágenes antes de publicarlas. Un bot puede aprovechar ese momento: alguien suelta la imagen en el canal, el bot la recorta de forma centrada a las medidas de cada red y responde con los ficheros ya listos. La pieza para subir imágenes a Slack es files.uploadV2, y la pieza para generar los formatos es SocialCutter. Esta guía cubre las dos y cómo encadenarlas.

Requisitos: app, bot y scopes

ScopePara qué
files:writeSubir, editar y borrar ficheros
chat:writePublicar mensajes y compartir por URL
files:readLeer metadatos (files.info, files.list), solo si los necesitas

El bot usa un token xoxb-. Debe estar invitado al canal donde va a publicar; si no, recibirás not_in_channel. Referencias oficiales: https://docs.slack.dev/messaging/working-with-files y https://docs.slack.dev/reference/scopes/files.write.

Subir un fichero con files.uploadV2

files.upload está retirado. En su lugar, Slack pide la secuencia files.getUploadURLExternal y files.completeUploadExternal. Las librerías oficiales la envuelven en un método «v2», así que no tienes que orquestar los dos pasos a mano:

from slack_sdk import WebClient

client = WebClient(token="xoxb-tu_token")

client.files_upload_v2(
    channel="C0123456789",
    file="./instagram-post.jpg",
    title="Instagram 1:1",
    filename="instagram-post.jpg",
    alt_text="Creatividad para el feed de Instagram",
    initial_comment="Formato listo para Instagram",
)
ParámetroPara qué
channelCanal donde se comparte el fichero
file / contentRuta del fichero o bytes en memoria
filenameNombre con extensión
titleTítulo visible del fichero
initial_commentMensaje que acompaña al fichero
alt_textTexto alternativo para accesibilidad

Aviso de versiones: Slack retiró files.upload y recomienda la secuencia de dos pasos; además, el comportamiento de los métodos de ficheros ha ido cambiando. Comprueba la documentación vigente en el changelog oficial antes de fijar una implementación: https://docs.slack.dev/changelog/2024-04-a-better-way-to-upload-files-is-here-to-stay.

Compartir por URL con Block Kit

Si ya tienes una URL pública (SocialCutter devuelve una por destino), no necesitas subirla a Slack: puedes mostrarla con un bloque image dentro de chat.postMessage. Este camino solo requiere chat:write.

client.chat_postMessage(
    channel="C0123456789",
    blocks=[
        {"type": "image", "image_url": out["url"], "alt_text": f"{out['platform']} {out['format']}"},
        {"type": "context", "elements": [{"type": "mrkdwn", "text": f"{out['width']}x{out['height']} listo para publicar"}]},
    ],
)

La pega: el bloque image carga la imagen desde Slack y necesita una URL accesible sin autenticación. Si el destino exige sesión o caduca rápido, sube el fichero con files.uploadV2 en lugar de enlazarlo. Como referencia general de bloques, consulta la documentación oficial de Block Kit: https://docs.slack.dev/block-kit.

Encadenar SocialCutter: el bot que responde con los formatos

El flujo completo:

  1. Alguien sube la imagen al canal. Tu servicio recibe el evento file_shared (o app_mention).
  2. El bot descarga el fichero. Con url_private_download y el token xoxb- en la cabecera Authorization.
  3. El bot llama a SocialCutter. POST /api/v1/images/process/upload por multipart, con el binario y la lista de destinations.
  4. SocialCutter responde con image_id y un array outputs, una URL por destino.
  5. El bot devuelve los formatos al canal, con files_upload_v2 por salida o con un bloque image.
import requests
from slack_sdk import WebClient

SLACK_TOKEN = "xoxb-tu_token"
SC_KEY = "sc_tu_clave"
client = WebClient(token=SLACK_TOKEN)

def procesar_fichero(file_id, channel):
    # 1. Metadatos y descarga con el token del bot
    info = client.files_info(file=file_id)["file"]
    img = requests.get(info["url_private_download"],
                       headers={"Authorization": f"Bearer {SLACK_TOKEN}"}, timeout=30)
    img.raise_for_status()

    # 2. Pide los formatos a SocialCutter
    r = requests.post(
        "https://api.socialcutter.theboomer.dev/api/v1/images/process/upload",
        headers={"X-API-Key": SC_KEY},
        files={"file": ("original.jpg", img.content, "image/jpeg")},
        data={"destinations": '[{"platform":"instagram","format":"post"},'
                              '{"platform":"twitter","format":"post"},'
                              '{"platform":"tiktok","format":"cover"}]'},
        timeout=60,
    )
    r.raise_for_status()
    outputs = r.json()["outputs"]

    # 3. Devuelve cada formato al canal
    for out in outputs:
        fichero = requests.get(out["url"], timeout=30)
        client.files_upload_v2(
            channel=channel,
            content=fichero.content,
            filename=f"{out['platform']}-{out['format']}.jpg",
            title=f"{out['platform']} {out['format']} ({out['width']}x{out['height']})",
            initial_comment=f"Listo para {out['platform']}",
        )

Detalles honestos sobre el resultado: el recorte es centrado y determinista; no hay detección de sujeto ni de rostros. Si tu imagen tiene el motivo descentrado, revisa el encuadre antes de publicar. Y SocialCutter no publica en Instagram, X o TikTok: entrega ficheros. Slack recibe las versiones; publicar es otro paso.

El file_shared trae el file_id y el channel_id; los campos exactos del payload del evento pueden variar entre versiones de la Events API, así que valida el evento contra la documentación oficial de Slack antes de depender de un campo concreto.

Coste

ConceptoValor
Coste por procesamiento1 uso por destino (plataforma y formato)
Destinos repetidos en la misma peticiónNo se cobran dos veces
Subida de ficheros a SlackSin coste por API, dentro de los límites del workspace
Planes de SocialCutter0, 3, 9 y 29 EUR, con API y MCP incluidos

El bot del ejemplo pide tres destinos: consume 3 usos por cada imagen que llegue al canal y devuelve tres ficheros.

Errores típicos

ErrorQué pasa realmenteQué hacer
invalid_authEl token no es válido o está revocadoReinstala la app y usa el xoxb- vigente
missing_scopeFalta files:write o chat:writeAñade el scope, reinstala y reinicia el bot
not_in_channelEl bot no está en el canal destinoInvítalo con /invite
file_not_foundEl file_id no existe o el bot no lo veComprueba el scope files:read y el id
El bloque image no se veLa URL no es pública o redirige a un loginSube el fichero con files.uploadV2
413 al procesarLa imagen supera los 5 MBComprime la maestra o sirve una versión más ligera
El fichero se ve borroso en SlackSe subió un recorte pequeño y se amplía en pantallaParte de una maestra mayor para ese destino

Siguientes pasos

Preguntas frecuentes

¿Qué scopes necesita el bot?

files:write para subir ficheros, chat:write para publicar mensajes y files:read si además lees metadatos de ficheros existentes. El token de bot empieza por xoxb-.

¿Sigue sirviendo files.upload?

No conviene. Slack retiró files.upload en favor de la secuencia getUploadURLExternal más completeUploadExternal. Las librerías oficiales lo envuelven en el método uploadV2, que es el que debes usar.

¿Cómo comparto una imagen sin descargarla a Slack?

Con un bloque image de Block Kit dentro de chat.postMessage, apuntando a una URL pública. Necesita solo chat:write, pero la imagen debe ser accesible sin autenticación.

¿Cuánto cuesta el bot que procesa las imágenes?

Cada procesamiento de SocialCutter consume 1 uso por destino. Slack no cobra por subir ficheros dentro de los límites del plan del workspace.

¿El bot publica en las redes sociales?

No. SocialCutter genera los ficheros con las medidas correctas y los devuelve como URLs; el bot los sube a Slack. Publicar en la red social es un paso aparte.