示例#1
0
def on_sticker_receive(update: Update, context: CallbackContext):
    logger.info('user sent the stciker to add')

    sticker = StickerFile(update.message.sticker)

    pack_link = utils.name2link(update.message.sticker.set_name)

    try:
        sticker.remove_from_set(context.bot)
    except error.PackInvalid:
        update.message.reply_html(
            Strings.REMOVE_STICKER_FOREIGN_PACK.format(pack_link), quote=True)
    except error.PackNotModified:
        update.message.reply_html(
            Strings.REMOVE_STICKER_ALREADY_DELETED.format(pack_link),
            quote=True)
    except error.UnknwonError as e:
        update.message.reply_html(Strings.REMOVE_STICKER_GENERIC_ERROR.format(
            pack_link, e.message),
                                  quote=True)
    else:
        # success
        update.message.reply_html(
            Strings.REMOVE_STICKER_SUCCESS.format(pack_link), quote=True)
    finally:
        # wait for other stickers
        return WAITING_STICKERS
示例#2
0
def on_static_sticker_receive(update: Update, _):
    logger.info('user sent a static stciker to convert')

    sticker = StickerFile(update.message.sticker)
    sticker.download(prepare_png=True)

    update.message.reply_document(sticker.png_file, filename=update.message.sticker.file_id + '.png', quote=True)

    sticker.close()
示例#3
0
def on_sticker_receive(update: Update, context: CallbackContext):
    logger.info('user sent a sticker to convert')

    if update.message.sticker.is_animated:
        # do nothing with animated stickers. We keep the code here just for debugging purposes
        return

    sticker = StickerFile(context.bot, update.message)
    sticker.download(prepare_png=not update.message.sticker.is_animated)

    request_kwargs = dict(
        caption=sticker.emojis_str,
        quote=True
    )

    if update.message.sticker.is_animated:
        request_kwargs['document'] = sticker.tgs_file
        request_kwargs['filename'] = update.message.sticker.file_id + '.tgs'
    else:
        request_kwargs['document'] = sticker.png_file
        request_kwargs['filename'] = update.message.sticker.file_id + '.png'

    sent_message: Message = update.message.reply_document(**request_kwargs)
    sticker.close()

    if sent_message.document:
        # only do this when we send the message as document
        # it will be useful to test problems with animated stickers. For example in mid 2020, the API started
        # to consider any animated sticker as invalid ("wrong file type" exception), and they were sent
        # back as file with a specific mimetype ("something something bad animated sticker"). In this way:
        # - sent back as animated sticker: everything ok
        # - sent back as file: there's something wrong with the code/api, better to edit the document with its mimetype
        sent_message.edit_caption(
            caption='{}\n<code>{}</code>'.format(
                sent_message.caption,
                Strings.TO_FILE_MIME_TYPE.format(sent_message.document.mime_type)
            ),
            parse_mode=ParseMode.HTML
        )
    elif sent_message.sticker:
        update.message.reply_text(Strings.ANIMATED_STICKERS_NO_FILE)
