Sistema Event Flow

Guía del editor de Event Flow

Crea automatizaciones fiables para Social Stream Ninja. Esta guía explica los fundamentos, nodos lógicos, flujo de señales y los trucos prácticos que más preguntan los creadores, como evitar ecos del chat y saber cuándo combinar bloques AND/NOT.

Español

0. Orientación rápida

Event Flow es un editor basado en nodos. Cada línea lleva los datos de un mensaje y un estado booleano (true = continuar, false = detener). Utiliza fuentes para introducir eventos, nodos lógicos para filtrar decisiones, y acciones para ejecutar acciones (enviar chat, controlar superposiciones, retransmitir mensajes, etc.).
¿Necesitas recordar participantes, comprobar su elegibilidad después, realizar un sorteo de usuarios únicos o borrar una lista con nombre? Abre la guía de memoria de usuarios para el modelo de estado compartido, capturas y un ejemplo importable.

¿Qué es este editor?

El editor Event Flow es la capa de automatización avanzada de Social Stream Ninja. Se sitúa por encima de los interruptores sencillos de la ventana emergente y permite programar tu propia lógica de enrutamiento. Utilízalo cuando necesites:

  • Retransmite chat entre servicios con filtros (por ejemplo, de Twitch a Discord, bloqueando comandos).
  • Crea comandos basados en fidelidad, juegos de palabras clave o condiciones de sorteos con lógica AND/OR/NOT.
  • Activa superposiciones personalizadas, audio, escenas de OBS o webhooks a partir de datos que amplíes en el flujo.
  • Combina varias plataformas en una sola automatización (Kick + Twitch + YouTube a través de un flujo).

La ventana emergente ofrece ajustes rápidos y Event Flow proporciona las herramientas para flujos personalizados.

Inicio y conceptos básicos

  • Abre el editor Event Flow desde el menú del panel principal (escritorio o extensión).
  • Cada proyecto se guarda localmente hasta exportarlo. Utiliza Export para guardar una copia o compartir.
  • Trabaja dentro de lienzos llamados flujos. Cada flujo puede suscribirse a varias plataformas a la vez.

Resumen de nodos

  • Entradas (puertos izquierdos) esperan el contexto del mensaje.
  • Salidas (puertos derechos) emiten el mismo contexto más los cambios.
  • Los nodos lógicos pueden emitir el canal true y un canal opcional false .

Estructura de los datos

Cada mensaje lleva un objeto JSON. Las claves obligatorias siguen docs/event-reference.html (platform, type, chatname, chatmessage, etc.). Añade datos personalizados en meta.

Cada flujo empieza con un activador

Los nodos de acción (verdes) nunca se ejecutan solos: solo se activan cuando un nodo activador (azul) anterior devuelve true. Un flujo formado solo por acciones encadenadas parece válido, pero permanece inactivo porque nada inicia la cadena. Los nombres de los nodos describen lo que el nodo hace, no cuándo ocurre: Destacar mensaje (Feature Message) destaca un mensaje cuando el flujo llega a él; no se activa cuando destacas un mensaje en otro lugar.

Dos nodos de acción encadenados sin nodo activador
❌ Nunca se ejecuta. Destacar mensaje y Leer texto son ambas acciones; sin un activador inicial, nada pone en marcha la cadena.
Activador Cualquier mensaje conectado a las acciones Destacar mensaje y Leer texto
✅ Funciona. Un activador Cualquier mensaje (Any Message) (o El mensaje contiene, una expresión regular, un evento de donación, etc.) inicia la cadena; ambas acciones se ejecutan entonces con cada mensaje coincidente.

Superposición Flow Actions (salida de acciones)

Empieza con una plantilla de alerta:

Elige Donación: celebración + voz para una animación preparada y un clip sintético de agradecimiento, o la plantilla avanzada Donación: animación + sonido + filtro de OBS . Las nuevas plantillas de alertas empiezan desactivadas para que puedas configurarlas y probarlas primero. Para la plantilla de OBS, elige una fuente y el mismo filtro normalmente desactivado en ambas acciones de filtro.

Reproducir clip de audio (Play Audio Clip) y Multi-Alerts ahora comparten una biblioteca de 17 sonidos: aplausos, redoble, barrido, caja registradora y otros efectos, cuatro frases sintéticas en inglés etiquetadas y sonidos sencillos. Escuchar / Detener previsualiza localmente con un estado de reproducción visible. También puedes subir una grabación o elegir un archivo local de la aplicación. Para nombres o mensajes variables, utiliza la acción existente Leer texto (Speak Text) .

Event Flow reproduce mediante la fuente de navegador Flow Actions ; Multi-Alerts reproduce mediante su propia fuente. Mantén el sonido activado solo en una para el mismo evento y evita reproducirlo dos veces. Usa Tab para seleccionar un nodo y pulsa Intro o Espacio para editar sus propiedades.

