me.rkfg.xmpp.bot.plugins.CoolStoryPlugin.java Source code

Java tutorial

Introduction

Here is the source code for me.rkfg.xmpp.bot.plugins.CoolStoryPlugin.java

Source

/*
 * Plugin for jbot that fetches random stories from different websites.
 * Copyright (C) 2017 Kona-chan
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package me.rkfg.xmpp.bot.plugins;

import org.apache.commons.lang3.ArrayUtils;
import org.jivesoftware.smack.packet.Message;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.safety.Whitelist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.ppsrk.gwt.client.ClientAuthException;
import ru.ppsrk.gwt.client.LogicException;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.jsoup.helper.HttpConnection.DEFAULT_UA;

/**
 * Fetches random stories from different websites.
 * At the moment supported websites are: killmepls.ru, ibash.org.ru, anekdot.ru, svalko.org.
 *
 * @author Kona-chan
 * @version 0.1.0
 */
public final class CoolStoryPlugin extends CommandPlugin {

    private enum Website {

        KILLMEPLS(new String[] { "kmp", "" },
                " ? ? ? ? killmepls.ru.",
                "http://killmepls.ru/random/",
                "div#stories > div.row + div.row > div"), IBASH(new String[] { "ib", "" },
                        " ? ? ? ? ibash.org.ru.",
                        "http://ibash.org.ru/random.php",
                        "div.quotbody"), ANEKDOTRU(new String[] { "aru", "" },
                                " ?  ? ? anekdot.ru.",
                                "https://www.anekdot.ru/random/anekdot/",
                                "div.content > div > div + div > div.text"), SVALKO(new String[] { "sv", "?" },
                                        " ? ? ? ? svalko.org",
                                        "http://svalko.org/random.html", "div#content > div.single > div.text");

        private final String[] commands;
        private final String help;
        private final String urlString;
        private final String cssQuery;

        Website(String[] commands, String help, String urlString, String cssQuery) {
            this.commands = commands;
            this.help = help;
            this.urlString = urlString;
            this.cssQuery = cssQuery;
        }

        private String[] getCommands() {
            return commands;
        }

        private String getHelp() {
            return Stream
                    .of(Arrays.stream(commands).map(c -> PREFIX + c).collect(Collectors.joining(", ")), "\t" + help)
                    .collect(Collectors.joining("\n"));
        }

        private String getUrlString() {
            return urlString;
        }

        private String getCssQuery() {
            return cssQuery;
        }

    }

    private static final Website[] WEBSITES = new Website[] { Website.KILLMEPLS, Website.IBASH, Website.ANEKDOTRU,
            Website.SVALKO, };

    private static final boolean CONFIG_REROLL_LONG_STORIES = true;
    private static final int CONFIG_MAX_STORY_LENGTH = 4096;
    private static final int CONFIG_MAX_STORY_LINES = 5;
    private static final int CONFIG_MAX_ROLLS = 5;

    private static final String HELP_AVAILABLE_COMMANDS = "? :";

    private static final String ERROR_WEBSITE_NOT_SUPPORTED = "?     ??.";
    private static final String ERROR_COULD_NOT_PARSE = "  ??   ?.";

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public List<String> getCommand() {
        return Arrays.stream(WEBSITES).flatMap(w -> Arrays.stream(w.getCommands())).collect(Collectors.toList());
    }

    @Override
    public String getManual() {
        return Stream.concat(Stream.of(HELP_AVAILABLE_COMMANDS), Arrays.stream(WEBSITES).map(Website::getHelp))
                .collect(Collectors.joining("\n"));
    }

    @Override
    public String processCommand(Message message, Matcher matcher) throws LogicException, ClientAuthException {
        try {
            final String command = matcher.group(1);

            final Optional<Website> website = Arrays.stream(WEBSITES)
                    .filter(w -> ArrayUtils.contains(w.getCommands(), command.toLowerCase())).findFirst();
            if (!website.isPresent()) {
                return ERROR_WEBSITE_NOT_SUPPORTED;
            }

            return fetchStory(website.get());

        } catch (Exception e) {
            logger.error("{}", e);
            return null;
        }
    }

    private String fetchStory(Website website) throws IOException {
        int roll = 0;
        String result;
        int resultLength;
        int resultLines;

        //noinspection ConstantConditions
        do {
            roll++;

            final Document doc = Jsoup.connect(website.getUrlString()).userAgent(DEFAULT_UA).get();
            doc.outputSettings(new Document.OutputSettings().prettyPrint(false));
            logger.info("Fetched a story from {}", doc.location());

            final Element story = doc.select(website.getCssQuery()).first();
            if (story == null) {
                return ERROR_COULD_NOT_PARSE;
            }

            story.select("div").remove();
            story.select("img").forEach(img -> img.replaceWith(new TextNode(img.attr("src"), "")));
            story.select("br").after("\\n");
            story.select("p").before("\\n\\n");
            final String storyHtml = story.html().replaceAll("\\\\n", "\n");

            result = Jsoup.clean(storyHtml, "", Whitelist.none(), new Document.OutputSettings().prettyPrint(false))
                    .trim();
            resultLength = result.length();
            resultLines = countLines(result);

        } while (CONFIG_REROLL_LONG_STORIES
                && (resultLength > CONFIG_MAX_STORY_LENGTH || resultLines > CONFIG_MAX_STORY_LINES)
                && roll <= CONFIG_MAX_ROLLS);

        return result;
    }

    private int countLines(String string) {
        final Matcher matcher = Pattern.compile("(\r\n)|(\r)|(\n)").matcher(string);
        int lines = 0;
        while (matcher.find()) {
            lines++;
        }
        return lines;
    }

}