Java tutorial
/** * @(#)NirvanaBasicBot.java 27.12.2015 * Copyright 2012-2015 Dmitry Trofimovich (KIN, Nirvanchik, DimaTrofimovich@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * WARNING: This file may contain Russian characters. * Recommended code page for this file is CP1251 (also called Windows-1251). * */ package org.wikipedia.nirvana; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import javax.security.auth.login.FailedLoginException; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.PropertyConfigurator; /** * How to use this bot framework? * 1) CONFIG * Write your config file in xml: config.xml * You may choose another file name, but specify it in command line when starting the bot: * java - <package_name_of_your_bot>.<YourBotClassName> <your_config_file_name>.xml * If you have special properties, load them in redefined {@link loadCustomProperties()} * Use {@link properties} to load your properties * 2) Bot INFO, LICENSE, USAGE * Redefine {@link showLicense()} if your prefer license other than GNU GPL * Redefine {@link showInfo()}. Redefine {@link showUsage()}. * 3) CONSTRUCTOR * Write your constructor and pass int flags to default constructor * 4) GO * Implement the body of {@link go()} - the main code of what your bot * 5) MAIN * Implement <code>public static void main()</code> where create your bot instance with specified flags * and call {@link run()} to run bot. */ public abstract class NirvanaBasicBot { public static final int FLAG_SHOW_LICENSE = 0b01; public static final int FLAG_CONSOLE_LOG = 0b010; protected static final boolean DEBUG_BUILD = false; protected static org.apache.log4j.Logger log = null; protected static Properties properties = null; protected boolean DEBUG_MODE = false; public static final String YES = "yes"; public static final String NO = "no"; protected int MAX_LAG = 15; protected int THROTTLE_TIME_MS = 10000; protected NirvanaWiki wiki; protected String LANGUAGE = "ru"; protected String DOMAIN = ".wikipedia.org"; protected String SCRIPT_PATH = "/w"; protected String PROTOCOL = "https://"; protected String COMMENT = ""; protected int flags = 0; private ConsoleAppender consoleAppender = null; public static String LICENSE = "This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, either version 3 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see <http://www.gnu.org/licenses/>\n"; public void showUsage() { } public void showInfo() { System.out.print("NirvanaBasicBot v1.2 Copyright (C) 2012-2013 Dmitry Trofimovich (KIN)\n\n"); } public void showLicense() { System.out.print(LICENSE); } public NirvanaBasicBot() { } public NirvanaBasicBot(int flags) { this.flags = flags; } public int getFlags() { return flags; } public void run(String args[]) { showInfo(); if ((flags & FLAG_SHOW_LICENSE) != 0) { showLicense(); } System.out.print("----------------------< BOT STARTED >-------------------------------\n"); String configFile = getConfig(args); System.out.println("applying config file: " + configFile); Map<String, String> launch_params = getLaunchArgs(args); startWithConfig(configFile, launch_params); cleanup(); System.out.print("----------------------< BOT FINISHED > -----------------------------\n"); } private void cleanup() { if (consoleAppender != null) { Logger.getRootLogger().removeAppender(consoleAppender); consoleAppender = null; } } private Map<String, String> getLaunchArgs(String[] args) { HashMap<String, String> params = new HashMap<String, String>(); for (int i = 1; i < args.length; i++) { if (!args[i].startsWith("-")) continue; String[] parts = args[i].substring(1).split("=", 2); String left = parts[0]; String right = "1"; if (parts.length == 2) { right = parts[1]; } params.put(left, right); } return params; } /** * this is an example of main function */ /** public static void main(String[] args) { NirvanaBasicBot bot = new NirvanaBasicBot(FLAG_CONSOLE_LOG); bot.run(args); }*/ public String getConfig(String[] args) { String configFile = null; if (args.length == 0) { if (DEBUG_BUILD) { configFile = "config_test.xml"; } else { configFile = "config.xml"; } } else { configFile = args[0]; } return configFile; } public void startWithConfig(String cfg, Map<String, String> launch_params) { properties = new Properties(); try { InputStream in = new FileInputStream(cfg); if (cfg.endsWith(".xml")) { properties.loadFromXML(in); } else { properties.load(in); } in.close(); } catch (FileNotFoundException e) { System.out.println("ABORT: file " + cfg + " not found"); return; } catch (IOException e) { System.out.println("ABORT: Error reading config: " + cfg); return; } initLog(); String login = properties.getProperty("wiki-login"); String pw = properties.getProperty("wiki-password"); if (login == null || pw == null || login.isEmpty() || pw.isEmpty()) { String accountFile = properties.getProperty("wiki-account-file"); if (accountFile == null || accountFile.isEmpty()) { System.out.println("ABORT: login info not found in properties"); log.fatal("wiki-login or wiki-password or wiki-account-file is not specified in settings"); return; } Properties loginProp = new Properties(); try { InputStream in = new FileInputStream(accountFile); if (accountFile.endsWith(".xml")) { loginProp.loadFromXML(in); } else { loginProp.load(in); } in.close(); } catch (FileNotFoundException e) { System.out.println("ABORT: file " + accountFile + " not found"); log.fatal("ABORT: file " + accountFile + " not found"); return; } catch (IOException e) { System.out.println("ABORT: failed to read " + accountFile); log.fatal("failed to read " + accountFile + " : " + e); return; } login = loginProp.getProperty("wiki-login"); pw = loginProp.getProperty("wiki-password"); if (login == null || pw == null || login.isEmpty() || pw.isEmpty()) { System.out.println("ABORT: login info not found in file " + accountFile); log.fatal("wiki-login or wiki-password or wiki-account-file is not found in file " + accountFile); return; } } log.info("login=" + login + ",password=(not shown)"); LANGUAGE = properties.getProperty("wiki-lang", LANGUAGE); log.info("language=" + LANGUAGE); DOMAIN = properties.getProperty("wiki-domain", DOMAIN); log.info("domain=" + DOMAIN); PROTOCOL = properties.getProperty("wiki-protocol", PROTOCOL); log.info("protocol=" + PROTOCOL); COMMENT = properties.getProperty("update-comment", COMMENT); MAX_LAG = Integer.valueOf(properties.getProperty("wiki-maxlag", String.valueOf(MAX_LAG))); THROTTLE_TIME_MS = Integer .valueOf(properties.getProperty("wiki-throttle", String.valueOf(THROTTLE_TIME_MS))); log.info("comment=" + COMMENT); DEBUG_MODE = properties.getProperty("debug-mode", DEBUG_MODE ? YES : NO).equals(YES); log.info("DEBUG_MODE=" + DEBUG_MODE); if (!loadCustomProperties(launch_params)) { log.fatal("Failed to load all required properties. Exiting..."); return; } String domain = DOMAIN; if (domain.startsWith(".")) { domain = LANGUAGE + DOMAIN; } wiki = createWiki(domain, SCRIPT_PATH, PROTOCOL); configureWikiBeforeLogin(); log.info("login to " + domain + ", login: " + login + ", password: (not shown)"); try { wiki.login(login, pw.toCharArray()); } catch (FailedLoginException e) { log.fatal("Failed to login to " + LANGUAGE + ".wikipedia.org, login: " + login + ", password: " + pw); return; } catch (IOException e) { log.fatal(e.toString()); e.printStackTrace(); return; } if (DEBUG_MODE) { wiki.setDumpMode(); } log.warn("BOT STARTED"); try { go(); } catch (InterruptedException e) { onInterrupted(e); } wiki.logout(); log.warn("EXIT"); } protected NirvanaWiki createWiki(String domain, String path, String protocol) { return new NirvanaWiki(domain, path, protocol); } protected void onInterrupted(InterruptedException e) { } /** * */ protected void configureWikiBeforeLogin() { wiki.setMaxLag(MAX_LAG); wiki.setThrottle(THROTTLE_TIME_MS); } protected abstract void go() throws InterruptedException; protected boolean loadCustomProperties(Map<String, String> launch_params) { return true; } protected void initLog() { String log4jSettings = properties.getProperty("log4j-settings"); if (log4jSettings == null || log4jSettings.isEmpty() || !(new File(log4jSettings)).exists()) { Properties properties = new Properties(); if ((flags & FLAG_CONSOLE_LOG) != 0) { System.out.println("INFO: console logs enabled"); ConsoleAppender console = new ConsoleAppender(); //create appender //configure the appender String PATTERN = "%d [%p|%C{1}] %m%n"; //%c will giv java path console.setLayout(new PatternLayout(PATTERN)); console.setThreshold(Level.DEBUG); console.activateOptions(); consoleAppender = console; //add appender to any Logger (here is root) Logger.getRootLogger().addAppender(console); //properties.setProperty("log4j.rootLogger", "DEBUG, stdout"); } else { System.out.println("INFO: logs disabled"); properties.setProperty("log4j.rootLogger", "OFF"); PropertyConfigurator.configure(properties); } } else { PropertyConfigurator.configure(log4jSettings); System.out.println("INFO: using log settings : " + log4jSettings); } log = org.apache.log4j.Logger.getLogger(this.getClass().getName()); } protected static int validateIntegerSetting(Properties pop, String name, int def, boolean notifyNotFound) { int val = def; try { String str = properties.getProperty(name); if (str == null) { if (notifyNotFound) { log.info("settings: integer value not found in settings (" + name + ")"); } return val; } val = Integer.parseInt(str); } catch (NumberFormatException e) { log.error("invalid settings: error when parsing integer values of " + name); } catch (NullPointerException e) { if (notifyNotFound) { log.info("settings: integer value not found in settings (" + name + "), using default"); } } return val; } public static boolean textOptionsToMap(String text, Map<String, String> parameters) { return textOptionsToMap(text, parameters, "#", "//"); } public static boolean textOptionsToMap(String text, Map<String, String> parameters, String... commentSeparators) { String lines[] = text.split("\r|\n"); for (String line : lines) { if (line.trim().isEmpty()) continue; log.debug(line); if (commentSeparators != null && commentSeparators.length > 0) { if (StringUtils.startsWithAny(line.trim(), commentSeparators)) { continue; } } int index = line.indexOf("="); if (index < 0) return false; parameters.put(line.substring(0, index).trim(), line.substring(index + 1).trim()); } return true; } protected void logPortalSettings(Map<String, String> parameters) { Set<Entry<String, String>> set = parameters.entrySet(); Iterator<Entry<String, String>> it = set.iterator(); while (it.hasNext()) { Entry<String, String> next = it.next(); log.debug(next.getKey() + " = " + next.getValue()); } } public static ArrayList<String> optionToStringArray(String option) { return optionToStringArray(option, false, ","); } public static ArrayList<String> optionToStringArray(String option, boolean withDQuotes) { return optionToStringArray(option, withDQuotes, ","); } public static ArrayList<String> optionToStringArray(String option, boolean withDQuotes, String separator) { ArrayList<String> list = new ArrayList<String>(); String separatorPattern; if (withDQuotes && option.contains("\"")) { separatorPattern = "(\"\\s*" + separator + "\\s*\"|^\\s*\"|\"\\s*$)"; } else { separatorPattern = separator; } String[] items = option.split(separatorPattern); for (int i = 0; i < items.length; ++i) { String cat = items[i].trim(); if (!cat.isEmpty()) { list.add(cat); } } return list; } public static boolean TryParseTemplate(String template, String userNamespace, String text, Map<String, String> parameters, boolean splitByNewLine) { log.debug("portal settings parse started"); log.debug("template = " + template); log.debug("text = " + StringTools.trancateTo(text, 100)); //String str = "^\\{\\{"+newpagesTemplateName+".*\\}\\}.*$"; // works //String str = "^(\\{\\{"+newpagesTemplateName+".*(\\{\\{.+\\}\\})?.*\\}\\})(.*)$"; String recognizeTemplate = template; String userEn = "User:"; String userLc = userNamespace + ":"; if (recognizeTemplate.startsWith(userEn)) { recognizeTemplate = recognizeTemplate.substring(userEn.length()); } else if (recognizeTemplate.startsWith(userLc)) { recognizeTemplate = recognizeTemplate.substring(userLc.length()); } recognizeTemplate = "(" + userEn + "|" + userLc + ")" + recognizeTemplate.replace("(", "\\(").replace(")", "\\)"); // We don't start from ^ because we allow any text before template if (!WikiUtils.parseTemplate(recognizeTemplate, text, parameters, splitByNewLine)) { log.error("portal settings parse error"); return false; } parameters.put("BotTemplate", template); log.debug("portal settings parse finished"); return true; } }