Nodos como Reproducir clip de audio (Play Audio Clip), Mostrar superposición multimedia (Display Media Overlay), y los controles de OBS necesitan una página donde mostrarse. Esa página es la superposición Flow Actions servida desde actions.html. Mantenla en ejecución en tu programa de transmisión (OBS, paneles de navegador de Streamer.bot, etc.) para que las acciones de Event Flow tengan dónde aparecer.

Activador Cualquier mensaje conectado a una acción Reproducir clip de audio
Este flujo está completo y se activa con cada mensaje, pero el sonido se reproduce en la página de superposición Flow Actions, no en el editor. El botón Vista previa del editor reproduce localmente; la reproducción en directo requiere tener abierta la superposición. Si el navegador bloquea la reproducción automática, haz clic en Activar audio en la página Flow Actions para reintentar el último clip bloqueado. Hacer clic en otro lugar de esa página también permite la reproducción. Una fuente de navegador de OBS normalmente permite la reproducción automática.
Cómo abrirla (desde la ventana emergente/panel):
  1. Abre la ventana emergente principal de Social Stream Ninja (la ventana cargada desde popup.html o el icono de la extensión).
  2. Desplázate hasta la tarjeta «Flow Actions». Utiliza el botón [copiar enlace] o haz clic en la URL dentro de la tarjeta.
  3. El enlace tiene un aspecto como https://socialstream.ninja/actions.html?session=YOURSESSION. Pégalo en una fuente de navegador de OBS (se sugiere 1920×1080) o ábrelo en cualquier navegador de superposiciones.
Utilizar medios locales en la aplicación independiente:
  1. En una acción Reproducir clip de audio o Mostrar superposición multimedia, haz clic en Elegir archivo local (Choose Local File).
  2. Haz clic en Copiar URL local de Flow Actions para OBS y utiliza esa URL localhost generada en lugar de la URL alojada de Flow Actions.
  3. Mantén SSApp en ejecución. Si un archivo seleccionado cambia de ubicación, vuelve a la acción y haz clic en Volver a vincular (Relink).

La extensión de Chrome no puede servir archivos del disco por sí sola. Utiliza Subir o una URL alojada cuando no haya una aplicación de escritorio auxiliar. Consulta la guía de archivos multimedia para Event Flow para la configuración completa.

Una vez cargada, esa superposición puede:

  • Mostrar GIPHY o URL directas de medios, texto y confeti activados por tus flujos.
  • Reproducir sonidos (TTS, clips de audio) localmente para que los espectadores los escuchen.
  • Comunicarse con OBS mediante los ajustes WebSocket de la sección Flow Actions de la ventana emergente (cambio de escenas, alternancia de fuentes, actualizaciones de texto GDI+/FreeType, búfer de repetición, etc.).
Modos de control de OBS:
  • API de fuente de navegador: disponible solo cuando actions.html se ejecuta dentro de una fuente de navegador de OBS con Nivel de acceso avanzado. Aquí funciona el cambio de escenas, y las acciones de grabación, transmisión y búfer de repetición pueden utilizarlo como alternativa.
  • OBS WebSocket: recomendado para un control coherente. Flow Actions de Social Stream Ninja utiliza la API OBS WebSocket v5 de OBS 28 o posterior y espera el conjunto moderno de solicitudes en el puerto 4455.
  • Contraseña: opcional. Añade únicamente &obspw=... a la URL de Flow Actions si tu servidor OBS está configurado para requerir autenticación.
  • Diagnóstico de la superposición: añade &obsdebug=1 a la URL de actions.html si quieres una pequeña insignia de conexión en vivo de OBS en la superposición mientras resuelves problemas.
  • Establecer fuente de texto: actualiza directamente entradas Texto (GDI+) y Texto (FreeType 2) de OBS y admite variables de plantilla de Event Flow como {counterValue} y {counterTarget}.
  • Instalaciones antiguas 4.x: si todavía utilizas obs-websocket 4.x / puerto 4444, las acciones de fuentes, filtros, silencio y texto no funcionarán hasta actualizar OBS/obs-websocket.

Consulta la guía específica Guía de control de OBS para todos los activadores, acciones, pasos de configuración y recetas probadas.

Ruta de diagnóstico recomendada:
  1. Abrir obs-websocket-test.html.
  2. Confirma que GetVersion, GetCurrentProgramScene, y GetSceneList se cumplen.
  3. Realiza allí la comprobación de la acción correspondiente antes de probar la automatización completa de Event Flow.
Mantén la superposición abierta. Cerrar la página Flow Actions pausa todas las acciones de superposición, audio y OBS de Event Flow. Ocúltala o ponla en otra pantalla en vez de cerrarla.

1. ¿Qué pasa por un nodo?

El motor de Event Flow transmite dos cosas por cada cable:

  1. Datos del evento – el objeto de datos del evento o mensaje.
  2. Señal de puerta – un bit true/false que indica al siguiente nodo si debe ejecutarse.
