GitHubのアクションをLINEに送信する方法

# はじめに

:::note
今回特に、無料で全てをやり遂げるところにフォーカスしました。使用するツールは以下の3つです。
- GitHub workflow
- LINE公式アカウント（LINE Messaging API）
- Google App Script
:::

実装のために以下の流れで進めていきます。
1. LINE公式アカウントの作成と準備
    1. LINE公式アカウントの作成
    1. 公式アカウントから送信されるリクエストを取得するため、GASのコードを作成
    1. GASをLINE公式アカウントに設定し、リクエストから`groupID`を取得
1. GitHubのワークフローを作成
1. プッシュ通知の確認

# 概要

GitHubには、アクション（Issueの作成やCloseしたときなど）に発火して、さまざまなアクションを行う機能が用意されています。

https://docs.github.com/ja/actions/writing-workflows/about-workflows

GUIで簡単なものを設定することもできるし、ymlファイルを作成してよりカスタマイズしたアクションを実行することもできます。
今回これを利用して、アクション発生時に以下のようなメッセージを送信する方法についてまとめました。

![LINE_capture.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/4e8c9e36-c35b-dc8f-b679-cb0901313651.jpeg)


LINEの通知に関して、以前は[LINE Notify](https://notify-bot.line.me/ja/)を用いて実装していましたが、2025年4月1日にサービス終了するそうです。
せっかくなので、よりリッチな通知を送信できるように[LINE Messaging API](https://developers.line.biz/ja/services/messaging-api/)を使って実装したいと思います。
今回利用するLINE Messaging APIも、基本利用料は無料です。
送信数には上限があるものの、個人利用の範疇では抵触しないと思います。（200通/月）

以下が今回のシステムの概要図です。

![概要図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/0de36811-2df6-2471-c80b-f7ba7f819b44.png)



# LINE Messaging APIを準備
まずはメッセージを送信するためのLINE公式アカウントを作成します。

## 使用するLINE公式アカウントの作成

以下URLよりログインします。ログインはLINEで使っているアカウントで構いません。
初めて利用する方はプロバイダの設定を行います。

https://account.line.biz/login

プロバイダの作成が完了したら、作成したプロバイダを開き「新規チャネル作成 > Messaging API」へと進みます。

![LINE Developers.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/376910ee-189a-b1df-4c5a-f20fe3aae4e8.png)

いくつか質問されるので、答えていきます。
適当に答えても特に機能は変わらないので、サクッと終わらせましょう。

## LINE公式アカウントの設定

作成が完了すると、[LINE Developer](https://developers.line.biz/console/)上に、先ほど作成した公式アカウントが表示されています。
公式アカウントをクリックし、Messaging APIに進み、「チャネルアクセストークン」の項目まで移動してください。ここから新たにチャネルアクセストークンを発行します。

![LINE Developers2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/895b8bdf-8688-157a-9ed6-879a306a86d5.png)

:::note
チャネルアクセストークンは、このアカウントを用いてプッシュ通知を送信するために必要になります。メモしておきましょう。
:::

次に、グループチャットに参加可能な状態にします。今度は[LINE Official Account Manager](https://manager.line.biz/account/)から、該当アカウントを開きます。
右上の「設定 > 機能の利用 > トークへの参加」で「グループ・複数人トークへの参加を許可する」を選択しましょう。

![LINE Official Account Manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/c031d118-a96c-509f-88db-428f9a1ad0eb.png)

これでグループでこのアカウントを使用できるようになりました。

## グループIDを取得する準備

LINE公式アカウントが特定のグループにメッセージを送信するためには、そのグループのグループIDというものが必要になります。これは、対象のグループでイベント（メッセージの送信や新規メンバーの参加など）が発生した場合に送信されるPOSTリクエストの中に含まれています。

詳しくは以下を参照してください。

https://developers.line.biz/ja/reference/messaging-api/#message-event

これを取得するために、LINE公式アカウントに、POSTリクエストを取得可能なウェブアプリを作成し、エンドポイントへ設定します。
Google App Script（GAS）でウェブアプリをホスティングでき、POSTリクエストをキャッチできるので、ここではGASを使用します。（しかも無料...）

### GASの実装 

:::note
取得したデータをスプレッドシートに記載するので、事前にスプレッドシートを作成しておいてください。また、スプレッドシートのIDも必要になるので取得しておいてください。参考：https://gainoheya.com/goole-id-spread-sheet/
:::

GASはGoogleDriveから作成することができます。
![Googleドライブ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/dc0bfaba-0e5c-7b30-18f6-c0001f915ee7.png)


新たなGASファイルを作成すると、コードエディタが開きます。以下のようなコードを作成してください。

```JavaScript:GAS
function doPost(e) {
  try {
    // 受信したリクエストの内容をJSONとしてパース
    var data = JSON.parse(e.postData.contents);
    
    // 保存先のスプレッドシートを取得
    // ここは自身のスプレッドシートのIDと、シート名に置き換えてください。
    var sheet = SpreadsheetApp.openById("YourSpreadSheetID").getSheetByName("シート1");
    
    // 生データ全体をシートに格納
    sheet.appendRow([new Date(), JSON.stringify(data)]);
    
    // `events` の中から情報を取得
    if (data.events && data.events.length > 0) {
      var event = data.events[0];
      
      // イベントの詳細を抽出
      var timestamp = new Date(event.timestamp);
      var eventType = event.type;
      var groupId = event.source.groupId || "N/A";
      var webhookEventId = event.webhookEventId || "N/A";
      var replyToken = event.replyToken || "N/A";
      
      if (!groupId || !eventType || !webhookEventId || !replyToken) {
        throw new Error("ValueError: 必要なフィールドが存在しません");
      }

      sheet.appendRow([timestamp, eventType, groupId, webhookEventId, replyToken]);
    }
  } catch (e) {
    Logger.log("エラーが発生しました: " + e.message);
  }
  
  return ContentService.createTextOutput(JSON.stringify({status: "success"}))
                        .setMimeType(ContentService.MimeType.JSON);
}

```
これは、POSTリクエストをキャッチし、リクエストの内容をスプレッドシートに書き込む処理を行なっています。

作成が完了したら、右上から「デプロイ > 新しいデプロイ > ウェブアプリ」と進みます。**アクセスできるユーザーを「全員」に設定**して、デプロイします。
このようにウェブアプリのURLが表示されるので、これをコピーします。

![GAS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/5186e5d4-b0f8-9a98-ab3e-ae9e8da19aa5.png)

以上でGASの実装は完了です。

### LINE公式アカウントのエンドポイントを設定

先ほど表示されたウェブアプリのURLを、LINE公式アカウントのエンドポイントに設定します。LINE Developerを開き、「Messaging API設定 > Webhook設定」にURLをペーストしましょう。
こんな感じで貼れていたらOKです。

![LINE Developers3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/899f4245-2f3d-5c69-1c9c-70c8469358b4.png)

:::note
「検証」を押してもエラーが発生します。先ほどのGASのコードで適切なレスポンスを返すように設定していないためです。ただ、グループIDを取得するだけが目的であり、別にエラーが起きていても問題ないので安心してください。
:::

### グループIDの取得

さて、これでグループIDを取得する準備はできました。この公式アカウントを友だち追加し、グループに招待しましょう。
友だち追加用のQRコードはLINE Official Account Managerから表示することができます。

![LINE Official Account Manager2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/ddaaecc2-cb75-df0c-86ac-aadc9ebf3005.png)

これを読み取ったら、友だち追加して、グループに招待してください。
スプレッドシートが更新されるはずです。
その中から、`groupID`に該当する部分を抜き出して、どこかにメモしておきましょう。
あとでGitHubのワークフローを作成するときに使います。

# GitHubのワークフローを作成

ワークフローを作成する方法はいくつかあります。ここでは本筋ではないので紹介は省略し、特定の方法で実装します。

まずはリポジトリに`.github/workflows/line_notify_with_create_issue.yml`というファイルを作成してください。
ローカルにクローンして作成・プッシュしても構いませんし、リポジトリに直接アップロードしても構いません。
これにIssue作成時にLINE公式アカウントから通知を飛ばす処理を書いていきます。

以下のコードをコピペしてください。中では[Flex Message](https://developers.line.biz/ja/docs/messaging-api/using-flex-messages/)を利用してリッチな見た目のメッセージを送信しています。
Flex Messageは柔軟にいろんなデザインを作成できるので、ぜひ試してみてください。

```yml:line_notify_with_create_issue.yml
name: Issue Created Webhook

on:
  issues:
    types: [opened]

jobs:
  call_api_on_issue:
    runs-on: ubuntu-latest
    steps:
      - name: Install jq
        run: sudo apt-get install jq
      - name: Call API when Issue is created
        env:
          ISSUE_TITLE: ${{ github.event.issue.title }}
          ISSUE_BODY: ${{ github.event.issue.body }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
          ISSUE_URL: ${{ github.event.issue.html_url }}
          ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
          ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
        run: |
          issue_number="# $ISSUE_NUMBER"
          issue_author="Created by: $ISSUE_AUTHOR"

          payload=$(jq -n --arg title "$ISSUE_TITLE" --arg body "$ISSUE_BODY" --arg number "$issue_number" --arg url "$ISSUE_URL" --arg author "$issue_author" '{
            "to": "C3b848f3f63653016a2b6a9f469f7f416",
            "messages": [
              {
                "type":"flex",
                "altText":"新しいIssueを作成しました。",
                "contents": {
                  "type": "bubble",
                  "header": {
                    "type": "box",
                    "layout": "horizontal",
                    "contents": [
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [],
                        "cornerRadius": "5000px",
                        "width": "16px",
                        "height": "16px",
                        "spacing": "none",
                        "margin": "none",
                        "backgroundColor": "#57ab5b",
                        "offsetTop": "2px"
                      },
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": $title,
                            "color": "#d0d7e0",
                            "action": {
                              "type": "uri",
                              "label": "action",
                              "uri": $url
                            },
                            "wrap": true,
                            "decoration": "underline"
                          },
                          {
                            "type": "text",
                            "wrap": true,
                            "text": $number,
                            "color": "#666666"
                          }
                        ]
                      }
                    ],
                    "backgroundColor": "#141c21",
                    "alignItems": "flex-start",
                    "spacing": "md"
                  },
                  "body": {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                      {
                        "type": "box",
                        "layout": "vertical",
                        "margin": "md",
                        "spacing": "none",
                        "contents": [
                          {
                            "type": "text",
                            "text": $body,
                            "wrap": true,
                            "color": "#666666",
                            "size": "sm"
                          }
                        ]
                      }
                    ]
                  },
                  "footer": {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                      {
                        "type": "text",
                        "text": $author,
                        "size": "xs",
                        "color": "#666666"
                      }
                    ],
                    "alignItems": "flex-end",
                    "backgroundColor": "#F3F3F3"
                  }
                }
              }
            ]
          }')

          curl -v -X POST https://api.line.me/v2/bot/message/push \
          -H 'Content-Type: application/json' \
          -H "Authorization: Bearer $ACCESS_TOKEN" \
          -d "$payload"
```

中身はシンプルで、メッセージをJSONで作成し、プッシュ通知を送信するためのURLにPOSTリクエストを送信しているだけです。

次に、この中で使用している環境変数を設定します。
GitHubのリポジトリで「Settings > Secret and varables > Actions」に進みます。
ここに前述のフローで取得した
- `ACCESS_TOKEN`（カスタムアクセストークン）
- `GROUP_ID`（グループID）

を指定します。

![GitHub3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3628974/8e6c9fff-ac7c-89d1-8b4e-afd52c8aafa6.png)

以上で準備完了です。
新たにIssueを作成してみましょう。綺麗な見た目の通知が飛んでくると思います。

:::note
個人チャットへ送信したい場合は、`groupID`→`userId`に変更するだけです。
:::
# 最後に

今回、GitHubのWorkflowを使用してグループに通知を送信する方法についてお伝えしました。
何かご質問やご要望あれば、コメントいただけると幸いです。

最後までお付き合いいただきありがとうございました。