Beispiel #1
0
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
Beispiel #2
0
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)
Beispiel #3
0
def test_parse_request_not_json():
    viber = Api(VIBER_BOT_CONFIGURATION)

    with pytest.raises(ValueError) as exc:
        viber.parse_request("dsfdfdsf\#")
Beispiel #4
0
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
Beispiel #5
0
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
Beispiel #6
0
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)