4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Slack APIでメッセージのURLをもとにそのメッセージに返信する

Last updated at Posted at 2023-12-08

やりたいこと

image.png

Slackメッセージの三点メニューから取得できるリンクのみを使って、任意のタイミングでSlack APIを使ってそのメッセージに返信したい。

How to retrieve Slack messages via API identified by permalink? - Stack Overflow

この記事を参考に、今回の記事を作成しました。
一部JavaScriptを使っていますが、メッセージリンク(URL)の加工をしているだけなので、ほかの言語でも同じロジックを組めば再現できると思います。

使用するメッセージリンクのサンプル

Slackのリンクは下記の構成であることを想定しています。もし、違ったパラメータ等を含んでいるときはうまく調整しながら参考にしてください。

👇親スレッドのリンクをコピーしたとき
https://<サブドメイン>.slack.com/archives/<Conversation ID>/p<ドットを除いたメッセージの作成日時のタイムスタンプ>

👇親スレッドに返信されたメッセージのリンクをコピーしたとき
https://<サブドメイン>.slack.com/archives/<Conversation ID>/p<ドットを除いたメッセージの作成日時のタイムスタンプ>?thread_ts=<スレッドのメッセージの作成日時のタイムスタンプ ドット付き>&cid=<Conversation ID>

上記どちらのリンクを使った場合でも、返信は親スレッドに対して行う想定でロジックを組み立てていきます。

名称 説明
サブドメイン slackワークスペース名
Conversation ID/チャンネルID メッセージが存在するチャンネルのID。チャンネルIDと呼ぶほうが多い?
ドットを除いたメッセージの作成日時のタイムスタンプ メッセージが作成されたときの小数点以下第6位まであるUNIX時刻からドットを除いた値
例:1701232196111111の場合、2023年11月29日13時29分56.111111秒
Slack APIドキュメントではドットが付いているUNIX時刻の値を thread_ts と呼んでおり、メッセージのIDのようなもののようです。

事前準備(Slackアプリ作成)

Slackでアプリケーションを作成し、OAuth Tokenを発行するまで #Slack - Qiita

こちらの記事を参考にSlackアプリケーションを作成します。
Slackアプリケーションにアプリ作成者の権限を委任しておくことで、あとの作業で行うメッセージへの返信ができるようになると思います。

image.png

写真ではほかにも権限つけていますが、今回の用途では chat:write を「User Token Scopes」に指定します。(ほかの4つは不要です)

Install to Workspace ボタンを最後に押して、許可しておきます。

最後に「OAuth Tokens for Your Workspace」から「User OAuth Token」の値をコピーしておきます。

メッセージに返信する(JavaScript)

下記 extractChannelIDThreadTS 関数を参考に、リンクからチャンネルIDとメッセージの作成日時(UNIX時刻)を取得します。
この2つの値があればメッセージに返信することができます。

/**
 * extractChannelIDThreadTS関数
 * SlackメッセージのURL(パーマリンク)から、チャンネルIDとメッセージのts(タイムスタンプ)を取得します。
 * 
 * @param {string} permaLink slackメッセージのURL
 * @returns {channelId, threadTS}
 */
function extractChannelIDThreadTS(permaLink) {

  // trueの場合、permaLinkがスレッドの親のURLではなく、スレッドに返信したメッセージのURLだと判断する
  const isIncludesthread_ts = permaLink.includes('thread_ts')

  // URLをスラッシュで分解
  const splitPermaLink = permaLink.substring(8).split('/')

  // チャンネルID
  const channelId = splitPermaLink[2]

  // 返信するスレッドの親メッセージのts_thread
  let threadTS = null

  if (isIncludesthread_ts) {
      const indexOfthread_ts = permaLink.indexOf('thread_ts=') + 10
      const thread_ts_length = 17
      threadTS = permaLink.substring(indexOfthread_ts, indexOfthread_ts + thread_ts_length)
  } else {
      // 先頭'p'の1文字を切り抜き、UNIX時刻になるようドット'.'を入れる
      threadTS = splitPermaLink[3].substring(1).substring(0, 10) + '.' + splitPermaLink[3].substring(1).substring(10)
  }
  
  return {channelId, threadTS}
}

// 以下例
const slackPermaLink = `https://<サブドメイン>.slack.com/archives/<Conversation ID>/p<ドットを除いたメッセージの作成日時のタイムスタンプ>`
const {channelId, threadTS} = extractChannelIDThreadTS(slackPermaLink)
console.log(channelId, threadTS)
// C05PB1R852L 1701336881.449019

次に replySlackMessage 関数を使ってSlack APIでメッセージに返信します。
headersAuthorizatin に直接トークンを書いていますが、ここは環境変数で置き換えてください。 xoxb から始まるトークンはボットトークンです。

/**
 * replySlackMessage関数
 * Slackメッセージに返信します。
 * 
 * @param {string} channel 返信するメッセージがある場所のチャンネルID
 * @param {string} thread_ts メッセージのts(UNIXタイムスタンプ)。小数点以下第6位まで必要。
 * @param {string} text 返信するテキスト
 */
async function replySlackMessage(channel, thread_ts, text) {

  const slackReqUrl = `https://slack.com/api/chat.postMessage`
  const resSlack = await fetch(slackReqUrl, {
    method: 'POST',
    body: JSON.stringify({channel, thread_ts, text}),
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer xoxb-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    }
  })

  if (!resSlack.ok) {
    alert('Slackスレッドの返信に失敗しました。')
  }
}

// 以下例
const replyMessage = '<@usercode>\n今日のTODO\n<https://www.google.co.jp/|Google トップ>'
replySlackMessage(channelId, threadTS, replyMessage)

上記のマークダウンに近いかたちで replyMessage を使うと
下記の見た目で返信されます。


@usercode
今日のTODO
Google トップ


以上で終わりです。
別言語でも同じロジックを組みさえすれば、同じことができると思います。

追記(フロントエンドからの送信時のCORSエラー)

フロントエンドから、Slack APIをたたくとCORSエラーになってしまうため、リクエストの Content-Typeapplication/x-www-form-urlencoded; charset=utf-8 にするとうまくいきました。
なので replySlackMessage 関数は下記に変更しました。

/**
 * replySlackMessage関数
 * Slackメッセージに返信します。
 * 
 * @param {string} channel 返信するメッセージがある場所のチャンネルID
 * @param {string} thread_ts メッセージのts(UNIXタイムスタンプ)。小数点以下第6位まで必要。
 * @param {string} text 返信するテキスト
 */
async function replySlackMessage(channel, thread_ts, text) {

  const slackReqUrl = `https://slack.com/api/chat.postMessage`
  const body = `token=${トークンの値そのまま}&channel=${channel}&thread_ts=${thread_ts}&text=${text}`
  const resSlack = await fetch(slackReqUrl, {
    method: 'POST',
    body,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
    }
  })

  if (!resSlack.ok) {
    alert('Slackプロジェクトスレッドへの返信に失敗しました。')
  }
}

こちらの記事を参考にさせていただきました。

SlackWebAPIでJSONが利用できなかった話 #JavaScript - Qiita

参考

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?