Example #1
0
    // Formatter for the results of both ~dt variants. Not a discord-accessible method.
    private async Task DerpiTagsDisplay(DerpiSearch element)
    {
        // Get "Info" block, made up of the Artists list and Image Rating.
        string artists = DerpiHelper.BuildArtistTags(element);
        string rating  = DerpiHelper.GetImageRating(element.tags);

        // Display `~dt` info block and all image tags.
        await ReplyAsync($"Info: **{rating}, {artists}**\n\nAll tags: ```{String.Join(", ",element.tags)}```");
    }
Example #2
0
    public async Task DerpiTags([Remainder] string search)
    {
        // Broadcasts "User is typing..." message to Discord channel.
        await Context.Channel.TriggerTypingAsync();

        // Image ID placeholder.
        int imageID;

        // First, check if search term is an image ID. An integer.
        if (int.TryParse(search, out imageID))
        {
            // Continue as normal, imageID is stored in the variable.
        }
        // Second, if not an integer, test if it is a valid Booru URL.
        else if (Global.IsBooruUrl(search))
        {
            // 1. Attempt to parse Integer ID from Booru URL.
            imageID = Global.ExtractBooruId(search);
            // 2a. Unparseable, quit early and apologize to user.
            if (imageID == -1)
            {
                await ReplyAsync("Sorry, I couldn't understand the URL you gave me. Please try again with another URL, or contact Hoovier if something is wrong.");
                await ReplyAsync("If you think this was in error, please contact Hoovier with the request you made.");

                return;
            }
            // 2b. Continue as normal, imageID is stored in the variable.
        }
        // Lastly, if not a valid Booru URL or ID, output a gentle error message.
        else
        {
            await ReplyAsync("Sorry, I couldn't understand the URL you gave me. Please try again with another URL, or contact Hoovier if something is wrong.");

            return;
        }

        // If you reach here, the "else" return case didn't happen and we can query Derpibooru.
        // We can use imageID to uniformly query DerpiBooru.
        string requestUrl = DerpiHelper.BuildDerpiUrl(this.baseURL, new Dictionary <string, string>()
        {
            { "filter_id", "56027" },
            { "q", $"id:{imageID}" },
        });
        string    DerpiJson     = Get.Derpibooru(requestUrl).Result;
        DerpiRoot DerpiResponse = JsonConvert.DeserializeObject <DerpiRoot>(DerpiJson);

        if (DerpiResponse.images.Count == 0)
        {
            await ReplyAsync("No results! The ID or URL provided might have had a typo or is deleted no longer exists.");
            await ReplyAsync("If you think this was in error, please contact Hoovier with the request you made.");
        }
        else
        {
            await DerpiTagsDisplay(DerpiResponse.images[0]);
        }
    }
Example #3
0
    public async Task DerpiReverse([Remainder] string url)
    {
        // Broadcasts "User is typing..." message to Discord channel.
        await Context.Channel.TriggerTypingAsync();

        // Validate that the user input a URL.
        if (string.IsNullOrEmpty(url))
        {
            await ReplyAsync("No URL given, please provide a valid URL!");

            return;
        }
        else if (DerpiHelper.IsReachableUrl(url))
        {
            await ReplyAsync("Invalid or inaccessible URL: `" + url + "`\nPlease try again, or contact Hoovier!");

            return;
        }

        // If we have a URL, then make a scraper request.
        // Deserialize (from JSON to DerpibooruResponse.RootObject) the Derpibooru search results.
        string       DerpiJson     = Get.ReverseSearch(this.reverseURL + url).Result;
        DerpiRevRoot DerpiResponse = JsonConvert.DeserializeObject <DerpiRevRoot>(DerpiJson);

        // Convert Search Array to a List, to use List functionality.
        List <DerpiRevSearch> imageList = DerpiResponse.images;

        if (imageList.Count == 0)
        {
            await ReplyAsync("Could not find the image on Derpibooru.org!");
        }
        else
        {
            // Get search result element. First element if there are more than one.
            DerpiRevSearch element = imageList.First();
            // Determine if Channel allows NSFW content or not.
            bool safeOnly = !DBTransaction.isChannelWhitelisted(Context.Channel.Id) && !Context.IsPrivate;

            if (safeOnly && !DerpiHelper.IsElementSafe(element.tags))
            {
                // If there is a NSFW artwork searched on a SFW channel, do not display result and inform the user.
                await ReplyAsync("Result found, but is NSFW. Please enable NSFW on this channel to view result, or ask again on an approved channel or private message.");
            }
            else
            {
                // Display the image link if allowed!
                string derpiURL = "https://derpibooru.org/" + imageList.First().id;
                await ReplyAsync("Result found on Derpibooru here: " + derpiURL);

                Global.LastDerpiID[Context.Channel.Id] = imageList.First().id.ToString();
            }
        }
    }