示例#4
0
def on_sticker_receive(update: Update, context: CallbackContext):
    logger.info('user sent a stciker from the pack to export')

    if not update.message.sticker.set_name:
        update.message.reply_text(Strings.EXPORT_PACK_NO_PACK)
        return Status.WAITING_STICKER

    sticker_set = context.bot.get_sticker_set(update.message.sticker.set_name)

    base_progress_message = Strings.EXPORT_PACK_START.format(
        html_escape(sticker_set.title))
    message_to_edit = update.message.reply_html(base_progress_message,
                                                quote=True)

    pack_emojis = dict(
    )  # we need to create this dict just in case the pyrogram request fails

    with tempfile.TemporaryDirectory() as tmp_dir:
        logger.info('using %s as TemporaryDirectory', tmp_dir)

        with tempfile.TemporaryFile() as tmp_file:  # temporary zip file
            with zipfile.ZipFile(tmp_file, 'w') as zip_file:

                total = len(sticker_set.stickers)
                skipped_stickers = 0
                for i, sticker in enumerate(sticker_set.stickers):
                    # noinspection PyTypeChecker
                    sticker_file = StickerFile(
                        sticker,
                        message=DummyMessage(
                            sticker
                        ),  # we do not have a Message but we need it,
                        emojis=[
                            sticker.emoji
                        ],  # we need to pass them explicitly so we avoid the Pyrogram request
                        temp_file=tempfile.NamedTemporaryFile(dir=tmp_dir))

                    # noinspection PyBroadException
                    try:
                        sticker_file.download()
                        png_file = utils.webp_to_png(sticker_file.tempfile)
                        pack_emojis[sticker.file_id] = sticker_file.emojis
                    except Exception:
                        logger.info(
                            'error while downloading and converting a sticker we need to export',
                            exc_info=True)
                        sticker_file.close()
                        skipped_stickers += 1
                        continue

                    sticker_file.close()

                    # https://stackoverflow.com/a/54202259
                    zip_file.writestr('{}.png'.format(sticker.file_id),
                                      png_file.read())

                    # edit message every 10 exported stickers, or when we're done
                    progress = i + 1
                    if progress == total or progress % 10 == 0:
                        try:
                            message_to_edit.edit_text(
                                '{} (progress: {}/{})'.format(
                                    base_progress_message, progress, total),
                                parse_mode=ParseMode.HTML)
                            time.sleep(1)  # we do not want to get rate-limited
                        except (TelegramError, BadRequest) as e:
                            logger.warning(
                                'error while editing progress message: %s',
                                e.message)

                try:
                    stickers_emojis_dict = get_set_emojis_dict(
                        update.message.sticker.set_name)
                except Exception as e:
                    logger.error(
                        'error while trying to get the pack emojis with pyrogram: %s',
                        str(e),
                        exc_info=True)
                    stickers_emojis_dict = pack_emojis

                with tempfile.SpooledTemporaryFile() as tmp_json:
                    tmp_json.write(
                        json.dumps(stickers_emojis_dict, indent=2).encode())
                    tmp_json.seek(0)
                    zip_file.writestr('emojis.json', tmp_json.read())

                message_to_edit.reply_text(Strings.EXPORT_PACK_UPLOADING,
                                           quote=True)

            tmp_file.seek(0)

            update.message.reply_document(
                tmp_file,
                filename='{}.zip'.format(sticker_set.name),
                caption='<a href="{}">{}</a>{}'.format(
                    utils.name2link(sticker_set.name),
                    html_escape(sticker_set.title),
                    Strings.EXPORT_SKIPPED_STICKERS.format(skipped_stickers)
                    if skipped_stickers != 0 else ""),
                parse_mode=ParseMode.HTML,
                quote=True)

    return ConversationHandler.END