Si un nodo emite false: los nodos posteriores dejan de ejecutarse salvo que reciban entrada por otra rama (por ejemplo, el puerto false de un nodo Condición). Así puedes crear fácilmente lógica alternativa sin duplicar flujos enteros.

Requisitos de entrada

  • Fuentes de eventos (Mensaje de Twitch, Temporizadores, Activador manual, etc.) ignoran las entradas previas: generan sus propios datos y siempre emiten true salvo que el propio nodo produzca un error.
  • Nodos de transformación y lógica leen los datos y pueden reescribir campos, establecer estado o cambiar la señal de puerta a false.
  • Nodos de acción se activan solo mientras la puerta siga en true. También pueden emitir datos actualizados si quieres seguir encadenando acciones.

Patrones de salida

Salida única

La mayoría de los nodos tienen una salida. Lo que entra (datos + puerta) sale sin cambios salvo que el nodo lo modifique.

Salidas True/False

Los nodos Condición, Comparar, Regex y Lógica tienen dos puertos de salida. Verdadero continúa por el puerto verde; false aparece en el puerto gris/rojo.

Transmitir sin cambios o sustituir

Algunos nodos (Establecer variable, Matemáticas, Sustituir texto) modifican los datos, pero siguen transmitiendo el estado true/false de su entrada. Otros (NOT, AND, OR) recalculan el booleano.

2. Referencia rápida de nodos lógicos

Estos bloques responden a las preguntas más habituales sobre qué significa true/false.

NOT

  • Entradas: 1 booleano (true/false) derivado del nodo anterior.
  • Salidas: el booleano invertido y los datos sin modificar.
  • Comportamiento predeterminado: Si no hay nada conectado a la entrada NOT, se evalúa como false, así que la salida es true.
Ejemplo: Coloca NOT después de «Contains Keyword» para activar una alerta cuando un espectador no utilice la palabra clave.

AND

  • Entradas: dos o más señales booleanas (A, B, ...). Puedes dejar puertos adicionales vacíos.
  • Salidas: true solo si todas las entradas conectadas son iguales a true.
  • Utiliza AND cuando varias condiciones deban cumplirse a la vez («es suscriptor» y «el mensaje de chat contiene !raffle»).

OR

  • Emite true si cualquier entrada conectada es verdadera.
  • Útil para activadores multiplataforma: conecta nodos de mensajes de Twitch y YouTube a un único OR y unifica la acción posterior.
¿Necesito siempre un nodo AND?
No. Muchos nodos ya proporcionan filtros combinados (por ejemplo, «Filtrar nivel de usuario» + «Contiene texto»). Utiliza AND solo cuando las opciones integradas no cubran tu combinación o cuando quieras un punto lógico reutilizable que compartan otras ramas.
NOT y entradas vacías: Un nodo NOT sin conectar seguirá emitiendo true. Mantenlo conectado a algo pertinente o desactiva el nodo para que no desbloquee accidentalmente un flujo.

3. Ejemplos de microflujos

A. Responder automáticamente salvo que el mensaje sea un comando

Mensaje de Twitch ──▶ Coincidencia Regex "^!" ─┐ │ ├─false──▶ Respuesta automática («¡Gracias por participar!») │ └─true──▶ No hacer nada

Aquí el nodo Regex emite true cuando el mensaje es un comando. Dirigimos el puerto false a nuestra respuesta, para que los participantes normales reciban un agradecimiento mientras los comandos simplemente pasan.

B. Exigir varias comprobaciones con AND

Mensaje de YouTube ──▶ Contiene «!queue» ─▶ AND ─▶ Retransmitir a Discord Membresía regalada ─▶ Rol de usuario = Miembro ──▲

El nodo AND garantiza que solo los miembros que utilicen la palabra clave correcta se retransmitan a Discord. Ambas ramas envían su resultado booleano al nodo AND; los datos de la primera rama continúan hacia los nodos siguientes.

C. Nodo NOT para bloquear alertas repetidas

Datos del evento ─▶ Comprobar estado (isAlertMuted) └─false─▶ NOT ─▶ Reproducir celebración

State Check emite el valor true cuando la alerta está silenciada. Al invertir ese resultado, NOT garantiza que solo reproduzcamos la celebración cuando el indicador sea false.

D. Reproducir aleatoriamente uno de dos sonidos

Flujo con puertas RANDOM, NOT y AND para reproducir uno de dos clips de audio al azar
Una elección al 50 % entre dos clips de audio. La puerta RANDOM hace una tirada por mensaje coincidente: si pasa, suena A; si falla, NOT invierte el resultado y AND permite que suene B.
Activador ──▶ RANDOM (50 %) ──▶ Reproducir sonido A │ └──▶ NOT ──▶ AND ──▶ Reproducir sonido B Activador ──────────────────▲