Example #4
0
    public async Task ArtistNoLink()
    {
        await Context.Channel.TriggerTypingAsync();

        // Get last Derpibooru Image ID from global cache.
        int imageID;

        // Check if an a "~derpi" search has been made in this channel yet.
        if (!Global.LastDerpiID.ContainsKey(Context.Channel.Id))
        {
            await ReplyAsync("You need to call `~derpi` (`~d` for short) to get some results before I can hoof you over more silly!");

            return;
        }

        // Try to parse the global cache into an integer ID.
        if (int.TryParse(Global.LastDerpiID[Context.Channel.Id], out imageID))
        {
            string requestUrl = DerpiHelper.BuildDerpiUrl(this.baseURL, new Dictionary <string, string>()
            {
                { "filter_id", "178065" },
                { "q", $"id:{imageID}" },
            });
            string    DerpiJson     = Get.Derpibooru(requestUrl).Result;
            DerpiRoot DerpiResponse = JsonConvert.DeserializeObject <DerpiRoot>(DerpiJson);

            if (DerpiResponse.images.Count == 0)
            {
                // Given ID does not exist.
                await ReplyAsync("No results! The tag may be misspelled, or the results could be filtered out due to channel!");
            }
            else
            {
                // Get artist Tag Link(s) and print image URL as well.
                bool   safeOnly    = !DBTransaction.isChannelWhitelisted(Context.Channel.Id) && !Context.IsPrivate;
                string artistLinks = DerpiHelper.BuildArtistTags(DerpiResponse.images[0], true, !safeOnly);
                await ReplyAsync($"https://derpibooru.org/{imageID}\n{artistLinks}");
            }
        }
        else
        {
            // Can't get an ID from cache.
            await ReplyAsync("No results! The tag may be misspelled, or the results could be filtered out due to channel!");
        }
    }
Example #5
0
    public async Task DerpistAsync([Remainder] string search)
    {
        await Context.Channel.TriggerTypingAsync();

        // Same base query as ~derpi, discounting sorting and asking for less results.
        Dictionary <string, string> queryParams = new Dictionary <string, string>()
        {
            { "filter_id", "178065" },
            { "per_page", "1" },
            { "page", "1" },
            { "q", search },
        };

        // Build the full request URL.
        string requestUrl = DerpiHelper.BuildDerpiUrl(this.baseURL, queryParams);

        // Make the request, and parse the JSON into a C#-friendly object.
        DerpiRoot results = JsonConvert.DeserializeObject <DerpiRoot>(Get.Derpibooru(requestUrl).Result);

        await ReplyAsync($"Total results: {results.total}");
    }
Example #6
0
    // Takes a Derpibooru singular search result node and returns a string message to be sent to Discord.
    /// <summary>Builds the artist tag section of most Derpibooru results.</summary>
    /// <param name="element">A Derpibooru Search Result Element.</param>
    /// <param name="artistAsLink">Whether or not to render it as a list of tags, or a list of links.</param>
    /// <param name="NSFW">Only valid if "artistAsLink" is true, then we need to flag if we are showing SFW results or not.</param>
    /// <returns>A string response consisting of the image element itself, and an artist tags block.</returns>
    public static string BuildDiscordResponse(DerpiSearch element, bool artistAsLink = false, bool NSFW = false)
    {
        // Get the full image URL in the format "//derpicdn.net/img/view/YYYY/M/d/IMAGE_ID.png"
        // Prepend the protocol, HTTPS, to the incomplete URL representation.
        string results = element.representations.full;

        //if its an ~artist command, respond with derpibooru/id link instead of derpicdn one
        if (artistAsLink)
        {
            results = "https://derpibooru.org/" + element.id;
        }

        // Get the artist block.
        string artistBlock = DerpiHelper.BuildArtistTags(element, artistAsLink, NSFW);

        // Add a newline in between if there are any results.
        if (!String.IsNullOrEmpty(artistBlock))
        {
            artistBlock = "\n" + artistBlock;
        }

        return(results + artistBlock);
    }
Example #7
0
 // TODO: ADD BETTER DOCUMENT/SUMMARY.
 // Check if a Derpibooru Search result is SFW, by way of scanning for a "safe" tag.
 public static bool IsElementSafe(List <string> tags)
 {
     return(DerpiHelper.GetImageRating(tags).Equals("safe"));
 }
