slackで、特定のチャネルに投稿があったら、指定したチャンネルに同じ内容のコピー投稿をする
前回よりも、インタラクティブな感じにする
1, slackアプリのスコープ(権限)を設定
「OAuth & Permissions」で以下のスコープを追加します:
chat:write(メッセージ送信)
channels:read(チャンネル情報の取得)
channels:history(メッセージイベントの取得)
app_mentions:read
2, イベントサブスクリプションを有効化
「Event Subscriptions」を有効にし、以下を設定:
Request URL: 後で作成する AWS Lambda の URL
Subscribe to bot events: message.channels を追加。
3, lambdaの設定から関数URLを設定して、slackが投稿されたらポストする先を取得して、手順2の所にコピペ
認証タイプ: 「なし(NONE)」を選択
CORS(Cross-Origin Resource Sharing)も有効にする(*)
これで、URLにアクセスするだけでpythonが実行される
4, コーディング
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
import json from slack_sdk import WebClient from slack_sdk.errors import SlackApiError # Slack Bot Token SLACK_BOT_TOKEN = "xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" client = WebClient(token=SLACK_BOT_TOKEN) # 監視対象チャネルのID SOURCE_CHANNEL = "C0123456789" # 監視元チャネルID TARGET_CHANNEL = "C9876543210" # 投稿先チャネルID def lambda_handler(event, context): try: # 受信データをログ出力 print("Received event:", json.dumps(event, indent=2)) # `body` が無い場合のエラーハンドリング if 'body' not in event or not event['body']: print("Error: No 'body' in event") return {"statusCode": 400, "body": "Invalid request: No body"} # JSON デコード body = json.loads(event['body']) # Slack の Challenge 応答 if "challenge" in body: print("Challenge received:", body["challenge"]) return { "statusCode": 200, "headers": {"Content-Type": "application/json"}, "body": json.dumps({"challenge": body["challenge"]}) } # Slack イベントが含まれているか確認 if "event" not in body: print("No event data found") return {"statusCode": 200, "body": "No event data"} slack_event = body["event"] # 受信したチャンネル情報をログに出力 print("Event received from channel:", slack_event.get("channel")) print("Channel type:", slack_event.get("channel_type")) # `channel_type` をチェックして、処理対象か判断 channel_type = slack_event.get("channel_type", "") if channel_type not in ["channel", "group", "im"]: print(f"Ignoring event from unsupported channel type: {channel_type}") return {"statusCode": 200, "body": "Ignoring event from unsupported channel type"} # 送信元のチャンネルが `SOURCE_CHANNEL` と一致するか確認 if slack_event.get("channel") != SOURCE_CHANNEL: print(f"Ignoring event: Not from source channel ({slack_event.get('channel')})") return {"statusCode": 200, "body": "Not from source channel"} # メッセージ内容を取得 message_text = slack_event.get("text") if not message_text: print("No message text found") return {"statusCode": 200, "body": "No message text"} # 指定したチャネルにメッセージを送信 response = client.chat_postMessage(channel=TARGET_CHANNEL, text=message_text) print(f"Message posted to {TARGET_CHANNEL}: {response['ts']}") return {"statusCode": 200, "body": "Message forwarded"} except SlackApiError as e: print(f"Slack API Error: {e.response['error']}") return {"statusCode": 500, "body": f"Error: {e.response['error']}"} except Exception as e: print(f"Unexpected Error: {str(e)}") return {"statusCode": 500, "body": "Internal Server Error"} |
5, 監視先のチャンネルに、このアプリを参加させる。
/invite @アプリ名
で投稿すれば参加ダイアログが出てくる。
※ curlだと動作するのに、このアプリ参加がないと駄目だった(コードでチャンネル指定するだけでは駄目)
1 2 3 4 5 6 7 |
$curl -X POST -H "Content-Type: application/json" -d '{ "event": { "type": "message", "text": "test", "channel": "CXXXXXXXX" } }' https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.lambda-url.ap-northeast-1.on.aws/ |
じゃあ、SOURCE_CHANNELで指定するのではなく、参加しているチャンネルの投稿を取得して、監視する方が良いじゃん!
と思って、アプリが参加しているチャネルを取得したら、TARGET_CHANNELも取得して、無限ループ投稿になってしまった・・・。
conversations.list API を実行するには、groups:read のスコープが必要 です。