La puerta AND no es opcional. Un NOT aislado emitiría true siempre que la puerta RANDOM esté inactiva, así que el sonido B se reproduciría con cada mensaje de chat que no coincida con tu activador. Conectar el activador a AND como segunda entrada limita el sonido B a mensajes coincidentes. El mismo patrón funciona para cualquier pareja de acciones alternativas, no solo audio.

4. Evitar ecos, bucles y realimentación de retransmisiones

Retransmitir chat entre entornos es potente, pero puede crear ecos infinitos si escuchas tu propia salida. Sigue estas precauciones:

Nota sobre el destino YouTube Shorts:
Tanto los activadores entrantes como los destinos salientes de Retransmitir chat distinguen entre youtube y youtubeshorts. Utiliza dos acciones de retransmisión cuando un mensaje deba llegar a ambas variantes. Consulta YouTube Shorts y Event Flow.
Retransmitir chat omite automáticamente los reflejos reconocidos.
Un reflejo es un mensaje saliente capturado de nuevo desde un chat de destino. Las acciones actuales Retransmitir chat omiten esos reflejos reconocidos; no hay una casilla independiente Sin reflejos. Para ocultar o limitar su visualización en el panel y las superposiciones, utiliza una acción Filtro de ecos (Reflection Filter) con Bloquear todos (Block All), Permitir el primero (Allow First), o Permitir todos (Allow All). Esto controla la visualización al volver a recibir el mensaje, no su envío. Sigue el Tutorial de retransmisión de Twitch y YouTube para una configuración completa.
  • Evita sistemas de retransmisión duplicados. Desactiva Retransmitir todo global cuando utilices rutas equivalentes de Event Flow y comprueba si otros servicios conectan los mismos chats. No se garantiza que los metadatos personalizados sobrevivan al pasar por un chat de plataforma.
  • Utiliza nodos Debounce o Cooldown para alertas que solo deban activarse una vez cada X segundos.
  • Rompe los ciclos deliberadamente. Si dos ramas se alimentan entre sí, añade un nodo lógico que compruebe una variable de estado («currentlyRelaying») para que el flujo termine antes cuando el indicador esté activo.

5. Entradas, salidas y preguntas prácticas

¿Qué entra en un nodo?

  • Los datos completos del mensaje.
  • El valor de puerta (true/false).
  • Contexto opcional (variables de estado, temporizadores) que el nodo solicite explícitamente.

¿Qué sale de un nodo?

  • Los mismos datos salvo que el nodo los modifique.
  • Un valor de puerta recalculado (nodos lógicos) o transmitido sin cambios (acciones).
  • La mayoría de los efectos, como enviar chat, no modifican los datos, pero las acciones de puntos pueden añadir campos de estado como pointsTotal o pointsSpendError para la lógica posterior.

¿Cuándo crear ramas?

Siempre que quieras reaccionar de manera distinta a true frente a false. Arrastra un cable desde la salida de color que necesites (verde = verdadero, gris/rojo = falso) al siguiente nodo.

Recuerda: Si no haces nada con una salida false , el flujo simplemente termina ahí. Es ideal para filtros («bloquear todo lo que no pase la comprobación»), pero no olvides conectar la ruta false si necesitas alternativas.

Preguntas y respuestas habituales

  • ¿Tengo que utilizar AND para cada par de filtros? No. Muchos nodos incluyen varias comprobaciones (por ejemplo, el filtro básico de mensajes admite palabra clave + rol). Utiliza AND solo para combinaciones avanzadas o al unir señales de distintos nodos.
  • ¿Cómo llegan los valores true/false al nodo NOT? Cualquier nodo con una salida verde emite true de forma predeterminada. Cuando una condición falla, emite false. Conecta ese cable a NOT para invertir el resultado.
  • ¿Puede un nodo emitir datos aunque devuelva false? Sí. Los datos siguen viajando por la salida false; tú decides adónde debe ir esa rama.
  • ¿Cómo identifico a los miembros del equipo de TikTok? Elige Miembro del equipo de TikTok (TikTok Team Member) en el nodo Rol de usuario. Reconoce niveles e insignias del club de fans/equipo de TikTok en el mensaje entrante y no depende de los ajustes de la superposición principal de chat.
  • ¿Puede cada nodo Leer texto utilizar una voz distinta? Sí. Introduce un nombre o ID de voz compatible con el proveedor en Voz alternativa (Voice Override), o déjalo vacío para usar la voz TTS predeterminada de Flow Actions.

6. Referencia de variables de plantilla

Varios nodos de acción (Mostrar texto, Establecer fuente de texto, Enviar mensaje, Retransmitir chat, Leer TTS, Llamar webhook e Imprimir etiqueta térmica) admiten variables de plantilla que se sustituyen por datos del evento durante la ejecución. Encierra los nombres de variables entre llaves, como {username}.

Variables principales (compatibles con versiones anteriores)

VariableAliasDescripciónEjemplo
{username}{chatname}Nombre visible del usuarioCoolViewer123
{message}{chatmessage}Texto del mensaje de chat¡Hola a todos!
{source}-Nombre de la plataforma (con mayúscula inicial)Twitch, YouTube
{type}-Nombre de la plataforma (original)twitch, youtube
{donation}{hasDonation}Etiqueta visible de donación/propina5,00 USD, 500 bits