Example #8
0
    public async Task DerpiLuckyMulti(int num, [Remainder] string search)
    {
        // Broadcasts "User is typing..." message to Discord channel.
        await Context.Channel.TriggerTypingAsync();

        // Limit ~lucky amounts.
        if (num < 1 || num > 5)
        {
            await ReplyAsync("You need to pick a number bigger than 0 and no more than 5");

            return;
        }

        // Set up the base query parameters.
        // Sorted randomly, gets "num" amount of items!
        Dictionary <string, string> queryParams = new Dictionary <string, string>()
        {
            { "filter_id", "178065" },
            { "sf", "random" },
            { "sd", "desc" },
            { "per_page", num.ToString() },
            { "page", "1" },
        };

        // If the channel is not on the list of NSFW enabled channels do not allow NSFW results.
        // the second part checks if the command was executed in DMS, DM channels do not have to be added to the NSFW enabled list.
        // In DMs the first check will fail, and so will the second, allowing for nsfw results to be displayed.
        bool safeOnly = !DBTransaction.isChannelWhitelisted(Context.Channel.Id) && !Context.IsPrivate;

        // Add search to the parameters list, with "AND safe" if it failed the NSFW check.
        queryParams.Add("q", safeOnly ? $"{search}+AND+safe" : search);

        // Build the full request URL.
        string requestUrl = DerpiHelper.BuildDerpiUrl(this.baseURL, queryParams);

        // Deserialize (from JSON to DerpibooruResponse.RootObject) the Derpibooru search results.
        DerpiRoot DerpiResponse = JsonConvert.DeserializeObject <DerpiRoot>(Get.Derpibooru(requestUrl).Result);

        // Actual request an. Try-catch to softly catch exceptions.
        try {
            if (DerpiResponse.images.Count == 0)
            {
                await ReplyAsync("No results! The tag may be misspelled, or the results could be filtered out due to channel!");

                return;
            }
            else if (DerpiResponse.images.Count < num)
            {
                await ReplyAsync($"Not enough results to post {num}, but here is what we have found!");
            }

            // Print all results of the search!
            // Sorted randomly by Derpibooru already, and will be between 1-5 elements.
            string message = $"Listing {DerpiResponse.images.Count} results\n";
            message += String.Join("\n",
                                   DerpiResponse.images.Select(
                                       element => element.representations.full));

            await ReplyAsync(message);
        } catch {
            await ReplyAsync("Sorry! Something went wrong, your search terms are probably incorrect.");

            return;
        }
    }
Example #9
0
    public async Task DerpiNextPick(int amount)
    {
        await Context.Channel.TriggerTypingAsync();

        string response = $"Posting {amount} links:\n";

        //prevents the useless "posting 1 links" thing.
        if (amount == 1)
        {
            response = "";
        }


        DerpiRoot DerpiResponse;

        // Check if an a "~derpi" search has been made in this channel yet.
        if (Global.DerpiSearchCache.ContainsKey(Context.Channel.Id))
        {
            DerpiResponse = JsonConvert.DeserializeObject <DerpiRoot>(Global.DerpiSearchCache[Context.Channel.Id]);
        }
        else
        {
            await ReplyAsync("You need to call `~derpi` (`~d` for short) to get some results before I can hoof you over more silly!");

            return;
        }

        if (DerpiResponse.images.Count() == 0)
        {
            // No Results Message.
            await ReplyAsync("No results! The tag may be misspelled, or the results could be filtered out due to channel!");

            return;
        }
        else if (DerpiResponse.images.Count < amount)
        {
            response = "Not enough images to post, posting all \n";
            amount   = DerpiResponse.images.Count;
        }
        else if (DerpiResponse.images.Count() == 1)
        {
            // Only a single result, no pagination.
            await ReplyAsync("Only a single result in this image set.\n");

            // No return, let it continue to parsing the image below.
        }

        //check user provided amount
        if (amount < 1 || amount > 5)
        {
            await ReplyAsync("Pick a number between 1 and 5!");

            return;
        }

        // If ~next goes off the globally cached page, loop around the beginning again.
        if (Global.DerpibooruSearchIndex[Context.Channel.Id] + amount > DerpiResponse.images.Count())
        {
            Global.DerpibooruSearchIndex[Context.Channel.Id] = 0;
        }
        for (int counter = 0; counter < amount; counter++)
        {
            if (DerpiResponse.images.Count < Global.DerpibooruSearchIndex[Context.Channel.Id] + 1)
            {
                await ReplyAsync("Reached end of results, resetting index. Use ~enext to start again.");

                Global.DerpibooruSearchIndex[Context.Channel.Id] = 0;
            }
            //if everythings fine, increase index by 1
            else
            {
                response = response + DerpiHelper.BuildDiscordResponse(DerpiResponse.images[Global.DerpibooruSearchIndex[Context.Channel.Id]]) + "\n";
                Global.DerpibooruSearchIndex[Context.Channel.Id]++;
            }
        }

        // Add image ID to Global.links.
        // TODO: Describe where this is used better?
        Global.LastDerpiID[Context.Channel.Id] = DerpiResponse.images[Global.DerpibooruSearchIndex[Context.Channel.Id] - 1].id.ToString();

        RestUserMessage msg = await Context.Channel.SendMessageAsync(response);

        await msg.AddReactionAsync(new Emoji("▶"));

        //set random info for running ~enext through emoji reactions.
        Global.derpiContext[Context.Channel.Id]        = Context;
        Global.derpiMessageToTrack[Context.Channel.Id] = msg.Id;
    }
