Beispiel #1
0
def test_from_url(mocked_responses):
    def request_callback(request):
        assert request.params['url'] == 'https://example.com/'
        return 500, {}, ''

    mocked_responses.add_callback(responses.POST,
                                  SAUCENAO_URL,
                                  callback=request_callback)

    saucenao = SauceNao()
    with pytest.raises(UnknownApiError):
        saucenao.from_url('https://example.com/')
Beispiel #2
0
def test_optional_params(mocked_responses):
    def request_callback(request):
        assert request.params['api_key'] == 'SauceNAO'
        assert request.params['dbmask'] == '12'
        assert request.params['dbmaski'] == '918'

        return 500, {}, ''

    mocked_responses.add_callback(responses.POST,
                                  SAUCENAO_URL,
                                  callback=request_callback)

    saucenao = SauceNao('SauceNAO', dbmask=12, dbmaski=918)
    with pytest.raises(UnknownApiError):
        saucenao.from_url('https://example.com/')
Beispiel #3
0
 async def sauce(self, ctx, url=None):
     sauce = SauceNao()
     if url is None:
         logger.info('Attachment search query.')
         if ctx.message.attachments:
             raise commands.MissingRequiredArgument(param='image attachment')
         elif len(ctx.message.attachments) == 1:
             attachment_url = ctx.message.attachments[0].url
             response = sauce.from_url(attachment_url)
             await ctx.send(embed=await self.create_sauce_info_embed(response))
         else:
             raise MultipleImagesQueryError
     else:
         logger.info('Url search query.')
         response = sauce.from_url(url)
         await ctx.send(embed=await self.create_sauce_info_embed(response))
Beispiel #4
0
def get_source(link):
    """Gets an image source from the Saucenao API."""
    sauce = SauceNao(api_key=saucenao_key)
    results = sauce.from_url(link)
    url = results[0].url
    if url:
        if "pixiv" in url:
            match = re.search(r"\d+", url)
            if match:
                url = f"https://www.pixiv.net/en/artworks/{match.group()}"
        return f"Title: {results[0].title} Artist: {results[0].author} Similarity: {results[0].similarity}\n\nSource: {url}"
    else:
        return "Sorry, could not find the source of this drawing!"
Beispiel #5
0
async def on_message(message):
    if message.author == client.user:
        return

# bot will parse each message and respond if the keyword "!sauce" is present
    if message.content.startswith('!sauce'):

        sauce = SauceNao()
        try:
            results = sauce.from_url(
                message.attachments[0].url
            )  # if the message provided has an attachment, parse it for the URL
        except:
            results = sauce.from_url(
                message.content[7:]
            )  # otherwise the message itself contains a URL

        best = results[0]  # get the best result based off of similarity
        result = best.title + "\nSimilarity: " + str(
            best.similarity
        ) + "%" + "\nSource URL: " + str(
            best.urls
        )  # get the numeric percentage of similarity and original source URL

        # additionally, if the source is from a video clip, add in the episode it is from and the time
        if isinstance(best, VideoSauce):
            result = result + "\nEpisode: " + str(
                best.part) + "\nTime: " + best.est_time
        await message.channel.send(result)

    elif message.content.startswith('!courses'):
        url_planet = "https://api.planetterp.com/v1/courses?department=" + message.content[
            9:13]
        courses = requests.get(url_planet).json()
        output = ""
        for j in courses:
            output = output + ", " + message.content[9:13] + j['course_number']
        await message.channel.send(output)
Beispiel #6
0
async def searchpic(session):
    piclist = session.current_arg_images
    if len(piclist) == 0:
        session.finish('未识别到图片,请发送图片以识别', at_sender=True)
    sauce = SauceNao()
    for i in piclist:
        count = 1
        reply = []
        res = sauce.from_url(i)
        if not res:
            await session.send(f'第{count}张图识别失败,请稍后再试', at_sender=True)
            continue
        best = res[0]
        reply.append('相似度:' + str(best.similarity) + '%')
        reply.append('标题:「' + best.title + '」')
        reply.append('作者:「' + best.author + '」')
        reply.append(f'[CQ:image,cache=0,file={best.thumbnail}]')
        reply.append('图片地址:' + best.url)
        await session.send('\n'.join(reply))
        count = count + 1
