Пример #1
0
def test_verify_signature():
    valid_signature = 'd21b343448c8aee33b8e93768ef6ceb64a6ba6163099973a2b8bd028fea510ef'
    message = "{\"event\":\"webhook\",\"timestamp\":4977069964384421269,\"message_token\":1478683725125}".encode(
        "utf-8")

    viber = Api(VIBER_BOT_CONFIGURATION)
    assert viber.verify_signature(message, valid_signature)
Пример #2
0
 def test_viber_verify_signature(self):
     bot_configuration = BotConfiguration(
         name='SKableBot',
         avatar='http://viber.com/avatar.jpg',
         auth_token=settings.VIBER_BOT_TOKEN)
     viber = Api(bot_configuration)
     request_header = '95a958f90bb88a0ef5312c2511e1c56650b968a37eb75f1e7326ebf52c1c9667'
     message = b'{"event":"message","timestamp":1524508607514,"message_token":5169273451608157073,"sender":{"id":"u2NYcWleYQgJQkgYdDhfzg==","name":"\\u0410\\u043B\\u0435\\u043A\\u0441\\u0430\\u043D\\u0434\\u0440 \\u0417\\u0430\\u0439\\u043A\\u0438\\u043D","avatar":"https://media-direct.cdn.viber.com/download_photo?dlid=PSF1BfvQJ9A1-blH6-shscNy-C3JnI4ax6BfvHWwdLyEAVEN_Obs-JD0DrUo6yVL_Y4Mokd8iyLhBA9a-KMVC7mXjWOEe5xDUy9htktinG1c9_lY5ejsWI8O3_T2sfjipqCj4g&fltp=jpg&imsz=0000","language":"ru","country":"RU","api_version":5},"message":{"text":"hello","type":"text"},"silent":false}'
     self.assertTrue(viber.verify_signature(message, request_header))
