monolithic kernel

ブログの更新を Activepieces を使って Bluesky に通知するようにした

Bluesky をメインで使っている人もいくらかいるようなので、試しにやってみた。

# 自分の場合は Twitter (X) の使用頻度が下がったことで、代わりに Threads や Bluesky を使うわけでもなく、Obsidian でプライベートなメモに書くようになった。思ったことをストレートに書けるし検索性も高いのでよかった。

Activepieces は、ノーコードでサービス間連携などのちょっとした自動化ができるツール。IFTTT みたいな感じだけど、ノードを複数つないでよりリッチなことができるし、無料でも結構使える。オープンソース版があるので (個人的には面倒なのでやりたくないものの) セルフホスティングもできる。もともと RSS の新着をトリガーに Twitter に投稿していて、RSS の新着をトリガーにする部分の状態管理を任せられるのがうれしかったので今回も採用した。

# なので、ノーコードがうれしいというよりは状態管理やサーバ管理を任せられるのがうれしくて使っている。Activepieces のグラフィカルなエディタは結構使いやすいとは思いつつ、YAML とかで書けるならそれに越したことはないという気持ち。

出力部分に関しては、Twitter への投稿は Activepieces の標準機能でいけたものの、Bluesky との連携はサポートされていないので、自前で API を叩く必要があった。

といっても、com.atproto.server.createSession で認証して、返ってきた JWT で com.atproto.repo.createRecord を呼ぶだけなので簡単。投稿時に時刻を渡してあげる必要があったので、そのためのステップも挟んでいる。ハッシュ値や HMAC の計算とかもできるので、もうちょっと使うのが難しい API でも対応できそう。

参考までにフローをエクスポートしたものも貼っておく。

{
  "created": "1738479770166",
  "updated": "1738479770166",
  "name": "ブログの更新を Bluesky に通知",
  "description": "",
  "tags": [],
  "pieces": [
    "@activepieces/piece-rss",
    "@activepieces/piece-http",
    "@activepieces/piece-date-helper"
  ],
  "template": {
    "displayName": "ブログの更新を Bluesky に通知",
    "trigger": {
      "name": "trigger",
      "valid": true,
      "displayName": "New Item In Feed",
      "type": "PIECE_TRIGGER",
      "settings": {
        "pieceName": "@activepieces/piece-rss",
        "pieceVersion": "0.3.7",
        "pieceType": "OFFICIAL",
        "packageType": "REGISTRY",
        "input": {
          "rss_feed_url": "<RSS_URL>"
        },
        "inputUiInfo": {
          "customizedInputs": {}
        },
        "triggerName": "new-item"
      },
      "nextAction": {
        "name": "step_1",
        "skip": false,
        "type": "PIECE",
        "valid": true,
        "settings": {
          "input": {
            "url": "https://bsky.social/xrpc/com.atproto.server.createSession",
            "body": {
              "data": {
                "password": "<APP_PASSWORD>",
                "identifier": "<USERNAME>.bsky.social"
              }
            },
            "method": "POST",
            "headers": {
              "Content-Type": "application/json; charset=UTF-8'"
            },
            "failsafe": false,
            "body_type": "json",
            "use_proxy": false,
            "queryParams": {},
            "proxy_settings": {}
          },
          "pieceName": "@activepieces/piece-http",
          "pieceType": "OFFICIAL",
          "actionName": "send_request",
          "inputUiInfo": {
            "customizedInputs": {}
          },
          "packageType": "REGISTRY",
          "pieceVersion": "0.6.1",
          "errorHandlingOptions": {
            "retryOnFailure": {
              "value": true
            },
            "continueOnFailure": {
              "value": false
            }
          }
        },
        "nextAction": {
          "name": "step_2",
          "skip": false,
          "type": "PIECE",
          "valid": true,
          "settings": {
            "input": {
              "timeZone": "Asia/Tokyo",
              "timeFormat": "YYYY-MM-DDTHH:mm:ssZ"
            },
            "pieceName": "@activepieces/piece-date-helper",
            "pieceType": "OFFICIAL",
            "actionName": "get_current_date",
            "inputUiInfo": {
              "customizedInputs": {
                "timeFormat": true
              }
            },
            "packageType": "REGISTRY",
            "pieceVersion": "0.1.6",
            "errorHandlingOptions": {
              "retryOnFailure": {
                "value": true
              },
              "continueOnFailure": {
                "value": false
              }
            }
          },
          "nextAction": {
            "name": "step_3",
            "skip": false,
            "type": "PIECE",
            "valid": true,
            "settings": {
              "input": {
                "url": "https://bsky.social/xrpc/com.atproto.repo.createRecord",
                "body": {
                  "data": {
                    "repo": "{{step_1['body']['handle']}}",
                    "record": {
                      "text": "{{trigger['title']}}",
                      "embed": {
                        "$type": "app.bsky.embed.external",
                        "external": {
                          "uri": "{{trigger['link']}}",
                          "title": "{{trigger['title']}}"
                        }
                      },
                      "createdAt": "{{step_2['result']}}"
                    },
                    "collection": "app.bsky.feed.post"
                  }
                },
                "method": "POST",
                "headers": {
                  "Content-Type": "application/json; charset=UTF-8",
                  "Authorization": "Bearer {{step_1['body']['accessJwt']}}"
                },
                "failsafe": false,
                "body_type": "json",
                "use_proxy": false,
                "queryParams": {},
                "proxy_settings": {}
              },
              "pieceName": "@activepieces/piece-http",
              "pieceType": "OFFICIAL",
              "actionName": "send_request",
              "inputUiInfo": {
                "customizedInputs": {
                  "data": false
                }
              },
              "packageType": "REGISTRY",
              "pieceVersion": "0.6.1",
              "errorHandlingOptions": {
                "retryOnFailure": {
                  "value": true
                },
                "continueOnFailure": {
                  "value": false
                }
              }
            },
            "displayName": "Send HTTP request"
          },
          "displayName": "Get Current Date"
        },
        "displayName": "Send HTTP request"
      }
    },
    "valid": true,
    "schemaVersion": "1"
  },
  "blogUrl": ""
}

こんな感じで投稿できた。

ブログの更新を Activepieces を使って Bluesky に通知するようにした

mono (@mono0x.bsky.social) 2025-02-02T07:21:36.655Z

# 記事への投稿の埋め込みは oEmbed で普通にできた。


Related articles