Usuário:SandroHcBot/Código Fonte/WikiSession

De RuneScape Wiki
Ir para: navegação, pesquisa

Handles wiki session and actions.

/*--------------------------------------------------------
 * AmauriceBot - RuneScape Wikia update task robot
 * Copyright (c) 2009-2012  Maurice Abraham.
 *
 * 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/>.
 *
 * Contributors:
 *   None
 *------------------------------------------------------*/

package amauricebot;

import java.io.IOException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Description(summary = "Handles wiki session and actions.")
public class WikiSession {
	private final static String API_URL = "http://pt.runescape.wikia.com/api.php";

	private HashMap<String, String> m_cookies = new HashMap<String, String>();

	private String m_loginUser = null;

	private String m_lastGetTitle = null;
	private String m_lastGetEditToken = null;
	private String m_lastGetTimestamp = null;
	private long m_lastEditTime = 0;

	private Pattern m_apiErrorPattern = Pattern.compile("<error code=\"([^\"]+)\" info=\"([^\"]+)");
	private Pattern m_substPattern = Pattern.compile("<expandtemplates[^>]*>(.*)</expandtemplates>", Pattern.DOTALL);
	private Pattern m_getTextPattern = Pattern.compile("<rev [^>]*>(.*)</rev>", Pattern.DOTALL);
	private Pattern m_loginTokenPattern = Pattern.compile(" token=\"([^\"]*)\"");
	private Pattern m_editTokenPattern = Pattern.compile(" edittoken=\"([^\"]*)\"");
	private Pattern m_timestampPattern = Pattern.compile(" timestamp=\"([^\"]*)\"");
	private Pattern m_userPattern = Pattern.compile(" user=\"([^\"]*)\"");
	private Pattern m_titlePattern = Pattern.compile(" title=\"([^\"]*)\"");
	private Pattern m_apFromPattern = Pattern.compile(" apfrom=\"([^\"]*)\"");
	private Pattern m_eiContPattern = Pattern.compile(" eicontinue=\"([^\"]*)\"");
	private Pattern m_revPattern = Pattern.compile("<rev .*?</rev>", Pattern.DOTALL);
	private Pattern m_anyAttrPattern = Pattern.compile(" (\\w+)=\"([^\"]*)\"");

	private DateFormat m_tsFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

	public WikiSession() {
		m_tsFmt.setTimeZone(TimeZone.getTimeZone("UTC"));
	}

	private String doCmd(String cmd) throws IOException {
		for (int tries = 1; true; tries++) {
			String result = Utils.getInternetPage(API_URL, cmd + "&format=xml", m_cookies);
			String[] toks = Utils.extract(m_apiErrorPattern, result);
			if (toks == null)
				return result; // No error found

			String code = toks[1];
			String info = toks[2];
			System.err.println("Mediawiki API Error: " + code + " (" + info + ")");
			if (tries > 20)
				return result; // Too many retries
			if (!code.matches(".*maxlag.*")
					&& !code.matches(".*DBConnectionError.*"))
				return result; // Not maxlag or DB connection error

			try {
				int delay = (tries + 5) * 2000;
				Thread.sleep(delay);
			} catch (Exception ex) {
				// ignore exception
			}
		}
	}

	public boolean login(String user, String passwd) throws IOException {
		String cmd = "action=login&lgname=" + user + "&lgpassword=" + passwd;
		String result = doCmd(cmd);
		if (result.matches(".*result=\"[Nn]eed[Tt]oken\".*")) {
			// MediaWiki 1.15.3 or later so need to send again with token
			String[] toks = Utils.extract(m_loginTokenPattern, result);
			if (toks != null) {
				cmd = cmd + "&lgtoken=" + Utils.decodeEntities(toks[1]);
				result = doCmd(cmd);
			}
		}
		if (!result.matches(".*result=\"[Ss]uccess\".*"))
			return false;

		m_loginUser = user;
		return true;
	}

	public String getLoginUser() {
		return m_loginUser;
	}

	public void logout() throws IOException {
		String cmd = "action=logout";
		doCmd(cmd);
		m_loginUser = null;
	}

	public HashMap<String, String> getSiteInfo() throws IOException {
		String cmd = "action=query&meta=siteinfo";
		String result = doCmd(cmd);

		HashMap<String, String> map = new HashMap<String, String>();
		Matcher attrMatcher = m_anyAttrPattern.matcher(result);
		while (attrMatcher.find()) {
			String name = attrMatcher.group(1);
			String value = attrMatcher.group(2);
			map.put(name, value);
		}
		return map;
	}

	public String subst(String str) throws IOException {
		String cmd = "action=expandtemplates&text="
				+ URLEncoder.encode(str, "utf-8");
		String result = doCmd(cmd);

		String[] toks = Utils.extract(m_substPattern, result);
		if (toks != null) {
			return Utils.decodeEntities(toks[1]);
		}

		return null;
	}

	public String getText(String title) throws IOException {
		String cmd = "action=query&prop=info|revisions&intoken=edit&titles=" + URLEncoder.encode(title, "utf-8") + "&rvprop=timestamp|content";
		String result = doCmd(cmd);

		m_lastGetTitle = title;

		String[] toks = Utils.extract(m_editTokenPattern, result);
		m_lastGetEditToken = (toks != null ? toks[1] : null);

		toks = Utils.extract(m_timestampPattern, result);
		m_lastGetTimestamp = (toks != null ? toks[1] : null);

		toks = Utils.extract(m_getTextPattern, result);
		if (toks != null) {
			return Utils.decodeEntities(toks[1]);
		}

		return null;
	}