示例#5
0
def on_first_sticker_receive(update: Update, context: CallbackContext):
    logger.info('first sticker of the pack received')
    logger.debug('user_data: %s', context.user_data)

    animated_pack = context.user_data['pack']['animated']
    if update.message.document:
        animated_sticker = False
    else:
        animated_sticker = update.message.sticker.is_animated

    if animated_pack and not animated_sticker:
        logger.info('invalid sticker: static sticker for an animated pack')
        update.message.reply_text(Strings.ADD_STICKER_EXPECTING_ANIMATED)
        return Status.CREATE_WAITING_FIRST_STICKER
    elif not animated_pack and animated_sticker:
        logger.info('invalid sticker: animated sticker for a static pack')
        update.message.reply_text(Strings.ADD_STICKER_EXPECTING_STATIC)
        return Status.CREATE_WAITING_FIRST_STICKER
    else:
        logger.info(
            'sticker type ok (animated pack: %s, animated sticker: %s)',
            animated_pack, animated_sticker)

    title, name = context.user_data['pack'].get(
        'title', None), context.user_data['pack'].get('name', None)
    if not title or not name:
        logger.error('pack title or name missing (title: %s, name: %s)', title,
                     name)
        update.message.reply_text(
            Strings.PACK_CREATION_FIRST_STICKER_PACK_DATA_MISSING)

        context.user_data.pop('pack', None)  # remove temp info

        return ConversationHandler.END

    full_name = '{}_by_{}'.format(name, context.bot.username)

    user_emojis = context.user_data['pack'].pop('emojis',
                                                None)  # we also remove them
    sticker = StickerFile(bot=context.bot,
                          message=update.message,
                          emojis=user_emojis)
    sticker.download()

    try:
        logger.debug('executing API request...')
        request_payload = dict(
            user_id=update.effective_user.id,
            title=title,
            name=full_name,
            emojis=''.join(sticker.emojis),
        )

        sticker.create_set(**request_payload)
    except (error.PackInvalid, error.NameInvalid,
            error.NameAlreadyOccupied) as e:
        logger.error('Telegram error while creating stickers pack: %s',
                     e.message)
        if isinstance(e, error.NameAlreadyOccupied):
            # there's already a pack with that link
            update.message.reply_html(
                Strings.PACK_CREATION_ERROR_DUPLICATE_NAME.format(
                    utils.name2link(full_name)))
        elif isinstance(e, (error.PackInvalid, error.NameInvalid)):
            update.message.reply_text(Strings.PACK_CREATION_ERROR_INVALID_NAME)

        context.user_data['pack'].pop('name', None)  # remove pack name
        sticker.close()

        return Status.CREATE_WAITING_NAME  # do not continue, wait for another name
    except error.InvalidAnimatedSticker as e:
        logger.error('Telegram error while creating animated pack: %s',
                     e.message)
        update.message.reply_html(Strings.ADD_STICKER_INVALID_ANIMATED,
                                  quote=True,
                                  disable_web_page_preview=True)

        return Status.CREATE_WAITING_FIRST_STICKER
    except error.FloodControlExceeded as e:
        logger.error('Telegram error while creating animated pack: %s',
                     e.message)
        retry_in = re.search(r'retry in (\d+) seconds', e.message,
                             re.I).group(1)  # Retry in 8 seconds
        text = Strings.ADD_STICKER_FLOOD_EXCEPTION.format(retry_in)

        update.message.reply_html(text,
                                  quote=True,
                                  disable_web_page_preview=True)

        return ConversationHandler.END  # do not continue, end the conversation
    except error.UnknwonError as e:
        logger.error('Unknown error while creating the pack: %s', e.message)
        update.message.reply_html(
            Strings.PACK_CREATION_ERROR_GENERIC.format(e.message))

        context.user_data.pop('pack', None)  # remove temp data
        sticker.close()

        return ConversationHandler.END  # do not continue, end the conversation
    else:
        # success

        pack_row = Pack(user_id=update.effective_user.id,
                        name=full_name,
                        title=title,
                        is_animated=animated_pack)
        with session_scope() as session:
            session.add(pack_row)

        # db.save_pack(update.effective_user.id, full_name, title)
        pack_link = utils.name2link(full_name)
        update.message.reply_html(
            Strings.PACK_CREATION_PACK_CREATED.format(pack_link))

        sticker.close()  # remove sticker files

        context.user_data['pack']['name'] = full_name
        # do not remove temporary data (user_data['pack']) because we are still adding stickers

        # wait for other stickers
        if animated_pack:
            return Status.WAITING_ANIMATED_STICKERS
        else:
            return Status.WAITING_STATIC_STICKERS
示例#6
0
def on_sticker_receive(update: Update, context: CallbackContext):
    logger.info('user sent a stciker from the pack to export')

    if not update.message.sticker.set_name:
        update.message.reply_text(Strings.EXPORT_PACK_NO_PACK)
        return WAITING_STICKER

    sticker_set = context.bot.get_sticker_set(update.message.sticker.set_name)

    base_progress_message = Strings.EXPORT_PACK_START.format(
        html_escape(sticker_set.title))
    message_to_edit = update.message.reply_html(base_progress_message,
                                                quote=True)

    pack_emojis = dict()

    with tempfile.TemporaryDirectory() as tmp_dir:
        logger.info('using %s as TemporaryDirectory', tmp_dir)
        with tempfile.TemporaryFile() as tmp_file:  # temporary zip file
            with zipfile.ZipFile(tmp_file, 'w') as zip_file:

                total = len(sticker_set.stickers)
                for i, sticker in enumerate(sticker_set.stickers):
                    sticker_file = StickerFile(
                        sticker,
                        temp_file=tempfile.NamedTemporaryFile(dir=tmp_dir))
                    try:
                        sticker_file.download(prepare_png=True)
                        pack_emojis[sticker.file_id] = sticker.emoji
                    except Exception as e:
                        logger.info(
                            'error while downloading and converting a sticker we need to export: %s',
                            str(e))
                    finally:
                        sticker_file.close(keep_result_png_open=True)

                    # https://stackoverflow.com/a/54202259
                    zip_file.writestr('{}.png'.format(sticker.file_id),
                                      sticker_file.png_file.read())

                    # edit message every 10 exported stickers, or when we're done
                    progress = i + 1
                    if progress == total or progress % 10 == 0:
                        try:
                            message_to_edit.edit_text(
                                '{} (progress: {}/{})'.format(
                                    base_progress_message, progress, total),
                                parse_mode=ParseMode.HTML)
                        except (TelegramError, BadRequest) as e:
                            logger.warning(
                                'error while editing progress message: %s',
                                e.message)

                with tempfile.SpooledTemporaryFile() as tmp_json:
                    tmp_json.write(json.dumps(pack_emojis, indent=2).encode())
                    tmp_json.seek(0)
                    zip_file.writestr('emojis.json', tmp_json.read())

                message_to_edit.reply_text(Strings.EXPORT_PACK_UPLOADING,
                                           quote=True)

            tmp_file.seek(0)

            update.message.reply_document(
                tmp_file,
                filename='{}.zip'.format(sticker_set.name),
                caption='<a href="{}">{}</a>'.format(
                    utils.name2link(sticker_set.name),
                    html_escape(sticker_set.title)),
                parse_mode=ParseMode.HTML,
                quote=True)

    return ConversationHandler.END