Пример #3
0
class Viber(BaseMessenger):
    """
    IM connector for Viber Bot API
    """

    # region Interface

    def __init__(self, token: str, **kwargs):
        super().__init__(token, **kwargs)

        self.bot = Api(
            BotConfiguration(
                auth_token=token,
                name=kwargs.get('name'),
                avatar=kwargs.get('avatar'),
            ))

    def enable_webhook(self, url: str, **kwargs):
        return self.bot.set_webhook(url=url)

    def disable_webhook(self):
        return self.bot.unset_webhook()

    def get_account_info(self) -> Dict[str, Any]:
        # data = {
        #    "status":0,
        #    "status_message":"ok",
        #    "id":"pa:75346594275468546724",
        #    "name":"account name",
        #    "uri":"accountUri",
        #    "icon":"http://example.com",
        #    "background":"http://example.com",
        #    "category":"category",
        #    "subcategory":"sub category",
        #    "location":{
        #       "lon":0.1,
        #       "lat":0.2
        #    },
        #    "country":"UK",
        #    "webhook":"https://my.site.com",
        #    "event_types":[
        #       "delivered",
        #       "seen"
        #    ],
        #    "subscribers_count":35,
        #    "members":[
        #       {
        #          "id":"01234567890A=",
        #          "name":"my name",
        #          "avatar":"http://example.com",
        #          "role":"admin"
        #       }
        #    ]
        # }
        try:
            data = self.bot.get_account_info()
        except Exception as err:
            raise MessengerException(err)

        return {
            'id': data.get('id'),
            'username': data.get('name'),
            'uri': data.get('uri'),  # check this
            'info': data
        }

    def get_user_info(self, user_id: str, **kwargs) -> Dict[str, Any]:
        # data = {
        #   "id":"01234567890A=",
        #   "name":"John McClane",
        #   "avatar":"http://avatar.example.com",
        #   "country":"UK",
        #   "language":"en",
        #   "primary_device_os":"android 7.1",
        #   "api_version":1,
        #   "viber_version":"6.5.0",
        #   "mcc":1,
        #   "mnc":1,
        #   "device_type":"iPhone9,4"
        # }
        try:
            data = self.bot.get_user_details(user_id)
        except Exception as err:
            if 'failed with status: 12' in str(err):
                raise RequestsLimitExceeded(err)
            raise MessengerException(err)

        return {
            'id': data.get('id'),
            'username': data.get('name'),
            'avatar': data.get('avatar'),
            'info': data,
        }

    def parse_message(self, request: HttpRequest) -> Message:
        # Verify signature
        sign = request.META.get('HTTP_X_VIBER_CONTENT_SIGNATURE')
        data = json.loads(request.body)
        if not self.bot.verify_signature(request.body, sign):
            raise MessengerException(f'Viber message not verified; '
                                     f'Data={data}; Sign={sign};')

        return self._from_viber_message(self.bot.create_request(data))

    def send_message(self, receiver: str,
                     messages: Union[Message, List[Message]]) -> List[str]:
        if isinstance(messages, MessageList):
            messages = messages.as_list()
        elif isinstance(messages, Message):
            messages = [messages]

        vb_messages = []
        for message in messages:
            vb_messages.append(self._to_viber_message(message))

        try:
            return self.bot.send_messages(receiver, vb_messages)
        except Exception as err:
            if 'failed with status: 6, message: notSubscribed' in str(err):
                raise NotSubscribed(err)
            raise MessengerException(err)

    def welcome_message(self, text: str) -> Union[str, Dict[str, Any], None]:
        return {
            "sender": {
                "name": self.name,
                "avatar": self.avatar_url
            },
            "type": "text",
            "text": text
        }

    # endregion

    # region Help methods

    @staticmethod
    def _from_viber_message(vb_request: ViberRequest) -> Message:
        if isinstance(vb_request, vbr.ViberMessageRequest):
            assert isinstance(vb_request.message, TypedMessage)

            vb_message = vb_request.message
            if isinstance(vb_message, vbm.TextMessage):
                if 'btn-' in vb_message.text:
                    return Button(id=vb_request.message_token,
                                  user_id=vb_request.sender.id,
                                  timestamp=vb_request.timestamp,
                                  command=vb_message.text)
                return Text(id=vb_request.message_token,
                            user_id=vb_request.sender.id,
                            timestamp=vb_request.timestamp,
                            text=vb_message.text)
            elif isinstance(vb_message, vbm.PictureMessage):
                return Picture(id=vb_request.message_token,
                               user_id=vb_request.sender.id,
                               timestamp=vb_request.timestamp,
                               file_url=vb_message.media)
            elif isinstance(vb_message, vbm.VideoMessage):
                return Video(id=vb_request.message_token,
                             user_id=vb_request.sender.id,
                             timestamp=vb_request.timestamp,
                             file_url=vb_message.media,
                             file_size=vb_message.size)
            elif isinstance(vb_message, vbm.FileMessage):
                return File(id=vb_request.message_token,
                            user_id=vb_request.sender.id,
                            timestamp=vb_request.timestamp,
                            file_url=vb_message.media,
                            file_size=vb_message.size)
            elif isinstance(vb_message, vbm.RichMediaMessage):
                return RichMedia(id=vb_request.message_token,
                                 user_id=vb_request.sender.id,
                                 timestamp=vb_request.timestamp,
                                 text=vb_message.alt_text,
                                 rich_media=vb_message.rich_media)
            elif isinstance(vb_message, vbm.ContactMessage):
                return Contact(id=vb_request.message_token,
                               user_id=vb_request.sender.id,
                               timestamp=vb_request.timestamp,
                               contact=vb_message.contact)
            elif isinstance(vb_message, vbm.LocationMessage):
                return Location(id=vb_request.message_token,
                                user_id=vb_request.sender.id,
                                timestamp=vb_request.timestamp,
                                location=vb_message.location)
            elif isinstance(vb_message, vbm.URLMessage):
                return Url(id=vb_request.message_token,
                           user_id=vb_request.sender.id,
                           timestamp=vb_request.timestamp,
                           url=vb_message.media)
            elif isinstance(vb_message, vbm.StickerMessage):
                return Sticker(id=vb_request.message_token,
                               user_id=vb_request.sender.id,
                               timestamp=vb_request.timestamp,
                               file_id=vb_message.sticker_id)
            return Text(id=vb_request.message_token,
                        user_id=vb_request.sender.id,
                        timestamp=vb_request.timestamp,
                        text=str(vb_message))
        elif isinstance(vb_request, vbr.ViberConversationStartedRequest):
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.START,
                         user_name=vb_request.user.name,
                         context=vb_request.context)
        elif isinstance(vb_request, vbr.ViberSubscribedRequest):
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.SUBSCRIBED,
                         user_name=vb_request.user.name)
        elif isinstance(vb_request, vbr.ViberUnsubscribedRequest):
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.UNSUBSCRIBED)
        elif isinstance(vb_request, vbr.ViberDeliveredRequest):
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.DELIVERED)
        elif isinstance(vb_request, vbr.ViberSeenRequest):
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.SEEN)
        elif isinstance(vb_request, vbr.ViberFailedRequest):
            log.warning(f'Client failed receiving message; Error={vb_request}')
            return Event(id=vb_request.message_token,
                         user_id=vb_request.user_id,
                         timestamp=vb_request.timestamp,
                         event_type=EType.START,
                         context=vb_request.desc)
        elif vb_request.event_type == 'webhook':
            return Event(timestamp=vb_request.timestamp)

        log.warning(f'ViberRequest type={type(vb_request)}; '
                    f'Object={vb_request};')
        return Text(timestamp=vb_request.timestamp, text=str(vb_request))

    def _to_viber_message(self, message: Message) -> VbMessage:
        kb = self._get_keyboard(message.buttons)

        if isinstance(message, Text):
            return vbm.TextMessage(text=message.text, keyboard=kb)
        if isinstance(message, Sticker):
            return vbm.StickerMessage(sticker_id=message.file_id, keyboard=kb)
        elif isinstance(message, Picture):
            return vbm.PictureMessage(media=message.file_url,
                                      text=message.text,
                                      keyboard=kb)
        elif isinstance(message, Video):
            return vbm.VideoMessage(media=message.file_url,
                                    size=message.file_size,
                                    text=message.text,
                                    keyboard=kb)
        elif isinstance(message, (File, Audio)):
            return vbm.FileMessage(media=message.file_url,
                                   size=message.file_size or 0,
                                   file_name=message.file_name or '',
                                   keyboard=kb)
        elif isinstance(message, Contact):
            contact = message.contact
            return vbm.ContactMessage(contact=contact, keyboard=kb)
        elif isinstance(message, Url):
            return vbm.URLMessage(media=message.url, keyboard=kb)
        elif isinstance(message, Location):
            location = message.location
            return vbm.LocationMessage(location=location, keyboard=kb)
        elif isinstance(message, RichMedia):
            rich_media = message.rich_media
            return vbm.RichMediaMessage(rich_media=rich_media,
                                        alt_text=message.text,
                                        keyboard=kb)

    @staticmethod
    def _get_keyboard(buttons: List[Button]) -> Optional[Dict[str, Any]]:
        # TODO do refactoring
        if not buttons:
            return None

        vb_buttons = []
        for button in buttons:
            # assert isinstance(button, Button), f'{button=} {type(button)}'
            vb_btn = {
                'Columns':
                2,  # TODO: how is it storage in Model?
                'Rows':
                1,
                'BgColor':
                '#aaaaaa',
                'ActionType':
                'reply',
                'ActionBody':
                button.command,
                'Text':
                '<font color="{clr}"><b>{text}'
                '</b></font>'.format(text=button.text, clr='#131313'),
                'TextVAlign':
                'middle',
                'TextHAlign':
                'center',
                'TextOpacity':
                60,
                'TextSize':
                'large',
                'TextPaddings': [12, 8, 8, 20],  # [up, left, right, bottom]
            }

            if hasattr(button, 'image'):
                domain = Site.objects.get_current().domain
                vb_btn.update({
                    'BgMedia': f'https://{domain}{button.image}',
                    'BgMediaScaleType': 'fill'
                })

            vb_buttons.append(vb_btn)

        return {
            'Type': 'keyboard',
            'BgColor': '#ffffff',
            'min_api_version': 6,
            'Buttons': vb_buttons,
        }
Пример #4
0
def test_verify_signature_failure():
    invalid_signature = 'aaaaaaaaaaaaaaaaaaaaaa'
    message = "{\"event\":\"webhook\"}".encode("utf-8")

    viber = Api(VIBER_BOT_CONFIGURATION)
    assert not viber.verify_signature(message, invalid_signature)
Пример #5
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
Пример #6
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