Variables ampliadas

VariableDescripciónEjemplo
{displayname}Nombre visible (campo alternativo)CoolViewer123
{donoValue}Equivalente de donación en USD, proporcionado o estimado; Event Flow deriva los valores de umbral de etiquetas normalizadas hasDonation como valor, $valor, valor + unidad o unidad/valor compacto. Las unidades virtuales de nombre desconocido utilizan 100 unidades = 0,01 USD; los regalos de TikTok sin precio utilizan una moneda por regalo (0,01 USD cada uno). {donationAmount} es un alias antiguo5.00
{event}Identificador del tipo de eventocheer, raid, new_follower
{membership}Estado de membresíaMEMBERSHIP, new_sponsor
{subtitle}Contexto adicionalMiembro desde hace 3 meses
{userid}ID del usuario en la plataforma12345678
{chatimg}URL del avatar del usuariohttps://...
{contentimg}URL de imagen adjuntahttps://...
{rewardTitle}Nombre de la recompensa cuando la fuente ofrece un campo de título de recompensa de primer nivelDestacar mi mensaje (Highlight My Message)
{meta}Datos estructurados del evento (JSON){"viewers":100}
{counterValue}Valor actual del contador después de un paso Contador o Comprobar contador12
{counterTarget}Valor objetivo del contador30
{counterRemaining}Objetivo del contador menos el valor actual, con mínimo de 018
La coincidencia de variables no distingue entre mayúsculas y minúsculas. {USERNAME}, {Username}, y {username} funcionan todos igual.
Los campos añadidos por el flujo también funcionan. Si una acción anterior añade un valor de primer nivel al mensaje, las plantillas posteriores pueden leerlo directamente. Así es como Check Counter ofrece {counterValue}, {counterTarget}, y {counterRemaining}.
JSON de Llamar webhook: Las variables de plantilla funcionan en valores de texto JSON a cualquier profundidad de objetos o matrices anidados. Las claves de objetos no se procesan como plantillas y un cuerpo personalizado sin marcadores se envía sin cambios.

Plantillas de ejemplo

  • Mostrar texto: {username} just cheered {hasDonation}!
  • Establecer fuente de texto de OBS: {username}: now {counterValue}, need {counterTarget}
  • Retransmitir chat: [{source}] {username}: {message}
  • TTS: {username} says {message}
  • Alerta de donación: {username} donated {donation} - {subtitle}
  • Etiqueta térmica: {username}, una nueva línea y luego {donation}. Consulta la Guía de impresoras térmicas para configurar la impresora, etiquetas de tamaño fijo y un flujo completo.
  • Llamar webhook de Discord: {"content":"{message}","username":"{username}","avatar_url":"{chatimg}"}
Las variables ausentes se convierten en cadenas vacías. Si un evento no tiene un campo concreto (por ejemplo, {donation} en un mensaje normal de chat), el marcador se sustituye por una cadena vacía en lugar de mostrar el texto literal {donation} .

7. Lista de buenas prácticas

  • Pon nombre y color a tus nodos para que en el futuro sepas qué hace cada rama.
  • Prueba con el simulador integrado (Enviar evento de prueba) antes de activar un flujo en vivo.
  • Agrupa la lógica cerca de la fuente. Filtra lo antes posible para evitar procesamiento adicional más adelante.
  • Registra repeticiones en nodos de estado. Utiliza contadores, interruptores y fechas y horas para evitar alertas duplicadas.
  • Documenta los campos de meta. Cuando añadas claves personalizadas de meta , documéntalas para mantener coherentes las superposiciones y los clientes remotos.
Guarda versiones. Exporta tu flujo al alcanzar un hito. Importar es la forma más sencilla de recuperar un estado anterior si un experimento sale mal.

8. Más posibilidades

Ejecutar flujos personalizados desde Stream Deck o la API: activadores con nombre, plantilla inicial, descubrimiento de flujos, datos adicionales, ejemplos HTTP/WebSocket/P2P y gestos de diales.

¿Quieres profundizar?

  • Usa Nodos de estado (contadores, interruptores, temporizadores) para mantener contexto entre eventos.
  • Combina Variables + lógica para crear sistemas de colas, sorteos o motores de puntuación.
  • Conéctate al sistema Puntos y recompensas para que los espectadores puedan activar flujos intencionalmente.
  • ¿Utilizas la aplicación de escritorio SSApp? Desbloquear Nodos de JavaScript personalizado para lógica arbitraria que no cubra ningún nodo integrado.
  • Consulta la Referencia de eventos para documentación detallada de los datos de todas las plataformas.

Esta guía es independiente de forma intencional: cópiala localmente, adáptala a tu equipo y sigue experimentando en el editor.