示例#7
0
def add_sticker_to_set(update: Update, context: CallbackContext,
                       animated_pack):
    name = context.user_data['pack'].get('name', None)
    if not name:
        logger.error('pack name missing (%s)', name)
        update.message.reply_text(Strings.ADD_STICKER_PACK_DATA_MISSING)

        context.user_data.pop('pack', None)  # remove temp info

        return ConversationHandler.END

    user_emojis = context.user_data['pack'].pop('emojis',
                                                None)  # we also remove them
    sticker = StickerFile(bot=context.bot,
                          message=update.message,
                          emojis=user_emojis)
    sticker.download(prepare_png=True)

    pack_link = utils.name2link(name)

    # we edit this flag so the 'finally' statement can end the conversation if needed by an 'except'
    end_conversation = False
    try:
        logger.debug('executing request...')
        sticker.add_to_set(name)
    except error.PackFull:
        max_pack_size = MAX_PACK_SIZE_ANIMATED if animated_pack else MAX_PACK_SIZE_STATIC
        update.message.reply_html(Strings.ADD_STICKER_PACK_FULL.format(
            pack_link, max_pack_size),
                                  quote=True)

        end_conversation = True  # end the conversation when a pack is full
    except error.FileDimensionInvalid:
        logger.error('resized sticker has the wrong size: %s', str(sticker))
        update.message.reply_html(
            Strings.ADD_STICKER_SIZE_ERROR.format(*sticker.size), quote=True)
    except error.InvalidAnimatedSticker:
        update.message.reply_html(Strings.ADD_STICKER_INVALID_ANIMATED,
                                  quote=True,
                                  disable_web_page_preview=True)
    except error.PackInvalid:
        # pack name invalid or that pack has been deleted: delete it from the db
        with session_scope() as session:
            deleted_rows = session.query(Pack).filter(
                Pack.user_id == update.effective_user.id,
                Pack.name == name).delete('fetch')
            logger.debug('rows deleted: %d', deleted_rows or 0)

            # get the remaining packs' titles
            pack_titles = [
                t.title for t in session.query(Pack.title).filter_by(
                    user_id=update.effective_user.id).all()
            ]

        if not pack_titles:
            # user doesn't have any other pack to chose from, reset his status
            update.message.reply_html(
                Strings.ADD_STICKER_PACK_NOT_VALID_NO_PACKS.format(pack_link))

            logger.debug('calling sticker.delete()...')
            sticker.close()
            return ConversationHandler.END
        else:
            # make the user select another pack from the keyboard
            markup = Keyboard.from_list(pack_titles)
            update.message.reply_html(
                Strings.ADD_STICKER_PACK_NOT_VALID.format(pack_link),
                reply_markup=markup)
            context.user_data['pack'].pop('name',
                                          None)  # remove temporary data

            logger.debug('calling sticker.delete()...')
            sticker.close()
            return Status.ADD_WAITING_TITLE
    except error.UnknwonError as e:
        update.message.reply_html(Strings.ADD_STICKER_GENERIC_ERROR.format(
            pack_link, e.message),
                                  quote=True)
    except Exception as e:
        logger.error('non-telegram exception while adding a sticker to a set',
                     exc_info=True)
        raise e  # this is not raised
    else:
        if not user_emojis:
            text = Strings.ADD_STICKER_SUCCESS.format(pack_link)
        else:
            text = Strings.ADD_STICKER_SUCCESS_USER_EMOJIS.format(
                pack_link, ''.join(user_emojis))

        update.message.reply_html(text, quote=True)
    finally:
        # this is entered even when we enter the 'else' or we return in an 'except'
        # https://stackoverflow.com/a/19805746
        logger.debug('calling sticker.close()...')
        sticker.close()

        if end_conversation:
            return ConversationHandler.END

        if animated_pack:
            return Status.WAITING_ANIMATED_STICKERS
        else:
            return Status.WAITING_STATIC_STICKERS
