사용자 정의 자동화 만들기

:information_source: 이 문서는 초안이며 추가 작업이 필요할 수 있습니다.

용어

  • trigger: 트리거의 이름을 나타냅니다. 예: user_added_to_group
  • triggerable: 트리거와 관련된 코드 로직을 나타냅니다. 예: triggers/user_added_to_group_.rb
  • script: 스크립트의 이름을 나타냅니다. 예: send_pms
  • scriptable: 스크립트와 관련된 코드 로직을 나타냅니다. 예: scripts/send_pms.rb

플러그인 API

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

스크립터블 API

field

field :name, component:는 자동화 UI에 사용자 정의 값을 추가할 수 있게 해줍니다.

유효한 컴포넌트 목록:

# foo는 고유해야 하며 필드의 이름을 나타냅니다.

field :foo, component: :text # 텍스트 입력 필드를 생성합니다
field :foo, component: :list # 사용자가 값을 입력할 수 있는 다중 선택 텍스트 입력 필드를 생성합니다
field :foo, component: :choices, extra: { content: [ {id: 1, name: 'your.own.i18n.key.path' } ] } # 사용자 정의 내용을 가진 콤보 박스를 생성합니다
field :foo, component: :boolean # 체크박스 입력 필드를 생성합니다
field :foo, component: :category # 카테고리 선택기를 생성합니다
field :foo, component: :group # 그룹 선택기를 생성합니다
field :foo, component: :date_time # 날짜 및 시간 선택기를 생성합니다
field :foo, component: :tags # 태그 선택기를 생성합니다
field :foo, component: :user  # 사용자 선택기를 생성합니다
field :foo, component: :pms  # 하나 이상의 개인 메시지 템플릿을 생성할 수 있습니다
field :foo, component: :categories  # 하나 이상의 카테고리를 선택할 수 있습니다
field :foo, component: :key-value  # 키-값 쌍을 생성할 수 있습니다
field :foo, component: :message  # 치환 가능한 변수를 가진 개인 메시지를 작성할 수 있습니다
field :foo, component: :trustlevel  # 하나 이상의 신뢰 수준을 선택할 수 있습니다
triggerables 및 triggerable!
# 스크립트에 허용되는 트리거러블 목록을 정의합니다
triggerables %i[recurring]

# 스크립트에 트리거러블을 강제하고 필드에 일부 상태를 강제할 수 있습니다
field :recurring, component: :boolean
triggerable! :recurring, state: { foo: false }
placeholders
# 텍스트에서 플레이스홀더 문법 `%%sender%%`을 사용하여 키를 치환 가능하도록 표시합니다
placeholder :sender

플레이스홀더에 대한 값을 제공하고 input = utils.apply_placeholders(input, { sender: 'bob' })를 사용하여 치환을 적용하는 것은 스크립트의 책임임을 유의하세요.

script

이것은 자동화의 핵심이며 모든 로직이 실행되는 곳입니다.

# context는 자동화가 트리거될 때 전송되며, 트리거에 따라 크게 다를 수 있습니다
script do |context, fields, automation|
end

지역화

사용할 각 필드는 i18n 키에 의존하며, 해당 트리거/스크립트에 네임스페이스가 부여됩니다.

예를 들어 다음 내용을 가진 스크립터블이 있다고 가정해 보겠습니다:

field :post_created_edited, component: :category

이 경우 client.en.yml에서 다음 키가 필요합니다:

en:
  js:
    discourse_automation:
      scriptables:
        post_created_edited:
          fields:
            restricted_category:
              label: Category
               description: Optional, allows to limit trigger execution to this category

여기서 description은 선택 사항임을 유의하세요.


이 문서는 버전 관리됩니다 - 변경 사항을 github에서 제안하십시오.

9개의 좋아요

이게 새 주제라는 걸 보고 설레었습니다! 더 자세한 내용이 공유된 줄 알았거든요! :laughing:

Ruby로 프로그래밍을 하지는 않지만, 워크플로 자동화에 매우 관심이 있는 사람으로서, 예시를 통해 조금이라도 더 이해할 수 있기를 바랐습니다…

:thinking:

아마 Developing Discourse Plugins - Part 1 - Create a basic plugin… 에서부터 시작해야 할 것 같습니다… :sweat_smile:

8개의 좋아요

기본적으로 이 부분을 플러그인 토픽에서 잘라내어, 기존 기능을 사용하려면 이 내용을 알아야 하는 것처럼 보이게 하고 싶지 않았습니다. :slight_smile:

이 내용이 조금 더 단계별로 정리되면 좋겠다는 의견에 동의합니다. 혹시 이런 것에 경험이 있는 사람이 있는지 확인하기 위해 커뮤니티 도움을 요청하는 플레어를 보냈습니다: :crossed_fingers:

5개의 좋아요

hello world 예제가 있으면 좋겠네요.
스크립트는 어디에 저장해야 하나요? 좀 실험해 보고 싶어서요.

4개의 좋아요

이 플러그인과 Chat GPT를 함께 사용하여 사용자 지정 스크립트를 작성할 수 있다고 생각합니다.