9. JavaScript personalizado Solo SSApp / escritorio

Dos nodos del editor Event Flow permiten escribir JavaScript arbitrario que se ejecuta dentro del flujo: Código personalizado (Custom Code) (activador) y Ejecutar código personalizado (Execute Custom Code) (acción). Son la alternativa para lo que no se puede expresar con los nodos integrados.

Requiere la aplicación de escritorio. Los nodos JavaScript personalizados están desactivados en la extensión del navegador porque la política de seguridad de contenido de Manifest V3 de Chrome bloquea new Function() / eval(). Abre el editor mediante la aplicación de escritorio SSApp para activarlos. En modo extensión, los nodos aparecen atenuados con la etiqueta «Solo escritorio».
Editar código: selecciona un nodo Código personalizado y haz clic en Abrir editor de código (Open Code Editor) para una ventana de edición grande. Guardar y cerrar (Save & Close) comprueba la sintaxis JavaScript y guarda todo el flujo; Ctrl+S o Cmd+S hace lo mismo. Cancelar deja el nodo sin cambios.
Editor Event Flow: estado vacío
El editor Event Flow. El panel izquierdo enumera los nodos disponibles; el lienzo punteado sirve para construir flujos; el panel derecho muestra las propiedades del nodo seleccionado.

Código personalizado: nodo activador

Arrastra Código personalizado (Custom Code) desde el grupo Avanzado del panel Disparadores al lienzo. Actúa como una puerta: el flujo continúa solo cuando tu código devuelve true.

Panel de activadores con el nodo Código personalizado en el grupo Avanzado
Código personalizado se encuentra en el grupo Avanzado del panel Activadores.
Panel de propiedades del activador Código personalizado con el editor JavaScript
Panel de propiedades después de colocar el activador. Escribe cualquier expresión que devuelva true o false.
Firma: tu código se ejecuta como function(message) { ... }
Debe devolver: un booleano: true para permitir que continúe el flujo, false para detenerlo.
Disponible: el objeto message (consulta API de mensajes abajo), además de convertCurrency(value, targetCurrency, source) y convertToUSD(value, source).

Ejecutar código personalizado: nodo de acción

Arrastra Ejecutar código personalizado (Execute Custom Code) desde el grupo Integraciones del panel Acciones . Puede modificar el mensaje, bloquearlo o añadir metadatos que lean los nodos posteriores.

Panel de acciones con Ejecutar código personalizado en el grupo Integraciones
Ejecutar código personalizado en el grupo Integraciones del panel Acciones.
Panel de propiedades de Ejecutar código personalizado con el editor de código
Propiedades de la acción. Devuelve un objeto para incorporar los cambios al resultado del flujo.
Firma: tu código se ejecuta como function(message, result) { ... }
Debería devolver: un objeto o una Promise que se incorpora a result— consulta API de resultados.
Disponible: message (los datos del evento), result (estado actual del resultado del flujo), printThermal(html, options), además de convertCurrency(value, targetCurrency, source) y convertToUSD(value, source).
Impresión térmica en SSApp: elige la impresora y calibra el ancho del papel y los márgenes seguros en Control de impresora (Printer Control), luego devuelve printThermal('<strong>' + message.chatname + '</strong>'). SSApp pone el trabajo en cola silenciosamente mediante la API nativa de impresoras de Windows y utiliza esos ajustes guardados. Un flujo puede sustituirlos con opciones como { width: '58mm', marginLeft: '3mm', marginRight: '3mm', marginTop: '2mm', marginBottom: '2mm', feed: '3mm', marginType: 'printableArea' }. Devolver la Promise permite que Event Flow espere el envío e informe de errores.
Lienzo con un activador Código personalizado y una acción Ejecutar código personalizado uno junto al otro
Un activador Código personalizado (azul) y una acción Ejecutar código personalizado (verde) en el lienzo. Conecta la salida del activador a la entrada de la acción.

El objeto message

Ambos nodos reciben los datos completos del evento como message. Los campos siguientes siempre están disponibles; los eventos específicos de plataformas pueden incluir otros.

CampoTipo de datosDescripciónEjemplo
message.chatmessagecadenaEl texto del mensaje de chat (puede contener HTML)"Hello stream!"
message.chatnamecadenaNombre visible del remitente"CoolViewer"
message.useridcadenaID de usuario de la plataforma"12345678"
message.typecadenaPlataforma de origen (minúsculas)"twitch", "youtube", "kick"
message.hasDonationcadenaCadena de donación formateada, si existe"$5.00", "500 bits"
message.donoValuenúmero / cadenaEquivalente de donación en USD cuando lo proporciona la fuente; se respetan los valores cero válidos. Event Flow utiliza como alternativa currency.js para convertir etiquetas normalizadas de hasDonation para comparar umbrales, incluidas 100 unidades de nombre desconocido = 0,01 USD. No interpreta el texto libre de chatmessage para los valores de donación.5
message.eventcadenaIdentificador del tipo de evento"new_follower", "cheer", "raid"
message.membershipcadenaEstado de membresía cuando corresponda"MEMBERSHIP"
message.subtitlecadenaLínea secundaria de contexto"Member for 3 months"
message.modbooleanoEl remitente es moderadortrue
message.subscriberbooleanoEl remitente es suscriptortrue
message.vipbooleanoEl remitente tiene estado VIPtrue
message.chatimgcadenaURL del avatar del usuario"https://..."
message.metaobjetoDatos estructurados arbitrarios adjuntos al evento{ viewers: 120 }
Conversión de moneda: utiliza convertCurrency(message.hasDonation, 'EUR', message.type) para convertir la etiqueta formateada de donación a EUR. Devuelve un número o null cuando la moneda de destino solicitada no es compatible. El conversor utiliza los tipos internos aproximados de Social Stream Ninja; no consulta un servicio externo de cambio.

