2022年7月26日火曜日

Slack の過去の投稿を再投稿して 90 日制限を回避する

えっと。はじめに断っておきますが、ネタ記事です。

Slack 初の料金改定とフリープランの内容変更のお知らせ | Slack

Slack の改定により、フリープランでは 90 日間の履歴しか閲覧できなくなりました。
この変更は結構困っている人は多そうで、Twitter などでもちょこちょこつぶやいている人を見かけました。
これを機に Slack から別ツールへ移行する人も多いようですが、私は今現在は主に通知用として使っているだけなので、特になにもせずに使い続けようと思ってます。
ただ、メモ置きとして使っていたような気もするので、実はそのうち困るかもしれません。。

そこでひらめきました!(のび太の画像)
90 日前までの履歴しか見えないのであれば、見えなくなる前に投稿し直せばいいのでは?

というわけでサクッと Make formary Integromat でシナリオ書いてみたのが冒頭のツイート。
ネタだけどちょこっと解説していきます。Integormat についてはこのブログでも何度か書いてるのでそのへんを参考にしてください。

シナリオ解説
概要
処理の流れは大体こんな感じ。トリガーは一日一回。
  1. 検索日(現在時刻から指定日数前の YYYY-MM-DD)と検索チャンネルの変数をセット
  2. Slack Search モジュールで検索
  3. 一度バンドルを集計(ホントはここで filter かけて集計したかった)
  4. 再投稿用のスレッドの親メッセージを投稿
  5. 集計したバンドル配列で以下の処理を各要素繰り返し
    1. 投稿ユーザー情報を取得(ユーザーのアイコンつけたかっただけ)
    2. 検索したメッセージとユーザー情報を使って、内容を再投稿
5 の前に検索結果が空でないかのフィルターをセットしてます。
また 6.1 の前にユーザー情報が取得可能でフィルターをセットしてます。

検索結果のバンドルをそのまま流して再投稿することも可能ですが、スレッドにしたかったので一度集計してバンドルを1つにしてます。(Integromat はモジュールの output バンドルの数だけ以降のシナリオが実行される仕組みです。集計してバンドルを1つにすることで、スレッドの親メッセージの投稿処理を1回だけにしてます。)

関係ないけど Slack の Block Kit Builder を初めて使いました。便利でした。
Blue print
Integromat は Blue print でシナリオの export/import が可能なので Gist に置いておきました。これを見る、もしくは import してもらって見たほうが理解しやすいと思います。