가장 먼저 살펴볼 만한 곳은 Data Explorer 플러그인에 추가되는 자동화 스크립트입니다: discourse-data-explorer/plugin.rb at main · discourse/discourse-data-explorer · GitHub. 또한 Automation 플러그인의 기존 스크립트와 트리거를 살펴보는 것도 좋습니다: https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation

Meta에 커스텀 자동화를 추가하는 방법에 대한 정보가 많지 않아, 사용자의 Activity Summary 이메일 설정을 업데이트하는 스크립트를 추가하는 plugin.rb 파일 예시를 여기에 공유합니다. 이 스크립트는 Automation 플러그인의 ‘user_added_to_group’ 또는 ‘user_removed_from_group’ 트리거로 실행될 수 있습니다.

# 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

전체 플러그인 코드는 여기에 있습니다: GitHub - scossar/automation-script-example: An example of how to add a custom script to the Discourse Automation plugin. · GitHub.

:warning: 이 코드를 그대로 프로덕션 사이트에서 사용하지 마세요. 저는 오늘 밤까지 Automation 코드를 확인하지 않았습니다. 코드에 대한 잠재적 문제점에 대한 피드백을 받으면 이 게시글과 GitHub 저장소를 업데이트하겠습니다.

수정: 제 우려는 ‘user_added_to_group’ 또는 ‘user_removed_from_group’ 트리거 중 하나로 여러 자동화 스크립트가 트리거되는 경우를 어떻게 가장 잘 처리할 것인가였습니다. 플러그인의 초기 버전은 다음을 확인하고 있었습니다:

fields.has_key?("email_digests")

하지만 그 방식은 다소 불안정하게 느껴졌습니다. 만약 email_digests 키를 가진 다른 스크립트가 추가되면 어떻게 될까요?

업데이트된 코드는 automation 매개변수를 코드 블록으로 전달하고 다음을 확인합니다:

automation.script == "user_update_summary_email_options"

이렇게 하면 스크립트가 잘못된 자동화에 의해 실행되지 않도록 보장할 수 있습니다.

… 더 생각해보니, 스크립트가 설정되지 않은 자동화에 의해 트리거될 가능성은 낮을 것 같습니다 :slight_smile:

7개의 좋아요

저도 이 부분이 궁금합니다 - @simon처럼 리포지토리를 만든 후, 플러그인이 이를 어떻게 접근하나요?

기존 스크립트들이 있는 https://github.com/discourse/discourse-automation/tree/main/lib/discourse_automation/scripts? 에 전체 플러그인을 포크하여 추가해야 하는 건가요? 아니면 더 우아한 방법이 있나요?

1개의 좋아요

Discourse의 다른 플러그인과 마찬가지로 설치해야 합니다: Discourse에서 플러그인 설치. 따라서 Automation 플러그인을 설치하고, 사용자 정의 스크립트를 추가하는 사용자 플러그인을 설치해야 합니다. 이 방식이 작동하는 이유는 여기에서 정의된 메서드들 때문입니다: https://github.com/discourse/discourse-automation/blob/main/lib/plugin/instance.rb. 위에서 게시한 예제 코드에서 볼 수 있듯이, 사용자 정의 스크립트는 add_automation_scriptable 호출을 통해 추가됩니다.

