Créer des automatisations personnalisées

:information_source: Ceci est un brouillon et pourrait nécessiter des travaux supplémentaires.

Vocabulaire

  • trigger : représente le nom du déclencheur, par exemple : user_added_to_group
  • triggerable : représente la logique de code associée à un déclencheur, par exemple : triggers/user_added_to_group_.rb
  • script : représente le nom du script, par exemple : send_pms
  • scriptable : représente la logique de code associée à un script, par exemple : scripts/send_pms.rb

API de Plugin

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

API Scriptable

field

field :name, component: vous permet d’ajouter une valeur personnalisable dans l’interface utilisateur de votre automatisation.

Liste des composants valides :

# foo doit être unique et représente le nom de votre champ.

field :foo, component: :text # génère un champ de saisie de texte
field :foo, component: :list # génère un champ de saisie de texte multi-sélection où les utilisateurs peuvent saisir des valeurs
field :foo, component: :choices, extra: { content: [ {id: 1, name: 'your.own.i18n.key.path' } ] } # génère une boîte combinée avec un contenu personnalisé
field :foo, component: :boolean # génère une case à cocher
field :foo, component: :category # génère un sélecteur de catégorie
field :foo, component: :group # génère un sélecteur de groupe
field :foo, component: :date_time # génère un sélecteur de date et d'heure
field :foo, component: :tags # génère un sélecteur d'étiquettes
field :foo, component: :user  # génère un sélecteur d'utilisateur
field :foo, component: :pms  # permet de créer un ou plusieurs modèles de MP
field :foo, component: :categories  # permet de sélectionner zéro ou plusieurs catégories
field :foo, component: :key-value  # permet de créer des paires clé-valeur
field :foo, component: :message  # permet de composer un MP avec des variables remplaçables
field :foo, component: :trustlevel  # permet de sélectionner un ou plusieurs niveaux de confiance
triggerables et triggerable!
# Définit la liste des triggerables autorisés pour un script
triggerables %i[recurring]

# Force un triggerable pour votre script et permet également de forcer un état sur les champs
field :recurring, component: :boolean
triggerable! :recurring, state: { foo: false }
placeholders
# Marque une clé comme remplaçable dans les textes en utilisant la syntaxe de placeholder `%%sender%%`
placeholder :sender

Notez qu’il incombe au script de fournir les valeurs pour les placeholders et d’appliquer le remplacement en utilisant input = utils.apply_placeholders(input, { sender: 'bob' })

script

C’est le cœur d’une automatisation et là où toute la logique se déroule.

# context est envoyé lorsque l'automatisation est déclenchée, et peut beaucoup varier entre les déclencheurs
script do |context, fields, automation|
end

Localisation

Chaque champ que vous utiliserez dépendra de clés i18n et sera nommé par espace de noms en fonction de leur déclencheur/script.

Par exemple, un scriptable avec ce contenu :

field :post_created_edited, component: :category

Nécessitera les clés suivantes dans client.en.yml :

en:
  js:
    discourse_automation:
      scriptables:
        post_created_edited:
          fields:
            restricted_category:
              label: Catégorie
              description: Optionnel, permet de limiter l'exécution du déclencheur à cette catégorie

Notez que la description est facultative ici.


Ce document est contrôlé par version - suggérez des modifications sur github.

7 « J'aime »

Quand j’ai vu que c’était un nouveau sujet, je me suis réjoui : je pensais que plus de détails avaient été partagés ! :rire:

En tant que personne qui ne programme pas en Ruby, mais qui est très intéressée par l’automatisation des flux de travail, j’espérais pouvoir comprendre un peu plus par l’exemple…

:réfléchir:

Je suppose que je devrai commencer par Developing Discourse Plugins - Part 1 - Create a basic plugin… :sourire_en_sueur:

8 « J'aime »

J’ai essentiellement retiré cette section du sujet du plugin pour qu’il ne semble pas que vous deviez la connaître pour utiliser celles qui existent déjà. :slight_smile:

Je suis d’accord qu’il serait formidable si cela était un peu plus étape par étape. J’ai lancé un appel à l’aide de la communauté ici pour voir si quelqu’un a de l’expérience dans ce domaine : :crossed_fingers:

5 « J'aime »

Un exemple de « hello world » serait génial.
Où dois-je stocker les scripts ? J’aimerais expérimenter un peu.

4 « J'aime »

Je pense que vous pouvez écrire des scripts personnalisés en utilisant Chat GPT avec ce plugin.