Qué devuelve la acción

Devuelve un objeto simple desde el código de tu acción. Los campos que incluyas se incorporan al objeto result ; los campos que omitas conservan sus valores actuales.

Campo devueltoTipo de datosEfecto
modifiedbooleanoConfigura true si cambiaste campos de message . Indica a los nodos posteriores que se han modificado los datos.
messageobjetoDevuelve el mensaje, posiblemente modificado, para que los nodos posteriores reciban tus cambios.
blockedbooleanoConfigura true para impedir que el mensaje se muestre o retransmita.
Retorno mínimo seguro: return { modified: false, message };
Aunque no hayas cambiado nada, devolver message permite que continúe al siguiente nodo.

Ejemplos de fragmentos

Copia cualquiera de estos fragmentos en el área Código JavaScript del tipo de nodo correspondiente.

Fragmentos de activadores: devuelve true para continuar el flujo

Coincidencia de palabra clave (sin distinguir mayúsculas)
Continúa el flujo solo cuando el mensaje contenga una palabra o frase concreta.
// Matches "!hello" anywhere in the message return (message.chatmessage || '').toLowerCase().includes('!hello');
Detección de comandos con Regex
Coincidir con mensajes que empiecen con un comando de una lista definida (p. ej., !queue, !raffle, !enter).
return /^!(queue|raffle|enter)\b/i.test(message.chatmessage || '');
Donación por encima de un umbral
Activa solo cuando una donación alcance o supere un importe mínimo.
const amount = message.donoValue !== undefined && message.donoValue !== null && message.donoValue !== '' ? Number(message.donoValue) : (typeof convertToUSD === 'function' ? convertToUSD(message.hasDonation || '', message.type || '') : Number(String(message.hasDonation || '').replace(/[^0-9.]/g, '') || 0)); return amount >= 5;
YouTube Super Chat o Super Sticker en un intervalo de EUR
Convierte la etiqueta estándar de donación de YouTube a EUR, excluye Jewels/Gifts y selecciona un intervalo para sonido o imagen.
var eventName = String(message.event || '').toLowerCase(); if (eventName !== 'superchat' && eventName !== 'supersticker') return false; var eurValue = convertCurrency(message.hasDonation || '', 'EUR', message.type || ''); if (typeof eurValue !== 'number' || !isFinite(eurValue)) return false; message.eurValue = eurValue; return eurValue >= 10 && eurValue < 25;
Filtro de plataforma
Procesar solo eventos de plataformas concretas.
return ['twitch', 'youtube'].includes(message.type);
Condición de suscriptor / VIP / moderador
Permite que el flujo continúe solo para usuarios con privilegios.
return !!(message.subscriber || message.vip || message.mod);
Varias condiciones: VIP + palabra clave
Combina comprobación de rol y contenido del mensaje en una sola expresión que no cubra ningún activador integrado.
const isPrivileged = !!(message.subscriber || message.vip || message.mod); const isCommand = /^!feature\b/i.test(message.chatmessage || ''); return isPrivileged && isCommand;
Condición de longitud del mensaje
Procesar solo mensajes con suficiente contenido (útil para TTS o retransmisión y evitar spam de un solo emoji).
return (message.chatmessage || '').replace(/<[^>]+>/g, '').trim().length >= 20;

Fragmentos de acciones: devuelve { modified, message }