참고: 제 GitHub 리포지토리의 예제 자동화를 설치하지 마세요. Automation 플러그인을 확장하는 방법의 예시로만 참고하십시오. (여기에 링크를 걸었다는 사실을 잊고 있었으며, 이제 제 포크된 버전의 Discourse Automation 플러그인에서만 작동하도록 업데이트했습니다. 다만 여기에 링크한 코드는 여전히 유효합니다: Create custom Automations - #6 by simon. Automation 플러그인의 포크된 버전에서 제가 한 변경 사항 없이도 작동하도록 automation-script-example 플러그인을 최대한 빨리 업데이트할 것입니다.)

제 우려는 근거가 없었습니다. 이 조건은 필요하지 않습니다:

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

곧 예제를 업데이트하겠습니다.

4개의 좋아요

사용자가 정의한 자동화 기능은 자체 호스팅 설치(또는 Discourse가 설치된 파일 시스템에 대한 직접적인 백엔드 액세스)가 필요하다는 것이 맞나요?

2개의 좋아요

네, 하지만 커뮤니티에서 만든 새로운 자동화 스크립트를 병합하는 데 매우 개방적입니다. 무엇을 만들 생각인가요?

6개의 좋아요

구체적으로, 게시물 내의 특정 문자열(예: @ref `Random.rand!` 와 같은 평문)을 Random.rand!과 같은 포맷된 링크로 대체하는 방법을 찾고 있습니다. 정확한 URL을 조회하는 과정은 수십만 개의 가능한 대상이 있어 정규식(예: Auto linkify words/watched words)으로는 실현 불가능할 만큼 복잡하지만, 튜링 완전한 플러그인 형태의 환경에서는 훨씬 쉽게 처리할 수 있습니다. 그래서 자동화(automations)로 이를 수행할 수 있을지 궁금했습니다.

전체 이전 게시물을 인용할 때 @system 사용자가 수행하는 것과 유사한, 게시물 편집(post-edit) 동작을 찾고 있었습니다. “게시물 생성”(또는 게시물이 쿠킹된 후)에 트리거되는 “게시물 편집” 스크립트라고 할 수 있겠습니다. 다만 자동화 프레임워크에서는 이러한 일반적인 “게시물 편집” 동작을 허용하지 않을 것 같습니다. 커뮤니티 빌드에서는 의미가 없는, Julia 문서 링크로 연결하는 매우 구체적인 커스텀 자동화가 필요할 것입니다.

커스텀 자동화라는 방향 자체가 틀렸을 수 있습니다. 그저 가능한 범위를 탐색해 보고 있을 뿐입니다. 물론 프로그래밍 언어 애호가들을 위한 포럼인 만큼, 이미 담대한 사용자들은 discourse API를 활용하여 이 작업을 수행할 수 있는 봇을 구상하고 있을 것입니다.

1개의 좋아요

자동화가 여기서 원하는 방향인지 확신이 서지 않습니다. 왜냐하면 여기서는 “최종 사용자” 경험이 매우 중요하게 느껴지기 때문입니다. 자동화를 사용하면 이 부분은 사후에나 대체될 수 있을 것입니다.

이런 유형의 문제를 고려해 보면, 커스텀 플러그인이나 테마 컴포넌트를 사용하는 것을 권장할 것입니다.

테마 컴포넌트는 다음과 같이 작동할 수 있습니다:

  1. 사용자가 ^Rand를 입력합니다.
  2. 사용자가 호스팅하는 백엔드 서비스로 HTTP 호출을 보내 URL과 함께 모든 옵션을 나열합니다.
  3. 사용자가 원하는 옵션을 선택하고 엔터를 누릅니다.
  4. 마크다운이 [Random.rand!](https://docs.julialang.org/en/v1/stdlib/Random/#Random.rand!)로 교체됩니다.

마크다운 파이프라인을 수정하는 플러그인은 onebox와 유사하게 작동하여, 원래 문법을 유지한 채로 타이핑하는 동안 자동으로 링크를 만들 수 있습니다. 예: ^Random.rand

링크파이가 이상적이지 않다는 점에 공감합니다. 발견(발견 가능성)이 어렵고, 또한 정규화를 위해 웹사이트를 호스팅해야 할 수도 있습니다. 예를 들어 lookup.docs.julialang.org?q=Random.rand!처럼요.

정말 흥미로운 문제입니다. 여기서는 테마 컴포넌트의 UX가 합리적일 수 있다고 생각합니다.

2개의 좋아요

정말 멋집니다. 여기에서 생각과 조언을 나눠주셔서 감사합니다! 더 많은 질문(또는 답변 :slight_smile: )이 생기면 별도의 주제로 이어서 다뤄보겠습니다.

2개의 좋아요

글이 변경될 때 실행되는 커스텀 플러그인을 만드는 것이 가장 적합할 것 같습니다. 사용자의 사용 사례가 트리거가 단순하므로, 자동화 플러그인을 전체적으로 구성하는 것보다 플러그인을 직접 작성하는 것이 더 쉬울 것으로 보입니다.

1개의 좋아요

@McUles와 @nathank, 찾으시던 정보를 찾았나요?

커스텀 자동화를 작동시키기 위해 다음 파일들을 수정해야 했습니다:

커스텀 자동화 스크립트 생성

업데이트: server.en.yml

yml 파일의 scriptables 섹션에 커스텀 자동화 이름, 제목, 설명을 추가했습니다.

업데이트: client.en.yml

scriptables에 커스텀 자동화 이름을 추가하고, ‘field’ 키워드를 추가했으며, field 키워드 안에 ‘field_name’ 뒤에 'label’과 'description’을 추가했습니다.

업데이트: scripts.rb

스크립트 목록에 커스텀 자동화 이름을 추가했습니다. 예: FILE_NAME = “file_name”

업데이트: plugin.rb

‘after_initialize do’ 안에 커스텀 자동화 스크립트의 경로를 추가했습니다. 예: ‘lib/discourse_automation/scripts/file_name’

거기에 넣으신 내용을 완전히 이해하지는 못하겠습니다. 이는 Automations 플러그인에 대한 수정 사항인가요, 아니면 커스텀 자동화를 포함하는 자매 플러그인의 중요한 구성 요소인가요?

이를 원본 게시물에 통합하면 좋겠습니다.

2개의 좋아요

그렇습니다. 질문하신 내용에 대한 답을 정확히 몰라서, 제가 답을 찾은 과정과 함께 "어디서 예시를 볼 수 있나요?"라는 질문에 대한 답도 함께 드리겠습니다.

먼저, 이 저장소를 받아오세요: GitHub - discourse/all-the-plugins · GitHub

그 다음 “add_automation_scriptable” 같은 키워드로 grep을 수행하면, 어떤 곳에서 이를 사용하고 있는지 알 수 있습니다.

 (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

따라서 discourse-assign이나 data-explorer를 살펴보는 것이 좋을 것 같습니다.

2개의 좋아요