class ViberRequestHandler: _instance = None def __init__(self): bot_config = _get_bot_configuration() self.viber_client = Api(bot_config) self.state_machine = StateMachine(initial_state=BootStrapState()) @classmethod def create_instance(cls): if cls._instance is None: cls._instance = ViberRequestHandler() return cls._instance def parse(self, request): viber_request = self.viber_client.parse_request(request.body) if isinstance(viber_request, (ViberSubscribedRequest, ViberMessageRequest)): trigger = create_trigger_from_request(viber_request, self.viber_client) # получение пользовательского id - viber_request.sender.id # получение время обращения timestamp - viber_request.timestamp # сохранение статистики self.state_machine.fire(trigger) elif isinstance(viber_request, ViberFailedRequest): pass
def test_parse_request_unicode(): viber = Api(VIBER_BOT_CONFIGURATION) with open( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_data', 'unicode_request')) as f: req = f.read() viber_request = viber.parse_request(req) assert isinstance(viber_request, ViberMessageRequest)
def test_parse_request_not_json(): viber = Api(VIBER_BOT_CONFIGURATION) with pytest.raises(ValueError) as exc: viber.parse_request("dsfdfdsf\#")
class ViberChatBot(ChatBot): def __init__(self): name = 'Guide' avatar = 'https://st2.depositphotos.com/'\ '3146979/9765/v/950/depositphotos_97658722'\ '-stock-illustration-vector-round-icon-pile-of.jpg' auth_token = os.environ['VIBER_AUTH_TOKEN'] viber_bot_config = BotConfiguration(name=name, avatar=avatar, auth_token=auth_token) self.viber_bot = Api(viber_bot_config) def get_message(self, request): signature = request.headers.get('X-Viber-Content-Signature') if not self.viber_bot.verify_signature(request.get_data(), signature): raise 'Invalid Signature!', signature viber_request = self.viber_bot.parse_request(request.get_data()) if isinstance(viber_request, ViberMessageRequest): user_id = viber_request.sender.id text = viber_request.message.text return {'user_id': user_id, 'text': text} elif isinstance(viber_request, ViberSubscribedRequest): pass elif isinstance(viber_request, ViberSeenRequest): pass elif isinstance(viber_request, ViberDeliveredRequest): pass elif isinstance(viber_request, ViberFailedRequest): raise 'client failed receiving message. failure: {0}'.format( viber_request) else: pass def send_message(self, user_id, answer): viber_answer = [] if 'text' in answer: text_message = TextMessage(text=answer['text']) viber_answer.append(text_message) if 'image' in answer: url_prefix = 'https://drive.google.com/uc?export=download&id=' image_url = url_prefix + answer['image'] picture_message = PictureMessage(media=image_url) viber_answer.append(picture_message) if 'options' in answer: keyboard = self.create_keyboard(answer['options']) keyboard_message = KeyboardMessage(tracking_data=None, keyboard=keyboard) viber_answer.append(keyboard_message) self.viber_bot.send_messages(user_id, viber_answer) def create_keyboard(self, connections): keyboard = {} keyboard['Type'] = 'keyboard' keyboard['Buttons'] = map(self.create_button, connections) return keyboard def create_button(self, connection): button = {} button['Columns'] = 6 button['Rows'] = 1 button['Text'] = '<font color="#000000"><b>' + \ connection + '</b></font>' button['TextSize'] = 'large' button['TextHAlign'] = 'center' button['TextVAlign'] = 'middle' button['ActionType'] = 'reply' button['ActionBody'] = connection button['BgColor'] = '#b9e6f5' return button
class ViberFlaskWrapper(Flask): # Stores session states sessionStorage = {} # Viber object for API interaction viber = None # Yandex Disk object for API interaction disk = None # List of users allowed to use bot allowedUsers = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.add_url_rule('/message', view_func=self.message, methods=['POST']) bot_configuration = BotConfiguration( name=os.environ['VIBERBOT_NAME'], avatar=os.environ['VIBERBOT_AVATAR'], auth_token=os.environ['VIBERBOT_TOKEN']) self.viber = Api(bot_configuration) self.allowedUsers = os.environ['VIBERBOT_ALLOWED_USERS'] self.disk = YadiskWrapper(os.environ['YADISK_TOKEN']) def message(self): """Retrieves request body and generates response""" logging.debug("Processing new request") # verify message signature if not self.viber.verify_signature( request.get_data(), request.headers.get('X-Viber-Content-Signature')): logging.debug("User invalid signature: %s", request.headers.get('X-Viber-Content-Signature')) return Response(status=403) # this library supplies a simple way to receive a request object viber_request = self.viber.parse_request(request.get_data()) # process only message requests if not isinstance(viber_request, ViberMessageRequest): logging.debug("Message is not ViberMessageRequest") return Response(status=200) # check if user allowed to iteract with bot if not viber_request.sender.id in self.allowedUsers: logging.debug("Unknown user: %s", viber_request.sender.id) message = TextMessage(text="403") self.viber.send_messages(viber_request.sender.id, [message]) return Response(status=200) # fix for duplicate message processing if viber_request.message_token not in self.sessionStorage: self.sessionStorage[viber_request.message_token] = 'processing' else: return Response(status=200) # Process Text message if isinstance(viber_request.message, TextMessage): logging.debug("Processing TextMessage") request_text = viber_request.message.text response_text = 'Saving...' message = TextMessage(text=response_text) self.viber.send_messages(viber_request.sender.id, [message]) # Create saving thread logging.debug("Starting Saving Thread") self.thread_save_to_disk(viber_request.sender.id, request_text, None) # save_thread = threading.Thread( # target=self.thread_save_to_disk, # args=( # viber_request.sender.id, # request_text, # None)) # save_thread.start() # Process Picture, Video and File messages elif isinstance(viber_request.message, PictureMessage) \ or isinstance(viber_request.message, VideoMessage) \ or isinstance(viber_request.message, FileMessage): logging.debug("Processing FileMessage") url = viber_request.message.media # URL of sent file response_text = 'Saving...' message = TextMessage(text=response_text) self.viber.send_messages(viber_request.sender.id, [message]) # Create saving thread logging.debug("Starting Saving Thread") self.thread_save_to_disk(viber_request.sender.id, None, url) # save_thread = threading.Thread( # target=self.thread_save_to_disk, # args=( # viber_request.sender.id, # None, # url)) # save_thread.start() # Process Location message elif isinstance(viber_request.message, LocationMessage): logging.debug("Processing LocationMessage") request_text = str(viber_request.message.location) response_text = 'Saving...' message = TextMessage(text=response_text) self.viber.send_messages(viber_request.sender.id, [message]) # Create saving thread logging.debug("Starting Saving Thread") self.thread_save_to_disk(viber_request.sender.id, request_text, None) # save_thread = threading.Thread( # target=self.thread_save_to_disk, # args=( # viber_request.sender.id, # request_text, # None)) # save_thread.start() # Process other messages else: logging.debug("Received unsupported message") response_text = 'Not supported yet' message = TextMessage(text=response_text) self.viber.send_messages(viber_request.sender.id, [message]) # fix for duplicate message processing if viber_request.message_token in self.sessionStorage: del self.sessionStorage[viber_request.message_token] return Response(status=200) def thread_save_to_disk(self, user_id, note, file_url): """Saves data to Yandex Disk and sends report to user_id""" logging.debug("Saving Thread started") response_text = 'Saved' # if text note provided if note: logging.debug("Saving note") if not self.disk.save_note(note): response_text = 'Cannot save note.' # if file provided if file_url: logging.debug("Saving file") # extract filename from URL url_parsed = urlparse(file_url) filename = os.path.basename(url_parsed.path) # download file to temp directory with urllib.request.urlopen(file_url) as response: logging.debug("Downloading file from Viber server") data = response.read() logging.debug("Uploading file to Disk") # upload file to Disk if not self.disk.save_file(filename, data): response_text = 'Cannot save file.' message = TextMessage(text=response_text) self.viber.send_messages(user_id, [message]) return
class ViberWebhook: def __init__(self, appConfig, dbUsers = 'vb_users.json') -> None: http_tunnel = ngrok.connect() self.public_url = http_tunnel.public_url.replace('http', 'https') print('Public URL acquired: ' + self.public_url) self.usersDb = TinyDB(dbUsers) self.app = Flask(__name__) self.config = appConfig self.viber = Api(BotConfiguration( name = self.config.getProperty('Publishers.Viber.Name'), avatar = self.config.getProperty('Publishers.Viber.Avatar'), auth_token = self.config.getProperty('Publishers.Viber.Token') )) self.query = Query() ## Delayed webhook setup scheduler = sched.scheduler(time.time, time.sleep) scheduler.enter(5, 1, self.set_webhook, (self.viber,)) t = threading.Thread(target=scheduler.run) t.start() self.app.add_url_rule('/', 'incoming', self.incoming, methods=['POST']) self.app.add_url_rule('/ctrl', '', self.control, methods=['POST', 'GET']) self.t_webApp = threading.Thread(target=self.flaskThread) self.t_webApp.setDaemon(True) print("Viber worker created.") def __del__(self): self.usersDb.close() def flaskThread(self): self.app.run(host='0.0.0.0', port=80, debug=False) def Run(self): self.t_webApp.run() def GetAdmins(self): admins = self.usersDb.search(self.query.admin == '1') return admins def NotifyAdmins(self, admins, message): for admin in admins: self.viber.send_messages(admin['id'], [ TextMessage(text = message) ]) def IsAdmin(self, user_id, admins): return next((admin for admin in admins if admin['id'] == user_id), None) != None def Reboot(): command = "/usr/bin/sudo /sbin/shutdown -r now" import subprocess process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) output = process.communicate()[0] print(output) def RestartViber(): command = "service Viber restart" import subprocess process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) output = process.communicate()[0] print(output) def incoming(self): admins = self.GetAdmins() print(request.path) viber_request = self.viber.parse_request(request.get_data().decode('utf8')) if isinstance(viber_request, ViberMessageRequest): message = viber_request.message if isinstance(message, TextMessage): is_admin = self.IsAdmin(viber_request.sender.id, admins) if is_admin: print("IsAdmin: True") ## HANDLE ADMIN REQUESTS usersListStr = '' if(message.text.strip() == "/ListUsers" and is_admin): for user in self.usersDb.all(): usersListStr += user['name'] + '\n' self.NotifyAdmins(admins, 'Korisnici: \n' + usersListStr) return Response(status=200) if(message.text.strip() == "/ListAdmins" and is_admin): for user in self.usersDb.search(self.query.admin == '1'): usersListStr += user['name'] + '\n' self.NotifyAdmins(admins, 'Administratori: \n' + usersListStr) return Response(status=200) if(message.text.strip() == "/GetPublicURL" and is_admin): self.NotifyAdmins(admins, 'Javna adresa: \n' + self.public_url) return Response(status=200) if(message.text.strip() == "/GetLocalIP" and is_admin): self.NotifyAdmins(admins, 'Lokalna adresa: \n' + socket.gethostbyname(socket.gethostname())) return Response(status=200) if(message.text.strip() == "/XRebootMe" and is_admin): self.NotifyAdmins(admins, 'Rebooting...') self.Reboot() return Response(status=200) if(message.text.strip() == "/XRestartViberService" and is_admin): self.NotifyAdmins(admins, 'Restarting Viber service...') self.RestartViber() return Response(status=200) UserQ = Query() # Handle standard requests if message.text.strip().lower() == 'stop': self.usersDb.update({'active': '0'}, UserQ.id == viber_request.sender.id) else: if len(self.usersDb.search(UserQ.id == viber_request.sender.id)) == 0: self.usersDb.insert({'id': viber_request.sender.id, 'name': viber_request.sender.name, 'active': '1', 'admin': '0'}) else: self.usersDb.update({'active': '1'}, UserQ.id == viber_request.sender.id) self.viber.send_messages(viber_request.sender.id, [ TextMessage(text = 'Uspešna prijava! Pošalji STOP za odjavu.') ]) #self.viber.send_messages("/qNmzm5H8vXHIuuJAmJZvw==", [ TextMessage(text = 'Novi korisnik: ' + viber_request.sender.name) ]) self.NotifyAdmins(admins, 'Novi korisnik: ' + viber_request.sender.name) elif isinstance(viber_request, ViberConversationStartedRequest): UserQ = Query() #self.viber.send_messages(viber_request.user.id, [ TextMessage(text='Za prijavu pošaljite bilo kakvu poruku.') ]) if len(self.usersDb.search(UserQ.id == viber_request.user.id)) == 0: self.usersDb.insert({'id': viber_request.user.id, 'name': viber_request.user.name, 'active': '1', 'admin': '0'}) else: self.usersDb.update({'active': '0'}, UserQ.id == viber_request.user.id) elif isinstance(viber_request, ViberSubscribedRequest): UserQ = Query() self.viber.send_messages(viber_request.user.id, [ TextMessage(text='Za prijavu pošaljite bilo kakvu poruku.') ]) if len(self.usersDb.search(UserQ.id == viber_request.user.id)) == 0: self.usersDb.insert({'id': viber_request.user.id, 'name': viber_request.user.name, 'active': '1', 'admin': '0'}) else: self.usersDb.update({'active': '1'}, UserQ.id == viber_request.user.id) elif isinstance(viber_request, ViberUnsubscribedRequest): UserQ = Query() self.usersDb.update({'active': '0'}, UserQ.id == viber_request.user_id) elif isinstance(viber_request, ViberFailedRequest): logger.warn("client failed receiving message. failure: {0}".format(viber_request)) return Response(status=200) def control(self): admins = self.GetAdmins() #data = request.get_data().decode('utf8') if(request.args.get('command') == 'users'): if(request.args.get('a') == '0'): usersListStr = "" for user in self.usersDb.all(): usersListStr += user['name'] + ';' return Response(status=200, response=usersListStr) else: usersListStr = "" for user in self.usersDb.search(self.query.admin == '1'): usersListStr += user['name'] + ';' return Response(status=200, response=usersListStr) def set_webhook(self, viber): self.viber.set_webhook(self.public_url)