首页 > 解决方案 > Slack 交互式消息:POST 请求有效负载具有意外的格式

问题描述

我在 Slack 的 Flask 应用程序中收到 POST 请求。当用户按下交互式消息按钮时发送请求。根据 Slack文档,我必须提取请求的正文以验证签名。不过,我计算的签名与 Slack 发送的签名不匹配。事实上,请求的主体是一些编码字符串。正如预期的那样,该字符串实际上是一个编码字典而不是查询 str 参数。

这是我观点的开始:

@app.route('/register', methods=['POST'])
def register_visit():
    data = request.get_data()
    signature = request.headers.get('X-Slack-Signature', None)
    timestamp = request.headers.get('X-Slack-Request-Timestamp', None)
    signing_secret = b'aaaaaaaaaaaaaaaa'


    # old message, ignore
    if round(actual_time.time() - float(timestamp)) > 60 * 5:
        return
    concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8')
    computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated, digestmod=hashlib.sha256).hexdigest()
    if hmac.compare_digest(computed_signature, signature):
        ...

我试图格式化接收到的数据,使其看起来像:

token=fdjkgjl&user_id=1234...但我不知道数据中必须存在的所有必要参数。

任何想法都受到高度赞赏。

消息的正文如下 - 在被 URL 解码后(注意我修改了可能的敏感数据):

b'payload={"type":"interactive_message","actions": [{"name":"yes_button","type":"button","value":"236"}],"callback_id":" visit_button","team":{"id":"fffff","domain":"ffff"},"channel":{"id":"ffff","name":"directmessage"},"user" :{"id":"ffffff","name":"fffft"},"action_ts":"1540403943.419120","message_ts":"1541403949.000100","attachment_id":"1","token":"8LpjBuv13J7xAjhl2lEajoBU" ,"is_app_unfurl":false,"original_message":{"text":"Test","bot_id":"DDDDDDDDD","附件":[{"callback_id":"visit_button","text":"注册","id":1,"color":"3AA3E3","actions":[{"id":"1" ,"name":"yes_button","text":"Yes","type":"button","value":"236","style":""}],"fallback":"Register"} ],"type":"message","subtype":"bot_message","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e" ,"trigger_id":"464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'text":"Register","id":1,"color":"3AA3E3","actions":[{"id":"1","name":"yes_button","text":"Yes" ,"type":"button","value":"236","style":""}],"fallback":"Register"}],"type":"message","subtype":"bot_message ","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab09'b'btext":"Register","id":1,"color":"3AA3E3","actions":[{"id":"1","name":"yes_button","text":"Yes" ,"type":"button","value":"236","style":""}],"fallback":"Register"}],"type":"message","subtype":"bot_message ","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab09'b'byes_button","text":"是","type":"button","value":"236","style":""}],"fallback":"Register"}],"type": "消息","子类型":"bot_message","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":" 464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'yes_button","text":"是","type":"button","value":"236","style":""}],"fallback":"Register"}],"type": "消息","子类型":"bot_message","ts":"1540413949.000100"},"response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":" 464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'response_url":"https://hooks.slack.com/actions/ffffff/ffffff/tXJjx1XInaUhrikj6oEzK08e","trigger_id":"464662548327.425084163429.dda35a299eedb940ab98dbb9386b56f0"}'

标签: pythonflaskslack-api

解决方案


您获得“乱码”数据的原因是您正在使用request.get_data(). 该方法将返回请求的原始数据,但不会为您进行任何解码。

更方便的是使用request.form.get('payload'),它会直接给你请求对象的 JSON 字符串。然后,您可以将其转换为 dict 对象,json.loads()以便在您的应用程序中进一步处理它。

请注意,您收到的格式是交互式消息的正确格式。您不会得到您所建议的查询字符串(例如“token=abc;user_id?def...”)(例如斜杠命令请求)。交互式消息请求将始终将请求作为 JSON 字符串包含在有效负载表单属性中。请参阅此处以供参考。

这是一个简单的工作示例,它将向按下按钮的用户回复问候。它将直接与 Slack 一起工作,但我建议使用Postman来测试它。

#app.py

from flask import Flask, request #import main Flask class and request object
import json

app = Flask(__name__) #create the Flask app

@app.route('/register', methods=['POST'])
def register_visit():
    slack_req = json.loads(request.form.get('payload'))
    response = '{"text": "Hi, <@' + slack_req["user"]["id"] + '>"}'
    return response, 200, {'content-type': 'application/json'}

if __name__ == '__main__':
    app.run(debug=True, port=5000) #run app in debug mode on port 5000

推荐阅读