webhook.py 4.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import os
  2. import sys
  3. sys.path.append(os.path.dirname(os.path.dirname(__file__)))
  4. import asyncio
  5. import json
  6. import hashlib
  7. import hmac
  8. from typing import Optional,Any
  9. from fastapi import FastAPI,APIRouter, File, HTTPException, Depends, Request,Header, UploadFile
  10. from fastapi.responses import JSONResponse
  11. from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
  12. from config import logger,CLIENT_KEY,CLIENT_SECRET
  13. from grpc_m.send_data_to_vector import langchain_chat
  14. from api.comment import item_comment_reply
  15. webhook_route = APIRouter()
  16. def verify_signature(body: bytes, signature: bytes, secret: bytes) -> bool:
  17. combined = secret + body # 直接拼接字节
  18. computed_signature = hashlib.sha1(combined).hexdigest() # 计算签名并转为十六进制字符串
  19. logger.debug(f"computed_signature {computed_signature}")
  20. return hmac.compare_digest(computed_signature.encode('utf-8'), signature)
  21. async def get_verified_data(request: Request) -> Any:
  22. data = await request.json()
  23. signature = request.headers.get('X-Douyin-Signature')
  24. if not signature:
  25. raise HTTPException(status_code=400, detail="Signature header is missing")
  26. if isinstance(signature, str):
  27. signature = signature.encode('utf-8')
  28. body_bytes = await request.body()
  29. logger.debug(f"body_bytes {type(body_bytes)} {body_bytes}")
  30. logger.debug(f"signature {type(signature)} {signature}")
  31. if not verify_signature(body_bytes, signature, CLIENT_SECRET.encode('utf-8')):
  32. raise HTTPException(status_code=403, detail="Invalid signature")
  33. return data
  34. @webhook_route.post("/webhook")
  35. async def webhook(verified_data: Any = Depends(get_verified_data)):
  36. event = verified_data.get("event")
  37. if event == "verify_webhook":
  38. challenge = verified_data.get("content").get("challenge")
  39. return JSONResponse(content={"challenge": challenge})
  40. await handle_event(verified_data)
  41. return JSONResponse(status_code=200, content={"message": "success"})
  42. # https://developer.open-douyin.com/docs/resource/zh-CN/dop/develop/openapi/interaction-management/comment-management-user/accept-comment-reply-event
  43. async def handle_event(data:dict):
  44. # 处理其他类型的消息
  45. event = data.get("event")
  46. if event == "item_comment_reply":
  47. asyncio.create_task(item_comment_reply(data))
  48. def main():
  49. body = b'{"event":"item_comment_reply","client_key":"aw6aipmfdtplwtyq","from_user_id":"_000QadFMhmU1jNCI3JdPnyVDL6XavC70dFy","to_user_id":"_000LiV_o0FGKMwaZgqGMCvcVSf-UAnNU_kk","content":"{\\"at_user_id\\":\\"\\",\\"avatar\\":\\"https://p3.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_66c4e34ae8834399bbf967c3d3c919db.jpeg?from=3782654143\\",\\"comment_id\\":\\"@9VxS1/qCUc80K2etd8wkUc791mbgP/iCPZZwrQmjLFUVafP660zdRmYqig357zEBbPDOginb1DAizzy/rp2MrA==\\",\\"comment_user_id\\":\\"_000QadFMhmU1jNCI3JdPnyVDL6XavC70dFy\\",\\"content\\":\\"\xe5\x95\x8a\xe5\x95\x8a\\",\\"create_time\\":1708802048,\\"digg_count\\":0,\\"level\\":1,\\"nick_name\\":\\"\xe7\xa8\x8b\xe5\xba\x8f\xe5\x91\x98\xe9\xa9\xac\xe5\xb7\xa5\\",\\"parent_id\\":\\"7332749358502333711\\",\\"reply_comment_total\\":0,\\"reply_to_comment_id\\":\\"0\\",\\"reply_to_item_id\\":\\"@9VxS1/qCUc80K2etd8wkUc791mbrOvyLOJB5rAqkLFIQafD960zdRmYqig357zEBrOff1pAnFpeMgJjQVMqang==\\"}","log_id":"021708802052557fdbdfdbdfdbdfdbd00000000000000ab9c49ee"}'
  50. body_json_dict ={'event': 'item_comment_reply', 'client_key': 'aw6aipmfdtplwtyq', 'from_user_id': '_000QadFMhmU1jNCI3JdPnyVDL6XavC70dFy', 'to_user_id': '_000LiV_o0FGKMwaZgqGMCvcVSf-UAnNU_kk', 'content': '{"at_user_id":"","avatar":"https://p3.douyinpic.com/aweme/720x720/aweme-avatar/tos-cn-i-0813_66c4e34ae8834399bbf967c3d3c919db.jpeg?from=3782654143","comment_id":"@9VxS1/qCUc80K2etd8wkUc791mbgOfqKOZZ2rwiiL1QTavn+60zdRmYqig357zEBuXn2PviubsArHSFhW7SksQ==","comment_user_id":"_000QadFMhmU1jNCI3JdPnyVDL6XavC70dFy","content":"123","create_time":1708849799,"digg_count":0,"level":1,"nick_name":"程序员马工","parent_id":"7332749358502333711","reply_comment_total":0,"reply_to_comment_id":"0","reply_to_item_id":"@9VxS1/qCUc80K2etd8wkUc791mbrOvyLOJB5rAqkLFIQafD960zdRmYqig357zEBrOff1pAnFpeMgJjQVMqang=="}', 'log_id': '021708849808460fdbdfdbdfdbdfdbd00000000000000ab5553a6'}
  51. body_json_str = json.dumps(body_json_dict)
  52. body_json_bytes = body_json_str.encode('utf-8')
  53. body_str = body.decode('utf-8').rstrip('\n')
  54. body_bytes = body_str.encode('utf-8')
  55. signature = b'b3513d6b1e5c80db0f82d88892e68826063f3b18'
  56. v = verify_signature(body_json_str, signature, CLIENT_SECRET.encode())
  57. logger.info(f"{v}")
  58. if __name__ == "__main__":
  59. main()