Example #1
0
def message_text_handler(update: telegram.Update,
                         context: telegram.ext.CallbackContext) -> None:
    message = update.effective_message

    if message is None:
        return

    chat = update.effective_chat

    if chat is None:
        return

    chat_type = chat.type
    bot = context.bot

    if cli_args.debug and not utils.check_admin(
            bot, context, message, analytics_handler, ADMIN_USER_ID):
        return

    message_id = message.message_id
    chat_id = message.chat.id
    user = message.from_user
    entities = message.parse_entities()

    if user is not None:
        create_or_update_user(bot, user)

        analytics_handler.track(context, analytics.AnalyticsType.MESSAGE, user)

    valid_entities = {
        entity: text
        for entity, text in entities.items() if entity.type in
        [telegram.MessageEntity.URL, telegram.MessageEntity.TEXT_LINK]
    }
    entity, text = next(iter(valid_entities.items()))

    if entity is None:
        return

    input_link = entity.url

    if input_link is None:
        input_link = text

    with io.BytesIO() as output_bytes:
        caption = None
        video_url = None
        audio_url = None

        try:
            yt_dl_options = {'logger': logger, 'no_color': True}

            with youtube_dl.YoutubeDL(yt_dl_options) as yt_dl:
                video_info = yt_dl.extract_info(input_link, download=False)

            if 'entries' in video_info:
                video = video_info['entries'][0]
            else:
                video = video_info

            if 'title' in video:
                caption = video['title']
            else:
                caption = input_link

            file_size = None

            if 'requested_formats' in video:
                requested_formats = video['requested_formats']

                video_data = list(
                    filter(
                        lambda requested_format: requested_format['vcodec'] !=
                        'none', requested_formats))[0]
                audio_data = list(
                    filter(
                        lambda requested_format: requested_format['acodec'] !=
                        'none', requested_formats))[0]

                if 'filesize' in video_data:
                    file_size = video_data['filesize']

                video_url = video_data['url']

                if file_size is None:
                    file_size = utils.get_file_size(video_url)

                audio_url = audio_data['url']
            elif 'url' in video:
                video_url = video['url']
                file_size = utils.get_file_size(video_url)

            if file_size is not None:
                if not utils.ensure_size_under_limit(
                        file_size, telegram.constants.MAX_FILESIZE_UPLOAD,
                        update, context):
                    return

        except Exception as error:
            logger.error(f'youtube-dl error: {error}')

        if chat_type == telegram.Chat.PRIVATE and (caption is None
                                                   or video_url is None):
            bot.send_message(chat_id,
                             'No video found on this link.',
                             disable_web_page_preview=True,
                             reply_to_message_id=message_id)

            return

        mp4_bytes = utils.convert(constants.OutputType.VIDEO,
                                  input_video_url=video_url,
                                  input_audio_url=audio_url)

        if not utils.ensure_valid_converted_file(
                file_bytes=mp4_bytes, update=update, context=context):
            return

        if mp4_bytes is not None:
            output_bytes.write(mp4_bytes)

        output_bytes.seek(0)

        if caption is not None:
            caption = caption[:telegram.constants.MAX_CAPTION_LENGTH]

        utils.send_video(bot, chat_id, message_id, output_bytes, caption,
                         chat_type)
Example #2
0
def message_text_handler(update: Update, context: CallbackContext):
    message = update.message
    chat_type = update.effective_chat.type
    bot = context.bot

    if cli_args.debug and not check_admin(bot, message, analytics,
                                          ADMIN_USER_ID):
        return

    message_id = message.message_id
    chat_id = message.chat.id
    user = message.from_user
    entities = message.parse_entities()

    create_or_update_user(bot, user)

    analytics.track(AnalyticsType.MESSAGE, user)

    entity, text = next(
        ((entity, text) for entity, text in entities.items()
         if entity.type in [MessageEntity.URL, MessageEntity.TEXT_LINK]), None)

    if entity is None:
        return

    input_link = entity.url

    if input_link is None:
        input_link = text

    with io.BytesIO() as output_bytes:
        caption = None
        video_url = None

        try:
            yt_dl_options = {'logger': logger, 'no_color': True}

            with youtube_dl.YoutubeDL(yt_dl_options) as yt_dl:
                video_info = yt_dl.extract_info(input_link, download=False)

            if 'entries' in video_info:
                video = video_info['entries'][0]
            else:
                video = video_info

            if 'title' in video:
                caption = video['title']
            else:
                caption = input_link

            requested_formats = video['requested_formats']

            video_data = list(
                filter(lambda format: format['vcodec'] != 'none',
                       requested_formats))[0]
            audio_data = list(
                filter(lambda format: format['acodec'] != 'none',
                       requested_formats))[0]

            if not ensure_size_under_limit(video_data['filesize'],
                                           MAX_FILESIZE_UPLOAD, update,
                                           context):
                return

            video_url = video_data['url']
            audio_url = audio_data['url']
        except Exception as error:
            logger.error('youtube-dl error: {}'.format(error))

        if chat_type == Chat.PRIVATE and (caption is None
                                          or video_url is None):
            bot.send_message(chat_id,
                             'No video found on "{}".'.format(input_link),
                             disable_web_page_preview=True,
                             reply_to_message_id=message_id)

            return

        mp4_bytes = convert(OutputType.VIDEO,
                            input_video_url=video_url,
                            input_audio_url=audio_url)

        output_bytes.write(mp4_bytes)
        output_bytes.seek(0)

        caption = caption[:MAX_CAPTION_LENGTH]

        # Video note isn't supported for videos downloaded from URLs yet.
        send_video(bot, chat_id, message_id, output_bytes, None, caption,
                   chat_type)
