Crear automatizaciones personalizadas

:information_source: Este es un borrador y puede necesitar trabajo adicional.

Vocabulario

  • trigger: representa el nombre del disparador, por ejemplo: user_added_to_group
  • triggerable: representa la lógica de código asociada a un disparador, por ejemplo: triggers/user_added_to_group_.rb
  • script: representa el nombre del script, por ejemplo: send_pms
  • scriptable: representa la lógica de código asociada a un script, por ejemplo: scripts/send_pms.rb

API de Plugins

add_automation_scriptable(name, &block)
add_automation_triggerable(name, &block)

API de Scriptables

campo

field :name, component: te permite agregar un valor personalizable en la interfaz de usuario de tu automatización.

Lista de componentes válidos:

# foo debe ser único y representa el nombre de tu campo.

field :foo, component: :text # genera una entrada de texto
field :foo, component: :list # genera una entrada de texto de selección múltiple donde los usuarios pueden completar valores
field :foo, component: :choices, extra: { content: [ {id: 1, name: 'your.own.i18n.key.path' } ] } # genera un cuadro combinado con contenido personalizado
field :foo, component: :boolean # genera una casilla de verificación
field :foo, component: :category # genera un selector de categoría
field :foo, component: :group # genera un selector de grupo
field :foo, component: :date_time # genera un selector de fecha y hora
field :foo, component: :tags # genera un selector de etiquetas
field :foo, component: :user  # genera un selector de usuario
field :foo, component: :pms  # permite crear una o más plantillas de PM
field :foo, component: :categories  # permite seleccionar cero o más categorías
field :foo, component: :key-value  # permite crear pares de clave-valor
field :foo, component: :message  # permite componer un PM con variables reemplazables
field :foo, component: :trustlevel  # permite seleccionar uno o más niveles de confianza
disparadores y disparable!
# Te permite definir la lista de disparables permitidos para un script
triggerables %i[recurring]

# Te permite forzar un disparable para tu script y también te permite forzar algún estado en los campos
field :recurring, component: :boolean
triggerable! :recurring, state: { foo: false }
marcadores de posición
# Te permite marcar una clave como reemplazable en textos usando la sintaxis de marcador de posición `%%sender%%`
placeholder :sender

Ten en cuenta que es responsabilidad del script proporcionar valores para los marcadores de posición y aplicar el reemplazo usando input = utils.apply_placeholders(input, { sender: 'bob' })

script

Este es el corazón de una automatización y donde ocurre toda la lógica.

# el contexto se envía cuando se activa la automatización y puede diferir mucho entre los disparadores
script do |context, fields, automation|
end

Localización

Cada campo que utilices dependerá de claves i18n y se organizará por su disparador/script.

Por ejemplo, un scriptable con este contenido:

field :post_created_edited, component: :category

Requerirá las siguientes claves en client.en.yml:

en:
  js:
    discourse_automation:
      scriptables:
        post_created_edited:
          fields:
            restricted_category:
              label: Category
              description: Opcional, permite limitar la ejecución del disparador a esta categoría

Ten en cuenta que la descripción es opcional aquí.


Este documento está controlado por versiones - sugiere cambios en github.

7 Me gusta

Cuando vi que era un tema nuevo, me emocioné: ¡pensé que se habían compartido más detalles! :laughing:

Como alguien que no programa en Ruby, pero muy interesada en la automatización del flujo de trabajo, esperaba poder entender un poco más con un ejemplo…

:thinking:

Supongo que tendré que empezar en Developing Discourse Plugins - Part 1 - Create a basic plugin:sweat_smile:

8 Me gusta

Básicamente, acabo de extraer esta sección del tema del plugin para que no pareciera que necesitabas conocerla para usar las existentes. :slight_smile:

Estoy de acuerdo en que sería genial si esto fuera un poco más paso a paso. He enviado una señal de socorro para pedir ayuda a la comunidad aquí para ver si alguien tiene experiencia en este tipo de cosas: :crossed_fingers:

5 Me gusta

Un ejemplo de “hola mundo” sería genial.
¿Dónde deberían almacenarse los scripts? Me gustaría experimentar un poco con él.

4 Me gusta

Creo que puedes escribir scripts personalizados usando Chat GPT junto con este plugin.

Probablemente el mejor lugar para empezar a buscar sea en el script de automatización que se añade al plugin Data Explorer: discourse-data-explorer/plugin.rb at main · discourse/discourse-data-explorer · GitHub. También vale la pena echar un vistazo a los scripts y desencadenadores existentes del plugin Automation: https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation

Dado que no hay mucha información en Meta sobre cómo añadir automatizaciones personalizadas, aquí tienes un archivo plugin.rb de ejemplo que añade un script para actualizar la preferencia de correo electrónico del Resumen de Actividad de un usuario. El script puede ser activado por los desencadenadores ‘user_added_to_group’ o ‘user_removed_from_group’ del plugin Automation.