Example #10
0
    // "Master" Derpibooru/~derpi searching method. Not a discord-accessible method.
    private async Task DerpiMaster(bool artistAsLink, int Sort, string search)
    {
        // Broadcasts "User is typing..." message to Discord channel.
        await Context.Channel.TriggerTypingAsync();

        // Validate that a valid sorting option was chosen.
        if (Sort < 0 || Sort >= this.sortingOptions.Length)
        {
            await ReplyAsync("Invalid sorting option: " + Sort + ". Please try again");

            return;
        }
        // Choose sorting method from the available list.
        string sortParam = this.sortingOptions[Sort];

        // Set up the base query parameters.
        // "q" is our search terms
        // "filter_id" is the filter against content banned by Discord TOS.
        Dictionary <string, string> queryParams = new Dictionary <string, string>()
        {
            { "filter_id", "178065" },
            { "sf", sortParam },
            { "sd", "desc" },
            { "per_page", "50" },
            { "page", "1" },
        };

        // If the channel is not on the list of NSFW enabled channels do not allow NSFW results.
        // the second part checks if the command was executed in DMS, DM channels do not have to be added to the NSFW enabled list.
        // In DMs the first check will fail, and so will the second, allowing for nsfw results to be displayed.
        bool safeOnly = !DBTransaction.isChannelWhitelisted(Context.Channel.Id) && !Context.IsPrivate;

        // Add search to the parameters list, with "AND safe" if it failed the NSFW check.
        queryParams.Add("q", safeOnly ? $"{search}+AND+safe" : search);

        // Build the full request URL.
        string requestUrl = DerpiHelper.BuildDerpiUrl(this.baseURL, queryParams);

        // Global.DerpiSearchCache is a dictionary-based cache with the last search result in that channel, if applicable.
        // Always stores results globally for other commands like ~next to keep track.
        Global.DerpiSearchCache[Context.Channel.Id] = Get.Derpibooru(requestUrl).Result;

        // Deserialize (from JSON to DerpibooruResponse.RootObject) the Derpibooru search results.
        DerpiRoot DerpiResponse = JsonConvert.DeserializeObject <DerpiRoot>(Global.DerpiSearchCache[Context.Channel.Id]);

        // Actual request an. Try-catch to softly catch exceptions.
        try {
            // Convert Search Array to a List, to use List functionality.
            List <DerpiSearch> imageList = DerpiResponse.images;
            if (imageList.Count == 0)
            {
                await ReplyAsync("No results! The tag may be misspelled, or the results could be filtered out due to channel!");

                return;
            }
            // Get random number generator and random entry.
            var rng  = new Random();
            int rand = rng.Next(imageList.Count);
            Global.DerpibooruSearchIndex[Context.Channel.Id] = rand + 1;
            DerpiSearch randomElement = imageList.ElementAt(rand);

            // Add image ID to Global.links.
            // TODO: Describe where this is used better?
            Global.LastDerpiID[Context.Channel.Id] = randomElement.id.ToString();
            RestUserMessage msg = await Context.Channel.SendMessageAsync(
                DerpiHelper.BuildDiscordResponse(randomElement, artistAsLink, !safeOnly)
                );

            await msg.AddReactionAsync(new Emoji("▶"));

            //set random info for running ~enext through emoji reactions.
            Global.derpiContext[Context.Channel.Id]        = Context;
            Global.derpiMessageToTrack[Context.Channel.Id] = msg.Id;
        } catch {
            await ReplyAsync("Sorry! Something went wrong, your search terms are probably incorrect.");

            return;
        }
    }