	public Date getLastTimestamp() {
		return parseDate(m_lastGetTimestamp);
	}

	public int getLastAgeDays() {
		long msecs = System.currentTimeMillis() - getLastTimestamp().getTime();
		return (int) (msecs / 86400000);
	}

	public boolean editText(String title, String text, String comment, boolean allowCreate) throws IOException {
		if (!title.equals(m_lastGetTitle))
			return false;

		long timeSinceLastEdit = System.currentTimeMillis() - m_lastEditTime;
		long delay = 6000 - timeSinceLastEdit;

		if (delay > 0) {
			try {
				Thread.sleep(delay);
			} catch (Exception ex) { }
		}

		String cmd = "action=edit&bot=true&title="
				+ URLEncoder.encode(title, "utf-8") + "&summary="
				+ URLEncoder.encode(comment, "utf-8") + "&text="
				+ URLEncoder.encode(text, "utf-8")
				+ (allowCreate ? "" : "&nocreate=1") + "&token="
				+ URLEncoder.encode("" + m_lastGetEditToken, "utf-8")
				+ "&basetimestamp="
				+ URLEncoder.encode("" + m_lastGetTimestamp, "utf-8")
				+ "&maxlag=5";
		String result = doCmd(cmd);

		m_lastEditTime = System.currentTimeMillis();

		return result.matches(".*result=\"[Ss]uccess\".*");
	}

	public SortedSet<String> getAllPages(int namespace, String from, int limit) throws IOException {
		String cmd = "action=query&list=allpages&apnamespace=" + namespace
				+ "&apfilterredir=nonredirects&aplimit=" + (limit < 500 ? limit : 500);
		if (from != null)
			cmd += "&apfrom=" + URLEncoder.encode(from, "utf-8");
		String result = doCmd(cmd);

		SortedSet<String> allTitles = new TreeSet<String>();
		while (allTitles.size() < limit) {
			Matcher titleMatcher = m_titlePattern.matcher(result);
			while (titleMatcher.find()) {
				String title = titleMatcher.group(1);
				allTitles.add(Utils.decodeEntities(title));
			}

			String[] toks = Utils.extract(m_apFromPattern, result);
			if (toks == null)
				break; // no more

			String contTitle = toks[1];
			// Wiki API bug misses next title if contains "+"
			// so use continue without "+" (and discard duplicates)
			contTitle = contTitle.replaceFirst("\\+.*", "");

			String contCmd = cmd + "&apfrom=" + contTitle;
			result = doCmd(contCmd);
		}
		return allTitles;
	}

	public SortedSet<String> getEmbeddedUsages(String templateTitle,
			int namespace, int limit) throws IOException {
		String cmd = "action=query&list=embeddedin" + "&eititle=" + URLEncoder.encode(templateTitle, "utf-8") + "&einamespace=" + namespace + "&eilimit=" + (limit < 500 ? limit : 500);
		String result = doCmd(cmd);

		SortedSet<String> allTitles = new TreeSet<String>();
		while (allTitles.size() < limit) {
			Matcher titleMatcher = m_titlePattern.matcher(result);
			while (titleMatcher.find()) {
				String title = titleMatcher.group(1);
				allTitles.add(Utils.decodeEntities(title));
			}

			String[] toks = Utils.extract(m_eiContPattern, result);
			if (toks == null)
				break; // no more

			String contCmd = cmd + "&eicontinue=" + toks[1];
			result = doCmd(contCmd);
		}
		return allTitles;
	}

	// The getHistory returns most recent revision first
	public PageRevision[] getHistory(String title, int limit)
			throws IOException {
		String cmd = "action=query&prop=revisions" + "&titles=" + URLEncoder.encode(title, "utf-8")
				+ "&rvprop=timestamp|user|content" + "&rvlimit=" + (limit < 50 ? limit : 50);
		String result = doCmd(cmd);

		ArrayList<PageRevision> history = new ArrayList<PageRevision>();
		Matcher revMatcher = m_revPattern.matcher(result);
		while (revMatcher.find()) {
			String revisionXml = revMatcher.group(0);

			String[] toks = Utils.extract(m_timestampPattern, revisionXml);
			String timestamp = (toks != null ? toks[1] : null);

			toks = Utils.extract(m_userPattern, revisionXml);
			String user = (toks != null ? toks[1] : null);

			toks = Utils.extract(m_getTextPattern, revisionXml);
			if (toks == null)
				continue;
			String content = Utils.decodeEntities(toks[1]);

			history.add(new PageRevision(timestamp, user, content));
		}

		return history.toArray(new PageRevision[history.size()]);
	}

	public Date parseDate(String text) {
		if (text != null) {
			try {
				return m_tsFmt.parse(text);
			} catch (Exception e) {	}
		}
		return null;
	}

	public String formatDate(Date date) {
		return date != null ? m_tsFmt.format(date) : "";
	}

	@Override
	protected void finalize() throws Throwable {
		try {
			if (m_loginUser != null) {
				logout();
			}
		} finally {
			super.finalize();
		}
	}

	public class PageRevision {
		private String m_timestamp = null;
		private String m_user = null;
		private String m_content = null;

		PageRevision(String timestamp, String user, String content) {
			m_timestamp = timestamp;
			m_user = user;
			m_content = content;
		}

		public Date getTimestamp() {
			return parseDate(m_timestamp);
		}

		public String getUser() {
			return m_user;
		}

		public String getContent() {
			return m_content;
		}
	}
}