def _forceanalysis_callback(self, update: Update, context: CallbackContext, image_hash: str or None) -> None: """ /forceanalysis command handler (with an argument) :param update: the chat update object :param context: telegram context """ bot = context.bot message = update.effective_message chat_id = update.effective_chat.id with _session_scope() as session: if image_hash is not None: entity = self._persistence.find_by_image_hash(session, image_hash) elif message.reply_to_message is not None: reply_to_message = message.reply_to_message entity = self._find_entity_for_message(session, bot.id, reply_to_message) else: send_message(bot, chat_id, ":exclamation: Missing image reply or image hash argument".format(image_hash), reply_to=message.message_id) return if entity is None: send_message(bot, chat_id, ":exclamation: Image entity not found".format(image_hash), reply_to=message.message_id) return entity.analyser = None entity.analyser_quality = None self._persistence.update(session, entity) send_message(bot, chat_id, ":wrench: Reset analyser data for image with hash: {})".format(entity.image_hash), reply_to=message.message_id)
def _send_random_quote(self, update: Update, context: CallbackContext) -> None: """ Sends a quote from the pool to the requesting chat :param update: the chat update object :param context: telegram context """ bot = context.bot chat_id = update.effective_chat.id bot.send_chat_action(chat_id=chat_id, action=ChatAction.TYPING) with _session_scope() as session: entity = self._persistence.get_random(session) LOGGER.debug("Sending random quote '{}' to chat id: {}".format(entity.image_hash, chat_id)) caption = None if self._config.TELEGRAM_CAPTION_IMAGES_WITH_TEXT.value: caption = entity.text telegram_file_ids_for_current_bot = self.find_telegram_file_ids_for_current_bot(bot.token, entity) if len(telegram_file_ids_for_current_bot) > 0: file_ids = send_photo(bot=bot, chat_id=chat_id, file_id=telegram_file_ids_for_current_bot[0].id, caption=caption) bot_token = self._persistence.get_bot_token(session, bot.token) for file_id in file_ids: entity.add_file_id(bot_token, file_id) self._persistence.update(session, entity) return image_bytes = self._persistence.get_image_data(entity) file_ids = send_photo(bot=bot, chat_id=chat_id, image_data=image_bytes, caption=caption) bot_token = self._persistence.get_bot_token(session, bot.token) for file_id in file_ids: entity.add_file_id(bot_token, file_id) self._persistence.update(session, entity, image_bytes)
def _inline_query_callback(self, update: Update, context: CallbackContext) -> None: """ Responds to an inline client request with a list of 16 randomly chosen images :param update: the chat update object :param context: telegram context """ LOGGER.debug('Inline query') query = update.inline_query.query offset = update.inline_query.offset if not offset: offset = 0 else: offset = int(offset) badge_size = self._config.TELEGRAM_INLINE_BADGE_SIZE.value with _session_scope() as session: if len(query) > 0: entities = self._persistence.find_by_text(session, query, badge_size, offset) else: entities = self._persistence.get_random(session, page_size=badge_size) results = list(map(lambda x: self._entity_to_inline_query_result(x), entities)) LOGGER.debug('Inline query "{}": {}+{} results'.format(query, len(results), offset)) if len(results) > 0: new_offset = offset + badge_size else: new_offset = '' update.inline_query.answer( results, next_offset=new_offset )
def __init__(self, config: AppConfig, persistence: ImageDataPersistence, image_analysers: [ImageAnalyser]): """ Creates an instance :param config: the configuration :param persistence: the persistence :param image_analysers: available image analysers """ super().__init__(config.IMAGE_ANALYSIS_INTERVAL.value) self._config = config self._persistence = persistence self._image_analysers = image_analysers if len(self._image_analysers) <= 0: LOGGER.warning("No image analyser provided") self._target_quality = 0 else: self._target_quality = sorted(self._image_analysers, key=lambda x: x.get_quality(), reverse=True)[0].get_quality() with _session_scope() as session: self._not_optimal_ids = set( self._persistence.find_non_optimal(session, self._target_quality))
def start(self): if len(self._image_analysers) <= 0: LOGGER.warning("No image analyser provided, not starting.") return with _session_scope(False) as session: self._update_stats(session) super().start()
def __init__(self, config: AppConfig, persistence: ImageDataPersistence, bot: Bot): super().__init__(config.UPLOADER_INTERVAL.value) self._persistence = persistence self._bot = bot self._chat_id = config.UPLOADER_CHAT_ID.value with _session_scope() as session: self._not_uploaded_ids = set( self._persistence.get_not_uploaded_image_ids( session, self._bot.token))
def wrapper(self, update: Update, context: CallbackContext, *args, **kwargs): bot = context.bot message = update.effective_message chat_id = message.chat_id reply_to_message = message.reply_to_message with _session_scope(False) as session: entity = self._find_entity_for_message(session, bot.id, reply_to_message) if entity is None: send_message(bot, chat_id, ":exclamation: You must directly reply to an image send by this bot to use reply commands.", reply_to=message.message_id) # otherwise call wrapped function as normal return func(self, update, context, entity, *args, **kwargs)
def _reply_text_command_callback(self, update: Update, context: CallbackContext, entity_of_reply: Image or None, text: str) -> None: """ /text reply command handler :param update: the chat update object :param context: telegram context """ bot = context.bot message = update.effective_message chat_id = update.effective_chat.id entity_of_reply.analyser = IMAGE_ANALYSIS_TYPE_HUMAN entity_of_reply.analyser_quality = 1.0 entity_of_reply.text = text with _session_scope() as session: self._persistence.update(session, entity_of_reply) send_message(bot, chat_id, ":wrench: Updated text for referenced image to '{}' (Hash: {})".format(entity_of_reply.text, entity_of_reply.image_hash), reply_to=message.message_id)
def _reply_delete_command_callback(self, update: Update, context: CallbackContext, entity_of_reply: Image or None) -> None: """ /text reply command handler :param update: the chat update object :param context: telegram context """ bot = context.bot message = update.effective_message chat_id = update.effective_chat.id is_edit = hasattr(message, 'edited_message') and message.edited_message is not None if is_edit: LOGGER.debug("Ignoring edited delete command") return with _session_scope() as session: self._persistence.delete(session, entity_of_reply) send_message(bot, chat_id, "Deleted referenced image from persistence (Hash: {})".format(entity_of_reply.image_hash), reply_to=message.message_id)
def _run(self): with _session_scope() as session: queue_length = len(self._not_uploaded_ids) UPLOADER_QUEUE_LENGTH.set(queue_length) if queue_length <= 0: # sleep for a longer time period to reduce load time.sleep(60) return image_id = self._not_uploaded_ids.pop() entity = self._persistence.get_image(session, image_id) image_data = self._persistence.get_image_data(entity) if image_data is None: LOGGER.warning( "Missing image data for entity, trying to download: {}". format(entity)) try: image_data = download_image_bytes(entity.url) self._persistence.update(session, entity, image_data) entity = self._persistence.get_image(session, image_id) except Exception as e: LOGGER.error( "Error trying to download missing image data for url '{}', deleting entity." .format(entity.url), e) self._persistence.delete(session, entity) return file_ids = send_photo(bot=self._bot, chat_id=self._chat_id, image_data=image_data) bot_token = self._persistence.get_bot_token( session, self._bot.token) for file_id in file_ids: entity.add_file_id(bot_token, file_id) self._persistence.update(session, entity, image_data) LOGGER.debug( "Send image '{}' to chat '{}' and updated entity with file_id {}." .format(entity.url, self._chat_id, file_ids))
def _run(self): """ The job that is executed regularly by this crawler """ with _session_scope() as session: queue_length = len(self._not_optimal_ids) IMAGE_ANALYSIS_QUEUE_LENGTH.set(queue_length) if queue_length <= 0: # sleep for a longer time period to reduce load time.sleep(60) self._not_optimal_ids = set( self._persistence.find_non_optimal(session, self._target_quality)) return entity = None while entity is None: if queue_length <= 0: return image_id = self._not_optimal_ids.pop() queue_length -= 1 IMAGE_ANALYSIS_QUEUE_LENGTH.set(queue_length) entity = self._persistence.get_image(session, image_id) if entity is None: LOGGER.warning( f"Image id scheduled for analysis not found: {image_id}" ) # the entity has probably been removed in the meantime continue analyser = select_best_available_analyser(session, self._image_analysers, self._persistence) if analyser is None: # No analyser available, skipping # sleep for a longer time period to reduce db load time.sleep(60) return if entity.analyser_quality is not None and entity.analyser_quality >= analyser.get_quality( ): LOGGER.debug( "Not analysing '{}' with '{}' because it wouldn't improve analysis quality ({} vs {})" .format(entity.url, analyser.get_identifier(), entity.analyser_quality, analyser.get_quality())) # sleep for a longer time period to reduce db load time.sleep(60) return image_data = self._persistence.get_image_data(entity) if image_data is None: LOGGER.warning( "No image data found for entity with image_hash {}, it will not be analysed." .format(entity.image_hash)) try: image_data = download_image_bytes(entity.url) self._persistence.update(session, entity, image_data) except Exception as e: # if len(entity.telegram_ids) > 0: # LOGGER.warning( # "Error downloading image data from original source, using telegram upload instead. {}".format( # entity)) # # TODO: # else: LOGGER.error( "Error trying to download missing image data for url '{}', deleting entity." .format(entity.url), e) self._persistence.delete(session, entity) return old_analyser = entity.analyser old_quality = entity.analyser_quality if old_quality is None: old_quality = 0 entity.analyser = analyser.get_identifier() entity.analyser_quality = analyser.get_quality() new_text = analyser.find_text(image_data) if (new_text is None or len(new_text) <= 0 ) and entity.text is not None and len(entity.text) > 0: LOGGER.debug( "Ignoring new analysis text because it would delete it") else: entity.text = new_text self._persistence.update(session, entity, image_data) LOGGER.debug( "Updated analysis of '{}' with '{}' (was '{}') with a quality improvement of {} ({} -> {}): {}" .format(entity.url, analyser.get_identifier(), old_analyser, entity.analyser_quality - old_quality, old_quality, entity.analyser_quality, format_for_single_line_log(entity.text))) self._update_stats(session)