Example #3
0
def message_file_handler(update: telegram.Update,
                         context: telegram.ext.CallbackContext) -> None:
    message = update.effective_message
    chat = update.effective_chat

    if chat is None:
        return

    chat_type = chat.type
    bot = context.bot

    if message is None:
        return

    if cli_args.debug and not utils.check_admin(
            bot, context, message, analytics_handler, ADMIN_USER_ID):
        return

    message_id = message.message_id
    chat_id = message.chat.id
    attachment = message.effective_attachment

    if attachment is None:
        return

    if type(attachment) is list:
        if chat_type == telegram.Chat.PRIVATE:
            bot.send_message(
                chat_id,
                'You need to send the image as a file to convert it to a sticker.',
                reply_to_message_id=message_id)

        return

    if not isinstance(
            attachment,
        (telegram.Audio, telegram.Document, telegram.Voice, telegram.Sticker)):
        return

    message_type = telegram.utils.helpers.effective_message_type(message)

    file_size = attachment.file_size

    if file_size is None:
        return

    if not utils.ensure_size_under_limit(
            file_size, telegram.constants.MAX_FILESIZE_DOWNLOAD, update,
            context):
        return

    user = message.from_user

    input_file_id = attachment.file_id
    input_file_name = None

    if isinstance(attachment, (telegram.Audio, telegram.Document)):
        input_file_name = attachment.file_name

        if input_file_name is None and isinstance(attachment, telegram.Audio):
            input_file_name = attachment.title

    if user is not None:
        create_or_update_user(bot, user)

        analytics_handler.track(context, analytics.AnalyticsType.MESSAGE, user)

    if chat_type == telegram.Chat.PRIVATE:
        bot.send_chat_action(chat_id, telegram.ChatAction.TYPING)

    input_file = bot.get_file(input_file_id)
    input_file_url = input_file.file_path

    probe = None

    try:
        probe = ffmpeg.probe(input_file_url)
    except ffmpeg.Error:
        pass

    with io.BytesIO() as output_bytes:
        output_type = constants.OutputType.NONE
        caption = None
        invalid_format = None

        if message_type == 'voice':
            output_type = constants.OutputType.FILE

            mp3_bytes = utils.convert(output_type,
                                      input_audio_url=input_file_url)

            if not utils.ensure_valid_converted_file(
                    file_bytes=mp3_bytes, update=update, context=context):
                return

            if mp3_bytes is not None:
                output_bytes.write(mp3_bytes)

            output_bytes.name = 'voice.mp3'
        elif message_type == 'sticker':
            with io.BytesIO() as input_bytes:
                input_file.download(out=input_bytes)

                try:
                    image = PIL.Image.open(input_bytes)

                    with io.BytesIO() as image_bytes:
                        image.save(image_bytes, format='PNG')

                        image_bytes.seek(0)

                        output_bytes.write(image_bytes.read())

                        output_type = constants.OutputType.PHOTO

                        sticker = message['sticker']
                        emoji = sticker['emoji']
                        set_name = sticker['set_name']

                        caption = f'Sticker for the emoji "{emoji}" from the set "{set_name}"'
                except Exception as error:
                    logger.error(f'PIL error: {error}')
        else:
            if probe:
                for stream in probe['streams']:
                    codec_name = stream.get('codec_name')

                    if codec_name is not None:
                        invalid_format = codec_name

                    if codec_name in constants.VIDEO_CODEC_NAMES:
                        output_type = constants.OutputType.VIDEO

                        mp4_bytes = utils.convert(
                            output_type, input_video_url=input_file_url)

                        if not utils.ensure_valid_converted_file(
                                file_bytes=mp4_bytes,
                                update=update,
                                context=context):
                            return

                        if mp4_bytes is not None:
                            output_bytes.write(mp4_bytes)

                        break

                    continue

                if output_type == constants.OutputType.NONE:
                    for stream in probe['streams']:
                        codec_name = stream.get('codec_name')

                        if codec_name is not None:
                            invalid_format = codec_name

                        if codec_name in constants.AUDIO_CODEC_NAMES:
                            output_type = constants.OutputType.AUDIO

                            opus_bytes = utils.convert(
                                output_type, input_audio_url=input_file_url)

                            if not utils.ensure_valid_converted_file(
                                    file_bytes=opus_bytes,
                                    update=update,
                                    context=context):
                                return

                            if opus_bytes is not None:
                                output_bytes.write(opus_bytes)

                            break
                        elif codec_name == 'opus':
                            input_file.download(out=output_bytes)

                            output_type = constants.OutputType.AUDIO

                            break

                        continue

        if output_type == constants.OutputType.NONE:
            with io.BytesIO() as input_bytes:
                input_file.download(out=input_bytes)

                input_bytes.seek(0)

                try:
                    images = pdf2image.convert_from_bytes(input_bytes.read())
                    image = images[0]

                    with io.BytesIO() as image_bytes:
                        image.save(image_bytes, format='PNG')

                        image_bytes.seek(0)

                        output_bytes.write(image_bytes.read())

                        output_type = constants.OutputType.PHOTO
                except Exception as error:
                    logger.error(f'pdf2image error: {error}')

                if output_type == constants.OutputType.NONE:
                    try:
                        image = PIL.Image.open(input_bytes)

                        with io.BytesIO() as image_bytes:
                            image.save(image_bytes, format='WEBP')

                            image_bytes.seek(0)

                            output_bytes.write(image_bytes.read())

                            output_type = constants.OutputType.STICKER
                    except Exception as error:
                        logger.error(f'PIL error: {error}')

        if output_type == constants.OutputType.NONE:
            if chat_type == telegram.Chat.PRIVATE:
                if invalid_format is None and input_file_url is not None:
                    parts = os.path.splitext(input_file_url)

                    if parts is not None and len(parts) >= 2:
                        extension = parts[1]

                        if extension is not None:
                            invalid_format = extension[1:]

                bot.send_message(
                    chat_id=chat_id,
                    text=f'File type "{invalid_format}" is not yet supported.',
                    reply_to_message_id=message_id)

            return

        output_bytes.seek(0)

        output_file_size = output_bytes.getbuffer().nbytes

        if caption is None and input_file_name is not None:
            caption = input_file_name[:telegram.constants.MAX_CAPTION_LENGTH]

        if output_type == constants.OutputType.AUDIO:
            if not utils.ensure_size_under_limit(
                    output_file_size,
                    telegram.constants.MAX_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_chat_action(chat_id, telegram.ChatAction.UPLOAD_AUDIO)

            bot.send_voice(chat_id,
                           output_bytes,
                           caption=caption,
                           reply_to_message_id=message_id)

            return
        elif output_type == constants.OutputType.VIDEO:
            if not utils.ensure_size_under_limit(
                    output_file_size,
                    telegram.constants.MAX_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_chat_action(chat_id, telegram.ChatAction.UPLOAD_VIDEO)

            utils.send_video(bot, chat_id, message_id, output_bytes, caption,
                             chat_type)

            return
        elif output_type == constants.OutputType.PHOTO:
            if not utils.ensure_size_under_limit(
                    output_file_size,
                    constants.MAX_PHOTO_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_photo(chat_id,
                           output_bytes,
                           caption=caption,
                           reply_to_message_id=message_id)

            return
        elif output_type == constants.OutputType.STICKER:
            bot.send_sticker(chat_id,
                             output_bytes,
                             reply_to_message_id=message_id)

            return
        elif output_type == constants.OutputType.FILE:
            if not utils.ensure_size_under_limit(
                    output_file_size,
                    telegram.constants.MAX_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_chat_action(chat_id, telegram.ChatAction.UPLOAD_DOCUMENT)

            bot.send_document(chat_id,
                              output_bytes,
                              reply_to_message_id=message_id)

            return

    if chat_type == telegram.Chat.PRIVATE:
        bot.send_message(chat_id,
                         'File type is not yet supported.',
                         reply_to_message_id=message_id)
Example #4
0
def message_file_handler(update: Update, context: CallbackContext):
    message = update.message
    chat_type = update.effective_chat.type
    bot = context.bot

    if cli_args.debug and not check_admin(bot, message, analytics,
                                          ADMIN_USER_ID):
        return

    message_id = message.message_id
    chat_id = message.chat.id
    attachment = message.effective_attachment

    if type(attachment) is list:
        if chat_type == Chat.PRIVATE:
            bot.send_message(
                chat_id,
                'You need to send the image as a file to convert it to a sticker.',
                reply_to_message_id=message_id)

        return

    if not ensure_size_under_limit(attachment.file_size, MAX_FILESIZE_DOWNLOAD,
                                   update, context):
        return

    user = message.from_user

    input_file_id = attachment.file_id
    input_file_name = attachment.file_name if getattr(
        attachment, 'file_name', None) else attachment.title

    create_or_update_user(bot, user)

    analytics.track(AnalyticsType.MESSAGE, user)

    if chat_type == Chat.PRIVATE:
        bot.send_chat_action(chat_id, ChatAction.TYPING)

    input_file = bot.get_file(input_file_id)
    input_file_url = input_file.file_path

    probe = None

    try:
        probe = ffmpeg.probe(input_file_url)
    except:
        pass

    with io.BytesIO() as output_bytes:
        output_type = OutputType.NONE

        invalid_format = None

        if probe:
            for stream in probe['streams']:
                codec_name = stream.get('codec_name')
                codec_type = stream.get('codec_type')

                if codec_name is not None and codec_type == VIDEO_CODED_TYPE:
                    invalid_format = codec_name

                if codec_name == 'mp3':
                    output_type = OutputType.AUDIO

                    opus_bytes = convert(output_type,
                                         input_video_url=input_file_url)

                    output_bytes.write(opus_bytes)

                    break
                elif codec_name == 'opus':
                    input_file.download(out=output_bytes)

                    output_type = OutputType.AUDIO

                    break
                elif codec_name in VIDEO_CODEC_NAMES:
                    output_type = OutputType.VIDEO

                    mp4_bytes = convert(output_type,
                                        input_video_url=input_file_url)

                    output_bytes.write(mp4_bytes)

                    break
                else:
                    continue

        if output_type == OutputType.NONE:
            with io.BytesIO() as input_bytes:
                input_file.download(out=input_bytes)

                try:
                    images = convert_from_bytes(input_bytes.getbuffer())
                    image = images[0]

                    with io.BytesIO() as image_bytes:
                        image.save(image_bytes, format='PNG')

                        output_bytes.write(image_bytes.getbuffer())

                        output_type = OutputType.PHOTO
                except Exception as error:
                    logger.error('pdf2image error: {}'.format(error))

                if output_type == OutputType.NONE:
                    try:
                        image = Image.open(input_bytes)

                        with io.BytesIO() as image_bytes:
                            image.save(image_bytes, format='WEBP')

                            output_bytes.write(image_bytes.getbuffer())

                            output_type = OutputType.STICKER
                    except Exception as error:
                        logger.error('PIL error: {}'.format(error))

        if output_type == OutputType.NONE:
            if chat_type == Chat.PRIVATE:
                if invalid_format is None:
                    invalid_format = os.path.splitext(input_file_url)[1][1:]

                bot.send_message(chat_id,
                                 'File type "{}" is not yet supported.'.format(
                                     invalid_format),
                                 reply_to_message_id=message_id)

            return

        output_bytes.seek(0)

        output_file_size = output_bytes.getbuffer().nbytes
        caption = None

        if input_file_name is not None:
            caption = input_file_name[:MAX_CAPTION_LENGTH]

        if output_type == OutputType.AUDIO:
            if not ensure_size_under_limit(
                    output_file_size,
                    MAX_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_chat_action(chat_id, ChatAction.UPLOAD_AUDIO)

            bot.send_voice(chat_id,
                           output_bytes,
                           caption=caption,
                           reply_to_message_id=message_id)

            return
        elif output_type == OutputType.VIDEO:
            if not ensure_size_under_limit(
                    output_file_size,
                    MAX_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_chat_action(chat_id, ChatAction.UPLOAD_VIDEO)

            send_video(bot, chat_id, message_id, output_bytes, attachment,
                       caption, chat_type)

            return
        elif output_type == OutputType.PHOTO:
            if not ensure_size_under_limit(
                    output_file_size,
                    MAX_PHOTO_FILESIZE_UPLOAD,
                    update,
                    context,
                    file_reference_text='Converted file'):
                return

            bot.send_photo(chat_id,
                           output_bytes,
                           caption=caption,
                           reply_to_message_id=message_id)

            return
        elif output_type == OutputType.STICKER:
            bot.send_sticker(chat_id,
                             output_bytes,
                             reply_to_message_id=message_id)

            return

    if chat_type == Chat.PRIVATE:
        bot.send_message(chat_id,
                         'File type is not yet supported.',
                         reply_to_message_id=message_id)