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
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()
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)
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
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
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
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
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