def line(event, context): # イベントデータの表示 logger.info('headers:' + str(event['headers']['X-Line-Signature'])) logger.info('body:' + str(event['body'])) # リクエスト本体と、X-LINE-Signatureヘッダを取出す body = event['body'] signature = event['headers']['X-Line-Signature'] # Channel Secretを使って入力が正しいかを確認する secret = 'ここにシークレットを入力' parser = WebhookParser(secret) try: events = parser.parse(body,signature) except InvalidSignatureError: logger.error('InvalidSignatureError') return {"stautsCode" : 400,"body" : ""}; # LineBotAPIオブジェクトを作成する token = 'ここにトークンを入力' line_bot_api = LineBotApi(token) try: events = parser.parse(body,signature) except InvalidSignatureError: return {"stautsCode" : 400,"body" : ""}; # for event in events: logger.info('events:' + str(events)) # DBにメッセージを保存 user_id = events[0].source.user_id line_message = events[0].message.text put_response = table.put_item( Item={ 'timestamp': int(datetime.datetime.now().timestamp()), 'id': user_id, 'message': line_message } ) reply_token = events[0].reply_token success_message = TextSendMessage( text='メッセージを登録しました!\n\n' 'メッセージを修正する場合はもう一度メッセージを送ってください\n\n' 'メッセージにはあなたのお名前がわかるようにしてください' ) try: line_bot_api.reply_message(reply_token,success_message) except LineBotApiError as e: print(e.status_code) print(e.error.message) print(e.error.details) return {"stautsCode" : 200,"body" : "OK"};
def register_temperature(request): execution_id = request.headers.get('Function-Execution-Id') channel_secret = os.environ.get('LINE_CHANNEL_SECRET') channel_access_token = os.environ.get('LINE_CHANNEL_ACCESS_TOKEN') bot_api = LineBotApi(channel_access_token) parser = WebhookParser(channel_secret) body = request.get_data(as_text=True) hash = hmac.new(channel_secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha256).digest() signature = base64.b64encode(hash).decode() if signature != request.headers['X_LINE_SIGNATURE']: return abort(405) try: events = parser.parse(body, signature) except InvalidSignatureError: return abort(405) for event in events: if not isinstance(event, MessageEvent): continue if not isinstance(event.message, TextMessage): continue handle_event(event, channel_access_token, bot_api, execution_id) return jsonify({'message': 'ok'})
def callback(): if channel_secret is None: print('Specify LINE_CHANNEL_SECRET as environment variable.') sys.exit(1) if channel_access_token is None: print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.') sys.exit(1) line_bot_api = LineBotApi(channel_access_token) parser = WebhookParser(channel_secret) signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) # app.logger.info("Request body: " + body) print("Request body: %s" % body) # parse webhook body try: events = parser.parse(body, signature) for event in events: if isinstance(event, MessageEvent): handle_message(event) except InvalidSignatureError: abort(400) return 'OK~~'
def main(request): body = request.get_data(as_text=True) line_receiver_id = request.headers.get('LINE_RECEIVER_ID') if line_receiver_id and request.headers.get('GOOD_MORNING_AUTH') == GOOD_MORNING_AUTH: push_good_morning_image(line_receiver_id) else: hash = hmac.new(LINE_CHANNEL_SECRET.encode('utf-8'), body.encode('utf-8'), hashlib.sha256).digest() signature = base64.b64encode(hash).decode() parser = WebhookParser(LINE_CHANNEL_SECRET) if signature != request.headers['X_LINE_SIGNATURE']: return abort(405) try: events = parser.parse(body, signature) except InvalidSignatureError: return abort(405) for event in events: if not isinstance(event, MessageEvent): continue if isinstance(event.message, ImageMessage) and (event.source.type == 'room' and event.source.user_id == LINE_TARGET_ID): image_message_id = event.message.id image_content = line_bot_api.get_message_content(image_message_id).content upload_image(image_content, GOOD_MORNING_ALBUM_ID) return jsonify({ 'message': 'ok'})
def callback(): channel_secret = os.getenv('LINE_CHANNEL_SECRET', None) channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None) if not channel_secret: print('Specify LINE_CHANNEL_SECRET as environment variable.') abort(500) if not channel_access_token: print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.') abort(500) line_bot_api = LineBotApi(channel_access_token) parser = WebhookParser(channel_secret) signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) app.logger.info("Request body: " + body) # parse webhook body try: events = parser.parse(body, signature) # if event is MessageEvent and message is TextMessage, then echo text for event in events: if not isinstance(event, MessageEvent): continue if not isinstance(event.message, TextMessage): continue line_bot_api.reply_message(event.reply_token, get_message(event.message.text)) except InvalidSignatureError: abort(400) return 'OK'
def linebot(request): signature = request.headers['X-Line-Signature'] # get channel_secret and channel_access_token from your environment variable channel_secret = os.getenv('LINE_CHANNEL_SECRET', None) channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None) if channel_secret is None: print('Specify LINE_CHANNEL_SECRET as environment variable.') sys.exit(1) if channel_access_token is None: print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.') sys.exit(1) line_bot_api = LineBotApi(channel_access_token) parser = WebhookParser(channel_secret) # get request body as text body = request.get_data(as_text=True) events = parser.parse(body, signature) # if event is MessageEvent and message is TextMessage, then echo text for event in events: if not isinstance(event, MessageEvent): continue if not isinstance(event.message, TextMessage): continue line_bot_api.reply_message(event.reply_token, set_reply_message(event.message.text))
def callback(self, signature, body): parser = WebhookParser(self.CHANNEL_SECRET) try: events = parser.parse(body, signature) self.__handle_message(events[0]) except InvalidSignatureError as e: print(e)
class PollService: def __init__(self, secret: str): self.op_interrupts = {} self.parser = WebhookParser(secret) def handle_operation(self, body: str, signature: str): """Operation Handler LINEから飛んできたWebhookをParseし、Wrapper向けに書き直す :param body: string 飛んできたWebHookの内容 :param signature: string シグネチャー :return: None """ ops = self.parser.parse(body, signature) for op in ops: if op.type not in self.op_interrupts: continue self.set_reply_token(op) self.execute_func(op) def set_reply_token(self, op: Event): """ set reply token for talk Service :param op: Event :return: None """ self.reply_token = getattr(op, "reply_token") def execute_func(self, op: Event): """func executer :param op_type: parsed Event from Webhook :return: None """ try: self.op_interrupts[op.type](self, op) except Exception: print_exc() def add_op_interrupt(self, op_type: OpType, func: Callable): """ Add Event to handler :param op_type: Event which you want to handle :param func: Function which you want to call :return: None """ if op_type in self.op_interrupts: warnings.warn(f"{op_type} is already added to interrupts", stacklevel=2) self.op_interrupts[op_type] = func def add_op_interrupts(self, dicts: Dict[OpType, Callable]): """Add Events to handler :param dicts: Dict[Event, func] :return: None """ for event, fnc in dicts.items(): self.add_op_interrupt(event, fnc)
def main(request): """This is a LINE Bot function Args: request (flask.Request): [description] Returns: Response|HTTPException: [description] """ # LINEアプリ設定(環境変数) channel_secret = os.environ.get('LINE_CHANNEL_SECRET') channel_access_token = os.environ.get('LINE_CHANNEL_ACCESS_TOKEN') # LINE Bot(Messaging) API line_bot_api = LineBotApi(channel_access_token) parser = WebhookParser(channel_secret) # WebhookリクエストからBODYを取得 body = request.get_data(as_text=True) # チャネルの秘密鍵からシグネチャを取得 hashcode = hmac.new(channel_secret.encode('utf-8'), body.encode('utf-8'), hashlib.sha256).digest() signature = base64.b64encode(hashcode).decode() # シグネチャの合致判定 if signature != request.headers['X_LINE_SIGNATURE']: return abort(405) # リクエストボディからWebhookイベントオブジェクト(Payload)を取得 try: events = parser.parse(body, signature) except InvalidSignatureError: return abort(405) # Webhookイベントオブジェクトを処理 for event in events: if not isinstance(event, MessageEvent): # メッセージイベント以外は未処理 continue if not isinstance(event.message, TextMessage): # テキスト以外は未処理 continue if event.message.text == u'おわり': # エコーバックしないワードの処理 # 「おわり」というメッセで「ほんとに?」を返す line_bot_api.reply_message(event.reply_token, TextSendMessage(text=u"ほんとに?")) else: # 上記以外のメッセはそのままオウム返し line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) return ("", 200)
class Linebot: bot = None webhookParser = None def __init__(self, channel_access_token, channel_secret): self.bot = LineBotApi(channel_access_token) self.webhookParser = WebhookParser(channel_secret) def parse(self, body, signature): #把Line訊息轉換成要的東西 resultLIST = [] try: events = self.webhookParser.parse(body, signature) for event in events: if event.source.user_id == "Udeadbeefdeadbeefdeadbeefdeadbeef": continue if event.type == "message": try: resultDICT = { "status": True, "type": event.type, "userID": event.source.user_id, "replyToken": event.reply_token, "message": event.message.text, "timestamp": event.timestamp } except Exception as e: print("Linebot parseError => {}".format(str(e))) resultDICT = {"status": False} resultLIST.append(resultDICT) except InvalidSignatureError as e: print("Linebot InvalidSignatureError => {}".format(str(e))) except LineBotApiError as e: print("Linebot LineBotApiError => {}".format(str(e))) return resultLIST def respTexts(self, replyToken, textLIST): messageLIST = [TextSendMessage(text=text) for text in textLIST] self.bot.reply_message(replyToken, messageLIST, notification_disabled=True) def respText(self, replyToken, text): self.bot.reply_message(replyToken, TextSendMessage(text=text), notification_disabled=True)
async def main(req: func.HttpRequest) -> func.HttpResponse: # create api client line_api = AioLineBotApi( channel_access_token="<YOUR CHANNEL ACCESS TOKEN>") # get events from request parser = WebhookParser(channel_secret="<YOUR CHANNEL SECRET>") events = parser.parse(req.get_body().decode("utf-8"), req.headers.get("X-Line-Signature", "")) for ev in events: # reply echo await line_api.reply_message( ev.reply_token, TextMessage(text=f"You said: {ev.message.text}")) # 200 response return func.HttpResponse("ok")
def main(request): line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN) parser = WebhookParser(LINE_CHANNEL_SECRET) body = request.get_data(as_text=True) hash = hmac.new(LINE_CHANNEL_SECRET.encode('utf-8'), body.encode('utf-8'), hashlib.sha256).digest() signature = base64.b64encode(hash).decode() if signature != request.headers['X_LINE_SIGNATURE']: return abort(405) try: events = parser.parse(body, signature) except InvalidSignatureError: return abort(405) for event in events: if not isinstance(event, MessageEvent): continue if not isinstance(event.message, TextMessage): continue text = event.message.text if '新增圖片\n' in text: tmp, desc, image_url = text.split('\n') response = upload_image_by_url(image_url, desc, MAIN_ALBUM_ID) message = ('格式錯誤 新增失敗', '新增成功')[response.status_code == 200] reply_instance = TextSendMessage(text=message) elif text == '關鍵字查詢': images = get_images(MAIN_ALBUM_ID) desc_list = [image['description'] for image in images] #uniqueness uniq_desc_list = list(set(desc_list)) reply_instance = TextSendMessage(text=','.join(uniq_desc_list)) else: #從一般相簿中過濾 or 從美女相簿中隨機 images = ([ image for image in get_images(MAIN_ALBUM_ID) if image['description'] == text ], get_images(SUB_ALBUM_ID))[text == '抽'] image_url = random.choice(images)['link'] reply_instance = ImageSendMessage(original_content_url=image_url, preview_image_url=image_url) line_bot_api.reply_message(event.reply_token, reply_instance) return jsonify({'message': 'ok'})
def home(request): line_bot_api = LineBotApi(LINE_CAT) webhook_parser = WebhookParser(LINE_CS) signature = "" if request.META["HTTP_X_LINE_SIGNATURE"]: signature = request.META["HTTP_X_LINE_SIGNATURE"] body = request.body try: events = webhook_parser.parse(body, signature) for event in events: print event.reply_token line_bot_api.reply_message( event.reply_token, TextSendMessage(text=event.message.text)) except InvalidSignatureError: return HttpResponse("Signature invalid") return HttpResponse("")
async def main(req: func.HttpRequest) -> func.HttpResponse: # APIインターフェイスの初期化 # api = LineBotApi(channel_access_token="<YOUR CHANNEL ACCESS TOKEN>") # <-- 同期APIを利用した場合 api = AioLineBotApi(channel_access_token="<YOUR CHANNEL ACCESS TOKEN>") # リクエストからイベントを取得 parser = WebhookParser(channel_secret="<YOUR CHANNEL SECRET>") events = parser.parse(req.get_body().decode("utf-8"), req.headers.get("X-Line-Signature", "")) for ev in events: # おうむ返し # api.reply_message(ev.reply_token, TextMessage(text=f"You said: {ev.message.text}")) # <-- 同期APIを利用した場合 await api.reply_message( ev.reply_token, TextMessage(text=f"You said: {ev.message.text}")) # HTTPのレスポンス return func.HttpResponse("ok")
class LineGateway(Gateway): def __init__(self, channel_access_token:str, channel_secret:str, host="localhost", port="5000", webhook_suffix="line", dispatcher=RequestDispatcher.load(), message_parser=LineParser): super().__init__(dispatcher=dispatcher, message_parser=message_parser) self.channel_access_token = channel_access_token self.channel_secret = channel_secret self.port = port self.webhook_suffix = webhook_suffix self.host = host self.line_bot_api = LineBotApi(self.channel_access_token) self.webhook_handler = WebhookHandler(self.channel_secret) self.parser = WebhookParser(self.channel_secret) bottle.default_app().route(path='/', callback=self.landing) bottle.default_app().route('/{}'.format(webhook_suffix), callback=self.webhook_callback, method="POST") def landing(self): return "Running" def webhook_callback(self): signature = bottle.request.headers['X-Line-Signature'] body = bottle.request.body.getvalue().decode('utf-8') print('%s' % signature) try: events = self.parser.parse(body, signature) for event in events: message = self.message_parser.parse(event) print(message) if message is not None: self.dispatcher.dispatch(message) except InvalidSignatureError as e: print(e) bottle.abort(400) return 'OK' def run(self): print("Started line webhook on http://{}:{}/{}".format( self.host, self.port, self.webhook_suffix)) bottle.run(host=self.host, port=self.port, debug=True)
def test_parse(self): file_dir = os.path.dirname(__file__) webhook_sample_json_path = os.path.join(file_dir, 'text', 'webhook.json') with open(webhook_sample_json_path) as fp: body = fp.read() parser = WebhookParser('channel_secret') # mock parser.signature_validator.validate = lambda a, b: True events = parser.parse(body, 'channel_secret') # MessageEvent, SourceUser, TextMessage self.assertIsInstance(events[0], MessageEvent) self.assertEqual(events[0].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[0].type, 'message') self.assertEqual(events[0].timestamp, 1462629479859) self.assertIsInstance(events[0].source, SourceUser) self.assertEqual(events[0].source.type, 'user') self.assertEqual(events[0].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[0].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[0].message, TextMessage) self.assertEqual(events[0].message.id, '325708') self.assertEqual(events[0].message.type, 'text') self.assertEqual(events[0].message.text, 'Hello, world') # MessageEvent, SourceRoom, TextMessage self.assertIsInstance(events[1], MessageEvent) self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[1].type, 'message') self.assertEqual(events[1].timestamp, 1462629479859) self.assertIsInstance(events[1].source, SourceRoom) self.assertEqual(events[1].source.type, 'room') self.assertEqual(events[1].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[1].source.user_id, None) self.assertEqual(events[1].source.sender_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertIsInstance(events[1].message, ImageMessage) self.assertEqual(events[1].message.id, '325708') self.assertEqual(events[1].message.type, 'image') # MessageEvent, SourceUser, VideoMessage self.assertIsInstance(events[2], MessageEvent) self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[2].type, 'message') self.assertEqual(events[2].timestamp, 1462629479859) self.assertIsInstance(events[2].source, SourceUser) self.assertEqual(events[2].source.type, 'user') self.assertEqual(events[2].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[2].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[2].message, VideoMessage) self.assertEqual(events[2].message.id, '325708') self.assertEqual(events[2].message.type, 'video') # MessageEvent, SourceUser, AudioMessage self.assertIsInstance(events[3], MessageEvent) self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[3].type, 'message') self.assertEqual(events[3].timestamp, 1462629479859) self.assertIsInstance(events[3].source, SourceUser) self.assertEqual(events[3].source.type, 'user') self.assertEqual(events[3].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[3].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[3].message, AudioMessage) self.assertEqual(events[3].message.id, '325708') self.assertEqual(events[3].message.type, 'audio') # MessageEvent, SourceUser, LocationMessage self.assertIsInstance(events[4], MessageEvent) self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[4].type, 'message') self.assertEqual(events[4].timestamp, 1462629479859) self.assertIsInstance(events[4].source, SourceUser) self.assertEqual(events[4].source.type, 'user') self.assertEqual(events[4].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[4].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[4].message, LocationMessage) self.assertEqual(events[4].message.id, '325708') self.assertEqual(events[4].message.type, 'location') self.assertEqual(events[4].message.title, 'my location') self.assertEqual(events[4].message.address, 'Tokyo') self.assertEqual(events[4].message.latitude, 35.65910807942215) self.assertEqual(events[4].message.longitude, 139.70372892916203) # MessageEvent, SourceUser, StickerMessage self.assertIsInstance(events[5], MessageEvent) self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[5].type, 'message') self.assertEqual(events[5].timestamp, 1462629479859) self.assertIsInstance(events[5].source, SourceUser) self.assertEqual(events[5].source.type, 'user') self.assertEqual(events[5].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[5].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[5].message, StickerMessage) self.assertEqual(events[5].message.id, '325708') self.assertEqual(events[5].message.type, 'sticker') self.assertEqual(events[5].message.package_id, '1') self.assertEqual(events[5].message.sticker_id, '1') # FollowEvent, SourceUser self.assertIsInstance(events[6], FollowEvent) self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[6].type, 'follow') self.assertEqual(events[6].timestamp, 1462629479859) self.assertIsInstance(events[6].source, SourceUser) self.assertEqual(events[6].source.type, 'user') self.assertEqual(events[6].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[6].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # UnfollowEvent, SourceUser self.assertIsInstance(events[7], UnfollowEvent) self.assertEqual(events[7].type, 'unfollow') self.assertEqual(events[7].timestamp, 1462629479859) self.assertIsInstance(events[7].source, SourceUser) self.assertEqual(events[7].source.type, 'user') self.assertEqual(events[7].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[7].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # JoinEvent, SourceGroup self.assertIsInstance(events[8], JoinEvent) self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[8].type, 'join') self.assertEqual(events[8].timestamp, 1462629479859) self.assertIsInstance(events[8].source, SourceGroup) self.assertEqual(events[8].source.type, 'group') self.assertEqual(events[8].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[8].source.user_id, None) self.assertEqual(events[8].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') # LeaveEvent, SourceGroup self.assertIsInstance(events[9], LeaveEvent) self.assertEqual(events[9].type, 'leave') self.assertEqual(events[9].timestamp, 1462629479859) self.assertIsInstance(events[9].source, SourceGroup) self.assertEqual(events[9].source.type, 'group') self.assertEqual(events[9].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[9].source.user_id, None) self.assertEqual(events[9].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') # PostbackEvent, SourceUser self.assertIsInstance(events[10], PostbackEvent) self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[10].type, 'postback') self.assertEqual(events[10].timestamp, 1462629479859) self.assertIsInstance(events[10].source, SourceUser) self.assertEqual(events[10].source.type, 'user') self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[10].postback.params, None) # BeaconEvent, SourceUser self.assertIsInstance(events[11], BeaconEvent) self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[11].type, 'beacon') self.assertEqual(events[11].timestamp, 1462629479859) self.assertIsInstance(events[11].source, SourceUser) self.assertEqual(events[11].source.type, 'user') self.assertEqual(events[11].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[11].beacon.type, 'enter') self.assertEqual(events[11].beacon.dm, None) self.assertEqual(events[11].beacon.device_message, None) # BeaconEvent, SourceUser (with device message) self.assertIsInstance(events[12], BeaconEvent) self.assertEqual(events[12].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[12].type, 'beacon') self.assertEqual(events[12].timestamp, 1462629479859) self.assertIsInstance(events[12].source, SourceUser) self.assertEqual(events[12].source.type, 'user') self.assertEqual(events[12].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[12].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[12].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[12].beacon.type, 'enter') self.assertEqual(events[12].beacon.dm, '1234567890abcdef') self.assertEqual(events[12].beacon.device_message, bytearray(b'\x124Vx\x90\xab\xcd\xef')) # MessageEvent, SourceGroup with userId, TextMessage self.assertIsInstance(events[13], MessageEvent) self.assertEqual(events[13].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[13].type, 'message') self.assertEqual(events[13].timestamp, 1462629479859) self.assertIsInstance(events[13].source, SourceGroup) self.assertEqual(events[13].source.type, 'group') self.assertEqual(events[13].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[13].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[13].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertIsInstance(events[13].message, TextMessage) self.assertEqual(events[13].message.id, '325708') self.assertEqual(events[13].message.type, 'text') self.assertEqual(events[13].message.text, 'Hello, world') # MessageEvent, SourceRoom with userId, TextMessage self.assertIsInstance(events[14], MessageEvent) self.assertEqual(events[14].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[14].type, 'message') self.assertEqual(events[14].timestamp, 1462629479859) self.assertIsInstance(events[14].source, SourceRoom) self.assertEqual(events[14].source.type, 'room') self.assertEqual(events[14].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[14].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[14].source.sender_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertIsInstance(events[14].message, TextMessage) self.assertEqual(events[14].message.id, '325708') self.assertEqual(events[14].message.type, 'text') self.assertEqual(events[14].message.text, 'Hello, world') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[15], PostbackEvent) self.assertEqual(events[15].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[15].type, 'postback') self.assertEqual(events[15].timestamp, 1462629479859) self.assertIsInstance(events[15].source, SourceUser) self.assertEqual(events[15].source.type, 'user') self.assertEqual(events[15].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[15].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[15].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[15].postback.params['date'], '2013-04-01') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[16], PostbackEvent) self.assertEqual(events[16].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[16].type, 'postback') self.assertEqual(events[16].timestamp, 1462629479859) self.assertIsInstance(events[16].source, SourceUser) self.assertEqual(events[16].source.type, 'user') self.assertEqual(events[16].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[16].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[16].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[16].postback.params['time'], '10:00') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[17], PostbackEvent) self.assertEqual(events[17].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[17].type, 'postback') self.assertEqual(events[17].timestamp, 1462629479859) self.assertIsInstance(events[17].source, SourceUser) self.assertEqual(events[17].source.type, 'user') self.assertEqual(events[17].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[17].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[17].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[17].postback.params['datetime'], '2013-04-01T10:00')
class Bot(object): COMMAND_PREFIX = '@bot' def __init__(self): secret, access_token = self.check_required_environmental_variables() self.commands = [PingCommand(), JinguReservationStateThisWeek()] self.line_bot_api = LineBotApi(access_token) self.parser = WebhookParser(secret) def check_required_environmental_variables(self): channel_secret = os.getenv('LINEBOT_TENNIS_LINE_CHANNEL_SECRET', None) channel_access_token = os.getenv( 'LINEBOT_TENNIS_LINE_CHANNEL_ACCESS_TOKEN', None) if channel_secret is None: raise Exception( 'Specify LINEBOT_TENNIS_LINE_CHANNEL_SECRET as environment variable.' ) if channel_access_token is None: raise Exception( 'Specify LINEBOT_TENNIS_LINE_CHANNEL_ACCESS_TOKEN as environment variable.' ) return (channel_secret, channel_access_token) def handle_request(self, request): if request.method != 'POST': return Response('404', status=404) signature = request.headers.get('X_LINE_SIGNATURE') wsgi_input = request.headers.get('wsgi.input') content_length = int(request.headers.get('CONTENT_LENGTH')) #body = wsgi_input.read(content_length).decode('utf-8') body = request.stream.read().decode('utf-8') try: events = self.parser.parse(body, signature) except InvalidSignatureError: return Response('Bad request', status=400) for event in events: if self.is_event_for_connection_test(event): print('Ignore the message because it is connection test') elif event.type == 'message' and event.message.type == 'text': self.handle_message(event.message.text, event.reply_token) return Response('OK', status=200) def send_help_string(self, reply_token): help_string = 'Available commands:\n' + '\n'.join( [c.help() for c in self.commands]) self.line_bot_api.reply_message(reply_token, TextSendMessage(text=help_string)) def is_event_for_connection_test(self, event): return (event.type == 'message' and (event.message.id == '100001' or event.message.id == '100002')) def handle_message(self, message_text, reply_token): if message_text.startswith(self.COMMAND_PREFIX): message_body = message_text[len(self.COMMAND_PREFIX):].strip() for command in self.commands: if command.is_match(message_body): command.reply(message_body, reply_token, self.line_bot_api) return self.send_help_string(reply_token)
def test_parse(self): file_dir = os.path.dirname(__file__) webhook_sample_json_path = os.path.join(file_dir, 'text', 'webhook.json') with open(webhook_sample_json_path) as fp: body = fp.read() parser = WebhookParser('channel_secret') # mock parser.signature_validator.validate = lambda a, b: True events = parser.parse(body, 'channel_secret') # MessageEvent, SourceUser, TextMessage self.assertIsInstance(events[0], MessageEvent) self.assertEqual(events[0].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[0].type, 'message') self.assertEqual(events[0].timestamp, 1462629479859) self.assertIsInstance(events[0].source, SourceUser) self.assertEqual(events[0].source.type, 'user') self.assertEqual(events[0].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[0].message, TextMessage) self.assertEqual(events[0].message.id, '325708') self.assertEqual(events[0].message.type, 'text') self.assertEqual(events[0].message.text, 'Hello, world') # MessageEvent, SourceRoom, TextMessage self.assertIsInstance(events[1], MessageEvent) self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[1].type, 'message') self.assertEqual(events[1].timestamp, 1462629479859) self.assertIsInstance(events[1].source, SourceRoom) self.assertEqual(events[1].source.type, 'room') self.assertEqual(events[1].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[1].source.user_id, None) self.assertIsInstance(events[1].message, ImageMessage) self.assertEqual(events[1].message.id, '325708') self.assertEqual(events[1].message.type, 'image') self.assertEqual(events[1].message.content_provider.type, 'external') self.assertEqual(events[1].message.content_provider.original_content_url, "https://example.com") self.assertEqual(events[1].message.content_provider.preview_image_url, "https://example.com") # MessageEvent, SourceUser, VideoMessage self.assertIsInstance(events[2], MessageEvent) self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[2].type, 'message') self.assertEqual(events[2].timestamp, 1462629479859) self.assertIsInstance(events[2].source, SourceUser) self.assertEqual(events[2].source.type, 'user') self.assertEqual(events[2].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[2].message, VideoMessage) self.assertEqual(events[2].message.id, '325708') self.assertEqual(events[2].message.type, 'video') self.assertEqual(events[2].message.duration, 60000) self.assertEqual(events[2].message.content_provider.type, 'external') self.assertEqual(events[2].message.content_provider.original_content_url, "https://example.com") self.assertEqual(events[2].message.content_provider.preview_image_url, "https://example.com") # MessageEvent, SourceUser, AudioMessage self.assertIsInstance(events[3], MessageEvent) self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[3].type, 'message') self.assertEqual(events[3].timestamp, 1462629479859) self.assertIsInstance(events[3].source, SourceUser) self.assertEqual(events[3].source.type, 'user') self.assertEqual(events[3].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[3].message, AudioMessage) self.assertEqual(events[3].message.id, '325708') self.assertEqual(events[3].message.type, 'audio') self.assertEqual(events[3].message.duration, 60000) self.assertEqual(events[3].message.content_provider.type, 'external') self.assertEqual(events[3].message.content_provider.original_content_url, "https://example.com") # MessageEvent, SourceUser, LocationMessage self.assertIsInstance(events[4], MessageEvent) self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[4].type, 'message') self.assertEqual(events[4].timestamp, 1462629479859) self.assertIsInstance(events[4].source, SourceUser) self.assertEqual(events[4].source.type, 'user') self.assertEqual(events[4].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[4].message, LocationMessage) self.assertEqual(events[4].message.id, '325708') self.assertEqual(events[4].message.type, 'location') self.assertEqual(events[4].message.title, 'my location') self.assertEqual(events[4].message.address, 'Tokyo') self.assertEqual(events[4].message.latitude, 35.65910807942215) self.assertEqual(events[4].message.longitude, 139.70372892916203) # MessageEvent, SourceUser, StickerMessage self.assertIsInstance(events[5], MessageEvent) self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[5].type, 'message') self.assertEqual(events[5].timestamp, 1462629479859) self.assertIsInstance(events[5].source, SourceUser) self.assertEqual(events[5].source.type, 'user') self.assertEqual(events[5].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[5].message, StickerMessage) self.assertEqual(events[5].message.id, '325708') self.assertEqual(events[5].message.type, 'sticker') self.assertEqual(events[5].message.package_id, '1') self.assertEqual(events[5].message.sticker_id, '1') # FollowEvent, SourceUser self.assertIsInstance(events[6], FollowEvent) self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[6].type, 'follow') self.assertEqual(events[6].timestamp, 1462629479859) self.assertIsInstance(events[6].source, SourceUser) self.assertEqual(events[6].source.type, 'user') self.assertEqual(events[6].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # UnfollowEvent, SourceUser self.assertIsInstance(events[7], UnfollowEvent) self.assertEqual(events[7].type, 'unfollow') self.assertEqual(events[7].timestamp, 1462629479859) self.assertIsInstance(events[7].source, SourceUser) self.assertEqual(events[7].source.type, 'user') self.assertEqual(events[7].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # JoinEvent, SourceGroup self.assertIsInstance(events[8], JoinEvent) self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[8].type, 'join') self.assertEqual(events[8].timestamp, 1462629479859) self.assertIsInstance(events[8].source, SourceGroup) self.assertEqual(events[8].source.type, 'group') self.assertEqual(events[8].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[8].source.user_id, None) # LeaveEvent, SourceGroup self.assertIsInstance(events[9], LeaveEvent) self.assertEqual(events[9].type, 'leave') self.assertEqual(events[9].timestamp, 1462629479859) self.assertIsInstance(events[9].source, SourceGroup) self.assertEqual(events[9].source.type, 'group') self.assertEqual(events[9].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[9].source.user_id, None) # PostbackEvent, SourceUser self.assertIsInstance(events[10], PostbackEvent) self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[10].type, 'postback') self.assertEqual(events[10].timestamp, 1462629479859) self.assertIsInstance(events[10].source, SourceUser) self.assertEqual(events[10].source.type, 'user') self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[10].postback.params, None) # BeaconEvent, SourceUser self.assertIsInstance(events[11], BeaconEvent) self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[11].type, 'beacon') self.assertEqual(events[11].timestamp, 1462629479859) self.assertIsInstance(events[11].source, SourceUser) self.assertEqual(events[11].source.type, 'user') self.assertEqual(events[11].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[11].beacon.type, 'enter') self.assertEqual(events[11].beacon.dm, None) self.assertEqual(events[11].beacon.device_message, None) # BeaconEvent, SourceUser (with device message) self.assertIsInstance(events[12], BeaconEvent) self.assertEqual(events[12].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[12].type, 'beacon') self.assertEqual(events[12].timestamp, 1462629479859) self.assertIsInstance(events[12].source, SourceUser) self.assertEqual(events[12].source.type, 'user') self.assertEqual(events[12].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[12].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[12].beacon.type, 'enter') self.assertEqual(events[12].beacon.dm, '1234567890abcdef') self.assertEqual(events[12].beacon.device_message, bytearray(b'\x124Vx\x90\xab\xcd\xef')) # AccountEvent, SourceUser self.assertIsInstance(events[13], AccountLinkEvent) self.assertEqual(events[13].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[13].type, 'accountLink') self.assertEqual(events[13].timestamp, 1462629479859) self.assertIsInstance(events[13].source, SourceUser) self.assertEqual(events[13].source.type, 'user') self.assertEqual(events[13].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[13].link.result, 'ok') self.assertEqual(events[13].link.nonce, 'Vb771wDYtXuammLszK6h9A') # MessageEvent, SourceGroup with userId, TextMessage self.assertIsInstance(events[14], MessageEvent) self.assertEqual(events[14].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[14].type, 'message') self.assertEqual(events[14].timestamp, 1462629479859) self.assertIsInstance(events[14].source, SourceGroup) self.assertEqual(events[14].source.type, 'group') self.assertEqual(events[14].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[14].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[14].message, TextMessage) self.assertEqual(events[14].message.id, '325708') self.assertEqual(events[14].message.type, 'text') self.assertEqual(events[14].message.text, 'Hello, world') # MessageEvent, SourceRoom with userId, TextMessage self.assertIsInstance(events[15], MessageEvent) self.assertEqual(events[15].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[15].type, 'message') self.assertEqual(events[15].timestamp, 1462629479859) self.assertIsInstance(events[15].source, SourceRoom) self.assertEqual(events[15].source.type, 'room') self.assertEqual(events[15].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[15].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[15].message, TextMessage) self.assertEqual(events[15].message.id, '325708') self.assertEqual(events[15].message.type, 'text') self.assertEqual(events[15].message.text, 'Hello, world') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[16], PostbackEvent) self.assertEqual(events[16].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[16].type, 'postback') self.assertEqual(events[16].timestamp, 1462629479859) self.assertIsInstance(events[16].source, SourceUser) self.assertEqual(events[16].source.type, 'user') self.assertEqual(events[16].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[16].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[16].postback.params['date'], '2013-04-01') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[17], PostbackEvent) self.assertEqual(events[17].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[17].type, 'postback') self.assertEqual(events[17].timestamp, 1462629479859) self.assertIsInstance(events[17].source, SourceUser) self.assertEqual(events[17].source.type, 'user') self.assertEqual(events[17].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[17].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[17].postback.params['time'], '10:00') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[18], PostbackEvent) self.assertEqual(events[18].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[18].type, 'postback') self.assertEqual(events[18].timestamp, 1462629479859) self.assertIsInstance(events[18].source, SourceUser) self.assertEqual(events[18].source.type, 'user') self.assertEqual(events[18].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[18].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[18].postback.params['datetime'], '2013-04-01T10:00') # ThingsEvent, SourceUser, link self.assertIsInstance(events[19], ThingsEvent) self.assertEqual(events[19].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[19].type, 'things') self.assertEqual(events[19].timestamp, 1462629479859) self.assertIsInstance(events[19].source, SourceUser) self.assertEqual(events[19].source.type, 'user') self.assertEqual(events[19].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[19].things, DeviceLink) self.assertEqual(events[19].things.type, 'link') self.assertEqual(events[19].things.device_id, 't2c449c9d1') # MemberJoinedEvent self.assertIsInstance(events[20], MemberJoinedEvent) self.assertEqual(events[20].reply_token, '0f3779fba3b349968c5d07db31eabf65') self.assertEqual(events[20].type, 'memberJoined') self.assertEqual(events[20].timestamp, 1462629479859) self.assertIsInstance(events[20].source, SourceGroup) self.assertEqual(events[20].source.type, 'group') self.assertEqual(events[20].source.group_id, 'C4af4980629...') self.assertEqual(len(events[20].joined.members), 2) self.assertIsInstance(events[20].joined.members[0], SourceUser) self.assertEqual(events[20].joined.members[0].user_id, 'U4af4980629...') self.assertEqual(events[20].joined.members[1].user_id, 'U91eeaf62d9...') # MemberLeftEvent self.assertIsInstance(events[21], MemberLeftEvent) self.assertEqual(events[21].type, 'memberLeft') self.assertEqual(events[21].timestamp, 1462629479960) self.assertIsInstance(events[21].source, SourceGroup) self.assertEqual(events[21].source.type, 'group') self.assertEqual(events[21].source.group_id, 'C4af4980629...') self.assertEqual(len(events[21].left.members), 2) self.assertIsInstance(events[21].left.members[0], SourceUser) self.assertEqual(events[21].left.members[0].user_id, 'U4af4980629...') self.assertEqual(events[21].left.members[1].user_id, 'U91eeaf62d9...') # ThingsEvent, SourceUser, unlink self.assertIsInstance(events[22], ThingsEvent) self.assertEqual(events[22].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[22].type, 'things') self.assertEqual(events[22].timestamp, 1462629479859) self.assertIsInstance(events[22].source, SourceUser) self.assertEqual(events[22].source.type, 'user') self.assertEqual(events[22].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[22].things, DeviceUnlink) self.assertEqual(events[22].things.type, 'unlink') self.assertEqual(events[22].things.device_id, 't2c449c9d1') # MessageEvent, SourceUser, FileMessage self.assertIsInstance(events[23], MessageEvent) self.assertEqual(events[23].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[23].type, 'message') self.assertEqual(events[23].timestamp, 1462629479859) self.assertIsInstance(events[23].source, SourceUser) self.assertEqual(events[23].source.type, 'user') self.assertEqual(events[23].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[23].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[23].message, FileMessage) self.assertEqual(events[23].message.id, '325708') self.assertEqual(events[23].message.type, 'file') self.assertEqual(events[23].message.file_name, "file.txt") self.assertEqual(events[23].message.file_size, 2138) # ThingsEvent, SourceUser, scenarioResult self.assertIsInstance(events[24], ThingsEvent) self.assertEqual(events[24].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[24].type, 'things') self.assertEqual(events[24].timestamp, 1547817848122) self.assertIsInstance(events[24].source, SourceUser) self.assertEqual(events[24].source.type, 'user') self.assertEqual(events[24].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[24].things, ScenarioResult) self.assertEqual(events[24].things.type, 'scenarioResult') self.assertEqual(events[24].things.device_id, 't2c449c9d1') self.assertEqual(events[24].things.result.scenario_id, 'XXX') self.assertEqual(events[24].things.result.revision, 2) self.assertEqual(events[24].things.result.start_time, 1547817845950) self.assertEqual(events[24].things.result.end_time, 1547817845952) self.assertEqual(events[24].things.result.result_code, 'success') self.assertEqual(events[24].things.result.ble_notification_payload, 'AQ==') self.assertIsInstance(events[24].things.result.action_results[0], ActionResult) self.assertEqual(events[24].things.result.action_results[0].type, 'binary') self.assertEqual(events[24].things.result.action_results[0].data, '/w==') self.assertIsInstance(events[24].things.result.action_results[1], ActionResult) self.assertEqual(events[24].things.result.action_results[1].type, 'void')
class LineBotClient(FlaskRestBotClient): def __init__(self, argument_parser=None): FlaskRestBotClient.__init__(self, "line", argument_parser) self.create_line_bot() YLogger.debug(self, "Line Client is running....") def get_description(self): return 'ProgramY AIML2.0 Line Client' def get_client_configuration(self): return LineConfiguration() def get_license_keys(self): self._channel_secret = self.license_keys.get_key("LINE_CHANNEL_SECRET") self._channel_access_token = self.license_keys.get_key("LINE_ACCESS_TOKEN") def create_line_bot(self): self._line_bot_api = LineBotApi(self._channel_access_token) self._parser = WebhookParser(self._channel_secret) def handle_text_message(self, event): question = event.message.text userid = event.source.user_id answer = self.ask_question(userid, question) self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text=answer)) def get_unknown_response(self, userid): if self.configuration.client_configuration.unknown_command_srai is None: unknown_response = self.configuration.client_configuration.unknown_command else: unknown_response = self.ask_question(userid, self.configuration.client_configuration.unknown_command_srai) if unknown_response is None or unknown_response == "": unknown_response = self.configuration.client_configuration.unknown_command return unknown_response def handle_unknown_event(self, event): userid = "" unknown_response = self.get_unknown_response(userid) self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text=unknown_response)) def handle_unknown_message(self, event): userid = "" unknown_response = self.get_unknown_response(userid) self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text=unknown_response)) def handle_message_request(self, body, signature): events = self._parser.parse(body, signature) for event in events: if isinstance(event, MessageEvent): if isinstance(event.message, TextMessage): self.handle_text_message(event) else: self.handle_unknown_message(event) else: self.handle_unknown_event(event) def receive_message(self, request): if self.configuration.client_configuration.debug is True: self.dump_request(request) # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) # handle webhook body try: self.handle_message_request(body, signature) except InvalidSignatureError as excep: YLogger.exception(self, "Line error", excep) abort(500) return Response(status=200)
def test_parse(self): file_dir = os.path.dirname(__file__) webhook_sample_json_path = os.path.join(file_dir, 'text', 'webhook.json') with open(webhook_sample_json_path) as fp: body = fp.read() parser = WebhookParser('channel_secret') # mock parser.signature_validator.validate = lambda a, b: True events = parser.parse(body, 'channel_secret') # MessageEvent, SourceUser, TextMessage self.assertIsInstance(events[0], MessageEvent) self.assertEqual(events[0].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[0].type, 'message') self.assertEqual(events[0].timestamp, 1462629479859) self.assertIsInstance(events[0].source, SourceUser) self.assertEqual(events[0].source.type, 'user') self.assertEqual(events[0].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[0].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[0].message, TextMessage) self.assertEqual(events[0].message.id, '325708') self.assertEqual(events[0].message.type, 'text') self.assertEqual(events[0].message.text, 'Hello, world') # MessageEvent, SourceRoom, TextMessage self.assertIsInstance(events[1], MessageEvent) self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[1].type, 'message') self.assertEqual(events[1].timestamp, 1462629479859) self.assertIsInstance(events[1].source, SourceRoom) self.assertEqual(events[1].source.type, 'room') self.assertEqual(events[1].source.room_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[1].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[1].message, ImageMessage) self.assertEqual(events[1].message.id, '325708') self.assertEqual(events[1].message.type, 'image') # MessageEvent, SourceUser, VideoMessage self.assertIsInstance(events[2], MessageEvent) self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[2].type, 'message') self.assertEqual(events[2].timestamp, 1462629479859) self.assertIsInstance(events[2].source, SourceUser) self.assertEqual(events[2].source.type, 'user') self.assertEqual(events[2].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[2].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[2].message, VideoMessage) self.assertEqual(events[2].message.id, '325708') self.assertEqual(events[2].message.type, 'video') # MessageEvent, SourceUser, AudioMessage self.assertIsInstance(events[3], MessageEvent) self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[3].type, 'message') self.assertEqual(events[3].timestamp, 1462629479859) self.assertIsInstance(events[3].source, SourceUser) self.assertEqual(events[3].source.type, 'user') self.assertEqual(events[3].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[3].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[3].message, AudioMessage) self.assertEqual(events[3].message.id, '325708') self.assertEqual(events[3].message.type, 'audio') # MessageEvent, SourceUser, LocationMessage self.assertIsInstance(events[4], MessageEvent) self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[4].type, 'message') self.assertEqual(events[4].timestamp, 1462629479859) self.assertIsInstance(events[4].source, SourceUser) self.assertEqual(events[4].source.type, 'user') self.assertEqual(events[4].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[4].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[4].message, LocationMessage) self.assertEqual(events[4].message.id, '325708') self.assertEqual(events[4].message.type, 'location') self.assertEqual(events[4].message.title, 'my location') self.assertEqual(events[4].message.address, 'Tokyo') self.assertEqual(events[4].message.latitude, 35.65910807942215) self.assertEqual(events[4].message.longitude, 139.70372892916203) # MessageEvent, SourceUser, StickerMessage self.assertIsInstance(events[5], MessageEvent) self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[5].type, 'message') self.assertEqual(events[5].timestamp, 1462629479859) self.assertIsInstance(events[5].source, SourceUser) self.assertEqual(events[5].source.type, 'user') self.assertEqual(events[5].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[5].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[5].message, StickerMessage) self.assertEqual(events[5].message.id, '325708') self.assertEqual(events[5].message.type, 'sticker') self.assertEqual(events[5].message.package_id, '1') self.assertEqual(events[5].message.sticker_id, '1') # FollowEvent, SourceUser self.assertIsInstance(events[6], FollowEvent) self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[6].type, 'follow') self.assertEqual(events[6].timestamp, 1462629479859) self.assertIsInstance(events[6].source, SourceUser) self.assertEqual(events[6].source.type, 'user') self.assertEqual(events[6].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[6].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # UnfollowEvent, SourceUser self.assertIsInstance(events[7], UnfollowEvent) self.assertEqual(events[7].type, 'unfollow') self.assertEqual(events[7].timestamp, 1462629479859) self.assertIsInstance(events[7].source, SourceUser) self.assertEqual(events[7].source.type, 'user') self.assertEqual(events[7].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[7].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # JoinEvent, SourceGroup self.assertIsInstance(events[8], JoinEvent) self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[8].type, 'join') self.assertEqual(events[8].timestamp, 1462629479859) self.assertIsInstance(events[8].source, SourceGroup) self.assertEqual(events[8].source.type, 'group') self.assertEqual(events[8].source.group_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') self.assertEqual(events[8].source.sender_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') # LeaveEvent, SourceGroup self.assertIsInstance(events[9], LeaveEvent) self.assertEqual(events[9].type, 'leave') self.assertEqual(events[9].timestamp, 1462629479859) self.assertIsInstance(events[9].source, SourceGroup) self.assertEqual(events[9].source.type, 'group') self.assertEqual(events[9].source.group_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') self.assertEqual(events[9].source.sender_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') # PostbackEvent, SourceUser self.assertIsInstance(events[10], PostbackEvent) self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[10].type, 'postback') self.assertEqual(events[10].timestamp, 1462629479859) self.assertIsInstance(events[10].source, SourceUser) self.assertEqual(events[10].source.type, 'user') self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red') # BeaconEvent, SourceUser self.assertIsInstance(events[11], BeaconEvent) self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[11].type, 'beacon') self.assertEqual(events[11].timestamp, 1462629479859) self.assertIsInstance(events[11].source, SourceUser) self.assertEqual(events[11].source.type, 'user') self.assertEqual(events[11].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[11].beacon.type, 'enter')
class OAClient(object): def __init__(self, client): self.lock = Lock() with Acquire(client.lock, self.lock): self.client = client with Acquire(self.lock): self.thread = None self._1client = LineBotApi(self.channelAccessToken) if self.channelSecret is None: self.parser = None else: self.parser = WebhookParser(self.channelSecret) self.idLocks = {} self.adminObjs = [] self.running = False @property def name(self): return self.client.oAName @property def obj(self): return self.client.oAObj @obj.setter def obj(self, value): self.client.oAObj = value @property def mid(self): return self.client.oAMid @mid.setter def mid(self, value): self.client.oAMid = value @property def db(self): return self.client.db def GetCursor(self): return self.client.db.GetCursor() @property def adminIds(self): return self.client.adminIds @adminIds.setter def adminIds(self, value): self.client.adminIds = value @property def tries(self): return self.client.tries @tries.setter def tries(self, value): self.client.tries = value @property def channelSecret(self): return self.client.channelSecret @channelSecret.setter def channelSecret(self, value): self.client.channelSecret = value @property def channelAccessToken(self): return self.client.channelAccessToken @channelAccessToken.setter def channelAccessToken(self, value): self.client.channelAccessToken = value def _1TextMessage(self, id, text, chatroom, sender = None): chatroom.hasOA = True return self.client._1TextMessage(id, text, chatroom, Receiver.oA, sender=sender) def _1StickerMessage(self, id, packageId, stickerId, chatroom, sender = None): chatroom.hasOA = True return self.client._1StickerMessage(id, packageId, stickerId, chatroom, Receiver.oA, sender=sender) def _1LocationMessage(self, id, title, address, latitude, longitude, chatroom, sender = None): chatroom.hasOA = True return self.client._1LocationMessage(id, title, address, latitude, longitude, chatroom, Receiver.oA, sender=sender) def _1ImageMessage(self, id, chatroom, sender = None, url=None, bytes=None): chatroom.hasOA = True return self.client._1ImageMessage(id, chatroom, Receiver.oA, sender=sender, url=url, bytes=bytes) def _1AudioMessage(self, id, chatroom, sender = None, url=None, bytes=None): chatroom.hasOA = True return self.client._1AudioMessage(id, chatroom, Receiver.oA, sender=sender, url=url, bytes=bytes) def _1VideoMessage(self, id, chatroom, sender = None, url=None, bytes=None): chatroom.hasOA = True return self.client._1VideoMessage(id, chatroom, Receiver.oA, sender=sender, url=url, bytes=bytes) def _1Update(self, chatroom): chatroom.hasOA = True return self.client._1Update(chatroom, Receiver.oA) def _1Unfollowed(self, chatroom): chatroom.hasOA = False return self.client._1Unfollowed(chatroom, Receiver.oA) def _1Followed(self, chatroom): chatroom.hasOA = True return self.client._1Followed(chatroom, Receiver.oA) def _1Joined(self, chatroom): chatroom.hasOA = True return self.client._1Joined(chatroom, Receiver.oA) def _1Left(self, chatroom): chatroom.hasOA = False return self.client._1Left(chatroom, Receiver.oA) def _1Invited(self, chatroom): chatroom.hasOA = False return self.client._1Invited( chatroom, Receiver.oA) def _1Report(self, msg): if len(self.adminObjs) > 0: return self.adminObjs[0].SendText(msg) elif len(self.adminIds) > 0: return self._2SendText(self.adminIds[0], msg) raise Exception("[OAClient._1Report : No report Id]\n" + msg) def _1ReportAll(self, msg): if len(self.adminObjs) > 0: return self._1SendText(self.adminObjs, msg) elif len(self.adminIds) > 0: return self._2SendText(self.adminIds, msg) raise Exception("[OAClient._1Report : No report Id]\n" + msg) def _1GetContentRaw(self, msgId): for i in range(0, self.tries): try: return self._1client.get_message_content(msgId) except (timeout, ReadTimeout, Timeout): pass def _1GetContent(self, msgId): content = self._1GetContentRaw(msgId) if content is None: return None ret = bytes() for chunk in content.iter_content(): ret = ret + bytes(chunk) return ret def _1Leave(self, room): if not room.hasOA: return False #if room.uObj: # room.RemoveLink() if room.chatroomType == ChatroomType.room: return self._2LeaveRoom(room.id) if room.chatroomType == ChatroomType.group: return self._2LeaveGroup(room.id) raise Exception("[OAClient._1Leave] 'room' is a User") def _2LeaveRoom(self, lineId): ex = '' for i in range(0, self.tries): try: return self._1client.leave_room(lineId) except (timeout, ReadTimeout, Timeout): pass def _2LeaveGroup(self, lineId): ex = '' for i in range(0, self.tries): try: return self._1client.leave_group(lineId) except (timeout, ReadTimeout, Timeout): pass def _1GetProfile(self, user): profile0 = self._2GetProfile(user.id) user._1SetProfileAndTime(profile0) return user._1profile def _2GetProfile(self, lineId): ex = '' for i in range(0, self.tries): try: return self._1client.get_profile(lineId) except (timeout, ReadTimeout, Timeout): pass def _1SendButtons(self, to, buttons): if isinstance(to, list): for t in to: return self._1SendButtons(t, buttons) if not to.hasOA: raise Exception("[OAClient._1SendButtons] No OA Client") carsb = buttons.Build() for car in carsb[0]: colLen = len(car.template.columns) if colLen==0 or (colLen == 1 and len(car.template.columns[-1].actions) == 0): continue if len(car.template.columns[-1].actions) == 0: car.template.columns.remove(car.template.columns[-1]) self._1PushMessage(to.id, car) if carsb[1] is not None: self._1PushMessage(to.id, carsb[1]) def _1SendText(self, to, text): if isinstance(to, list): return self._2SendText([t.id for t in to], text) return self._2SendText(to.id, text) def _2SendText(self, to, text): if type(to) is list: return self._1Multicast(to, TextSendMessage(text=text)) return self._1PushMessage(to, TextSendMessage(text=text)) def _1SendImageWithBytes(self, to, bytes): if self.client.pyImgurKey is None: raise Exception("[OAClient._1SendImage] Client.pyImgurKey must be set") url = self.client.UploadPyImgur(bytes) return self._1SendImageWithUrl(to, url) def _1SendImageWithUrl(self, to, url): if type(to) is list: return self._2SendImageWithUrl([t.id for t in to], url) return self._2SendImageWithUrl(to.id, url) def _1SendSticker(self, to, packageId, stickerId): if type(to) is list: return self._1Multicast(to, StickerSendMessage(package_id=packageId, sticker_id=stickerId)) return self._1PushMessage(to, StickerSendMessage(package_id=packageId, sticker_id=stickerId)) def _1SendLocation(self, to, title, address, latitude, longitude, phone=None): if type(to) is list: return self._1Multicast(to, LocationSendMessage(title=title, address=address, latitude=latitude, longitude=longitude)) return self._1PushMessage(to, LocationSendMessage(title=title, address=address, latitude=latitude, longitude=longitude)) def _1SendVideoWithUrl(self, to, url, previewUrl="https://thumb7.shutterstock.com/display_pic_with_logo/677413/595756799/stock-vector-no-preview-rubber-stamp-595756799.jpg"): if type(to) is list: return self._1Multicast(to, VideoSendMessage(original_content_url=url, preview_image_url=previewUrl)) return self._1PushMessage(to, VideoSendMessage(original_content_url=url, preview_image_url=previewUrl)) def _1SendAudioWithUrl(self, to, url, duration=240000): if type(to) is list: return self._1Multicast(to, AudioSendMessage(original_content_url=url, duration=duration)) return self._1PushMessage(to, AudioSendMessage(original_content_url=url, duration=duration)) def _2SendImageWithUrl(self, to, url): if url.startswith("https://"): url = url[8:] elif url.startswith("http://"): url = url[7:] linka = "" linkb = "" if url.startswith('i.imgur.com'): if "?" in url: url = "https://" + url + "&" else: url = "https://" + url + "?" linka = url + "maxwidth=1024&maxheight=1024" linkb = url + "maxwidth=240&maxheight=240" elif url.startswith('memegen.link'): url = "https://" + url linka = url + "&width=1024&height=1024" linkb = url + "&width=240&height=240" elif not url.startswith('i.scaley.io'): url = "http://" + url linka = ScaleyUrl(url, max=1024) linkb = ScaleyUrl(url, max=240) #self._2SendText(to, "LinkA : " + linka + "\nLinkB : " + linkb) if type(to) is list: return self._1Multicast(to, ImageSendMessage(original_content_url=linka, preview_image_url=linkb)) return self._1PushMessage(to, ImageSendMessage(original_content_url=linka, preview_image_url=linkb)) def _1Multicast(self, ids, msg): idslen = len(ids) if idslen == 0: return False if idslen == 1: return self._1PushMessage(ids[0], msg) userIds = [] roomOrGroupIds = [] for id in ids: t = id[:1] if t == 'U': userIds.append(id) else: roomOrGroupIds.append(id) ret = True if len(userIds) > 0: for i in range(0, self.tries): try: self._1client.multicast(userIds, msg) break except (timeout, ReadTimeout, Timeout): pass except LineBotApiError: if i == self.tries-1: self.client.Report("[Multicast:Ids]\n" + str(userIds)) self.client.Report("[Multicast:Msg]\n" + str(msg)) self.client.Report("[Multicast:Exc]\n" + format_exc()) ret = False for roomOrGroupId in roomOrGroupIds: ret = ret and self._1PushMessage(roomOrGroupId, msg) return ret def _1PushMessage(self, id, msg): for i in range(0, self.tries): try: self._1client.push_message(id, msg) return True except (timeout, ReadTimeout, Timeout): print("[OAClient._1PushMessage:Timeout]\n" + format_exc()) pass except LineBotApiError: e = format_exc() print("[OAClient._1PushMessage:LineBotApiError:idnmsg]\n" + str(id) + "\n" + str(msg)) print("[OAClient._1PushMessage:LineBotApiError]\n" + e) if i == self.tries-1: if isinstance(msg, TextSendMessage): text = msg.text textLen = len(msg.text) if textLen > 2000: ret = True while textLen > 2000: ret = self._1PushMessage(id, TextSendMessage(text=text[:2000])) text = text[2000:] textLen = len(text) return ret elif msg.text[:12] == "[PushMessage": continue self.client.Report("[PushMessage:Id]\n" + str(id)) self.client.Report("[PushMessage:Msg]\n" + str(msg)) self.client.Report("[PushMessage:Exc]\n" + e) raise return False def _1GetObjByLineId(self, id, msgId = None, hasOA=defaultParameter, init=True, messageText = '', userInChatroom=False): if id is None: return default=False if hasOA == defaultParameter: hasOA = True default=True with Acquire(self.lock): if id not in self.idLocks: self.idLocks[id] = Lock() lock = self.idLocks[id] with Acquire(lock): if id in self.client._1objectsByLineId: ret = self.client._1objectsByLineId[id] ret.TrySync() if msgId is None or ret.hasUser or (ret.chatroomType == ChatroomType.user and (not userInChatroom or not msgId or not (msgId in self.client._1objectsOAByMsgId or userInChatroom.hasUser))) or not self.client.hasUser: return ret if messageText.startswith("[INITROOM]"): key = messageText.rpartition(' ')[2] room = self.client._1waitingRoom.pop(key, None) if room: room.Sync(ret) msgFound = False with Acquire(self.client.lock): obj2 = None if ret.chatroomType == ChatroomType.user: if userInChatroom and msgId in self.client._1senderObjectsUserByMsgId: obj2 = self.client._1senderObjectsUserByMsgId[msgId] else: if msgId in self.client._1objectsUserByMsgId: obj2 = self.client._1objectsUserByMsgId[msgId] if obj2: print("Merging chatroom") obj2.Sync(ret) msgFound = True if not msgFound: if self.client.db is not None: with self.client.GetCursor() as cur: cur.Execute("SELECT lineMid, hasUser, hasOA, id, uId FROM ChatRooms WHERE lineId=%s", (id,)) f = cur.FetchOne() if f is not None: if f[2] != hasOA: if default: ret.hasOA = f[2] else: cur.Execute("UPDATE ChatRooms SET hasOA=%s WHERE id=%s", (ret._2hasOA, f[3],)) cur.Commit() ret._2id = f[3] if ret.chatroomType == ChatroomType.room: ret.uId = f[4] ret.mid = f[0] ret.hasUser = f[1] if ret.mid: synced = False with Acquire(self.client.lock): if ret.mid in self.client._1objectsByLineMid: self.client._1objectsByLineMid[ret.mid].Sync(ret) synced = True with Acquire(self.client.lock): if ret.chatroomType == ChatroomType.user: if userInChatroom and (msgId in self.client._1objectsOAByMsgId or userInChatroom.hasUser): self.client._1senderObjectsOAByMsgId[msgId] = ret else: self.client._1objectsOAByMsgId[msgId] = ret else: #print("Creating new User for id " + str(id)) isUser = id[0] == 'U' if isUser: ret = User(id=id, client=self.client, init=init) if msgId and userInChatroom and (msgId in self.client._1objectsOAByMsgId or userInChatroom.hasUser): self.client._1senderObjectsOAByMsgId[msgId] = ret else: if id[0] == 'R': ret = Room(id=id, client=self.client, hasOA=hasOA, init=init) elif id[0] == 'C': ret = Group(id=id, client=self.client, hasOA=hasOA, init=init) else: raise Exception("[OAClient.GetObjByLineId : Invalid Id]\n" + id) if msgId and self.client.hasUser: self.client._1objectsOAByMsgId[msgId] = ret with Acquire(self.client.lock): if id in self.client._1objectsByLineId: return self._1GetObjByLineId(id) self.client._1objectsByLineId[id] = ret if ret.chatroomType == ChatroomType.user: self.client.users.append(ret) if ret.chatroomType == ChatroomType.room: self.client.rooms.append(ret) if ret.chatroomType == ChatroomType.group: self.client.groups.append(ret) if self.client.db is not None: with self.client.GetCursor() as cur: cur.Execute("SELECT lineMid, hasOA, hasUser, id, uId FROM ChatRooms WHERE lineId=%s", (id,)) fLM = cur.FetchOne() if fLM is None: b = False if ret.chatroomType != ChatroomType.user and msgId is not None: b = self._1CheckMsgId(ret, msgId) if not b: cur.Execute("INSERT INTO ChatRooms(lineId, type, hasOA) Values(%s, %s, %s) RETURNING id", (ret.id, ret.chatroomType, ret._2hasOA)) f = cur.FetchOne() ret._2id = f[0] cur.Commit() else: ret._2id = fLM[3] ret.hasUser = fLM[2] if fLM[1] != hasOA: if default: ret.hasOA = fLM[1] else: cur.Execute("UPDATE ChatRooms SET hasOA=%s WHERE id=%s", (ret._2hasOA, fLM[3],)) cur.Commit() if IsEmpty(fLM[0]): if ret.chatroomType != ChatroomType.user: ret.mid = self._1CheckMsgId(ret, msgId) else: ret.mid = fLM[0] if ret.chatroomType == ChatroomType.room: ret.uId = fLM[4] if self.client.hasUser and not IsEmpty(ret.mid): with Acquire(self.client.lock): if ret.mid in self.client._1objectsByLineMid: self.client._1objectsByLineMid[ret.mid].Sync(ret) ret.TrySync() return ret def _1CheckMsgId(self, obj, msgId): with self.client.GetCursor() as cur: if obj.chatroomType == ChatroomType.user: s = "Sender" else: s = "" cur.Execute("SELECT lineMid FROM " + s + "LineMidByMsgId WHERE msgId=%s", (msgId,)) fLM = cur.FetchOne() ret = None if fLM is None: cur.Execute("INSERT INTO " + s + "LineIdByMsgId(msgId, lineId) Values(%s, %s) ON CONFLICT(msgId) DO UPDATE SET lineId=%s", (msgId, obj.id, obj.id,)) cur.Commit() else: obj.mid = fLM[0] obj.hasUser = True if obj.mid in self.client._1objectsByLineMid: self.client._1objectsByLineMid[obj.mid].Sync(obj) else: obj.Sync() return obj.mid def HandleWebhook(self, environ, start_response): # check request method if environ['REQUEST_METHOD'] != 'POST': start_response('405 Method Not Allowed : ' + environ['REQUEST_METHOD'], []) return CreateBody('Method not allowed') # get X-Line-Signature header value signature = environ['HTTP_X_LINE_SIGNATURE'] # get request body as text wsgi_input = environ['wsgi.input'] content_length = int(environ['CONTENT_LENGTH']) body = wsgi_input.read(content_length).decode('utf-8') # parse webhook body try: events = self.parser.parse(body, signature) except InvalidSignatureError: start_response('400 Bad Request', []) return CreateBody('Bad request') # if event is MessageEvent and message is TextMessage, then echo text exc = '' msg = None for event in events: self.client.Thread(self.ParseEvent, [event]) start_response('200 OK', []) return CreateBody('OK') def Start(self, thread=True, port=None): if self.thread is not None and self.thread.isAlive: return self.thread if port is None: port0 = os.environ.get("PORT", None) if port0 is None: return port = int(port0) self.running = True if thread: self.thread = self.client.Thread(self._1Main, [port]) return self.thread self._1Main(port) self.running = False def _1Main(self, port=None): if port is None: port0 = os.environ.get("PORT", None) if port0 is None: return port = int(port0) self.running = True srv = make_server('0.0.0.0', port, self.HandleWebhook) srv.serve_forever() self.running = False def ParseEvent(self, event): while not self.client.started: with self.client.startCond: self.client.startCond.wait(1) try: msg = None if isinstance(event, MessageEvent): if event.message: messageText = '' if isinstance(event.message, _TextMessage): messageText = event.message.text chatroomObj = self._1GetObjByLineId(id=event.source.sender_id, msgId = event.message.id, messageText=messageText) sender=None if chatroomObj.chatroomType == ChatroomType.user: sender = chatroomObj if not sender and chatroomObj.chatroomType != ChatroomType.user: try: senderId = event.source.user_id except Exception: pass if senderId: sender = self._1GetObjByLineId(id=senderId, msgId = event.message.id, messageText=messageText, userInChatroom = chatroomObj) if isinstance(event.message, _TextMessage): msg = self._1TextMessage(id=event.message.id, text=event.message.text, chatroom = chatroomObj, sender=sender) if isinstance(event.message, _StickerMessage): msg = self._1StickerMessage(id=event.message.id, packageId=event.message.package_id, stickerId = event.message.sticker_id, chatroom = chatroomObj, sender=sender) if isinstance(event.message, _LocationMessage): msg = self._1LocationMessage(id=event.message.id, title=event.message.title, address=event.message.address, latitude=event.message.latitude, longitude=event.message.longitude, chatroom = chatroomObj, sender=sender) if isinstance(event.message, _ImageMessage): msg = self._1ImageMessage(id=event.message.id, chatroom = chatroomObj, sender=sender) if isinstance(event.message, _AudioMessage): msg = self._1AudioMessage(id=event.message.id, chatroom = chatroomObj, sender=sender) if isinstance(event.message, _VideoMessage): msg = self._1VideoMessage(id=event.message.id, chatroom = chatroomObj, sender=sender) else: print("UNKNOWN MESSAGE " + str(event)) else: chatroomObj = self._1GetObjByLineId(id=event.source.sender_id) if isinstance(event, LeaveEvent): chatroomObj.hasOA = False msg = self._1Left(chatroom = chatroomObj) elif isinstance(event, FollowEvent): chatroomObj.hasOA = True msg = self._1Followed(chatroom = chatroomObj) elif isinstance(event, UnfollowEvent): chatroomObj.hasOA = False msg = self._1Unfollowed(chatroom = chatroomObj) elif isinstance(event, JoinEvent): chatroomObj.hasOA = True msg = self._1Joined(chatroom = chatroomObj) if msg is not None and (msg.eventType != EventType.message or not (msg.chatroom.hasUser and self.client.userClient.running)): if self.client.main: self.client.AddEvent(msg) else: self.client.Thread(self.client.Handle, [msg, False]) except Exception as ex: err = format_exc() print(err) self.client.Report("[OAClient.ParseEvent]\n" + err)
class LineServer(object): """Line bot server""" def __init__(self): line_config = ConfigParser() root_dir = os.environ.get('ROOT_DIR') line_config_path = os.path.join(root_dir, 'credential', 'line_config.ini') line_config.read(line_config_path) if not line_config.has_option('Line Config', 'ACCESS_TOKEN'): print('Specify LINE_CHANNEL_SECRET as environment variable.') sys.exit(1) if not line_config.has_option('Line Config', 'SECRET'): print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.') sys.exit(1) channel_secret = line_config['Line Config']['SECRET'] channel_access_token = line_config['Line Config']['ACCESS_TOKEN'] self.line_bot_api = LineBotApi(channel_access_token) self.parser = WebhookParser(channel_secret) def __del__(self): print('Destroyed') def callback(self, environ, start_response): """Process every line message""" # check request path if environ['PATH_INFO'] != '/callback': start_response('404 Not Found', []) return self.create_body('Not Found') # check request method if environ['REQUEST_METHOD'] != 'POST': start_response('405 Method Not Allowed', []) return self.create_body('Method Not Allowed') # get X-Line-Signature header value signature = environ['HTTP_X_LINE_SIGNATURE'] # get request body as text wsgi_input = environ['wsgi.input'] content_length = int(environ['CONTENT_LENGTH']) body = wsgi_input.read(content_length).decode('utf-8') # parse webhook body try: events = self.parser.parse(body, signature) except InvalidSignatureError: start_response('400 Bad Request', []) return self.create_body('Bad Request') # database connect db = db_operator.DBConnector() table_name = 'USER' user_id = events[0].source.user_id # create user if not in database if not db.is_record(table_name, 'userID', user_id): data = {'userID': user_id} db.insert(table_name, data) # if event is MessageEvent and message is TextMessage, then echo text for event in events: if not isinstance(event, MessageEvent): continue if not isinstance(event.message, TextMessage): continue command = event.message.text.split() data = {'lastCmd': event.message.text} db.update(table_name, data, 'userID=\'{}\''.format(user_id)) if command[0] == '天氣': if len(command) == 1: fav = db.query(table_name, 'favorite', 'userID=\'{}\''.format(user_id)) command += fav.split() elif len(command) == 2: command.append(command[-1]) display = weather.getWeather(command[1:]) elif command[0] == '捷運': if len(command) < 3: display = '請輸入兩個車站。' else: display = metro.getDuration(command[1:]) elif command[0] == '設定': data = {'favorite': ' '.join(command[1:])} db.update(table_name, data, 'userID=\'{}\''.format(user_id)) display = '已經您的常用地點設為:{}'.format(' '.join(command[1:])) else: display = '我聽不懂你在說什麼,你可以試試:天氣 台北 大安' # Especially for Lion if user_id == 'U90101030d70543c2eb06911da7c7f93b': display = '獅子主人,底下是您查詢的結果:\n' + display self.line_bot_api.reply_message(event.reply_token, TextSendMessage(text=display)) start_response('200 OK', []) return self.create_body('OK') def create_body(self, text): if PY3: return [bytes(text, 'utf-8')] else: return text
def test_parse(self): file_dir = os.path.dirname(__file__) webhook_sample_json_path = os.path.join(file_dir, 'text', 'webhook.json') with open(webhook_sample_json_path) as fp: body = fp.read() parser = WebhookParser('a1b7349fed51378bae64e940012794db') # mock parser.signature_validator.validate = lambda a, b: True events = parser.parse(body, 'a1b7349fed51378bae64e940012794db') # MessageEvent, SourceUser, TextMessage self.assertIsInstance(events[0], MessageEvent) self.assertEqual(events[0].reply_token, '7u6uSOrfTJAqfs6gOM+DJ4zV5Mo3lV0mVlJ8qocjnGkTfz4jsJsqvGqnUCNL9EjwaS2tDzhTFn5QaMiL6rQge8x48zKcK/ceO0dXdepuiV+Y3vuBZ4GWVtlD0gOXOoNTN/3rKWvAmo7iqDUNHZC9aAdB04t89/1O/w1cDnyilFU=') self.assertEqual(events[0].type, 'message') self.assertEqual(events[0].timestamp, 1462629479859) self.assertIsInstance(events[0].source, SourceUser) self.assertEqual(events[0].source.type, 'user') self.assertEqual(events[0].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[0].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[0].message, TextMessage) self.assertEqual(events[0].message.id, '325708') self.assertEqual(events[0].message.type, 'text') self.assertEqual(events[0].message.text, 'Hello, world') # MessageEvent, SourceRoom, TextMessage self.assertIsInstance(events[1], MessageEvent) self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[1].type, 'message') self.assertEqual(events[1].timestamp, 1462629479859) self.assertIsInstance(events[1].source, SourceRoom) self.assertEqual(events[1].source.type, 'room') self.assertEqual(events[1].source.room_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[1].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[1].message, ImageMessage) self.assertEqual(events[1].message.id, '325708') self.assertEqual(events[1].message.type, 'image') # MessageEvent, SourceUser, VideoMessage self.assertIsInstance(events[2], MessageEvent) self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[2].type, 'message') self.assertEqual(events[2].timestamp, 1462629479859) self.assertIsInstance(events[2].source, SourceUser) self.assertEqual(events[2].source.type, 'user') self.assertEqual(events[2].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[2].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[2].message, VideoMessage) self.assertEqual(events[2].message.id, '325708') self.assertEqual(events[2].message.type, 'video') # MessageEvent, SourceUser, AudioMessage self.assertIsInstance(events[3], MessageEvent) self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[3].type, 'message') self.assertEqual(events[3].timestamp, 1462629479859) self.assertIsInstance(events[3].source, SourceUser) self.assertEqual(events[3].source.type, 'user') self.assertEqual(events[3].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[3].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[3].message, AudioMessage) self.assertEqual(events[3].message.id, '325708') self.assertEqual(events[3].message.type, 'audio') # MessageEvent, SourceUser, LocationMessage self.assertIsInstance(events[4], MessageEvent) self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[4].type, 'message') self.assertEqual(events[4].timestamp, 1462629479859) self.assertIsInstance(events[4].source, SourceUser) self.assertEqual(events[4].source.type, 'user') self.assertEqual(events[4].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[4].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[4].message, LocationMessage) self.assertEqual(events[4].message.id, '325708') self.assertEqual(events[4].message.type, 'location') self.assertEqual(events[4].message.title, 'my location') self.assertEqual(events[4].message.address, 'Tokyo') self.assertEqual(events[4].message.latitude, 35.65910807942215) self.assertEqual(events[4].message.longitude, 139.70372892916203) # MessageEvent, SourceUser, StickerMessage self.assertIsInstance(events[5], MessageEvent) self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[5].type, 'message') self.assertEqual(events[5].timestamp, 1462629479859) self.assertIsInstance(events[5].source, SourceUser) self.assertEqual(events[5].source.type, 'user') self.assertEqual(events[5].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[5].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[5].message, StickerMessage) self.assertEqual(events[5].message.id, '325708') self.assertEqual(events[5].message.type, 'sticker') self.assertEqual(events[5].message.package_id, '1') self.assertEqual(events[5].message.sticker_id, '1') # FollowEvent, SourceUser self.assertIsInstance(events[6], FollowEvent) self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[6].type, 'follow') self.assertEqual(events[6].timestamp, 1462629479859) self.assertIsInstance(events[6].source, SourceUser) self.assertEqual(events[6].source.type, 'user') self.assertEqual(events[6].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[6].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # UnfollowEvent, SourceUser self.assertIsInstance(events[7], UnfollowEvent) self.assertEqual(events[7].type, 'unfollow') self.assertEqual(events[7].timestamp, 1462629479859) self.assertIsInstance(events[7].source, SourceUser) self.assertEqual(events[7].source.type, 'user') self.assertEqual(events[7].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[7].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # JoinEvent, SourceGroup self.assertIsInstance(events[8], JoinEvent) self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[8].type, 'join') self.assertEqual(events[8].timestamp, 1462629479859) self.assertIsInstance(events[8].source, SourceGroup) self.assertEqual(events[8].source.type, 'group') self.assertEqual(events[8].source.group_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') self.assertEqual(events[8].source.sender_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') # LeaveEvent, SourceGroup self.assertIsInstance(events[9], LeaveEvent) self.assertEqual(events[9].type, 'leave') self.assertEqual(events[9].timestamp, 1462629479859) self.assertIsInstance(events[9].source, SourceGroup) self.assertEqual(events[9].source.type, 'group') self.assertEqual(events[9].source.group_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') self.assertEqual(events[9].source.sender_id, 'cxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') # PostbackEvent, SourceUser self.assertIsInstance(events[10], PostbackEvent) self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[10].type, 'postback') self.assertEqual(events[10].timestamp, 1462629479859) self.assertIsInstance(events[10].source, SourceUser) self.assertEqual(events[10].source.type, 'user') self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red') # BeaconEvent, SourceUser self.assertIsInstance(events[11], BeaconEvent) self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[11].type, 'beacon') self.assertEqual(events[11].timestamp, 1462629479859) self.assertIsInstance(events[11].source, SourceUser) self.assertEqual(events[11].source.type, 'user') self.assertEqual(events[11].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[11].beacon.type, 'enter')
class LineBotClient(BotClient): def __init__(self, argument_parser=None): BotClient.__init__(self, "line", argument_parser) self.get_tokens() self.create_line_bot() if logging.getLogger().isEnabledFor(logging.DEBUG): logging.debug("Line Client is running....") def set_environment(self): self.bot.brain.properties.add_property("env", 'line') def get_client_configuration(self): return LineConfiguration() def get_tokens(self): self._channel_secret = self.bot.license_keys.get_key("LINE_CHANNEL_SECRET") self._channel_access_token = self.bot.license_keys.get_key("LINE_ACCESS_TOKEN") def ask_question(self, clientid, question): response = "" try: response = self.bot.ask_question(clientid, question) except Exception as e: print(e) return response def create_line_bot(self): self._line_bot_api = LineBotApi(self._channel_access_token) self._parser = WebhookParser(self._channel_secret) def handle_text_message(self, event): question = event.message.text clientid = event.source.user_id answer = self.ask_question(clientid, question) self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text=answer)) def handle_unknown_event(self, event): self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text="Sorry, I only handle text messages right now!")) def handle_unknown_message(self, event): self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text="Sorry, I only handle text messages right now!")) def handle_message_request(self, body, signature): events = self._parser.parse(body, signature) for event in events: if isinstance(event, MessageEvent): if isinstance(event.message, TextMessage): self.handle_text_message(event) else: self.handle_unknown_message(event) else: self.handle_unknown_event(event) def handle_incoming(self, request): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) # handle webhook body try: self.handle_message_request(body, signature) except InvalidSignatureError: abort(400) return 'OK'
class LineBotClient(FlaskRestBotClient): def __init__(self, argument_parser=None): FlaskRestBotClient.__init__(self, "line", argument_parser) self.create_line_bot() YLogger.debug(self, "Line Client is running....") def get_client_configuration(self): return LineConfiguration() def get_license_keys(self): self._channel_secret = self.license_keys.get_key("LINE_CHANNEL_SECRET") self._channel_access_token = self.license_keys.get_key( "LINE_ACCESS_TOKEN") def create_line_bot(self): self._line_bot_api = LineBotApi(self._channel_access_token) self._parser = WebhookParser(self._channel_secret) def handle_text_message(self, event): question = event.message.text userid = event.source.user_id answer = self.ask_question(userid, question) self._line_bot_api.reply_message(event.reply_token, TextSendMessage(text=answer)) def get_unknown_response(self, userid): if self.configuration.client_configuration.unknown_command_srai is None: unknown_response = self.configuration.client_configuration.unknown_command else: unknown_response = self.ask_question( userid, self.configuration.client_configuration.unknown_command_srai) if unknown_response is None or unknown_response == "": unknown_response = self.configuration.client_configuration.unknown_command return unknown_response def handle_unknown_event(self, event): userid = "" unknown_response = self.get_unknown_response(userid) self._line_bot_api.reply_message( event.reply_token, TextSendMessage(text=unknown_response)) def handle_unknown_message(self, event): userid = "" unknown_response = self.get_unknown_response(userid) self._line_bot_api.reply_message( event.reply_token, TextSendMessage(text=unknown_response)) def handle_message_request(self, body, signature): events = self._parser.parse(body, signature) for event in events: if isinstance(event, MessageEvent): if isinstance(event.message, TextMessage): self.handle_text_message(event) else: self.handle_unknown_message(event) else: self.handle_unknown_event(event) def receive_message(self, request): if self.configuration.client_configuration.debug is True: self.dump_request(request) # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) # handle webhook body try: self.handle_message_request(body, signature) except InvalidSignatureError as excep: YLogger.exception(self, "Line error", excep) abort(500) return Response(status=200)
async def post(request): line_api = LineBotApi(channel_access_token=os.getenv("CHOCO_LINE_AT")) parser = WebhookParser(channel_secret=os.getenv("CHOCO_LINE_CS")) events = parser.parse((await request.body()).decode("utf-8"), request.headers.get("X-Line-Signature", "")) for ev in events: match_text = [ "チョコ", "ちょこ", "チョコレート", "ちょこれーと", "バレンタイン", "ばれんたいん", "🍫" ] tosend = False for text in match_text: if text in ev.message.text: tosend = True break if tosend: emojis = [ { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "058" }, { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "059" }, { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "178" }, { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "178" }, { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "178" }, { "index": 0, "productId": "5ac2213e040ab15980c9b447", "emojiId": "178" }, { "index": 0, "productId": "5ac2211e031a6752fb806d61", "emojiId": "201" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "059" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "061" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "061" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "061" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "061" }, { "index": 0, "productId": "5ac1de17040ab15980c9b438", "emojiId": "061" }, { "index": 0, "productId": "5ac2211e031a6752fb806d61", "emojiId": "182" }, { "index": 0, "productId": "5ac2197b040ab15980c9b43d", "emojiId": "010" }, ] line_api.reply_message( ev.reply_token, TextSendMessage(text="$", emojis=[emojis[randint(0, len(emojis) - 1)]])) return "ok"
class LYCLineBot(BaseBot): def __init__(self, access_token, secret): self.line_bot_api = LineBotApi(access_token) self.handler = WebhookHandler(secret) self.parser = WebhookParser(secret) def send_message(self, text, user_id): self.line_bot_api.push_message(user_id, TextSendMessage(text=text)) return "Send" def reply_message(self, text, body, signature): try: events = self.parser.parse(body, signature) for event in events: return_message = self.special_reply(event.message) # 沒有特殊處理過的回應, 解析一下訊息在處理回應 if not return_message: return_message = self.analysis_message(event.message) self.line_bot_api.reply_message(event.reply_token, return_message) except InvalidSignatureError as e: return "authorization failed" except LineBotApiError as e: return e.error.message else: return "OK" def user_profile(self, user_id): line_profile = self.line_bot_api.get_profile(user_id) if not line_profile: return None profile = UserProfile() profile.id = line_profile.user_id profile.name = line_profile.display_name profile.description = line_profile.status_message profile.picture = line_profile.picture_url return profile def analysis_message(self, message): """解析使用者傳的訊息來決定回傳什麼東西給使用者 Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] Returns: [SendMessage] -- [the message what postback to the user.] """ # 解析是不是有符合觸發事件 if message.type == "text": seg_list = ", ".join(jieba.cut(message.text)).split(', ') match_event_message = analyzer.match(seg_list) match_event_message = list( filter(lambda x: x is not None, match_event_message)) if match_event_message and len(match_event_message) > 0: return TextSendMessage(text=''.join( filter(lambda x: x is not None, match_event_message))) # 沒有就去當鸚鵡吧 allow_message_type = { "text": self.reply_by_text, "sticker": self.reply_by_sticker } func = allow_message_type.get(message.type, self.reply_default) return func(message) def reply_by_text(self, message): """reply a text Arguments: message {string} -- request message Returns: string -- response message """ return TextSendMessage(text=message.text) def reply_by_sticker(self, message): """reply by a sticker Arguments: message {string} -- request message Returns: string -- response sticker. """ if int(message.package_id) > 4: return TextSendMessage(text="中文豪難喔,公蝦聽謀捏。") return StickerSendMessage(package_id=message.package_id, sticker_id=message.sticker_id) def reply_default(self, message): return TextSendMessage(text="中文豪難喔,公蝦聽謀捏。") def template_buttons(self, message): """produce template with buttons Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] """ buttons_template_message = TemplateSendMessage( alt_text='Buttons template', template=ButtonsTemplate( thumbnail_image_url='https://i.imgur.com/ZgVAAMV.jpg', title='LYC電玩展', text='您今天想看什麼任天堂Switch遊戲', actions=[ PostbackTemplateAction(label='最新的', text='最新的', data='action=buy&itemid=1'), MessageTemplateAction(label='特價中的', text='特價中的'), URITemplateAction( label='我自己看吧', uri='https://lycnsbot.herokuapp.com/games') ])) return buttons_template_message def template_confirm(self, message): """produce template with confirm button Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] """ confirm_template_message = TemplateSendMessage( alt_text='Confirm template', template=ConfirmTemplate( text='你準備好買更多的任天堂了嗎?', actions=[ PostbackTemplateAction(label='當然啊', text='我要買任天堂Switch', data='action=buy&itemid=1'), MessageTemplateAction(label='不買行嗎', text='我不買任天堂Switch') ])) return confirm_template_message def template_carousel_buttons(self, message): """produce template with carousel buttons Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] """ carousel_template_message = TemplateSendMessage( alt_text='Carousel template', template=CarouselTemplate(columns=[ CarouselColumn( thumbnail_image_url='https://i.imgur.com/njZeeBz.jpg', title='Splatoon 2', text='花枝會打漆彈耶', actions=[ MessageTemplateAction(label='哇嗚好像很好玩', text='真的不錯玩'), MessageTemplateAction(label='我覺得不行', text='你不行? 你身上哪裡不行'), URITemplateAction( label='現在價錢如何', uri='https://eshop-prices.com/games/260-splatoon-2' ) ]), CarouselColumn( thumbnail_image_url='https://i.imgur.com/p3ozN9s.jpg', title='The Legend Of Zelda-Breath Of The Wild', text='薩爾達傳說之精神時光屋', actions=[ MessageTemplateAction(label='哇嗚好像很好玩', text='真的不錯玩'), MessageTemplateAction(label='我覺得不行', text='你不行? 你身上哪裡不行'), URITemplateAction( label='現在價錢如何', uri= 'https://eshop-prices.com/games/378-the-legend-of-zelda-breath-of-the-wild' ) ]) ])) return carousel_template_message def template_carousel_images(self, message): """produce template with carousel images Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] """ image_carousel_template_message = TemplateSendMessage( alt_text='ImageCarousel template', template=ImageCarouselTemplate(columns=[ ImageCarouselColumn( image_url='https://i.imgur.com/njZeeBz.jpg', action=PostbackTemplateAction(label='Splatoon 2 漆彈大作戰', text='快來打漆彈', data='action=buy&itemid=1')), ImageCarouselColumn( image_url='https://i.imgur.com/p3ozN9s.jpg', action=PostbackTemplateAction(label='薩爾達傳說之精神時光屋', text='人馬? 給虐嗎?', data='action=buy&itemid=2')) ])) return image_carousel_template_message def special_reply(self, message): """特殊處理的回覆訊息 Arguments: message {[MessageEvent.Message]} -- [the message event from user pass] Returns: [SendMessage] -- [the special message what postback to the user.] """ match_stickers = { "1-10": self.template_buttons, "1-2": self.template_confirm, "1-4": self.template_carousel_buttons, "1-13": self.template_carousel_images } if message.type == "sticker": key = "{0}-{1}".format(message.package_id, message.sticker_id) func = match_stickers.get(key, lambda s: None) return func(message) return None
class LineAdapter(Adapter): """ Adapter for LINE Messaging API Attributes ---------- bot : minette.Minette Instance of Minette channel_secret : str Channel Secret channel_access_token : str Channel Access Token parser : WebhookParser WebhookParser api : LineBotApi LineBotApi config : minette.Config Configuration timezone : pytz.timezone Timezone logger : logging.Logger Logger threads : int Number of worker threads to process requests executor : ThreadPoolExecutor Thread pool of workers debug : bool Debug mode """ def __init__(self, bot=None, *, threads=None, debug=False, channel_secret=None, channel_access_token=None, **kwargs): """ Parameters ---------- bot : minette.Minette, default None Instance of Minette. If None, create new instance of Minette by using `**kwargs` channel_secret : str, default None Channel Secret channel_access_token : str, default None Channel Access Token threads : int, default None Number of worker threads to process requests debug : bool, default None Debug mode """ super().__init__(bot=bot, threads=threads, debug=debug, **kwargs) self.channel_secret = channel_secret or \ self.config.get(section="line_bot_api", key="channel_secret") self.channel_access_token = channel_access_token or \ self.config.get(section="line_bot_api", key="channel_access_token") self.parser = WebhookParser(self.channel_secret) self.api = LineBotApi(self.channel_access_token) def handle_http_request(self, request_data, request_headers): """ Interface to chat with LINE Bot Parameters ---------- request_data : list of byte Request data from LINE Messaging API as string request_headers : dict Request headers from LINE Messaging API as dict Returns ------- response : Response Response that shows queued status """ try: events = self.parser.parse( request_data.decode("utf-8"), request_headers.get("X-Line-Signature", "")) for ev in events: if self.executor: self.executor.submit(self.handle_event, ev) else: self.handle_event(ev) return Response(messages=[Message(text="done", type="system")]) except InvalidSignatureError as ise: self.logger.error("Request signiture is invalid: " + str(ise) + "\n" + traceback.format_exc()) return Response( messages=[Message(text="invalid signiture", type="system")]) except Exception as ex: self.logger.error("Request parsing error: " + str(ex) + "\n" + traceback.format_exc()) return Response(messages=[ Message(text="failure in parsing request", type="system") ]) def handle_event(self, event): channel_messages, token = super().handle_event(event) for msg in channel_messages: if self.debug: self.logger.info(msg) else: self.logger.info("Minette> {}".format( msg.text if hasattr(msg, "text") else msg. alt_text if hasattr(msg, "alt_text") else msg.type)) self.api.reply_message(token, channel_messages) def _extract_token(self, event): """ Extract token from event Parameters ---------- event : Event Event from LINE Messaging API Returns ------- message : Message Request message object """ return event.reply_token if hasattr(event, "reply_token") else "" def _to_minette_message(self, event): """ Convert LINE Event object to Minette Message object Parameters ---------- event : Event Event from LINE Messaging API Returns ------- message : Message Request message object """ msg = Message( type=event.type, token=event.reply_token if hasattr(event, "reply_token") else None, channel="LINE", channel_detail="Messaging", channel_user_id=event.source.user_id, channel_message=event, timestamp=datetime.now(self.timezone)) if event.source.type in ["group", "room"]: if event.source.type == "group": msg.group = Group(id=event.source.group_id, type="group") elif event.source.type == "room": msg.group = Group(id=event.source.room_id, type="room") if isinstance(event, MessageEvent): msg.id = event.message.id msg.type = event.message.type if isinstance(event.message, TextMessage): msg.text = event.message.text elif isinstance(event.message, (ImageMessage, VideoMessage, AudioMessage)): msg.payloads.append( Payload( content_type=event.message.type, url="https://api.line.me/v2/bot/message/%s/content" % event.message.id, headers={ "Authorization": "Bearer {%s}" % self.channel_access_token })) elif isinstance(event.message, LocationMessage): msg.payloads.append( Payload(content_type=event.message.type, content={ "title": event.message.title, "address": event.message.address, "latitude": event.message.latitude, "longitude": event.message.longitude })) elif isinstance(event.message, StickerMessage): msg.payloads.append( Payload(content_type=event.message.type, content={ "package_id": event.message.package_id, "sticker_id": event.message.sticker_id, })) elif isinstance(event, PostbackEvent): msg.payloads.append( Payload(content_type="postback", content={ "data": event.postback.data, "params": event.postback.params })) elif isinstance(event, FollowEvent): pass elif isinstance(event, UnfollowEvent): pass elif isinstance(event, JoinEvent): pass elif isinstance(event, LeaveEvent): pass elif isinstance(event, MemberJoinedEvent): pass elif isinstance(event, MemberLeftEvent): pass elif isinstance(event, BeaconEvent): pass else: pass return msg @staticmethod def _to_channel_message(message): """ Convert Minette Message object to LINE SendMessage object Parameters ---------- response : Message Response message object Returns ------- response : SendMessage SendMessage object for LINE Messaging API """ payload = next( iter([ p for p in message.payloads if p.content_type != "quick_reply" ]), None) quick_reply = next( iter([ p.content for p in message.payloads if p.content_type == "quick_reply" ]), None) if message.type == "text": return TextSendMessage(text=message.text, quick_reply=quick_reply) elif message.type == "image": return ImageSendMessage(original_content_url=payload.url, preview_image_url=payload.thumb, quick_reply=quick_reply) elif message.type == "audio": return AudioSendMessage(original_content_url=payload.url, duration=payload.content["duration"], quick_reply=quick_reply) elif message.type == "video": return VideoSendMessage(original_content_url=payload.url, preview_image_url=payload.thumb, quick_reply=quick_reply) elif message.type == "location": cont = payload.content return LocationSendMessage(title=cont["title"], address=cont["address"], latitude=cont["latitude"], longitude=cont["longitude"], quick_reply=quick_reply) elif message.type == "sticker": return StickerSendMessage(package_id=payload.content["package_id"], sticker_id=payload.content["sticker_id"], quick_reply=quick_reply) elif message.type == "imagemap": return ImagemapSendMessage(alt_text=message.text, base_url=payload.url, base_size=payload.content["base_size"], actions=payload.content["actions"], quick_reply=quick_reply) elif message.type == "template": return TemplateSendMessage(alt_text=message.text, template=payload.content, quick_reply=quick_reply) elif message.type == "flex": return FlexSendMessage(alt_text=message.text, contents=payload.content, quick_reply=quick_reply) else: return None
def test_parse(self): file_dir = os.path.dirname(__file__) webhook_sample_json_path = os.path.join(file_dir, 'text', 'webhook.json') with open(webhook_sample_json_path) as fp: body = fp.read() parser = WebhookParser('channel_secret') # mock parser.signature_validator.validate = lambda a, b: True events = parser.parse(body, 'channel_secret') # MessageEvent, SourceUser, TextMessage self.assertIsInstance(events[0], MessageEvent) self.assertEqual(events[0].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[0].type, 'message') self.assertEqual(events[0].timestamp, 1462629479859) self.assertIsInstance(events[0].source, SourceUser) self.assertEqual(events[0].source.type, 'user') self.assertEqual(events[0].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[0].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[0].message, TextMessage) self.assertEqual(events[0].message.id, '325708') self.assertEqual(events[0].message.type, 'text') self.assertEqual(events[0].message.text, 'Hello, world') # MessageEvent, SourceRoom, TextMessage self.assertIsInstance(events[1], MessageEvent) self.assertEqual(events[1].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[1].type, 'message') self.assertEqual(events[1].timestamp, 1462629479859) self.assertIsInstance(events[1].source, SourceRoom) self.assertEqual(events[1].source.type, 'room') self.assertEqual(events[1].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[1].source.user_id, None) self.assertEqual(events[1].source.sender_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertIsInstance(events[1].message, ImageMessage) self.assertEqual(events[1].message.id, '325708') self.assertEqual(events[1].message.type, 'image') # MessageEvent, SourceUser, VideoMessage self.assertIsInstance(events[2], MessageEvent) self.assertEqual(events[2].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[2].type, 'message') self.assertEqual(events[2].timestamp, 1462629479859) self.assertIsInstance(events[2].source, SourceUser) self.assertEqual(events[2].source.type, 'user') self.assertEqual(events[2].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[2].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[2].message, VideoMessage) self.assertEqual(events[2].message.id, '325708') self.assertEqual(events[2].message.type, 'video') # MessageEvent, SourceUser, AudioMessage self.assertIsInstance(events[3], MessageEvent) self.assertEqual(events[3].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[3].type, 'message') self.assertEqual(events[3].timestamp, 1462629479859) self.assertIsInstance(events[3].source, SourceUser) self.assertEqual(events[3].source.type, 'user') self.assertEqual(events[3].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[3].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[3].message, AudioMessage) self.assertEqual(events[3].message.id, '325708') self.assertEqual(events[3].message.type, 'audio') # MessageEvent, SourceUser, LocationMessage self.assertIsInstance(events[4], MessageEvent) self.assertEqual(events[4].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[4].type, 'message') self.assertEqual(events[4].timestamp, 1462629479859) self.assertIsInstance(events[4].source, SourceUser) self.assertEqual(events[4].source.type, 'user') self.assertEqual(events[4].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[4].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[4].message, LocationMessage) self.assertEqual(events[4].message.id, '325708') self.assertEqual(events[4].message.type, 'location') self.assertEqual(events[4].message.title, 'my location') self.assertEqual(events[4].message.address, 'Tokyo') self.assertEqual(events[4].message.latitude, 35.65910807942215) self.assertEqual(events[4].message.longitude, 139.70372892916203) # MessageEvent, SourceUser, StickerMessage self.assertIsInstance(events[5], MessageEvent) self.assertEqual(events[5].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[5].type, 'message') self.assertEqual(events[5].timestamp, 1462629479859) self.assertIsInstance(events[5].source, SourceUser) self.assertEqual(events[5].source.type, 'user') self.assertEqual(events[5].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[5].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[5].message, StickerMessage) self.assertEqual(events[5].message.id, '325708') self.assertEqual(events[5].message.type, 'sticker') self.assertEqual(events[5].message.package_id, '1') self.assertEqual(events[5].message.sticker_id, '1') # FollowEvent, SourceUser self.assertIsInstance(events[6], FollowEvent) self.assertEqual(events[6].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[6].type, 'follow') self.assertEqual(events[6].timestamp, 1462629479859) self.assertIsInstance(events[6].source, SourceUser) self.assertEqual(events[6].source.type, 'user') self.assertEqual(events[6].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[6].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # UnfollowEvent, SourceUser self.assertIsInstance(events[7], UnfollowEvent) self.assertEqual(events[7].type, 'unfollow') self.assertEqual(events[7].timestamp, 1462629479859) self.assertIsInstance(events[7].source, SourceUser) self.assertEqual(events[7].source.type, 'user') self.assertEqual(events[7].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[7].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') # JoinEvent, SourceGroup self.assertIsInstance(events[8], JoinEvent) self.assertEqual(events[8].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[8].type, 'join') self.assertEqual(events[8].timestamp, 1462629479859) self.assertIsInstance(events[8].source, SourceGroup) self.assertEqual(events[8].source.type, 'group') self.assertEqual(events[8].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[8].source.user_id, None) self.assertEqual(events[8].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') # LeaveEvent, SourceGroup self.assertIsInstance(events[9], LeaveEvent) self.assertEqual(events[9].type, 'leave') self.assertEqual(events[9].timestamp, 1462629479859) self.assertIsInstance(events[9].source, SourceGroup) self.assertEqual(events[9].source.type, 'group') self.assertEqual(events[9].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[9].source.user_id, None) self.assertEqual(events[9].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') # PostbackEvent, SourceUser self.assertIsInstance(events[10], PostbackEvent) self.assertEqual(events[10].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[10].type, 'postback') self.assertEqual(events[10].timestamp, 1462629479859) self.assertIsInstance(events[10].source, SourceUser) self.assertEqual(events[10].source.type, 'user') self.assertEqual(events[10].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[10].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[10].postback.params, None) # BeaconEvent, SourceUser self.assertIsInstance(events[11], BeaconEvent) self.assertEqual(events[11].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[11].type, 'beacon') self.assertEqual(events[11].timestamp, 1462629479859) self.assertIsInstance(events[11].source, SourceUser) self.assertEqual(events[11].source.type, 'user') self.assertEqual(events[11].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[11].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[11].beacon.type, 'enter') self.assertEqual(events[11].beacon.dm, None) self.assertEqual(events[11].beacon.device_message, None) # BeaconEvent, SourceUser (with device message) self.assertIsInstance(events[12], BeaconEvent) self.assertEqual(events[12].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[12].type, 'beacon') self.assertEqual(events[12].timestamp, 1462629479859) self.assertIsInstance(events[12].source, SourceUser) self.assertEqual(events[12].source.type, 'user') self.assertEqual(events[12].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[12].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[12].beacon.hwid, 'd41d8cd98f') self.assertEqual(events[12].beacon.type, 'enter') self.assertEqual(events[12].beacon.dm, '1234567890abcdef') self.assertEqual(events[12].beacon.device_message, bytearray(b'\x124Vx\x90\xab\xcd\xef')) # MessageEvent, SourceGroup with userId, TextMessage self.assertIsInstance(events[13], MessageEvent) self.assertEqual(events[13].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[13].type, 'message') self.assertEqual(events[13].timestamp, 1462629479859) self.assertIsInstance(events[13].source, SourceGroup) self.assertEqual(events[13].source.type, 'group') self.assertEqual(events[13].source.group_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertEqual(events[13].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[13].source.sender_id, 'Ca56f94637cc4347f90a25382909b24b9') self.assertIsInstance(events[13].message, TextMessage) self.assertEqual(events[13].message.id, '325708') self.assertEqual(events[13].message.type, 'text') self.assertEqual(events[13].message.text, 'Hello, world') # MessageEvent, SourceRoom with userId, TextMessage self.assertIsInstance(events[14], MessageEvent) self.assertEqual(events[14].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[14].type, 'message') self.assertEqual(events[14].timestamp, 1462629479859) self.assertIsInstance(events[14].source, SourceRoom) self.assertEqual(events[14].source.type, 'room') self.assertEqual(events[14].source.room_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertEqual(events[14].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[14].source.sender_id, 'Ra8dbf4673c4c812cd491258042226c99') self.assertIsInstance(events[14].message, TextMessage) self.assertEqual(events[14].message.id, '325708') self.assertEqual(events[14].message.type, 'text') self.assertEqual(events[14].message.text, 'Hello, world') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[15], PostbackEvent) self.assertEqual(events[15].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[15].type, 'postback') self.assertEqual(events[15].timestamp, 1462629479859) self.assertIsInstance(events[15].source, SourceUser) self.assertEqual(events[15].source.type, 'user') self.assertEqual(events[15].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[15].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[15].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[15].postback.params['date'], '2013-04-01') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[16], PostbackEvent) self.assertEqual(events[16].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[16].type, 'postback') self.assertEqual(events[16].timestamp, 1462629479859) self.assertIsInstance(events[16].source, SourceUser) self.assertEqual(events[16].source.type, 'user') self.assertEqual(events[16].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[16].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[16].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[16].postback.params['time'], '10:00') # PostbackEvent, SourceUser, with date params self.assertIsInstance(events[17], PostbackEvent) self.assertEqual(events[17].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[17].type, 'postback') self.assertEqual(events[17].timestamp, 1462629479859) self.assertIsInstance(events[17].source, SourceUser) self.assertEqual(events[17].source.type, 'user') self.assertEqual(events[17].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[17].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[17].postback.data, 'action=buyItem&itemId=123123&color=red') self.assertEqual(events[17].postback.params['datetime'], '2013-04-01T10:00') # MessageEvent, SourceUser, FileMessage self.assertIsInstance(events[18], MessageEvent) self.assertEqual(events[18].reply_token, 'nHuyWiB7yP5Zw52FIkcQobQuGDXCTA') self.assertEqual(events[18].type, 'message') self.assertEqual(events[18].timestamp, 1462629479859) self.assertIsInstance(events[18].source, SourceUser) self.assertEqual(events[18].source.type, 'user') self.assertEqual(events[18].source.user_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertEqual(events[18].source.sender_id, 'U206d25c2ea6bd87c17655609a1c37cb8') self.assertIsInstance(events[18].message, FileMessage) self.assertEqual(events[18].message.id, '325708') self.assertEqual(events[18].message.type, 'file') self.assertEqual(events[18].message.file_name, "file.txt") self.assertEqual(events[18].message.file_size, 2138)