Le meilleur endroit pour commencer à chercher est probablement le script d’automatisation ajouté au plugin Data Explorer : discourse-data-explorer/plugin.rb at main · discourse/discourse-data-explorer · GitHub. Il est également intéressant de consulter les scripts et déclencheurs existants du plugin Automation : https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation

Comme il n’y a pas beaucoup d’informations sur Meta concernant l’ajout d’automatisations personnalisées, voici un exemple de fichier plugin.rb qui ajoute un script pour mettre à jour la préférence de l’utilisateur concernant l’e-mail de résumé d’activité. Le script peut être déclenché par les déclencheurs « user_added_to_group » ou « user_removed_from_group » du 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

Le code complet du plugin se trouve ici : GitHub - scossar/automation-script-example: An example of how to add a custom script to the Discourse Automation plugin..

:warning: veuillez ne pas utiliser ce code tel quel sur un site de production. Je n’avais pas examiné le code d’automatisation avant ce soir. Si je reçois des commentaires sur des problèmes potentiels avec le code, je mettrai à jour ce post et le dépôt GitHub.

Edit : ma préoccupation concernait la meilleure façon de gérer le cas où plusieurs scripts d’automatisation sont déclenchés par les déclencheurs « user_added_to_group » ou « user_removed_from_group ». La version initiale du plugin vérifiait :

fields.has_key?("email_digests")

mais cela semblait un peu fragile. Et si un autre script était ajouté qui avait également une clé email_digests ?

Le code mis à jour transmet le paramètre automation au bloc de code et vérifie :

automation.script == "user_update_summary_email_options"

Cela devrait garantir que le script ne s’exécute pas pour la mauvaise automatisation.
… en y réfléchissant encore, il est peu probable que le script soit déclenché par une automatisation pour laquelle il n’a pas été configuré :slight_smile:

7 « J'aime »

J’aimerais savoir aussi - une fois que vous avez créé un dépôt comme celui de @simon, comment le plugin y accède-t-il ?

Devons-nous forker tout le plugin et le déposer avec ceux existants dans https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation/scripts ? Ou existe-t-il un moyen plus élégant ?

1 « J'aime »

Vous devez l’installer comme n’importe quel autre plugin Discourse : Installer des plugins dans Discourse. Vous installeriez donc le plugin Automation et votre plugin qui ajoute les scripts personnalisés. La raison pour laquelle cela fonctionne est due aux méthodes définies ici : https://github.com/discourse/discourse-automation/blob/main/lib/plugin/instance.rb. Dans l’exemple de code que j’ai posté ci-dessus, vous verrez que le script personnalisé est ajouté avec un appel à add_automation_scriptable.

