def get_closest_media( self, tweet, log_index: typing.Optional[str] = None ) -> typing.Optional[typing.Tuple[TweetCache, TweetCache, typing.List[str]]]: """ Attempt to get the closest media element associated with this tweet and handle any errors if they occur Args: tweet: tweepy.models.Status log_index (Optional[str]): Index to use for system logs. Defaults to SYSTEM Returns: Optional[List] """ log_index = log_index or 'SYSTEM' try: original_cache, media_cache, media = self.twitter.get_closest_media( tweet) except tweepy.error.TweepError as error: # Error 136 means we are blocked if error.api_code == 136: # noinspection PyBroadException try: message = lang('Errors', 'blocked', user=tweet.author) self._post(msg=message, to=tweet.id) except Exception as error: self.log.exception( f"[{log_index}] An exception occurred while trying to inform a user that an account has blocked us" ) raise TwSauceNoMediaException # We attempted to process a tweet from a user that has restricted access to their account elif error.api_code in [179, 385]: self.log.info( f"[{log_index}] Skipping a tweet we don't have permission to view" ) raise TwSauceNoMediaException # Someone got impatient and deleted a tweet before we could get too it elif error.api_code == 144: self.log.info( f"[{log_index}] Skipping a tweet that no longer exists") raise TwSauceNoMediaException # Something unfamiliar happened, log an error for later review else: self.log.error( f"[{log_index}] Skipping due to unknown Twitter error: {error.api_code} - {error.reason}" ) raise TwSauceNoMediaException # Still here? Yay! We have something then. return original_cache, media_cache, media
async def send_reply(self, tweet_cache: TweetCache, media_cache: TweetCache, sauce_cache: TweetSauceCache, requested: bool = True, blocked: bool = False) -> None: """ Return the source of the image Args: tweet_cache (TweetCache): The tweet to reply to media_cache (TweetCache): The tweet containing media elements sauce_cache (Optional[GenericSource]): The sauce found (or None if nothing was found) requested (bool): True if the lookup was requested, or False if this is a monitored user account blocked (bool): If True, the account posting this has blocked the SauceBot Returns: None """ tweet = tweet_cache.tweet sauce = sauce_cache.sauce if sauce and self.ignored_indexes and (int(sauce.index_id) in self.ignored_indexes): self.log.info( f"Ignoring result from ignored index ID {sauce.index_id}") sauce = None if sauce is None: if self.failed_responses and requested: media = TweetManager.extract_media(media_cache.tweet) if not media: return yandex_url = f"https://yandex.com/images/search?url={media[sauce_cache.index_no]}&rpt=imageview" ascii_url = f"https://ascii2d.net/search/url/{media[sauce_cache.index_no]}" google_url = f"https://www.google.com/searchbyimage?image_url={media[sauce_cache.index_no]}&safe=off" message = lang('Errors', 'no_results', { 'yandex_url': yandex_url, 'ascii_url': ascii_url, 'google_url': google_url }, user=tweet.author) self._post(msg=message, to=tweet.id) return # Get the artists Twitter handle if possible twitter_sauce = None if isinstance(sauce, PixivSource): twitter_sauce = self.pixiv.get_author_twitter( sauce.data['member_id']) # If we're requesting sauce from the original artist, just say so if twitter_sauce and twitter_sauce.lstrip( '@').lower() == media_cache.tweet.author.screen_name.lower(): self.log.info( "User requested sauce from a post by the original artist") message = lang('Errors', 'sauced_the_artist') self._post(message, to=tweet.id) return # Lines with priority attributes incase we need to shorten them lines = [] # Add additional sauce URL's if available sauce_urls = [] if isinstance(sauce, AnimeSource): await sauce.load_ids() if self.anime_link in ['myanimelist', 'animal', 'all' ] and sauce.mal_url: sauce_urls.append(sauce.mal_url) if self.anime_link in ['anilist', 'animal', 'all' ] and sauce.anilist_url: sauce_urls.append(sauce.anilist_url) if self.anime_link in ['anidb', 'all']: sauce_urls.append(sauce.url) # Only add Twitter source URL's for booru's, otherwise we may link to something that angers the Twitter gods if isinstance(sauce, BooruSource): for url in sauce.urls: if 'twitter.com' in url: sauce_urls.append(url) if 'twitter.com' in sauce.source_url: sauce_urls.append(sauce.source_url) # For limiting the length of the title/author _repr = reprlib.Repr() _repr.maxstring = 32 # H-Misc doesn't have a source to link to, so we need to try and provide the full title if sauce.index not in ['H-Misc', 'E-Hentai', 'H-Anime']: title = _repr.repr(sauce.title).strip("'") else: _repr.maxstring = 128 title = _repr.repr(sauce.title).strip("'") # Format the similarity string similarity = lang('Accuracy', 'prefix', {'similarity': sauce.similarity}) if sauce.similarity >= 95: similarity = similarity + " " + lang('Accuracy', 'exact') elif sauce.similarity >= 85.0: similarity = similarity + " " + lang('Accuracy', 'high') elif sauce.similarity >= 70.0: similarity = similarity + " " + lang('Accuracy', 'medium') elif sauce.similarity >= 60.0: similarity = similarity + " " + lang('Accuracy', 'low') else: similarity = similarity + " " + lang('Accuracy', 'very_low') if requested: if sauce.similarity >= 60.0: reply = lang('Results', 'requested_found', {'index': sauce.index}, user=tweet.author) + "\n" lines.append(ReplyLine(reply, 1)) else: reply = lang('Results', 'requested_found_low_accuracy', {'index': sauce.index}, user=tweet.author) + "\n" lines.append(ReplyLine(reply, 1)) else: if sauce.similarity >= 60.0: reply = lang('Results', 'other_found', {'index': sauce.index}, user=tweet.author) + "\n" lines.append(ReplyLine(reply, 1)) else: reply = lang('Results', 'other_found_low_accuracy', {'index': sauce.index}, user=tweet.author) lines.append(ReplyLine(reply, 1)) # If it's a Pixiv source, try and get their Twitter handle (this is considered most important and displayed first) if twitter_sauce: reply = lang('Results', 'twitter', {'twitter': twitter_sauce}) lines.append(ReplyLine(reply, newlines=1)) # Print the author name if available if sauce.author_name: author = _repr.repr(sauce.author_name).strip("'") reply = lang('Results', 'author', {'author': author}) lines.append(ReplyLine(reply, newlines=1)) # Omit the title for Pixiv results since it's usually always non-romanized Japanese and not very helpful if not isinstance(sauce, PixivSource): reply = lang('Results', 'title', {'title': title}) lines.append(ReplyLine(reply, 10, newlines=1)) # Add the episode number and timestamp for video sources if isinstance(sauce, VideoSource) and sauce.episode: reply = lang('Results', 'episode', {'episode': sauce.episode}) if sauce.timestamp: reply += " " + lang('Results', 'timestamp', {'timestamp': sauce.timestamp}) lines.append(ReplyLine(reply, 5, newlines=1)) # Add character and material info for booru results if isinstance(sauce, BooruSource): if sauce.material: reply = lang('Results', 'material', {'material': sauce.material[0].title()}) lines.append(ReplyLine(reply, 5, newlines=1)) if sauce.characters: reply = lang('Results', 'character', {'character': sauce.characters[0].title()}) lines.append(ReplyLine(reply, 4, newlines=1)) # Add the chapter for manga sources if isinstance(sauce, MangaSource) and sauce.chapter: reply = lang('Results', 'chapter', {'chapter': sauce.chapter}) lines.append(ReplyLine(reply, 5, newlines=1)) # Display our confidence rating lines.append(ReplyLine(similarity, 2, newlines=1)) # Source URL's are not available in some indexes if sauce.index not in [ 'H-Misc', 'H-Anime', 'H-Magazines', 'H-Game CG', 'Mangadex' ]: if sauce_urls: reply = "\n".join(sauce_urls) lines.append(ReplyLine(reply, newlines=2)) elif sauce.source_url and not isinstance(sauce, BooruSource): lines.append(ReplyLine(sauce.source_url, newlines=2)) # Try and append bot instructions with monitored posts. This might make our post too long, though. if not requested: promo_footer = lang('Results', 'other_footer') if promo_footer: lines.append(ReplyLine(promo_footer, 0, newlines=2)) elif config.getboolean('System', 'display_patreon'): lines.append( ReplyLine( "Support SauceBot!\nhttps://www.patreon.com/saucebot", 3, newlines=2)) # trace.moe time! Let's get a video preview if sauce_cache.media_id: comment = self._post(msg=lines, to=tweet.id, media_ids=[sauce_cache.media_id]) # This was hentai and we want to avoid uploading hentai clips to this account else: comment = self._post(msg=lines, to=tweet.id) # If we've been blocked by this user and have the artists Twitter handle, send the artist a DMCA guide if blocked and twitter_sauce: self.log.info(f"Sending {twitter_sauce} DMCA takedown advice") message = lang('Errors', 'blocked_dmca', {'twitter_artist': twitter_sauce}) # noinspection PyUnboundLocalVariable self._post(msg=message, to=comment.id)