GAS や n8n でアドカレ運営を自動化する

[GAS]:https://workspace.google.com/intl/ja/products/apps-script/
[n8n]:https://n8n.io/

:::note
本記事は [リンクアンドモチベーション Advent Calendar 2024](https://qiita.com/advent-calendar/2024/lmi) の 19日目（※シリーズ３）です🎄
:::

こんにちは！ リンクアンドモチベーションでエンジニアリングマネージャーをしております川津と申します🌟

弊社は企業としてアドカレに参加していますが、Qiita 自体はアウトプット習慣を身につける良い機会だと思っており、変に指示せず各自の自由にやってほしいな！と考えております。

とは言え、以下の点は最低限チェックしておきたいなという気持ちもあります。

* 気づいたら記事が投稿されていなくて、カレンダーがスカスカは避けたい🥶
* 記事引用表記とか出来てなくて著作権トラブル...等の最低限のリスクは排除したい

> ※同じく他社でアドカレを運営されている方は同じ気持ちの筈...😅

↑ を担保しようとすると、やれリマインドだレビューだ...と **仕事感** が出てちょっと嫌だなと考えておりました。

# ということで自動化だ！

ロボ🤖が言ってくれると、仕事感も薄れるし、運営の手間も省けます笑

中身はほぼ同じなのですが、２案ありまして。
諸事情により今回は前者 ([GAS]) を実運用していますが、後者 ([n8n]) もちゃんと動くものが作れました。

**GAS 案**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/9ef84e21-8b1b-6009-1f21-c9655099dffe.png)

**n8n 案**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/1c64306a-6a39-35ed-feb1-9527cf0cb2ef.png)

# カレンダー情報を取得する

[GAS] でも [n8n] にしろ、まずはアドベントカレンダー情報が取ってこれなければ何も始まりません。

ちょっと API 等のうまい方法が見つからなかった為、苦肉の策としてアドカレページをスクレイピングして情報を返す API を作りました...😳

https://github.com/megmogmog1965/apis-qiita-advent-calendar

URL パラメータの `{year}` をカレンダー年度, `{name}` をカレンダーID に指定して http get すると、`application/json` 応答を返します。

* https://apis-qiita-advent-calendar.vercel.app/api/calendar/{year}/{name}

```json
{
  "adventCalendars": {
    "tableAdventCalendars": [{
      "items": [{
        "comment": "Cline コード生成 AI のソースコードを読んで、試しに自分でも作ってみました",
        "series": 1,
        "day": 2,
        "url": "https://qiita.com/megmogmog1965/items/a57aa4710528850338d1",
        "article": {
          "title": "Cline コード生成 AI のソースコードを読んで、試しに自分でも作ってみました",
          "author": {
            "urlName": "megmogmog1965"
          },
          "linkUrl": "https://qiita.com/megmogmog1965/items/a57aa4710528850338d1"
        },
        "user": {
          "urlName": "megmogmog1965"
        }
      }]
    }]
  }
}
```

これを使って [GAS] または [n8n] で処理を組んでいきます。

# 案1 : GAS

[GAS] を使った実現方法です。

**Pros:**
* GAS (JavaScript) を書けるので、基本何でも柔軟にできます
* SpreadSheet など、Google 他サービスとの連携はとてもしやすい

**Cons:**
* スクリプトが複雑化し、保守性や属人性が発生しやすい
* Google 系サービス以外との連携は、API の直呼び出しになりやすい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/9ef84e21-8b1b-6009-1f21-c9655099dffe.png)

処理の大半は JavaScript でなんでも書けるので、全ての制御処理はスクリプトに集約されています。スクリプトでしていることは次の通りです。

1. 前述の API を呼び出して、アドカレ情報を取得
1. *(運営者が見たいので)* Google Spreadsheet に記事の執筆予定・実績を書き出す
1. 公開日の３日前になった記事の執筆者に Slack でメンション通知

時間主導型トリガー (CRON) 呼び出しで１日おきにスクリプトを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/7c5a1493-c8d1-67c3-8b67-888af7cc5915.png)

Spreadsheet にはこの様に書き出しています。まだ記事がなければ `linkUrl` は空欄になります。

![422fb7de-c886-52ee-5f0d-8ad285b17a32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/887fd892-b26a-2ece-35b7-2c9ddba12d49.png)

また、Slack にはこの様に通知されます。ちょっとうざいかも...😅

![Pasted_Image_2024_12_17_21_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/e4aaf5e5-a7a8-cf3b-e54f-b9ef0c903bdc.png)


# 案1 : n8n

[n8n] を使った実現方法です。

**Pros:**
* OpenAI (LLM) を始め、外部サービスとの連携がしやすい
* ノンプログラマじゃなくても比較的使える

**Cons:**
* やはり一定の制限があって、スクリプト程柔軟に何でもできる訳ではない
* 連携する外部サービス毎に初期セットアップ (OAuth2) が必要で管理者権限を要する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/1c64306a-6a39-35ed-feb1-9527cf0cb2ef.png)

[GAS] でスクリプトを書くケースと比較して大分すっきりしますね。

やっていることはほとんど [GAS] 案と同じですが、[n8n] ワークフローの方が外部サービスとの連携はしやすいので、こちらでは LLM を用いた記事内容のチェック (著作権とかリスク面のみ) も試みています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60131/b4f0fd54-5388-6869-d431-a0394329dea3.png)

ワークフローの見たまんまですが、この様な制御処理にしています。

1. 前述の API を呼び出して、アドカレ情報 (全記事) を取得
1. 処理を１記事ごとに分岐 (並列実行)
1. IF (公開日が３日以内)
    1. 記事がまだ執筆されていなければ、Slack メンションでリマインド
1. IF (記事執筆が完了した)
    1. 記事の本文を取得 (http get)
    1. LLM で記事の広報観点 (e.g., 著作権侵害など) をチェック
    1. Slack で記事修正の依頼通知

# さいごに

近年の技術の進化で業務の自動化においても様々な選択肢が取れるようになりましたね。

今回は従来のようにガリガリとスクリプトを書く方式と、ワークフローツールを使ったノーコードの両方を試してみました。一長一短はありますが時代の流れとともに様々なことが簡単に実現できるようになって嬉しいですね！🥳