Beispiel #7
0
    async def source(self, ctx, arg):

        sauce = SauceNao()
        results = sauce.from_url(arg)
        best = results[0]

        embed = discord.Embed(color=discord.Color.dark_teal())

        embed.set_author(name=ctx.message.author,
                         icon_url='{}'.format(ctx.message.author.avatar_url))
        embed.set_thumbnail(url=arg)
        embed.add_field(name='Title: {}'.format(best.title),
                        value=best.urls,
                        inline=False)
        embed.add_field(name='Confidence',
                        value='Matched with a {} similarity.'.format(
                            best.similarity),
                        inline=False)
        embed.set_footer(text="Powered by SauceNAO")

        await ctx.send(embed=embed)
def saucenao_caching(image_hash):
    '''
    Caches the result from the given url\n
    Project Erina\n
    © Anime no Sekai - 2020
    '''
    try:
        log("ErinaCaches", f"Caching {str(image_hash)} SauceNAO data...")
        if str(config.Caches.keys.saucenao).replace(" ", "") not in ["None", ""]:
            saucenao_api = SauceNao(api_key=config.Caches.keys.saucenao, numres=1)
        else:
            saucenao_api = SauceNao(numres=1)
        if image_hash.has_url:
            try:
                api_results = saucenao_api.from_url(image_hash.url)[0]
            except:
                return CachingError("SAUCENAO_API_RESPONSE", "An error occured while retrieving SauceNAO API Data")
        else:
            try:
                api_results = saucenao_api.from_file(image_hash.ImageIO)[0]
            except:
                return CachingError("SAUCENAO_API_RESPONSE", "An error occured while retrieving SauceNAO API Data")
        
        StatsAppend(ExternalStats.saucenaoAPICalls)

        try:
            cache = saucenao.erina_from_api(api_results)
        except:
            traceback.print_exc()
            return CachingError("ERINA_CONVERSION", "An error occured while converting SauceNAO API Data to a caching format")
        try:
            TextFile(saucenao_cache_path + str(image_hash) + '.erina', blocking=False).write(cache)
        except:
            return CachingError("FILE_WRITE", "An error occured while writing out the cache data to a file")
        return saucenao_parser.SauceNAOCache(cache)
    except:
        return CachingError("UNKNOWN", "An unknown error occured while caching SauceNAO API Data")