{
"name": "Slack Free looper",
"flow": [
{
"id": 12,
"module": "util:SetVariables",
"version": 1,
"parameters": {},
"mapper": {
"variables": [
{
"name": "searchDate",
"value": "{{formatDate(addDays(now; -8); \"YYYY-MM-DD\")}}"
},
{
"name": "searchChannel",
"value": "random"
}
],
"scope": "roundtrip"
},
"metadata": {
"designer": {
"x": 0,
"y": 0
},
"restore": {
"expect": {
"variables": {
"items": [
null,
null
]
},
"scope": {
"label": "One cycle"
}
}
},
"expect": [
{
"name": "variables",
"type": "array",
"label": "Variables",
"spec": [
{
"name": "name",
"label": "Variable name",
"type": "text",
"required": true
},
{
"name": "value",
"label": "Variable value",
"type": "any"
}
]
},
{
"name": "scope",
"type": "select",
"label": "Variable lifetime",
"required": true,
"validate": {
"enum": [
"roundtrip",
"execution"
]
}
}
],
"interface": [
{
"name": "searchDate",
"label": "searchDate",
"type": "any"
},
{
"name": "searchChannel",
"label": "searchChannel",
"type": "any"
}
]
}
},
{
"id": 1,
"module": "slack:SearchMessages",
"version": 4,
"parameters": {
"__IMTCONN__": 276911
},
"mapper": {
"sort": "timestamp",
"limit": "30",
"query": "on:{{12.searchDate}}",
"sort_dir": "asc"
},
"metadata": {
"designer": {
"x": 300,
"y": 0
},
"restore": {
"expect": {
"sort": {
"mode": "chose",
"label": "Timestamp"
},
"sort_dir": {
"mode": "chose",
"label": "Ascending"
}
},
"parameters": {
"__IMTCONN__": {
"data": {
"scoped": "true",
"connection": "slack2"
},
"label": "zumix-home-on-make"
}
}
},
"parameters": [
{
"name": "__IMTCONN__",
"type": "account:slack2",
"label": "Connection",
"required": true
}
],
"expect": [
{
"name": "query",
"type": "text",
"label": "Query",
"required": true
},
{
"name": "limit",
"type": "uinteger",
"label": "Limit",
"required": true
},
{
"name": "sort",
"type": "select",
"label": "Sort by",
"validate": {
"enum": [
"score",
"timestamp"
]
}
},
{
"name": "sort_dir",
"type": "select",
"label": "Direction",
"validate": {
"enum": [
"asc",
"desc"
]
}
}
]
}
},
{
"id": 9,
"module": "builtin:BasicAggregator",
"version": 1,
"parameters": {
"feeder": 1
},
"mapper": {
"iid": "{{1.iid}}",
"team": "{{1.team}}",
"channel": "{{1.channel}}",
"type": "{{1.type}}",
"user": "{{1.user}}",
"username": "{{1.username}}",
"ts": "{{1.ts}}",
"text": "{{1.text}}",
"permalink": "{{1.permalink}}",
"blocks": "{{1.blocks}}"
},
"metadata": {
"designer": {
"x": 600,
"y": 0
},
"restore": {
"extra": {
"feeder": {
"label": "Slack - Search for Message [1]"
},
"target": {
"label": "Custom"
}
}
}
}
},
{
"id": 6,
"module": "slack:CreateMessage",
"version": 4,
"parameters": {
"__IMTCONN__": 276911
},
"filter": {
"name": "has message",
"conditions": [
[
{
"a": "{{9.array}}",
"b": "0",
"o": "array:notequal"
}
]
]
},
"mapper": {
"channelWType": "manualy",
"text": "{{12.searchDate}} のメッセージ",
"mrkdwn": true,
"channel": "{{12.searchChannel}}"
},
"metadata": {
"designer": {
"x": 900,
"y": 0
},
"restore": {
"parameters": {
"__IMTCONN__": {
"label": "zumix-home-on-make",
"data": {
"scoped": "true",
"connection": "slack2"
}
}
},
"expect": {
"channelWType": {
"label": "Enter manually"
},
"reply_broadcast": {
"mode": "chose"
},
"link_names": {
"mode": "chose"
},
"parse": {
"mode": "chose"
},
"mrkdwn": {
"mode": "chose"
},
"unfurl_links": {
"mode": "chose"
},
"unfurl_media": {
"mode": "chose"
}
}
},
"parameters": [
{
"name": "__IMTCONN__",
"type": "account:slack2,slack3",
"label": "Connection",
"required": true
}
],
"expect": [
{
"name": "channelWType",
"type": "select",
"label": "Enter a channel ID or name",
"required": true,
"validate": {
"enum": [
"manualy",
"list"
]
}
},
{
"name": "text",
"type": "text",
"label": "Text"
},
{
"name": "blocks",
"type": "text",
"label": "Blocks"
},
{
"name": "thread_ts",
"type": "text",
"label": "Thread message ID (time stamp)"
},
{
"name": "reply_broadcast",
"type": "boolean",
"label": "Reply broadcast"
},
{
"name": "link_names",
"type": "boolean",
"label": "Link names"
},
{
"name": "parse",
"type": "boolean",
"label": "Parse message text"
},
{
"name": "mrkdwn",
"type": "boolean",
"label": "Use markdown"
},
{
"name": "unfurl_links",
"type": "boolean",
"label": "Unfurl primarily text-based content"
},
{
"name": "unfurl_media",
"type": "boolean",
"label": "Unfurl media content"
},
{
"name": "icon_emoji",
"type": "text",
"label": "Icon emoji"
},
{
"name": "icon_url",
"type": "url",
"label": "Icon url"
},
{
"name": "username",
"type": "text",
"label": "User name"
},
{
"name": "channel",
"type": "text",
"label": "Channel ID or name",
"required": true
}
]
}
},
{
"id": 7,
"module": "builtin:BasicFeeder",
"version": 1,
"parameters": {},
"mapper": {
"array": "{{9.array}}"
},
"metadata": {
"designer": {
"x": 1200,
"y": 0
},
"restore": {
"expect": {
"array": {
"mode": "edit"
}
}
},
"expect": [
{
"mode": "edit",
"name": "array",
"spec": [],
"type": "array",
"label": "Array"
}
]
}
},
{
"id": 4,
"module": "slack:GetUser",
"version": 4,
"parameters": {
"__IMTCONN__": 276911
},
"filter": {
"name": "is user",
"conditions": [
[
{
"a": "{{7.user}}",
"o": "exist"
}
]
]
},
"mapper": {
"user": "{{7.user}}"
},
"metadata": {
"designer": {
"x": 1500,
"y": 0
},
"restore": {
"parameters": {
"__IMTCONN__": {
"data": {
"scoped": "true",
"connection": "slack2"
},
"label": "zumix-home-on-make"
}
}
},
"parameters": [
{
"name": "__IMTCONN__",
"type": "account:slack2",
"label": "Connection",
"required": true
}
],
"expect": [
{
"name": "user",
"type": "text",
"label": "User ID",
"required": true
}
]
}
},
{
"id": 3,
"module": "slack:CreateMessage",
"version": 4,
"parameters": {
"__IMTCONN__": 276911
},
"mapper": {
"channelWType": "manualy",
"blocks": "{\n\t\"blocks\": [\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"mrkdwn\",\n\t\t\t\t\"text\": \"{{7.permalink}}\\n{{7.username}} ({{parseDate(7.ts; \"X\"; 4.tz)}}) \"\n\t\t\t}\n\t\t},\n\t\t{\n\t\t\t\"type\": \"divider\"\n\t\t},\n\t\t{\n\t\t\t\"type\": \"section\",\n\t\t\t\"text\": {\n\t\t\t\t\"type\": \"plain_text\",\n\t\t\t\t\"text\": \"{{7.text}}\",\n\t\t\t\t\"emoji\": true\n\t\t\t},\n\t\t\t\"accessory\": {\n\t\t\t\t\"type\": \"image\",\n\t\t\t\t\"image_url\": \"{{if(4.profile.image_original != null; 4.profile.image_original; 4.profile.image_512)}}\",\n\t\t\t\t\"alt_text\": \"{{7.username}}\"\n\t\t\t}\n\t\t}\n\t]\n}",
"thread_ts": "{{6.ts}}",
"mrkdwn": true,
"channel": "{{7.channel.id}}"
},
"metadata": {
"designer": {
"x": 1800,
"y": 0
},
"restore": {
"parameters": {
"__IMTCONN__": {
"label": "zumix-home-on-make",
"data": {
"scoped": "true",
"connection": "slack2"
}
}
},
"expect": {
"channelWType": {
"label": "Enter manually"
},
"reply_broadcast": {
"mode": "chose"
},
"link_names": {
"mode": "chose"
},
"parse": {
"mode": "chose"
},
"mrkdwn": {
"mode": "chose"
},
"unfurl_links": {
"mode": "chose"
},
"unfurl_media": {
"mode": "chose"
}
}
},
"parameters": [
{
"name": "__IMTCONN__",
"type": "account:slack2,slack3",
"label": "Connection",
"required": true
}
],
"expect": [
{
"name": "channelWType",
"type": "select",
"label": "Enter a channel ID or name",
"required": true,
"validate": {
"enum": [
"manualy",
"list"
]
}
},
{
"name": "text",
"type": "text",
"label": "Text"
},
{
"name": "blocks",
"type": "text",
"label": "Blocks"
},
{
"name": "thread_ts",
"type": "text",
"label": "Thread message ID (time stamp)"
},
{
"name": "reply_broadcast",
"type": "boolean",
"label": "Reply broadcast"
},
{
"name": "link_names",
"type": "boolean",
"label": "Link names"
},
{
"name": "parse",
"type": "boolean",
"label": "Parse message text"
},
{
"name": "mrkdwn",
"type": "boolean",
"label": "Use markdown"
},
{
"name": "unfurl_links",
"type": "boolean",
"label": "Unfurl primarily text-based content"
},
{
"name": "unfurl_media",
"type": "boolean",
"label": "Unfurl media content"
},
{
"name": "icon_emoji",
"type": "text",
"label": "Icon emoji"
},
{
"name": "icon_url",
"type": "url",
"label": "Icon url"
},
{
"name": "username",
"type": "text",
"label": "User name"
},
{
"name": "channel",
"type": "text",
"label": "Channel ID or name",
"required": true
}
],
"advanced": true
}
}
],
"metadata": {
"instant": false,
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true,
"autoCommitTriggerLast": true,
"sequential": false,
"confidential": false,
"dataloss": false,
"dlq": false
},
"designer": {
"orphans": []
},
"zone": "us1.make.com"
}
}
補足

  • Blue print の connection は筆者の Integromat のものなのでそのままでは使えません。もし import したら conection を更新してください。
  • 今回のシナリオは Twitter モジュール使ってないので Integromat のフリープランの範囲で使えます。
  • ただし、そこそこオペレーター数を使うシナリオ、かつ Slack のメッセージごとに消費するので、メッセージが多いと Integromat のフリープランを超えてしまします。
    Pricing & Subscription Packages | Make
    ざっくり計算で1日あたり 7 件のメッセージを処理するのが限界じゃないかなーと思います。
    動作優先でオペレーターの最適化してないのでもっと減らせる(※)とは思いますが、仮に不可能ではありますが 1 オペレーターでシナリオが書けたとしても、Integromat のフリープランの最大 1,000 件は越えられないです。
    (※スレッドやめるとかアイコンやめるとか、1週間分まとめて処理するとか)
  • あと再投稿したスレッドがまた期日を迎えたときにどうなるかとか、ボットどうするかとか、添付ファイルとか、いろいろ確認してないので、ちゃんとやりたい人は頑張ってください。

(期日内であれば)件数無制限という点について

これは Slack からのお知らせのスクリーンショットです。

変更前だと直近 10,000 件しか閲覧できてなかったのですが、今回の改定で期日以内であればこの件数制限がなくなったようです。
(この記事書いてて気づいたレベルなので自分は困ってなかったみたいだ)

さて、もうお気づきかと思いますが、実は紹介した方法を使うとなんと!日数制限も超えてメッセージを無限に扱えるようになっちゃいます!

やってることは検索して再投稿なので Slack API 叩けば誰でもどこでも出来ちゃいます。GitHub Actions とかでやらせることもできちゃうと思います。(それなら Integromat とかの RPA よりも無料の範囲でできそう)

つまり(使いやすいかどうかは置いておいて)実質無制限になったわけですw

いやー、私はまぁそこまでして使おうと思いませんが。。

(でもこれ書いてて思ったけどファイルストレージは意外と?)

最後に
なんか書けそうな気がする。。

0 件のコメント:

コメントを投稿