# frozen_string_literal: true

# name: automation-script-example
# about: An example of how to add a script to an automation
# version: 0.0.1
# authors: scossar

enabled_site_setting :automation_script_example_enabled

after_initialize do
  reloadable_patch do
    if defined?(DiscourseAutomation)
      DiscourseAutomation::Scriptable::USER_UPDATE_SUMMARY_EMAIL_OPTIONS =
        "user_update_summary_email_options"
      add_automation_scriptable(
        DiscourseAutomation::Scriptable::USER_UPDATE_SUMMARY_EMAIL_OPTIONS
      ) do

        field :email_digests, component: :boolean

        version 1
        triggerables [:user_added_to_group, :user_removed_from_group]

        script do |context, fields, automation|
          if automation.script == "user_update_summary_email_options" && (context["kind"] == "user_added_to_group" || context["kind"] == "user_removed_from_group")
            user_id = context["user"].id
            digest_option = fields.dig("email_digests", "value")
            user_option = UserOption.find_by(user_id: user_id)

            if (user_option)
              user_option.update(email_digests: digest_option)
            end
          end
        end
      end
    end
  end
end

El código completo del plugin está aquí: GitHub - scossar/automation-script-example: An example of how to add a custom script to the Discourse Automation plugin..

:warning: por favor, no uses este código tal cual en un sitio de producción. No había mirado el código de Automation antes de esta noche. Si recibo algún comentario sobre posibles problemas con el código, actualizaré esta publicación y el repositorio de GitHub.

Edición: mi preocupación era cómo manejar mejor el caso de múltiples scripts de automatización activados por los desencadenadores ‘user_added_to_group’ o ‘user_removed_from_group’. La versión inicial del plugin comprobaba:

fields.has_key?("email_digests")

pero eso me pareció un poco inestable. ¿Y si se añadía otro script que también tuviera una clave email_digests?

El código actualizado pasa el parámetro automation al bloque de código y comprueba:

automation.script == "user_update_summary_email_options"

Eso debería asegurar que el script no se ejecute para la automatización incorrecta.
… pensándolo un poco más, es poco probable que el script se active por una automatización para la que no fue configurado :slight_smile:

7 Me gusta

Yo también me gustaría saberlo: una vez que hayas creado un repositorio como el de @simon, ¿cómo accede a él el plugin?

¿Tenemos que bifurcar todo el plugin y colocarlo junto con los existentes en https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation/scripts/? ¿O hay una forma más elegante?

1 me gusta

Necesitas instalarlo como cualquier otro plugin de Discourse: Instalar Plugins en Discourse. Así que instalarías el plugin de Automatización e instalarías tu plugin que añade los scripts personalizados. La razón por la que funciona es por los métodos definidos aquí: https://github.com/discourse/discourse-automation/blob/main/lib/plugin/instance.rb. En el código de ejemplo que publiqué anteriormente, verás que el script personalizado se está añadiendo con una llamada a add_automation_scriptable.

