Discourse REST API 활용 사례 공유하기 🧑‍💻

여러분 안녕하세요 :slight_smile:

많은 분들이 자동화된 작업부터 다른 애플리케이션과의 상호작용까지 다양한 목적으로 Discourse API를 사용하고 있습니다.

여러분은 어떻게 활용하고 계신가요?

  • 어떤 목적으로 사용하시나요?

  • 이를 위해 어떤 기술/소프트웨어/프로그래밍 언어를 사용하시나요?

몇 가지 예시를 들어 제가 어떻게 사용하는지 보여드리겠습니다:

  • 리뷰 가능한 항목(reviewable item) 웹훅을 사용하여 PHP 스크립트를 실행하고, 이 스크립트가 Discourse API를 통해 자동 스팸 계정 삭제를 트리거합니다. 제 커뮤니티에서는 매일 많은 게시물이 신고되고 있으며 스팸 탐지 정확도가 100%에 달하기 때문에, 관리자와 모더터가 대부분의 계정을 수동으로 검토할 필요가 없어 업무가 훨씬 쉬워졌습니다. (다만, 이러한 방법을 권장하지는 않으며, 사용 전 신중하게 고민하고 테스트를 거쳤습니다.)

  • Ko-Fi(기부 웹사이트) 웹훅을 사용해 PHP 스크립트를 통해 Discourse API로 테마 컴포넌트의 설정 값을 업데이트합니다. 목표는 Discourse에 실시간으로 업데이트되는 기부 진행률 바를 표시하는 것입니다.

  • Insomnia를 사용하여 테스트 목적으로 사용합니다. 테스트 인스턴스에 특정 콘텐츠를 채우거나, 만들고 싶은 프로젝트를 구상할 때 요청을 테스트하거나, meta에서 사람들에게 도움을 주거나 하는 등 다양한 용도로 활용합니다.

여러분은 어때요? 상세하게 작성하셔도 좋습니다. 흥미로운 활용 사례를 많이 들어볼 수 있을 것 같습니다 :slight_smile:


API에 대한 참고 주제: Discourse REST API Documentation
공식 문서: https://docs.discourse.org/

저는 아마추어 기상학자(기상 관찰자)들을 위한 포럼을 운영하고 있습니다. 저와 다른 한 명이 기상 데이터 피드를 제공하며, 전 세계 수백 명의 사람들이 이 데이터를 활용하여 초지역(hyper-local) 예보를 생성하고 있습니다.

포럼 사용자 중 한 명은 자신의 예보 품질을 검증하고 피드의 가용성을 확인하기 위해 복잡한 통계 스크립트를 작성했습니다. 이 스크립트는 피드에서 누락, 오류 또는 지연된 데이터를 감지하면, 예보 소프트웨어를 지원하는 데 사용되는 포럼 카테고리에 게시물을 생성합니다. 이를 통해 다른 예보 담당자들에게 자신들에게도 영향을 미칠 수 있는 문제가 있음을 알릴 수 있습니다.

피드 이상을 감지하는 스크립트는 PHP로 작성되었습니다. 이 스크립트는 문제를 설명하는 JSON 파일을 생성한 후, cURL을 사용하여 API를 통해 게시물을 업로드합니다.

최근에 여러 플랫폼에서 단계들을 연결하는 작업에 대해 다양한 실험을 해보았는데, 그중에서도 특히 흥미를 느낀 하나를 공유하려 합니다. 바로 이미지 내 텍스트를 감지하는 광학 문자 인식(OCR)을 수행한 후, 그 결과를 Discourse 게시글에 추가하는 것입니다.

내 샌드박스 토픽에서 어떻게 작동하는지 확인할 수 있습니다: https://notes.maiki.interi.org/t/testing-ocr-calls/403

처음에는 Pipedream을 사용했으며, 단계별로 진행 과정을 설명드리겠습니다. 해당 서비스에는 워크플로를 공유할 수 있는 베타 기능이 있으며, 아래 내용은 https://pipedream.com/new?h=tch_3Z6fa9에서 공유하고 있습니다.

:information_source: 이 글은 Pipedream을 Discourse와 함께 사용하는 방법에 대한 심층 가이드가 될 수 있지만, 저는 이미 해당 플랫폼을 떠나 더 많은 내용을 나중에 공유할 예정이므로 세부 사항은 가볍게 다룰 것입니다. :slight_smile:

단계

대략적인 개요는 다음과 같습니다: 이미지 URL을 Google Cloud Vision API로 전송하고, 결과를 ChatGPT를 통해 처리한 뒤, 그 결과를 Discourse의 게시글에 추가합니다.

트리거

Screenshot 2023-08-08 at 11-23-21 OCR Test - Pipedream

이것은 데이터를 전송할 웹훅을 제공합니다. Discourse에서는 두 가지 특정 설정으로 웹훅을 생성했습니다:

Screenshot 2023-08-08 at 11-24-41 Admin - notes

'Post Events’에서만 발동되도록 하면 초기 토픽이 프로세스를 트리거하지 않습니다. 이는 외부 함수를 적용하기 위한 용도로 토픽을 홀더로 사용할 수 있어 저에게 유용합니다(저는 이를 "기능적 노트"라고 부릅니다).

해당 태그에서만 발동되도록 하면 태그를 사용하여 토픽별로 생성되는 웹훅을 제어할 수 있습니다. 일반적으로 프로세스 로직을 단순하게 유지하기 위해 “functions” 태그 하나만 사용합니다.

웹훅은 많은 정보를 포함한 페이로드를 전송하며, 프로세스 후반부에 토픽 ID와 게시글 ID를 사용할 것입니다.

조건부 종료

이 단계는 수정 이유가 포함되었는지 확인합니다. 포함되면 워크플로가 중단됩니다.