Remarque : n’installez pas l’automatisation d’exemple de mon dépôt GitHub, prenez-la simplement comme un exemple de la manière d’étendre le plugin Automation. (J’ai oublié que j’avais mis un lien vers celui-ci ici et je l’ai mis à jour pour qu’il ne fonctionne qu’avec ma version forkée du plugin Discourse Automation. Le code auquel j’ai lié ici est toujours valide : Create custom Automations - #6 by simon. Je mettrai à jour le plugin automation-script-example dès que possible afin qu’il fonctionne sans les modifications que j’ai apportées à ma version forkée du plugin Automation.)

Ma préoccupation était infondée. Cette condition n’est pas nécessaire :

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

Je mettrai à jour l’exemple bientôt.

4 « J'aime »

Ai-je bien compris que les automatisations personnalisées nécessitent une installation auto-hébergée (ou un accès direct au backend au système de fichiers où Discourse est installé) ?

2 « J'aime »

Oui, cependant nous sommes très ouverts à la fusion de nouveaux scripts d’automatisation dans la construction communautaire, qu’est-ce que vous envisagez de construire ?

6 « J'aime »

Plus précisément, nous cherchons un moyen de remplacer une chaîne de caractères spécifique dans les publications (nous ne nous soucions pas trop de la syntaxe exacte, mais quelque chose comme le texte brut @ref `Random.rand!` ) par un lien formaté comme Random.rand!. La recherche de l’URL exacte est un processus compliqué avec des dizaines de milliers de cibles possibles, ce qui est totalement infaisable avec des expressions régulières (comme le fait Auto linkify words/watched words) et beaucoup plus facile avec un environnement de type plugin complet de Turing… donc je me demandais si les automatisations pouvaient le faire.

Je cherchais donc une action post-édition, un peu similaire à ce que fait l’utilisateur @system lorsque vous citez l’intégralité de la publication précédente (voir ici). Ce serait un script « Modifier la publication » qui se déclencherait lors de la « Création de la publication » (ou peut-être après la cuisson de la publication)… mais je suppose que le framework d’automatisation ne permettrait pas une action « post-édition » aussi générale. Je pense qu’il faudrait une automatisation personnalisée assez spécifique pour lier à la documentation Julia, ce qui n’a certainement pas de sens dans une construction communautaire.

Je pourrais me tromper en pensant aux automatisations personnalisées ; j’explorais simplement ce qui est possible. Bien sûr, en tant que forum pour les passionnés de langages de programmation, des utilisateurs intrépides réfléchissent déjà à la programmation de bots qui pourraient utiliser l’API Discourse pour faire cela.

1 « J'aime »

Je ne suis pas sûr que l’automatisation soit ce que vous recherchez ici, car quelque chose qui semble critique est l’expérience de « l’utilisateur final ». Avec l’automatisation, cela ne serait remplacé qu’après coup.

En réfléchissant à ce type de problème, je recommanderais probablement d’opter pour un plugin personnalisé ou un composant de thème.
Un composant de thème pourrait fonctionner comme suit :

  1. L’utilisateur tape : ^Rand
  2. Un appel HTTP est effectué vers un service backend que vous hébergez et qui liste toutes les options avec des URL.
  3. L’utilisateur sélectionne celle qu’il souhaite et appuie sur Entrée.
  4. Le Markdown est remplacé par [Random.rand!](https://docs.julialang.org/en/v1/stdlib/Random/#Random.rand!)

Un plugin qui modifie le pipeline Markdown pourrait fonctionner de manière similaire à onebox et simplement créer un lien automatique au fur et à mesure que vous tapez, en laissant la syntaxe d’origine. par exemple : ^Random.rand
Je comprends votre point de vue sur le fait que linkify n’est pas idéal, la découverte est difficile, en plus vous devrez peut-être héberger un site Web afin de le normaliser en lookup.docs.julialang.org?q=Random.rand!

Certainement un problème très intéressant. Je pense que l’expérience utilisateur d’un composant de thème peut être raisonnable ici.

2 « J'aime »

C’est très génial, merci pour vos réflexions et vos conseils ici ! Je vais aborder cela dans un sujet séparé si et quand j’aurai plus de questions (ou de réponses :slight_smile: ).

2 « J'aime »

Je pense qu’un plugin personnalisé qui se déclenche lors d’un changement de publication est ce que vous voulez créer. Je soupçonne que pour votre cas d’utilisation, qui a un déclencheur simple, il serait plus facile d’écrire un plugin plutôt que de se compliquer avec le plugin d’automatisation.

1 « J'aime »

@McUles et @nathank avez-vous trouvé les informations que vous cherchiez ?

Pour que l’automatisation personnalisée fonctionne, j’ai dû modifier les fichiers suivants :

Création du script d’automatisation personnalisé

Mis à jour : server.en.yml

ajout du nom, du titre et de la description de l’automatisation personnalisée dans la section scriptables du fichier yml.

Mis à jour : client.en.yml

ajout du nom de l’automatisation personnalisée dans les scriptables ; ajout du mot-clé ‘field’ ; à l’intérieur du mot-clé field, ajout de ‘field_name’ suivi de ‘label’ et ‘description’.

Mis à jour : scripts.rb

ajout du nom de l’automatisation personnalisée dans la liste des scripts. Exemple : FILE_NAME = “file_name”

Mis à jour : plugin.rb

à l’intérieur de ‘after_initialize do’, ajout du chemin d’accès au script d’automatisation personnalisé. Exemple : ‘lib/discourse_automation/scripts/file_name’

Je ne comprends pas tout à fait ce que vous avez écrit - s’agit-il de modifications du plugin Automations, ou de composants importants d’un plugin sœur qui contient l’automatisation personnalisée ?

Ce serait formidable d’avoir cela fusionné dans l’OP

2 « J'aime »

C’est ça. Je ne connaissais pas vraiment la réponse à votre question, alors voici comment j’ai trouvé une réponse et aussi la réponse à « Y a-t-il un exemple, quelque part, que je pourrais voir ? »

Tout d’abord, obtenez ceci : GitHub - discourse/all-the-plugins

Ensuite, vous faites grep pour quelque chose, comme « add_automation_scriptable », et vous pouvez alors découvrir ce qui l’utilise.

 (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

Alors peut-être regardez discourse-assign ou data-explorer

2 « J'aime »