Añadir una insignia o etiqueta al mensaje
Añade un indicador visual al final de cada mensaje que pase por esta acción.
message.chatmessage = (message.chatmessage || '').trimEnd() + ' ✅'; return { modified: true, message };
Bloquear un mensaje según una condición
Inspecciona el contenido y descarta silenciosamente el mensaje si coincide una regla; útil para patrones de spam que el filtro de palabras clave no puede expresar.
const text = (message.chatmessage || '').toLowerCase(); const spamPatterns = ['buy followers', 'free nitro', 'click here']; if (spamPatterns.some(p => text.includes(p))) { return { blocked: true, message }; } return { modified: false, message };
Eliminar @menciones
Elimina todas las menciones @username de un mensaje antes de retransmitirlo a otra plataforma.
message.chatmessage = (message.chatmessage || '').replace(/@\w+/g, '').trim(); return { modified: true, message };
Dar formato a un anuncio de donación
Reescribe chatmessage como un anuncio con formato uniforme cuando haya una donación.
const amount = parseFloat(message.donoValue || 0); if (amount > 0) { const note = (message.chatmessage || '').trim(); message.chatmessage = `💰 ${message.chatname} donated $${amount.toFixed(2)}!` + (note ? ` "${note}"` : ''); return { modified: true, message }; } return { modified: false, message };
Adjuntar metadatos de enrutamiento para nodos posteriores
Etiqueta el mensaje con un campo personalizado que una acción posterior Reenviar chat (Relay Chat) o Enviar mensaje (Send Message) puede leer desde una variable de plantilla ({meta}).
message.meta = message.meta || {}; // Assign a donation tier so the next node can use {meta} to decide overlay colour const amount = parseFloat(message.donoValue || 0); message.meta.donationTier = amount >= 20 ? 'gold' : amount >= 5 ? 'silver' : 'bronze'; return { modified: true, message };
Prefijo de mensaje según la plataforma
Añade una etiqueta de plataforma al principio al retransmitir entre plataformas para que los espectadores sepan el origen.
const labels = { twitch: '[Twitch]', youtube: '[YouTube]', kick: '[Kick]', tiktok: '[TikTok]', }; const label = labels[message.type] || `[${message.type || 'Chat'}]`; message.chatmessage = `${label} ${message.chatname}: ${message.chatmessage || ''}`; return { modified: true, message };

Ejemplo completo: bot de solicitudes de funciones para VIP

Este flujo escucha !feature <text> de suscriptores, VIP o moderadores, lo reformatea como una solicitud de una función y lo retransmite a un segundo destino (por ejemplo, Discord).

┌──────────────────────┐ ┌──────────────────────────┐ ┌──────────────────┐ │ Activador Código personalizado │────▶│ Acción Ejecutar código personalizado │────▶│ Retransmitir chat │ │ │ │ │ │ (a Discord) │ │ Puerta: VIP/suscriptor/mod │ │ Reformatear texto del mensaje │ │ │ │ + empieza por │ │ → "📋 Solicitud de una función │ │ │ │ !feature │ │ de {name}: {text}" │ │ │ └──────────────────────┘ └──────────────────────────┘ └──────────────────┘

Paso 1: activador Código personalizado (pégalo en el campo Código JavaScript del activador):

// Only let VIPs, subscribers, and mods through, and only for !feature commands const isPrivileged = !!(message.subscriber || message.vip || message.mod); const isCommand = /^!feature\b/i.test((message.chatmessage || '').trim()); return isPrivileged && isCommand;

Paso 2: acción Ejecutar código personalizado (pégalo en el campo Código JavaScript de la acción):

// Strip the "!feature" command word and format a clean announcement const featureText = (message.chatmessage || '') .replace(/^!feature\s*/i, '') .trim(); if (!featureText) { // No text after the command: block rather than relay an empty request return { blocked: true, message }; } message.chatmessage = `📋 Feature request from ${message.chatname}: ${featureText}`; return { modified: true, message };

Paso 3: acción Retransmitir chat: añade un nodo normal Retransmitir chat después de la acción y configúralo para tu destino de Discord u otro. Aquí no necesitas código personalizado; el valor reformateado de message.chatmessage pasa automáticamente.

Probar el flujo. Haz clic en este botón: Probar flujo (Test Flow) (arriba a la derecha del editor) para enviar un mensaje sintético por el flujo sin necesitar una transmisión en vivo. Establece chatname como un suscriptor, añade un mensaje como !feature dark mode support, y confirma que el destino de Retransmitir chat reciba la cadena reformateada.
Panel Probar flujo para enviar eventos sintéticos de prueba
El panel Probar flujo. Rellena los campos que correspondan a las condiciones de tu activador y haz clic en Ejecutar prueba (Run Test) para validar todo el proceso.

Consideraciones de seguridad

El código personalizado se ejecuta con los privilegios del proceso de renderizado. Dentro de SSApp, el código de los nodos JS personalizados tiene acceso completo al objeto window y cualquier API que exponga el script de precarga (p. ej., window.ninjafy). Trata los archivos de flujos importados como código ejecutable: importa únicamente flujos de fuentes de confianza.
  • Sin aislamiento de red. El código de acción puede llamar a fetch(). Si aceptas flujos compartidos por otras personas, revisa el JavaScript antes de activarlos.
  • Los errores se capturan. Un error de ejecución en tu código devuelve false (activador) o no hace nada (acción), y registra el error en la consola de DevTools; el flujo no se bloquea.
  • También los errores de sintaxis. Un error de tipo SyntaxError durante la compilación se captura del mismo modo. Comprueba DevTools (F12) si parece que un nodo no hace nada.