Beispiel #9
0
class SauceNAO(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.nao = SauceNao(api_key=config["api-key"])

    @commands.Cog.listener()
    async def on_message(self, message):
        if not message.channel.is_nsfw() and config["nsfw-only"]:
            return

        if message.attachments:
            url = message.attachments[0].url
        else:
            return

        result = self.nao.from_url(url)
        if result:
            result = result[0]

        if not result.urls:
            embed = Embed(
                title="No sauce found!",
                description="SauceNAO couldn't find a sauce to this image.",
                colour=0xff0000)
            await message.channel.send(embed=embed)
            return

        embed = Embed(title="Sauce found!",
                      url=result.urls[0],
                      colour=0x00ff00)
        embed.add_field(name="Title", value=result.title)
        embed.add_field(name="Author", value=result.author)
        embed.add_field(name="Similarity", value=f"{result.similarity}%")

        embed.set_thumbnail(url=url)
        await message.channel.send(embed=embed)

    @commands.command()
    async def sauce(self, ctx, *, url=None):
        if not url:
            url = ctx.message.attachments[0].url

        result = self.nao.from_url(url)
        if result:
            result = result[0]

        if not result.urls:
            embed = Embed(
                title="No sauce found!",
                description="SauceNAO couldn't find a sauce to this image.",
                colour=0xff0000)
            await message.channel.send(embed=embed)
            return

        embed = Embed(title="Sauce found!",
                      url=result.urls[0],
                      colour=0x00ff00)
        embed.add_field(name="Title", value=result.title)
        embed.add_field(name="Author", value=result.author)
        embed.add_field(name="Similarity", value=f"{result.similarity}%")

        embed.set_thumbnail(url=url)
        await ctx.send(embed=embed)
Beispiel #10
0
def get_sauce(url):
    sauce = SauceNao()
    return sauce.from_url(url)[0]
    """
Beispiel #11
0
class ImgSearch(CogInit):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.sn = SauceNao(Bot.sauce_nao_key, dbmask=1666715746400, numres=3)
        self.IMG_LINK_PATTERN = re.compile(
            r"(https?:\/\/[^\s]*(\?format=\w*&name=\d*x\d*|(\.png|\.jpg|\.jpeg)))"
        )

        self.result_list = {}
        self.reaction_emos = {
            r: i
            for i, r in enumerate(Reactions.numbers + Reactions.letters)
        }

    @staticmethod
    def _isfloat(param: Any) -> bool:
        try:
            float(param)
            return True
        except ValueError:
            return False

    def _get_result_embed(self, i: int, res: dict[str, Any],
                          remain: int) -> discord.Embed:
        embed = discord.Embed(title="搜尋結果", color=Colors.blue)
        # Footer
        embed.set_footer(text=f"第 {i} 張圖|24h 內剩餘可用次數: {remain}",
                         icon_url=self.bot.user.avatar_url)
        # Thumbnail
        if res.thumbnail:
            embed.set_thumbnail(url=res.thumbnail)
        # Fields
        # 作品標題
        if res.title:
            embed.add_field(name="標題", value=res.title)
        # 作者
        if res.author:
            embed.add_field(name="作者", value=res.author)
        # 相似度
        if res.similarity:
            embed.add_field(name="相似度", value=res.similarity, inline=False)
        # 作品連結
        if res.urls:
            for i, url in enumerate(res.urls, 1):
                embed.add_field(name=f"連結{i}", value=url, inline=False)
        # 連結來源
        if "source" in res.raw["data"]:
            if res.raw["data"]["source"]:
                embed.add_field(name="來源",
                                value=res.raw["data"]["source"],
                                inline=False)
        return embed

    def _get_no_result_embed(self, i: int, img_url: str,
                             remain: int) -> discord.Embed:
        embed = discord.Embed(title="搜尋結果", color=Colors.red)
        # Footer
        embed.set_footer(text=f"第 {i} 張圖|24h 內可使用次數: {remain}",
                         icon_url=self.bot.user.avatar_url)
        # Thumbnail
        embed.set_thumbnail(url=img_url)
        # Fields
        embed.add_field(name="沒有結果...",
                        value=f"我...我也沒辦法... {Emojis.pepe_hands}")

        return embed

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction: discord.Reaction,
                              user: discord.User) -> None:
        # 忽略機器人的反應添加事件
        if user.bot:
            return
        # 如果沒有結果訊息紀錄,或不是結果訊息,略過
        if not self.result_list or reaction.message.id not in self.result_list:
            return

        await reaction.remove(user)
        raw_reaction = str(reaction.emoji)
        # 如果不是指定的反應表符,略過
        if raw_reaction not in self.reaction_emos:
            return

        # 反應所代表的數字小於結果串列長度
        if self.reaction_emos[raw_reaction] < len(
                self.result_list[reaction.message.id]):
            await reaction.message.edit(
                content=reaction.message.content,
                embed=self.result_list[reaction.message.id][
                    self.reaction_emos[raw_reaction]],
            )

    @commands.Cog.listener()
    async def on_message_delete(self, msg: discord.Message) -> None:
        if not self.result_list:
            return

        if msg.id in self.result_list:
            del self.result_list[msg.id]

    @commands.command(aliases=["img_search", "is"])
    async def image_search(self, ctx: commands.Context, *args) -> None:
        try:
            # 確認是否有指定最低相似度
            last_isfloat = self._isfloat(args[-1]) if args else False
            min_similarity = float(args[-1]) if (args and last_isfloat) else 72

            queue = []
            # 若有回覆訊息,先抓取回覆訊息內附件、圖片連結
            if ctx.message.reference:
                ref_msg = await ctx.channel.fetch_message(
                    ctx.message.reference.message_id)
                queue += [a.url for a in ref_msg.attachments] + [
                    a[0]
                    for a in re.findall(self.IMG_LINK_PATTERN, ref_msg.content)
                ]
            # 若有上傳附件,獲取訊息內圖片連結
            if ctx.message.attachments:
                queue += [a.url for a in ctx.message.attachments]
            # 若有附上圖片連結,抓取訊息內圖片連結
            if args[:-1] if last_isfloat else args:
                queue += [
                    a for a in (args[:-1] if last_isfloat else args)
                    if re.match(self.IMG_LINK_PATTERN, a)
                ]
            # 執行至此佇列仍為空,判定為未給予搜尋要素
            if not queue:
                raise NoImageToQuery

            result_embeds = []
            # 最多僅搜尋佇列前 6
            for i, img_url in enumerate(queue[:6], 1):
                at_least_one_result = False

                results = self.sn.from_url(url=img_url)
                for res in results:
                    # 相似度小於指定相似度,略過
                    if res.similarity < min_similarity:
                        continue

                    at_least_one_result = True
                    # 獲取結果 Embed
                    result_embeds.append(
                        self._get_result_embed(i, res, results.long_remaining))
                # 完全沒有高於指定相似度的結果
                if not at_least_one_result:
                    # 獲取無結果 Embed
                    result_embeds.append(
                        self._get_no_result_embed(i, img_url,
                                                  results.long_remaining))
            # 送出搜尋結果訊息
            result_msg = await MessageUtils.reply_then_delete(
                ctx, "", 240, embed=result_embeds[0])
            # 添加搜尋結果訊息
            self.result_list[result_msg.id] = result_embeds
            # 添加反應
            for i in range(len(result_embeds)):
                await result_msg.add_reaction(
                    list(self.reaction_emos.keys())[i])

        except errors.UnknownApiError:
            await MessageUtils.reply_then_delete(
                ctx, f"嗚呼,搜圖 API 爆掉了 {Emojis.pepe_hypers}")
        except errors.UnknownServerError:
            await MessageUtils.reply_then_delete(
                ctx, f"搜圖伺服器爆掉了,窩無能為力 {Emojis.pepe_depressed}")
        except errors.LongLimitReachedError:
            await MessageUtils.reply_then_delete(
                ctx, f"今天的搜尋次數已達上限 {Emojis.pepe_hands}")
        except NoImageToQuery:
            await MessageUtils.reply_then_delete(
                ctx, f"你是不是沒有放上要找的圖 {Emojis.thonk}")
Beispiel #12
0
from saucenao_api import SauceNao

url = input("Paste image url here: ")
sauce = SauceNao()
results = sauce.from_url(url)
yn = input(
    f"I've found {results.long_remaining} images, may i show some of them? (y/n)"
)
if yn == "y":
    try:
        for i in range(int(results.long_remaining)):
            print(
                f"Title: {results[i].title} | Similarity {results[i].similarity} | Url: {results[i].urls}"
            )

    except:
        pass
else:
    pass
Beispiel #13
0
    async def sauce(self, ctx, arg=None):
        print('[+] Sauce command detected!')
        _sauce_api = ''
        _results = ''
        _img_url = ''
        _suffixes = ('.png', '.jpg', '.jpeg', '.jfif')

        if str(arg).endswith(_suffixes):
            try:
                _url_check = requests.get(arg)
                _img_url = arg
            except requests.ConnectionError as e:
                await ctx.channel.send('is that an url?')
                print(f'[!] Something went wrong, on acquiring argument {e}')

        elif len(str(arg).split(sep='?')) == 2:
            try:
                if str((str(arg).split(sep='?'))[0]).endswith(_suffixes):
                    _img_url = str((str(arg).split(sep='?'))[0])
                else:
                    await ctx.channel.send('Did you pasted an incomplete link?'
                                           )

            except Exception as e:
                print('[!] Wtf did he sent?')
                await ctx.channel.send(
                    f'wtf did you send? <@256713166149386240> {e}')

        else:
            try:
                _img_url = ctx.message.attachments[0].url
            except Exception as e:
                print(f'[!] Wrong image. {e}')
                await ctx.channel.send('pretty sure it isnt an image')
                return

        await ctx.channel.trigger_typing()
        await asyncio.sleep(7)
        await ctx.channel.send(choice(sauce_respo))

        await ctx.channel.trigger_typing()
        await asyncio.sleep(6)

        try:
            _sauce_api = SauceNao(getenv('sauce_api'))
            _results = _sauce_api.from_url(_img_url)
        except Exception as e:
            print(
                f'[!] Something went wrong, probably rate limited. Again {e}')
            await ctx.channel.send(
                f'{choice(errors)} <@256713166149386240> {e}')

        _best = _results[0]
        _title = _best.title
        _author = _best.author
        _thumbnail = _best.thumbnail
        _score = _best.similarity

        _url = _best.urls
        _url = [str(f'({str(url).replace(" ", "-")})') for url in _best.urls]
        _url = ", ".join(_url)

        _confidence = ''
        print(
            f'[+] Result:{_title}, {_author}, {_score}, {_url}, {_thumbnail}')

        if _score < 25:
            _confidence = "idk never seen it, prolly manga or something"
        elif _score < 50:
            _confidence = "its probably this, but dont quote me on that"
        elif _score < 75:
            _confidence = 'yo i think i seen this one'
        else:
            _confidence = 'oh i know this'

        _sauce = discord.Embed(
            title=_title,
            description=f'Sauce found with confidence level of {_score}%',
            color=0x87CEEB)
        _sauce.set_thumbnail(url=_thumbnail)
        _sauce.add_field(name=f"Author", value=_author, inline=True)

        if len(_url) == 0:
            await ctx.channel.send(_confidence, embed=_sauce)
        else:
            _sauce.add_field(name=f"Link", value=_url, inline=True)
            await ctx.channel.send(_confidence, embed=_sauce)