def message(event_dict): # 位置情報が送られてきた時 if event_dict['message']['type'] == 'location': station = module.isStation((event_dict['message']['latitude'],event_dict['message']['longitude'])) if not station: line_api.reply_message(event_dict['replyToken'], '近くに駅が存在しません') return if len(station) > 5: station = station[:4] line_api.reply_template_button(event_dict['replyToken'], '駅を選択して下さい', '駅を選択してください', station) return # 画像が送られてきた時 if event_dict['message']['type'] == 'image': l = LineBotApi(setting.ACCESS_TOKEN) content = l.get_message_content(event_dict['message']['id']) uuid = str(module.issueUnique()) file_path = 'contents/user/img/'+uuid+'.png' with open(file_path, 'wb') as fd: for chunk in content.iter_content(): fd.write(chunk) tempDB.add(id=event_dict['source']['userId'], type='image', value=uuid+'.png', timestamp=event_dict['timestamp']) # テキストが送られてきた時 elif event_dict['message']['type'] == 'text': tempDB.add(id=event_dict['source']['userId'], type='text', value=event_dict['message']['text'], timestamp=event_dict['timestamp']) line_api.reply_message(reply_token=event_dict['replyToken'], text='位置情報を送信してください')
def save_image(imageid, path): line_bot_api = LineBotApi(channelaccesstoken) message_content = line_bot_api.get_message_content(imageid) print('message_content:{}'.format(type(message_content))) with open(path, mode='wb') as fd: for chunk in message_content.iter_content(): fd.write(chunk)
def lambda_handler(even, context): for e in json.loads(even['body'])['events']: if e['type'] == 'message': if e['message']['type'] == 'image': # LINE authentication line_bot_api = LineBotApi(os.environ['channel_access_token']) handler = WebhookHandler(os.environ['channel_secret']) # extract image from message message_id = e['message']['id'] message_content = line_bot_api.get_message_content(message_id) image_path = FileIO.write_image_from_message(message_content) # image processing FF = FaceFinder(image_path) other = FF.index_from_collection() celebrity = FF.index_from_celebrities() print(other, celebrity) # generate token and write static to s3 token = uuid.uuid4().hex line_bot_api.reply_message(e['replyToken'], generateOption(token)) file_path = FileIO.write_token_json(token, { 'other': other, 'celebrity': celebrity }) FF.s3_client.upload_file(Filename=file_path, Bucket=os.environ['Bucket'], Key='to-push/' + token + '.json', ExtraArgs={'ACL': 'public-read'}) elif e['type'] == 'postback': # LINE authentication line_bot_api = LineBotApi(os.environ['channel_access_token']) handler = WebhookHandler(os.environ['channel_secret']) params = {k: v for k, v in parse_qs(e['postback']['data']).items()} if params: data = requests.get( 'https://%s.s3-ap-northeast-1.amazonaws.com/to-push/%s.json' % (os.environ['Bucket'], params['token'][0])).json() result = data[params['type'][0]] if result: contents = FlexSendMessage(alt_text='result', contents=generateReply(result)) else: contents = TextSendMessage( text='Sorry, No similar face has been found in %s' % params['type'][0]) line_bot_api.reply_message(e['replyToken'], contents)
def get_audio_content(event): line_bot_api = LineBotApi(YOUR_CHANNEL_ACCESS_TOKEN) audio_content = line_bot_api.get_message_content(event.message.id) with open(FILE_PATH + FILE_NAME, 'wb') as fd: for chunk in audio_content.iter_content(): fd.write(chunk) sound = AudioSegment.from_file(FILE_NAME, format="m4a") sound.export("audio_message", format="mp3") text_transcripted = speechtranscript.transcript(file_name=FILE_NAME) line_bot_api.reply_message(event.reply_token, TextSendMessage(text=text_transcripted))
def reply_message(request): request_json = request.get_json(silent=True) if request_json: reply_token = request_json['events'][0]['replyToken'] message_type = request_json['events'][0]['message']['type'] user_id = request_json['events'][0]['source']['userId'] line_bot_api = LineBotApi(config.get('ACCESS_TOKEN')) target_lang = 'en' if message_type == 'text': user_message = request_json['events'][0]['message']['text'] reply_text = translate_text(target_lang, user_message) elif message_type == 'image': message_id = message_type = request_json['events'][0]['message']['id'] print(message_id) reply_text = message_id try: message_content = line_bot_api.get_message_content(message_id) except LineBotApiError as e: raise ValuError(e) detected_text = detect_text(message_content.content) reply_text = translate_text(target_lang, detected_text) try: line_bot_api.reply_message(reply_token, TextSendMessage(text=reply_text)) except LineBotApiError as e: raise ValueError(e) else: raise ValueError('Invalid request.')
def upload_img(event): line_bot_api = LineBotApi(channel_access_token) static_tmp_path = os.path.join(os.path.dirname(__file__)) ext = 'png' message_content = line_bot_api.get_message_content(event.message.id) with tempfile.NamedTemporaryFile(dir=static_tmp_path, prefix=ext + '-', delete=False) as tf: for chunk in message_content.iter_content(): tf.write(chunk) tempfile_path = tf.name dist_path = tempfile_path + '.' + ext dist_name = os.path.basename(dist_path) os.rename(tempfile_path, dist_path) im = Image.open(dist_path) width, height = im.size client = ImgurClient(client_id, client_secret, access_token, refresh_token) path = os.path.join(dist_name) upimg = client.upload_from_path(path, config=None, anon=False) os.remove(path) print(upimg['link']) push_msg(event.source.user_id, "上傳成功") return upimg, width, height
def disp_moon(request): reply = "" request_json = json.loads(request.body.decode('utf-8')) # to get json for e in request_json['events']: reply_token = e['replyToken'] # to get reply_token message_type = e['message']['type'] # to get type # reply for test if message_type == 'text': text = e['message']['text'] # to get message # rep_meg = client.talk(text)["results"][0]["reply"] # to get reply message by recuitTalk reply += reply_text(reply_token, text) # reply for image if message_type == 'image': line_bot_api = LineBotApi(ACCESS_TOKEN) message_id = e['message']['id'] # to get messageID message_content = line_bot_api.get_message_content(message_id) img_pil = Image.open(io.BytesIO( message_content.content)) # バイナリストリーム -> PILイメージ img_np = np.asarray(img_pil) # PIL -> numpy配列(RGBA) img_np_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGBA2BGR) # RGBA -> BGR img_np_gray = cv2.cvtColor(img_np_bgr, cv2.COLOR_BGR2GRAY) # BGR -> GRAY ret, thresh_img = cv2.threshold(img_np_gray, 0, 255, cv2.THRESH_OTSU) # moon_img = img2moon(thresh_img) # moon_img.creat_moon_index_list() reply += reply_text(reply_token, text=moon_img.get_text()) return HttpResponse(reply) # for test
class TestLineBotApi(unittest.TestCase): def setUp(self): self.tested = LineBotApi('channel_secret') @responses.activate def test_error_handle(self): responses.add(responses.POST, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/push', json={"message": "Invalid reply token"}, status=401) try: self.tested.push_message('to', TextSendMessage(text='hoge')) except LineBotApiError as e: self.assertEqual(e.status_code, 401) self.assertEqual(e.error.message, 'Invalid reply token') @responses.activate def test_error_with_detail_handle(self): responses.add(responses.POST, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/push', json={ "message": "The request body has 2 error(s)", "details": [{ "message": "May not be empty", "property": "messages[0].text" }, { "message": "Must be one of the following values: [text" ", image, video, audio, location, sticker, " "richmessage, template, imagemap]", "property": "messages[1].type" }] }, status=400) try: self.tested.push_message('to', TextSendMessage(text='hoge')) except LineBotApiError as e: self.assertEqual(e.status_code, 400) self.assertEqual(e.error.message, 'The request body has 2 error(s)') self.assertEqual(e.error.details[0].message, 'May not be empty') self.assertEqual(e.error.details[0].property, 'messages[0].text') self.assertEqual( e.error.details[1].message, "Must be one of the following " "values: [text" ", image, video, audio, " "location, sticker, " "richmessage, template, imagemap]") self.assertEqual(e.error.details[1].property, 'messages[1].type') @responses.activate def test_error_handle_get_message_content(self): responses.add(responses.GET, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/1/content', json={"message": "Invalid reply token"}, status=404) try: self.tested.get_message_content(1) except LineBotApiError as e: self.assertEqual(e.status_code, 404) self.assertEqual(e.error.message, 'Invalid reply token')
def identify_request(request): reply = "" request_json = json.loads(request.body.decode('utf-8')) # to get json for e in request_json['events']: reply_token = e['replyToken'] # to get reply_token message_type = e['message']['type'] # to get type # テキスト形式ならword2vecの結果を返す if message_type == 'text': text = e['message']['text'] # to get message model_surface = word2vec.Word2Vec.load(BASE_DIR + '/data/surface.model') if text in model_surface: reply += reply_result_in_word2vec(text, model_surface, reply_token) else: reply += reply_text(reply_token, "指定された単語が辞書に存在しません.") # reply for image if message_type == 'image': line_bot_api = LineBotApi(ACCESS_TOKEN) message_id = e['message']['id'] # to get messageID message_content = line_bot_api.get_message_content(message_id) img_pil = Image.open(io.BytesIO( message_content.content)) # バイナリストリーム -> PILイメージ img_np = np.asarray(img_pil) # PIL -> numpy配列(RGBA) img_np_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGBA2BGR) # RGBA -> BGR img_np_gray = cv2.cvtColor(img_np_bgr, cv2.COLOR_BGR2GRAY) # BGR -> GRAY ret, thresh_img = cv2.threshold(img_np_gray, 0, 255, cv2.THRESH_OTSU) moon_img = img2moon(thresh_img) moon_img.creat_moon_index_list() reply += reply_text(reply_token, text=moon_img.get_text()) return HttpResponse(reply) # for test # @csrf_exempt # def webhook(request): # if request.method != 'POST': # return HttpResponse('ん?なんやようか?', status=405) # signature = request.META['HTTP_X_LINE_SIGNATURE'] # body = request.body.decode('utf-8') # try: # events = parser.parse(body, signature) # except InvalidSignatureError: # return HttpResponseForbidden() # except LineBotApiError: # return HttpResponseBadRequest() # for event in events: # if not isinstance(event, MessageEvent): # continue # if not isinstance(event.message, TextMessage): # continue # text_send_message = TextSendMessage(text=event.message.text) # line_bot_api.reply_message( # event.reply_token, # text_send_message # ) # return HttpResponse(status=200)
def get_image_content(imageid): line_bot_api = LineBotApi(channelaccesstoken) message_content = line_bot_api.get_message_content(imageid) return message_content.content
class LineAPI(): def __init__(self, code): self.bot = LineBotApi(code) def replyText(self, token, text): try: return self.bot.reply_message(token, TextSendMessage(text=text)) except Exception as e: raise e def replyImage(self, token, url): try: return self.bot.reply_message(token, ImageSendMessage(original_content_url=url, preview_image_url=url)) except Exception as e: raise e def replyAudio(self, token, url, duration=1): try: return self.bot.reply_message(token, AudioSendMessage(original_content_url=url, duration=duration)) except Exception as e: raise e def replyVideo(self, token, url, thumbnail): try: return self.bot.reply_message(token, VideoSendMessage(original_content_url=url, preview_image_url=thumbnail)) except Exception as e: raise e def replyLocation(self, token, title, address, lat, lng): try: return self.bot.reply_message(token, LocationSendMessage(title=title, address=address, latitude=lat, longitude=lng)) except Exception as e: raise e def replyCarrousel(self, token, data): try: return self.bot.reply_message(token, TemplateSendMessage(alt_text=data['alt'], template=data['template'])) except Exception as e: raise e def replySticker(self, token, package, sticker): try: return self.bot.reply_message(token, StickerSendMessage(package_id=str(package), sticker_id=str(sticker))) except Exception as e: raise e def replyButtons(self, token, data): try: return self.bot.reply_message(token, TemplateSendMessage(alt_text=data['alt'], template=ButtonsTemplate(thumbnail_image_url = data['thumbnail'], title = data['title'], text = data['text'], actions = data['action']))) except Exception as e: raise e def replyConfirm(self, token, data): try: return self.bot.reply_message(token, TemplateSendMessage(alt_text=data['alt'], template=ConfirmTemplate(text=data['text'], actions=data['action']))) except Exception as e: raise e def replyCustom(self, token, custom): try: return self.bot.reply_message(token, custom) except Exception as e: raise e def textMessage(self, text): return TextSendMessage(text=text) def imageMessage(self, url): return ImageSendMessage(original_content_url=url, preview_image_url=url) def audioMessage(self, url, duration=1): return AudioSendMessage(original_content_url=url, duration=duration) def videoMessage(self, url, thumbnail): return VideoSendMessage(original_content_url=url, preview_image_url=thumbnail) def locationMessage(self, title, address, lat lng): return LocationSendMessage(title=title, address=address, latitude=lat, longitude=lng) def templateMessage(self, data): return TemplateSendMessage(alt_text=data['alt'], template=data['template']) def stickerMessage(self, package, sticker): return StickerSendMessage(package_id=str(package), sticker_id=str(sticker)) def getProfile(self, userID): try: return self.bot.get_profile(userID) except Exception as e: raise e def leaveGroup(self, groupID): try: return self.bot.leave_group(groupID) except Exception as e: raise e def leaveRoom(self, roomID): try: return self.bot.leave_room(roomID) except Exception as e: raise e def getContent(self, messageID): try: return self.bot.get_message_content(messageID) except Exception as e: raise e def actionBuilder(self,amount, type, param1, param2): try: built = [] for i in range(0, amount): if type[i] == 'msg': ap = MessageTemplateAction(label=param1[i], text=param2[i]) elif type[i] == 'uri': ap = URITemplateAction(label=param1[i], uri=param2[i]) elif type[i] == 'pbk': ap = PostbackTemplateAction(label=param1[i], data=param2[i]) built.append(ap) return built except Exception as e: raise e def templateBuilder(self, amount, type, data): try: colum = [] for i in range(0, amount): if type =='tmp': ap = CarouselColumn( thumbnail_image_url=data[i]['thumbnail'], title=data[i]['title'], text=data[i]['text'], actions=data[i]['action'] ) elif type == 'img': ap = ImageCarouselColumn( image_url=data[i]['thumbnail'], action=data[i]['action'] ) colum.append(ap) if type == 'tmp': return CarouselTemplate(columns=colum) elif type == 'img': return ImageCarouselTemplate(columns=colum) except Exception as e: raise e
class TestLineBotApi(unittest.TestCase): def setUp(self): self.tested = LineBotApi('channel_secret') @responses.activate def test_error_handle(self): responses.add( responses.POST, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/push', json={ "message": "Invalid reply token" }, status=401 ) try: self.tested.push_message('to', TextSendMessage(text='hoge')) except LineBotApiError as e: self.assertEqual(e.status_code, 401) self.assertEqual(e.error.message, 'Invalid reply token') @responses.activate def test_error_with_detail_handle(self): responses.add( responses.POST, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/push', json={ "message": "The request body has 2 error(s)", "details": [ { "message": "May not be empty", "property": "messages[0].text" }, { "message": "Must be one of the following values: [text" ", image, video, audio, location, sticker, " "richmessage, template, imagemap]", "property": "messages[1].type" } ] }, status=400 ) try: self.tested.push_message( 'to', TextSendMessage(text='hoge') ) except LineBotApiError as e: self.assertEqual(e.status_code, 400) self.assertEqual(e.error.message, 'The request body has 2 error(s)') self.assertEqual(e.error.details[0].message, 'May not be empty') self.assertEqual(e.error.details[0].property, 'messages[0].text') self.assertEqual( e.error.details[1].message, "Must be one of the following " "values: [text" ", image, video, audio, " "location, sticker, " "richmessage, template, imagemap]" ) self.assertEqual(e.error.details[1].property, 'messages[1].type') @responses.activate def test_error_handle_get_message_content(self): responses.add( responses.GET, LineBotApi.DEFAULT_API_ENDPOINT + '/v2/bot/message/1/content', json={ "message": "Invalid reply token" }, status=404 ) try: self.tested.get_message_content(1) except LineBotApiError as e: self.assertEqual(e.status_code, 404) self.assertEqual(e.error.message, 'Invalid reply token')
class EventHandler: """ Handles LINE webhook events. """ MIME_TYPE_MAP = { "image/jpeg": "jpg", "image/png": "png", "image/gif": "gif" } def __init__(self, processing_dir=None, line_api=None): # configure event handling self.event_router = {"message": self.handle_message} # load configuration options from config file try: with open("config.json") as f: self.config = json.loads(f.read()) except: print("Missing or invalid configuration file: config.json") self.config = {} if "cv_key" not in self.config or "cv_region" not in self.config: print( "Config file must contain 'cv_key' and 'cv_region' settings.") exit() # set the processing directory # use the config value if constructor was not provided a value if processing_dir is None: if "processing_dir" in self.config: self.processing_dir = self.config["processing_dir"] else: self.processing_dir = "." else: self.processing_dir = processing_dir if not os.path.exists(self.processing_dir): os.makedirs(self.processing_dir) # set image URL root and LINE API implementation self.image_root_url = self.config["image_root_url"] if line_api is None: self.line_api = LineBotApi(self.config["line_channel_token"]) else: self.line_api = line_api """ Handles a web hook request. Each request message may contain multiple events that will need to be handled. :return: The LINE message sent in reply (if applicable). """ def handle(self, request): # handle each event in the request in order for event in request["events"]: if event["type"] in self.event_router: # event we can handle # TODO: try/except here? reply_msg = self.event_router[event["type"]](event) return reply_msg def handle_message(self, event): msg = event["message"] reply_token = event["replyToken"] reply_msg = None # only handle line messages if msg["type"] == "image": if msg["contentProvider"]["type"] != "line": raise Exception("Unable to handle message type: " + msg["contentProvider"]["type"]) # fetch binary data from content endpoint source_image = self.fetch_line_content(msg["id"]) # generate the haiku haiku_images = self.generate_haiku(source_image) # and send the response reply_msg = self.format_message_image(haiku_images[0], haiku_images[1]) self.line_api.reply_message(reply_token, reply_msg) else: print("Unable to handle type: " + msg["type"]) return reply_msg def format_message_image(self, image, thumbnail): """ Formats a LINE image message. :return: A formatted LINE message to be sent. """ msg = ImageSendMessage(original_content_url=urllib.parse.urljoin( self.image_root_url, os.path.basename(image)), preview_image_url=urllib.parse.urljoin( self.image_root_url, os.path.basename(thumbnail))) return msg """ Returns the correct file extension to use for the specified MIME type. :return: The extension, e.g., "jpg" or "png". """ def get_file_extension(self, mime_type): match = re.match(r"image/(\w+)", mime_type) if match: return match.group(1) return "dat" """ Fetches the LINE content for the specified message by ID. :return: The relative path/filename of the file created. """ def fetch_line_content(self, message_id): message_content = self.line_api.get_message_content(message_id) if message_content.response.status_code == 200: # write file content to disk content_type = message_content.response.headers["Content-Type"] filename = f"{self.processing_dir}/{message_id}.{self.get_file_extension(content_type)}" with open(filename, "wb") as f: for chunk in message_content.iter_content(): f.write(chunk) else: raise Exception( "Error attempting to fetch content from the LINE API") return filename """ Generates a haiku from an image overlaid on said image. :return: Tuple of generated image paths -- (haiku image, thumbnail). """ def generate_haiku(self, source_image): image_prefix = os.path.splitext(os.path.basename(source_image))[0] # generate haiku describer = ImageDescriber(self.config["cv_key"], self.config["cv_region"]) haikoo = Haikoo(describer, "fusion") result = haikoo.create_image( source_image, os.path.join(self.processing_dir, image_prefix + "_haikoo.png")) # generate thumbnail thumbnail = haikoo.create_thumbnail( result.image, os.path.join(self.processing_dir, image_prefix + "_thumb.png"), 256, 256) return (result.image, thumbnail)
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)
def webhook(channel_id): channel = Channel() webhook = Webhook() user = User() msg = Msg() chat = Chat() jsondata = request.get_json() # print("Webhook") jsondata["channel_id"] = channel_id # 連線 1 channel_data = channel.get_channel(channel_id) channel_access_token = channel_data["channel_access_token"] event = jsondata["events"][0] user_id = event["source"]["userId"] jsondata["user_id"] = user_id print(jsondata) # 連線 2 webhook.add_log(jsondata) if jsondata["channel_id"] == "1654006407": if event['type'] != 'message': print(event['type']) print('1654006407.....break') return '1654006407.....' # if event['type'] != 'message': # print('1654006407.....break') # return '1654006407.....break' # else: # if jsondata["channel_id"] == "1654006407": # print('1654006407.....message') # return '1654006407.....message' # 設定用戶追蹤狀態 # 連線 3 webhook.setfollow(channel_id, jsondata) # 使用者紀錄 if (user.chk_once(user_id, channel_id) == True): user.set_user_tag(user_id, channel_id, event['type']) else: user.add_once(user_id, 0, channel_id, channel_access_token) user.set_user_tag(user_id, channel_id, event['type']) # 如果有回覆碼可以用 開始自動處理判斷 try: # 如果有回覆碼可以用 if "replyToken" in event: replyToken = event["replyToken"] line_bot_api = LineBotApi(channel_access_token) user_data = user.get_once(user_id, channel_id) # 開始追縱歡迎 if event['type'] == 'follow': if 'welcome_msg' in channel_data: rebot_text = channel_data['welcome_msg'] line_bot_api.reply_message( replyToken, TextSendMessage(text=rebot_text)) if "message" in event: # 整理聊天室需要的基本資料格式 chat_data = { "user_id": user_id, "channel_id": channel_id, "replyToken": replyToken, "read_status": 0, "name": user_data['name'], "avator": user_data['avator'], "originator": "user", "id": event['message']['id'] } # 如果對方傳純文字訊息 if event['message']['type'] == "text": msg_data = msg.chk_listen_keyword(channel_id, event['message']['text']) # 判斷腳本 if msg_data != False: msg_id = msg_data['msg_id'] msg.reply_message(channel_id, msg_id, replyToken, user_id) else: # 判斷自動回應時間 rebot_text = chat.chk_auto_reply_time(channel_id) # print(rebot_text) if rebot_text != False: line_bot_api.reply_message( replyToken, TextSendMessage(text=rebot_text)) chat_data['text'] = event['message']['text'] chat_data['type'] = event['message']['type'] chat.add_chat(chat_data) else: # 判斷自動回應時間 rebot_text = chat.chk_auto_reply_time(channel_id) if rebot_text != False: line_bot_api.reply_message( replyToken, TextSendMessage(text=rebot_text)) # 如果是圖片 chat_data['type'] = event['message']['type'] message_content = line_bot_api.get_message_content( event['message']['id']) # 把資料檔案從 line 取回 file_name = event['message']['id'] + '.jpg' image_data = request.url_root + 'static/' + file_name with open('./static/' + file_name, 'wb') as fd: for chunk in message_content.iter_content(): fd.write(chunk) chat_data['src'] = image_data chat.add_chat(chat_data) # print("OK") # line_bot_api.reply_message(replyToken, TextSendMessage(text='Hello World!')) except (EOFError, KeyboardInterrupt): print(EOFError) print(KeyboardInterrupt) return "replyToken"