【GAS】SlackのチャンネルからQiitaの記事を取得して自動でストックする

# 結論
以下ソースコードをGASに登録し、Slack APIとQiita APIのトークンとその他を環境変数に登録するだけ。

```js
// 実行関数
function myFunction() {
  const messages = getSlackMessages()
  putStock(messages)
}
// 定期的にSlackからメッセージを取得するためのトリガーを設定
function setTrigger() {
  ScriptApp.newTrigger("myFunction").everyDays(1).atHour(20).create()
}

// Slack APIのトークン
const SLACK_TOKEN = getVal("SLACK_TOKEN")
// SlackチャンネルID
const SLACK_QIITA_CHANNEL_ID = getVal("SLACK_QIITA_CHANNEL_ID")
const SLACK_BOT_CHANNEL_ID = getVal("SLACK_BOT_CHANNEL_ID")
// Slack APIのメッセージ取得用エンドポイント
const SLACK_GET_MESSAGES = "https://slack.com/api/conversations.history"
// Slack APIのメッセージ送信用エンドポイント
const SLACK_POST_MESSAGES = "https://slack.com/api/chat.postMessage"
// Qiita APIのトークン
const QIITA_TOKEN = getVal("QIITA_TOKEN")
// Qiitaのユーザー名
const QIITA_USER_NAME = getVal("QIITA_USER_NAME")
// Qiita.comのリンクの正規表現
const QIITA_LINK_REGEX = /https?:\/\/qiita\.com\/[\w-._~:/?#[\]@!$&'()*+,;=]+\/items\/[\w-._~:/?#[\]@!$&'()*+,;=]+/g
// QiitaのリンクからuserNameとitemIdを抽出する正規表現
const QIITA_EXTRACTION_REGEX = /https:\/\/qiita\.com\/([^\/]+)\/items\/([^\/]+)/

// 任意のチャンネルからメッセージを取得
function getSlackMessages() {
  const options = {
    method: "post",
    contentType: "application/json; charset=utf-8",
    headers: {
      Authorization: "Bearer " + SLACK_TOKEN,
    },
  }
  
  const requestUrl = `${SLACK_GET_MESSAGES}?channel=${SLACK_QIITA_CHANNEL_ID}&pretty=1&oldest=${timestamp()}`
  const response = UrlFetchApp.fetch(requestUrl, options)
  const messages = JSON.parse(response.getContentText()).messages
  const results = []
  messages.map((message) => {
    const text = message.text
    const links = text.match(QIITA_LINK_REGEX)

    links && results.push(links)
  })
  return results
}
// ストックを追加
function putStocks(results) {
  const options = {
    method: "put",
    contentType: "application/json; charset=utf-8",
    headers: {
      Authorization: "Bearer " + QIITA_TOKEN,
    },
  }

  results.map((links) => {
    links.map((item) => {
      const matches = item.match(QIITA_EXTRACTION_REGEX)
      if (!matches || matches[1] === QIITA_USER_NAME) return
      const requestUrl = `https://qiita.com/api/v2/items/${matches[2]}/stock`
      try {
        const response = UrlFetchApp.fetch(requestUrl, options)
        if (response.getResponseCode() !== 204) throw new Error()
      } catch (e) {
        sendErrorMessage("ストック、失敗してまっせ。")
      }
    })
  })
}
function sendErrorMessage(message) {
  const options = {
    method: "post",
    payload: JSON.stringify({
      text: message,
      channel: SLACK_BOT_CHANNEL_ID,
    }),
    contentType: "application/json; charset=utf-8",
    headers: {
      Authorization: "Bearer " + SLACK_TOKEN,
    },
  }
  UrlFetchApp.fetch(SLACK_POST_MESSAGES, options)
}
// 環境変数から値を取得する
function getVal(e) {
  return PropertiesService.getScriptProperties().getProperty(e)
}
// 日本時間で今日の0時0分のUnixタイムスタンプを取得
function timestamp() {
  const now = new Date()
  const timezoneOffset = 9 * 60 // 日本時間はUTC+9
  now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + timezoneOffset)
  now.setHours(0, 0, 0, 0)
  return Math.floor(now.getTime() / 1000)
}