Nota: no instales la automatización de ejemplo de mi repositorio de GitHub, solo tómalo como un ejemplo de cómo extender el plugin de Automatización. (Olvidé que lo había enlazado aquí y lo actualicé para que solo funcione con mi versión bifurcada del plugin de Automatización de Discourse. El código que enlacé aquí sigue siendo válido: Create custom Automations - #6 by simon. Actualizaré el plugin automation-script-example lo antes posible para que funcione sin los cambios que hice en mi versión bifurcada del plugin de Automatización).

Mi preocupación era infundada. Esta condición no es necesaria:

if automation.script == "user_update_summary_email_options" && (context["kind"] == "user_added_to_group" || context["kind"] == "user_removed_from_group")

Actualizaré el ejemplo pronto.

4 Me gusta

¿Entiendo correctamente que las automatizaciones personalizadas requieren una instalación autoalojada (o, de lo contrario, acceso directo al backend al sistema de archivos donde está instalado Discourse)?

2 Me gusta

Sí, sin embargo, estamos muy abiertos a fusionar nuevos scripts de automatización en la compilación comunitaria, ¿qué estás pensando en construir?

6 Me gusta

Específicamente, estamos buscando una forma de reemplazar una cadena específica en las publicaciones (no nos importa mucho la sintaxis exacta, pero algo como el texto plano @ref `Random.rand!` ) con un enlace formateado como Random.rand!. Buscar la URL exacta es un proceso complicado con decenas de miles de objetivos posibles que es completamente inviable con expresiones regulares (como lo hace Auto linkify words/watched words) y mucho más fácil con un entorno similar a un plugin completo de Turing… así que tenía curiosidad si las automatizaciones podrían hacer esto.

Así que estaba buscando una acción posterior a la edición, algo similar a lo que hace el usuario @system cuando citas toda la publicación anterior (ver aquí). Sería un script de “Editar publicación” que se activaría en “Publicación creada” (o quizás después de que la publicación esté cocida)… pero supongo que el marco de automatización no permitiría una acción de “post-edición” tan general. Creo que necesitaría ser una automatización personalizada bastante específica de enlace a la documentación de Julia, lo que ciertamente no tiene sentido en una compilación comunitaria.

Podría estar buscando en el lugar equivocado con automatizaciones personalizadas; solo estaba explorando lo que es posible. Por supuesto, como foro para entusiastas de los lenguajes de programación, los usuarios intrépidos ya están pensando en programar bots que podrían usar la API de Discourse para hacer esto.

1 me gusta

No estoy seguro de que la automatización sea lo que buscas aquí, ya que algo que parece crítico es la experiencia del “usuario final”. Con la automatización, esto solo se reemplazaría después del hecho.

Pensando en este tipo de problema, probablemente recomendaría optar por un plugin personalizado o un componente de tema.
Un componente de tema podría funcionar así:

  1. El usuario escribe: ^Rand
  2. Se realiza una llamada HTTP a un servicio backend que alojas y que enumera todas las opciones con URL.
  3. El usuario selecciona la que desea y presiona Enter.
  4. Markdown se reemplaza por [Random.rand!](https://docs.julialang.org/en/v1/stdlib/Random/#Random.rand!)

Un plugin que modifique el pipeline de markdown podría funcionar de manera similar a onebox y simplemente autovincular mientras escribes, dejando la sintaxis original. por ejemplo: ^Random.rand
Entiendo lo que dices sobre que linkify no es ideal, el descubrimiento es difícil, además es posible que tengas que alojar un sitio web para normalizarlo a lookup.docs.julialang.org?q=Random.rand!

Ciertamente un problema muy interesante. Creo que la experiencia de usuario de un componente de tema puede ser razonable aquí.

2 Me gusta

¡Eso es genial, gracias por tus pensamientos y consejos! Los llevaré a un tema aparte si y cuando tenga más preguntas (o respuestas :).

2 Me gusta

Creo que un plugin personalizado que se active al cambiar una publicación es lo que quieres crear. Sospecho que para tu caso de uso, que tiene un disparador simple, sería más fácil escribir un plugin que lidiar con el plugin de automatización.

1 me gusta

@McUles y @nathank ¿han encontrado la información que buscaban?

Para que la automatización personalizada funcione, he tenido que modificar los siguientes archivos:

Creado el script de automatización personalizado

Actualizado: server.en.yml

añadido el nombre de la automatización personalizada; título; y descripción en la sección de scriptables del archivo yml.

Actualizado: client.en.yml

añadido el nombre de la automatización personalizada en scriptables; añadir la palabra clave ‘field’; dentro de la palabra clave field añadir ‘field_name’ seguido de ‘label’ y ‘description’

Actualizado: scripts.rb

añadir el nombre de la automatización personalizada en la lista de scripts. Ejemplo: FILE_NAME = “file_name”

Actualizado: plugin.rb

dentro de ‘after_initialize do’, añadir la ruta al script de automatización personalizado. Ejemplo: ‘lib/discourse_automation/scripts/file_name’

No entiendo completamente lo que has puesto ahí. ¿Son estas modificaciones al plugin de Automatizaciones o componentes importantes de un plugin hermano que contiene la automatización personalizada?

Sería genial tener esto unificado en el OP.

2 Me gusta

Eso es. Realmente no sabía la respuesta a tu pregunta, así que aquí te explico cómo encontré una respuesta y también la respuesta a “¿Hay algún ejemplo que pueda ver en alguna parte?”.

Primero, obtén esto: GitHub - discourse/all-the-plugins

Luego, usa grep para buscar algo, como “add_automation_scriptable”, y así podrás averiguar qué lo está usando.

 (main) pfaffman@noreno:~/src/discourse-repos/all-the-plugins/official$ grep -r add_automation_scriptable
discourse-assign/plugin.rb:    add_automation_scriptable("random_assign") do
discourse-chat-integration/plugin.rb:    add_automation_scriptable("send_slack_message") do
discourse-chat-integration/plugin.rb:    add_automation_scriptable("send_chat_integration_message") do
discourse-data-explorer/plugin.rb:      add_automation_scriptable("recurring_data_explorer_result_pm") do
discourse-data-explorer/plugin.rb:      add_automation_scriptable("recurring_data_explorer_result_topic") do

Así que quizás deberías mirar discourse-assign o data-explorer.

2 Me gusta