示例#8
0
def on_first_sticker_receive(update: Update, context: CallbackContext):
    logger.info('first sticker of the pack received')
    logger.debug('user_data: %s', context.user_data)

    title, name = context.user_data['pack'].get(
        'title', None), context.user_data['pack'].get('name', None)
    if not title or not name:
        logger.error('pack title or name missing (title: %s, name: %s)', title,
                     name)
        update.message.reply_text(
            Strings.PACK_CREATION_FIRST_STICKER_PACK_DATA_MISSING)

        context.user_data.pop('pack', None)  # remove temp info

        return ConversationHandler.END

    full_name = '{}_by_{}'.format(name, context.bot.username)

    sticker = StickerFile(update.message.sticker or update.message.document,
                          caption=update.message.caption)
    sticker.download(prepare_png=True)

    try:
        logger.debug('executing API request...')
        StickerFile.create_set(
            bot=context.bot,
            user_id=update.effective_user.id,
            title=title,
            name=full_name,
            emojis=sticker.emoji,
            # we need to use an input file becase a tempfile.SpooledTemporaryFile has a 'name' attribute which
            # makes python-telegram-bot retrieve the file's path using os (https://github.com/python-telegram-bot/python-telegram-bot/blob/2a3169a22f7227834dd05a35f90306375136e41a/telegram/files/inputfile.py#L58)
            #  to populate the 'filename' attribute, which would result an exception since it is
            #  a byte object. That means we have to do it ourself by  creating the InputFile and
            #  assigning it a custom 'filename'
            png_sticker=sticker.png_input_file)
    except (error.PackInvalid, error.NameInvalid,
            error.NameAlreadyOccupied) as e:
        logger.error('Telegram error while creating stickers pack: %s',
                     e.message)
        if isinstance(e, error.NameAlreadyOccupied):
            # there's already a pack with that link
            update.message.reply_html(
                Strings.PACK_CREATION_ERROR_DUPLICATE_NAME.format(
                    utils.name2link(full_name)))
        elif isinstance(e, (error.PackInvalid, error.NameInvalid)):
            update.message.reply_text(Strings.PACK_CREATION_ERROR_INVALID_NAME)

        context.user_data['pack'].pop('name', None)  # remove pack name
        sticker.close()

        return WAITING_NAME  # do not continue, wait for another name
    except error.UnknwonError as e:
        logger.error('Unknown error while creating the pack: %s', e.message)
        update.message.reply_html(
            Strings.PACK_CREATION_ERROR_GENERIC.format(e.message))

        context.user_data.pop('pack', None)  # remove temp data
        sticker.close()

        return ConversationHandler.END  # do not continue, end the conversation
    else:
        # success

        pack_row = Pack(user_id=update.effective_user.id,
                        name=full_name,
                        title=title)
        with session_scope() as session:
            session.add(pack_row)

        # db.save_pack(update.effective_user.id, full_name, title)
        pack_link = utils.name2link(full_name)
        update.message.reply_html(
            Strings.PACK_CREATION_PACK_CREATED.format(pack_link))

        sticker.close()  # remove sticker files

        context.user_data['pack']['name'] = full_name
        # do not remove temporary data (user_data['pack']) because we are still adding stickers

        return ADDING_STICKERS  # wait for other stickers