```

https://github.com/rtkjm22/qiita_automation/blob/main/index.gs

# 経緯
後で読みたい記事があったとき、一旦ストックとして登録しておいて、あとで見ることもあるかと思います。
そんな中で、会社内でQiitaの記事が盛んに投稿されており、見てない記事はストックの「後で見る」に自動で追加できないかなーと思って作ってみました。

# 導入方法
## 1. GASプロジェクトの作成
Googleアカウントを持っている方はすぐにできます。
[Google Drive](https://drive.google.com/drive/home)から「+新規」 > 「その他」　> 「Google Apps Script」にアクセスします。

![CleanShot 2024-06-06 at 01.16.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/ef659526-ec0c-1f01-9b4d-d42a836780cc.png)

そうしたら、[こちらの記事](https://www.isfnet-services.com/blog/125/GoogleAppsScript)を参考に適当なプロジェクトを作成してください。

## 2. ソースコードのコピー
結論で乗せているソースコードをそのままコピペしてください。
もうほぼ完成です。

## 3. Qiita APIのトークンを取得
Qiitaのアカウントを取得している方はすぐに取得できます。
以下の画像を参考にトークンを取得してください。
なお、「発行する」ボタンを押下して実際にトークンが発行された際は、**発行されたトークンは一度しか表示されない（リロードすると消える）** のでクリップボードに登録しておくなどして忘れないようにしてください。

![CleanShot 2024-06-06 at 01.29.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/ba4344c1-ce99-2a0e-72c9-d3e1eb425ace.png)

## 4. Slack APIのトークンを取得
以下記事を参考にトークンを取得してください。

https://qiita.com/kobayashi_ryo/items/a194e620b49edad27364

なお、今回使用するトークンは以下画像の箇所にあります。

![CleanShot 2024-06-06 at 01.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/209efeeb-0ff3-4b1f-dca9-5a2379d0d6c0.png)


## 5. Slack APIの権限設定
Slackで作成した各アプリはそれぞれ権限を設定できます。
メッセージを送信することができるようになる、スレッドの内容を取得できるなどですね。
設定箇所は以下になります。

まず、ダッシュボードのサイドバーにある「OAuth & Permission」へ遷移してください。

![CleanShot 2024-06-06 at 01.38.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/c4502617-2d48-3d78-1674-cc20ea311ba2.png)

次に「Scopes」という箇所があると思うので、「Bot Token Scopes」 > 「Add an OAuth Scope」から該当の権限を追加してください。

![CleanShot 2024-06-06 at 01.41.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/b0ce0aa3-65c8-33be-9210-7066bd545376.png)


なお、設定している権限はドキュメントに記載されているので、今後別のエンドポイントを使用する場合は参考にしてみてください。

以下は[チャンネル内にあるメッセージを取得するエンドポイント](https://api.slack.com/methods/conversations.history)になります。

![CleanShot 2024-06-06 at 01.46.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/84d47f5d-df65-02c7-f4c5-3beb23af0f87.png)

## 6. 環境変数を登録
GASのサイドバーにある歯車マークを押下すると「プロジェクトの設定」ページに遷移します。
画面下部にある「スクリプト プロパティ」という箇所でソースコードの実行に必要な環境変数が登録できます。

現在登録されているものとしては以下になります。
- QIITA_TOKEN : Qiita　APIを使う時に必要なトークン
- SLACK_BOT_CHANNEL_ID : Slack内でbotがあらゆるメッセージを送信するチャンネルのID
- SLACK_QIITA_CHANNEL_ID : Qiitaの記事を抽出する対象となるSlackチャンネルのID
- SLACK_TOKEN : Slack APIを使う時に必要なトークン

![CleanShot 2024-06-06 at 01.20.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358287/a0e7b4e7-ebe9-a8e5-41cd-4559010819ff.png)

## 7. 以上！！


# まとめ
ここまで見てくださってありがとうございました！

今後追加したい機能としては、
- 見てないストック記事を特定のSlackのチャンネルに定期送信したり
- そこから「いいね」が押せるようになる
- 読んだらストックから外れるようになる
などができるようになったらもっと便利になりそうですね！

また、GAS・Slack API・Qiita APIのそれぞれの記事は結構あったのですが、それらをつなぎこんだ記事は少ない印象でした。
なので、GASを使って両APIを使って実装する時に、本記事が皆さんの参考になったら嬉しいです！
また、参考になったよーっていう方は「**いいね**」をよろしくお願いします！

ではでは！