加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
event.py 4.18 KB
一键复制 编辑 原始数据 按行查看 历史
#!/usr/bin/env python3.8
import abc
import hashlib
import json
import typing as t
from flask import request
from lark_oapi import AESCipher
from util.utils import dict_2_obj
class Event(object):
callback_handler = None
# event base
def __init__(self, dict_data, token, encrypt_key):
# event check and init
header = dict_data.get("header")
event = dict_data.get("event")
if header is None or event is None:
raise InvalidEventException("request is not callback event(v2)")
self.header = dict_2_obj(header)
self.event = dict_2_obj(event)
self._validate(token, encrypt_key)
def _validate(self, token, encrypt_key):
if self.header.token != token:
raise InvalidEventException("invalid token")
timestamp = request.headers.get("X-Lark-Request-Timestamp")
nonce = request.headers.get("X-Lark-Request-Nonce")
signature = request.headers.get("X-Lark-Signature")
body = request.data
bytes_b1 = (timestamp + nonce + encrypt_key).encode("utf-8")
bytes_b = bytes_b1 + body
h = hashlib.sha256(bytes_b)
if signature != h.hexdigest():
raise InvalidEventException("invalid signature in event")
@abc.abstractmethod
def event_type(self):
return self.header.event_type
class MemberDeletedReceiveEvent(Event):
@staticmethod
def event_type():
return "im.chat.member.user.deleted_v1"
class MemberAddedReceiveEvent(Event):
@staticmethod
def event_type():
return "im.chat.member.user.added_v1"
class MessageReceiveEvent(Event):
@staticmethod
def event_type():
return "im.message.receive_v1"
class UrlVerificationEvent(Event):
# special event: url verification event
def __init__(self, dict_data):
self.event = dict_2_obj(dict_data)
@staticmethod
def event_type():
return "url_verification"
class EventManager(object):
event_callback_map = dict()
event_type_map = dict()
_event_list = [MessageReceiveEvent, UrlVerificationEvent, MemberAddedReceiveEvent, MemberDeletedReceiveEvent]
def __init__(self):
for event in EventManager._event_list:
EventManager.event_type_map[event.event_type()] = event
def register(self, event_type: str) -> t.Callable:
def decorator(f: t.Callable) -> t.Callable:
self.register_handler_with_event_type(event_type=event_type, handler=f)
return f
return decorator
@staticmethod
def register_handler_with_event_type(event_type, handler):
EventManager.event_callback_map[event_type] = handler
@staticmethod
def get_handler_with_event(token, encrypt_key):
dict_data = json.loads(request.data)
dict_data = EventManager._decrypt_data(encrypt_key, dict_data)
callback_type = dict_data.get("type")
# only verification data has callback_type, else is event
if callback_type == "url_verification":
event = UrlVerificationEvent(dict_data)
return EventManager.event_callback_map.get(event.event_type()), event
# only handle event v2
schema = dict_data.get("schema")
if schema is None:
raise InvalidEventException("request is not callback event(v2)")
# get event_type
event_type = dict_data.get("header").get("event_type")
# build event
event = EventManager.event_type_map.get(event_type)(dict_data, token, encrypt_key)
# get handler
return EventManager.event_callback_map.get(event_type), event
@staticmethod
def _decrypt_data(encrypt_key, data):
encrypt_data = data.get("encrypt")
if encrypt_key == "" and encrypt_data is None:
# data haven't been encrypted
return data
if encrypt_key == "":
raise Exception("ENCRYPT_KEY is necessary")
cipher = AESCipher(encrypt_key)
return json.loads(cipher.decrypt_str(encrypt_data))
class InvalidEventException(Exception):
def __init__(self, error_info):
self.error_info = error_info
def __str__(self) -> str:
return "Invalid event: {}".format(self.error_info)
__repr__ = __str__
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化