마지막 단계에서 게시글을 업데이트하고 수정 이유를 포함하며, 이 확인 단계는 게시글을 계속 업데이트하는 것을 방지합니다… :sweat_smile:

:information_source: Pipedream 사용을 중단한 이유 중 하나는 웹훅 체크가 서비스 크레딧을 소모했기 때문입니다. 조건부로 웹훅을 처리하는 데 비용을 지불해야 한다고 생각하지는 않으므로, 다른 곳으로 옮겼습니다…

이미지 URL 추출

이 테스트에서는 각 게시글에 하나의 이미지만 업로드하기로 결정했습니다. 이 단계는 “cooked” 값을 확인하고 다음 정규식을 사용하여 URL을 가져옵니다:

/https?:\/\/[^\s"]+/

Google Cloud Vision API 호출

이것은 Pipedream의 사용자 정의 코드 단계입니다. 미리 만들어진 컴포넌트는 제가 원하는 것을 수행하지 못했고, 해당 서비스에는 프롬프트로 코드를 작성할 수 있는 코드 어시스턴트도 있습니다. 이러한 API 호출은 직관적이므로 이 방법으로 생성하기가 쉬웠습니다.

이전 단계의 값({{steps.extract_by_regular_expression.$return_value[0].match}})을 받아들이며, 코드는 다음과 같습니다:

import { axios } from "@pipedream/platform";

export default defineComponent({
  props: {
    imageUrl: {
      type: "string",
      label: "Image URL",
      description: "URL of the image to be processed by Google Vision API",
    },
    apiKey: {
      type: "string",
      label: "API Key",
      description: "Your Google Cloud API Key",
      secret: true,
    },
  },
  async run() {
    const url = `https://vision.googleapis.com/v1/images:annotate?key=${this.apiKey}`;
    const body = {
      requests: [
        {
          image: {
            source: {
              imageUri: this.imageUrl,
            },
          },
          features: [
            {
              type: "TEXT_DETECTION",
            },
          ],
        },
      ],
    };

    const config = {
      method: "POST",
      url,
      data: body,
    };

    const response = await axios(this, config);
    return response;
  },
});

편집을 위한 ChatGPT

이전 단계의 출력({{steps.google_cloud.$return_value.responses[0].fullTextAnnotation.text}})을 가져와 User 메시지로 전달합니다. 시스템 메시지는 다음과 같습니다:

당신은 이미지에서 텍스트를 감지한 비전 API의 출력을 읽고 있습니다. 메시지를 검토하여 명확성을 위해 편집하세요. 주석 없이 편집된 텍스트만 반환하세요.

Discourse 게시글에 추가

또 다른 사용자 정의 코드 섹션으로, 미리 만들어진 Pipedream의 Discourse 액션은 몇 가지 시나리오(토픽 또는 게시글 생성)만 다루기 때문에, 저는 텍스트를 게시글에 추가하고 싶습니다.

먼저, 코드는 다음과 같습니다:

import { axios } from "@pipedream/platform";

export default defineComponent({
  props: {
    discourse: {
      type: "app",
      app: "discourse",
    },
    postId: {
      type: "string",
      label: "Post ID",
      description: "The ID of the post to append text to",
    },
    text: {
      type: "string",
      label: "Text",
      description: "The text to append to the post",
    },
    editReason: {
      type: "string",
      label: "Edit Reason",
      description: "The reason for editing the post",
      optional: true,
    },
  },
  async run({ steps, $ }) {
    const url = `https://${this.discourse.$auth.domain}/posts/${this.postId}.json`;
    const response = await axios($, {
      method: "GET",
      url: url,
      headers: {
        "Api-Username": `${this.discourse.$auth.api_username}`,
        "Api-Key": `${this.discourse.$auth.api_key}`,
      },
    });

    const updatedText = `${response.raw} ${this.text}`;

    return await axios($, {
      method: "PUT",
      url: url,
      headers: {
        "Api-Username": `${this.discourse.$auth.api_username}`,
        "Api-Key": `${this.discourse.$auth.api_key}`,
      },
      data: {
        post: {
          raw: updatedText,
          edit_reason: this.editReason,
        },
      },
    });
  },
});

해당 단계의 속성은 다음과 같이 채워집니다:

Post ID

원래 페이로드에서 게시글 ID를 가져옵니다: {{steps.trigger.event.body.post.id}}

이것은 해당 게시글을 직접 편집하는 데 사용됩니다.

Text
---

<blockquote>
 {{steps.chat.$return_value.generated_message.content}}
</blockquote>

[details="Detected text"]
{{steps.google_cloud.$return_value.responses[0].textAnnotations[0].description}}
[/details]

기본적으로 각 이미지 아래에 수평선, 편집된 텍스트의 인용 블록, 그리고 원시 출력을 확인하기 위한 세부 정보를 추가하려고 합니다.

각 게시글에 이미지가 하나씩 있으므로 이것은 매우 쉽게 작동합니다. 여러 이미지를 동시에 처리하는 방법은 어떻게 될지 궁금합니다. :thinking:

Edit Reason

OCR Text Detection

이것은 게시글 업데이트의 수정 이유로 추가되며, 시작 부분의 단계로 인한 게시글 업데이트 루프를 방지합니다. :point_up: :recycle: :ng:

:information_source: 특히 외부 서비스를 다룰 때 수정 이유를 항상 포함하는 것이 매우 유용하다고 생각합니다.


이것으로 끝입니다! 샌드박스에서 볼 수 있듯이 꽤 잘 작동합니다!

곧 여행을 가는데, OpenAI 편집을 미세 조정하여 필요시 영어로 번역하도록 할 계획입니다. 이 워크플로의 시스템 프롬프트에 추가할 수 있는 옵션 중 하나입니다.