简体   繁体   English

Slack 交互式消息:POST 请求有效负载具有意外的格式

[英]Slack Interactive Messages: POST request payload has an unexpected format

I'm getting a POST request inside a Flask app from Slack.我在 Slack 的 Flask 应用程序中收到 POST 请求。 The request is sent when a user presses on an interactive message button.当用户按下交互式消息按钮时发送请求。 According to Slack docs I must extract the body of the request to verify the signature.根据 Slack文档,我必须提取请求的正文以验证签名。 My computed signature doesn't match the one sent by Slack, though.不过,我计算的签名与 Slack 发送的签名不匹配。 In fact, the body of the request comes as some encoded string.事实上,请求的主体是一些编码字符串。 The string is actually an encoded dictionary instead of a query str parameters, as expected.正如预期的那样,该字符串实际上是一个编码字典而不是查询 str 参数。

Here's the beginning of my view:这是我观点的开始:

@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):
        ...

I've tried to format the received data to make it look like:我试图格式化接收到的数据,使其看起来像:

token=fdjkgjl&user_id=1234... but I am not aware of all of the necessary parameters that have to be present in the data. token=fdjkgjl&user_id=1234...但我不知道数据中必须存在的所有必要参数。

Any ideas are highly appreciated.任何想法都受到高度赞赏。

The body of the message is following - after being URL decoded (note I've modified possibly sensitive data):消息的正文如下 - 在被 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","attachments":[{"callback_id":"visit_button","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.dda35a299eedb940ab98dbb9386b56f0"}' 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","attachments":[{"callback_id":"visit_button","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.dda35a299eedb9560ab98dbb936}"

The reason you are getting the "garbled" data is that you are using request.get_data() .您获得“乱码”数据的原因是您正在使用request.get_data() That method will return the raw data of a request, but not do any decoding for you.该方法将返回请求的原始数据,但不会为您进行任何解码。

Much more convenient is to use request.form.get('payload') , which will directly give you the JSON string of the request object.更方便的是使用request.form.get('payload') ,它将直接为您提供请求对象的 JSON 字符串。 You can then convert that into a dict object with json.loads() to process it further in your app.然后,您可以使用json.loads()将其转换为 dict 对象,以便在您的应用程序中进一步处理它。

Note that the format you received is the correct format for interactive messages.请注意,您收到的格式是交互式消息的正确格式。 You will not get a query string (eg "token=abc;user_id?def...") as you suggested (like for slash command requests).您不会得到您所建议的查询字符串(例如“token=abc;user_id?def...”)(例如斜杠命令请求)。 Interactive message request will always contain the request as JSON string in a payload form property.交互式消息请求将始终将请求作为 JSON 字符串包含在有效负载表单属性中。 See here for reference.请参阅此处以供参考。

Here is a simple working example, which will reply a greeting to the user that pressed the button.这是一个简单的工作示例,它将向按下按钮的用户回复问候。 It will work directly with Slack, but I recommend using Postman to test it.它将直接与 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

OK, the issue wasn't related to how Slack sends me the message.好的,这个问题与 Slack 如何向我发送消息无关。 It was about misunderstanding which data comes as bytes and which data is unicode.这是关于误解哪些数据以字节为单位,哪些数据是 unicode。 The culprit was string formatting in my case - the line concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8') should have been concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data)) .就我而言,罪魁祸首是字符串格式- concatenated = ("v0:%s:%s" % (timestamp, data)).encode('utf-8')应该是concatenated = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data)) Data is already bytes, timestamp meanwhile is unicode.数据已经是字节,时间戳同时是 unicode。 Cannot believe I've banged my head on this for hours -_-不敢相信我已经为此努力了好几个小时-_-

@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 = (b"v0:%b:%b" % (timestamp.encode("utf-8"), data))
    computed_signature = 'v0=' + hmac.new(signing_secret, msg=concatenated, 
    digestmod=hashlib.sha256).hexdigest()
    if hmac.compare_digest(computed_signature, signature):
        ...

This worked for me这对我有用

from urllib import parse

parsed_text = parse.unquote('your bytes text here')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM