本記事はリンクアンドモチベーションAdventCalendar2024の16日目の記事です。
きっかけ
アイデアを思いついた時slackに呟いてもそのアイデアがすぐ流れてしまっていました。
1プロダクト、複数チャンネル運用なので色んなチャンネルでアイデアが出るものの集約のコストが掛かっていました。
なので、簡単にアイデアをまとめられないものかと思っていました。
やりたかったこと
最終的に作ったものは以下のようなものです。
※ 実際は別のチャンネルに送信されるものですが、今回は同じチャンネルに送信しました。
- メッセージに対してリアクションがつけられる
- リアクションのついたメッセージが特定のチャンネルに送信される(メッセージ内容 / リンク付き)
解決策の方針
- Slackのワークフローが便利なので、基本的には乗っかろうと思います
Slackは様々なevent でAPIを用意しています。詳しくはこちらを参照してください。リアクション追加は reaction_added というAPIです。
- チャンネルをまたぐことになるので、どのメッセージのリンクかはわかるようにしたい
- リンクによるプレビューだとメッセージが大きくなると全てを読めないので、メッセージも送信したい
生じた問題
slackのワークフローを使い、特定のメッセージにスタンプが押されると「アイデア集約chにメッセージを送信」するワークフローを組みました。
変数を利用すると、
- 誰にリアクションされたか
- どのメッセージにリアクションされたか
などは変数機能で簡単に取得することが出来ます。
とても便利で、簡単に作ることが出来ました。ただ、メッセージの本文の内容を取得することが出来ませんでした。
再度解決策
そこでSlack appを使った解決を試みました。
https://api.slack.com/automation/run
リアクションの取得やチャンネルへの送信は引き続きテンプレートに頼りつつ、メッセージーの取得用のアプリを作成しました。
import { DefineFunction, Schema, SlackFunction } from "deno-slack-sdk/mod.ts";
export const GetSlackMessageFunction = DefineFunction({
callback_id: "get_slack_message_function",
title: "slackのメッセージを取得",
description: "slackのメッセージのURLから、元のメッセージを取得します",
source_file: "functions/get_slack_message.ts",
input_parameters: {
properties: {
url: {
type: Schema.types.string,
description: "取得したいslackのメッセージのURL",
},
},
required: ["url"],
},
output_parameters: {
properties: {
message: {
type: Schema.types.string,
description: "取得したslackのメッセージ",
},
},
required: ["message"],
},
});
function parseSlackUrl(url: string): { channel: string; ts: string } | null {
const regex = /https:\/\/[\w-]+\.slack\.com\/archives\/([\w-]+)\/p(\d+)/;
const match = url.match(regex);
if (match) {
const [, channel, tsRaw] = match;
const ts = `${tsRaw.slice(0, -6)}.${tsRaw.slice(-6)}`;
return { channel, ts };
}
return null;
}
export default SlackFunction(
GetSlackMessageFunction,
async ({ inputs, client }) => {
const { url } = inputs;
const urlInfo = parseSlackUrl(url);
if (!urlInfo) {
return { error: "Invalid Slack message URL" };
}
const { channel, ts } = urlInfo;
try {
const response = await client.conversations.replies({
channel: channel,
ts: ts,
limit: 1,
});
if (response.messages && response.messages.length > 0) {
const message = response.messages[0].text;
return {
outputs: {
message,
},
};
} else {
return { error: `Message not found. Error: ${response.error}` };
}
} catch (error) {
return { error: `Failed to retrieve message: ${error.message}` };
}
},
);
※ 社内にすでにあった仕組みを一部改良しました。
セットアップ等はQuickStartを御覧ください。今回は割愛します。
このようにすることでリアクションがついたメッセージに対してそのメッセージ内容を他のチャンネルに送信することが出来るようになりました。
最後に
ローカルでの動作確認も簡単にできるので、開発体験もとてもよかったです!