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

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

Atualiza o número de jogadores com nível 99, etc.

/*--------------------------------------------------------
 * 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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Description(summary = "Atualiza o número de jogadores com nível 99, etc.")
public class Level99sTask implements BotTask {
	private WikiSession m_wiki = null;
	private DateFormat m_dateFmt = null;
	private final static boolean m_debug = false;

	private final static int m_maxOverall = 2496;
	private final static int m_maxXp = 200000000;

	private HSPage m_topPage = null;
	private int m_maxPageSize = 0;
	private int m_maxPageNum = 0;

	private final int HS_DELAY = 2 * 1000;

	private final static String[] m_skillList = { "Total", "Ataque", "Defesa",
			"Força", "Condição física", "Combate à distância", "Oração",
			"Magia", "Culinária", "Corte de lenha", "Arco e flecha", "Pesca",
			"Arte do fogo", "Artesanato", "Metalurgia", "Mineração",
			"Herbologia", "Agilidade", "Roubo", "Extermínio", "Agricultura",
			"Criação de runas", "Caça", "Construção", "Evocação", "Dungeon" };

	private final static String HS_URL = "http://services.runescape.com/m=hiscore/overall.ws?category_type=0";

	private final Pattern m_hsEntryPattern = Pattern.compile("columnRank[^<]*?<span>([^<]*)</span>"
					+ ".*?columnName[^<]*?<span>([^<]*)</span>" + ".*?columnLevel[^<]*?<span>([^<]*)</span>"
					+ ".*?columnXp[^<]*?<span>([^<]*)</span>", Pattern.DOTALL);

	private final Pattern m_hsMaxPagePattern = Pattern.compile("page=(\\d{3,})");

	private enum ScoreType {
		LEVEL, XP
	}

	public Level99sTask(WikiSession wiki) {
		m_wiki = wiki;

		// TODO Tentar colocar data em pt_PT
		Locale locale = new java.util.Locale("pt", "PT");

		m_dateFmt = new SimpleDateFormat("d MMMM yyyy", locale);
		m_dateFmt.setTimeZone(TimeZone.getTimeZone("UTC"));
	}

	public void perform() throws IOException {
		m_topPage = new HSPage(0, 1, ScoreType.LEVEL);
		if (!m_topPage.load())
			return;

		m_maxPageSize = m_topPage.getEntries().size();
		if (m_maxPageSize < 10) {
			Utils.log("ERRO: Encontrado apenas " + m_maxPageSize + " na página dos Recordes Totais");
			return;
		}

		m_maxPageNum = m_topPage.getMaxPage();
		if (m_maxPageNum < 1000) {
			Utils.log("ERRO: Não foi possível encontrar a última página nos Recordes Totais");
			return;
		}

		update99s();
		update200mxp();
		updateHsLowest();
	}

	private void update99s() throws IOException {
		String title = "Predefinição:99s";
		String text = m_wiki.getText(title);
		if (text == null) {
			Utils.log("ERRO: Não foi possível obter o conteúdo de [[" + title + "]]");
			return;
		}

		if (m_topPage.getHighestScore() != m_maxOverall)
			Utils.log("AVISO: Nível total não experado de " + m_topPage.getHighestScore());
		else
			text = update99sText(title, "Total", 0, ScoreType.LEVEL, m_maxOverall, text);

		for (int i = 1; i < m_skillList.length; i++) {
			text = update99sText(title, m_skillList[i], i, ScoreType.LEVEL, 99, text);
		}

		text = update99sText(title, "Dungeon 120", 25, ScoreType.LEVEL, 120, text);

		String newDate = m_dateFmt.format(new Date());
		text = text.replaceAll("\\| *Atualizado *=[^|\\n]*", "|Atualizado=" + newDate);

		if (!m_wiki.editText(title, text, "Atualizando números", false))
			Utils.log("AVISO: Falha ao atualizar [[" + title + "]] (possível conflicto)");
		else
			Utils.log("Números atualizados em [[" + title + "]]");
	}

	private void update200mxp() throws IOException {
		String title = "Predefinição:200mxp";
		String text = m_wiki.getText(title);
		if (text == null) {
			Utils.log("ERRO: Não foi possível obter os conteúdos de [[" + title + "]]");
			return;
		}

		for (int i = 1; i < m_skillList.length; i++) {
			text = update99sText(title, m_skillList[i].toLowerCase(), i, ScoreType.XP, m_maxXp, text);
		}

		String newDate = m_dateFmt.format(new Date());
		text = text.replaceAll("\\| *Atualizado *=[^|\\n]*", "|Atualizado=" + newDate);

		if (!m_wiki.editText(title, text, "Atualizando números", false))
			Utils.log("AVISO: Falha ao atualizar [[" + title + "]] (possível conflicto)");
		else
			Utils.log("Números atualizados em [[" + title + "]]");
	}

	private void updateHsLowest() throws IOException {
		String title = "Predefinição:Nível Mínimo para Recordes";
		String text = m_wiki.getText(title);
		if (text == null) {
			Utils.log("ERRO: Não foi possível obter os conteúdos de [[" + title + "]]");
			return;
		}

		for (int i = 0; i < m_skillList.length; i++) {
			HSPage firstPage = new HSPage(i, 1, ScoreType.XP);
			if (!firstPage.load())
				continue;

			int lastPageNum = firstPage.getMaxPage();
			if (lastPageNum < 1) {
				Utils.log("ERRO: Não foi possível obter a última página para a habilidade " + m_skillList[i]);
				continue;
			}

			HSPage lastPage = new HSPage(i, lastPageNum, ScoreType.XP);
			if (!lastPage.load())
				continue;

			List<HSEntry> entries = lastPage.getEntries();
			if (entries.isEmpty())
				continue;
			HSEntry lastEntry = entries.get(entries.size() - 1);

			String skillName = m_skillList[i];
			long level = lastEntry.getLevel();
			long rank = lastEntry.getRank();
			text = text.replaceAll("\\| *" + skillName + " *=[^|\\n]*",
					"|" + skillName + "=" + Utils.formatNum(level)).replaceAll(
					"\\| *" + skillName + "\\.Rank *=[^|\\n]*",
					"|" + skillName + ".Rank=" + Utils.formatNum(rank));

		}

		String newDate = m_dateFmt.format(new Date());
		text = text.replaceAll("\\| *Atualizado*=[^|\\n]*", "|Atualizado=" + newDate);

		if (!m_wiki.editText(title, text, "Atualizando números", false))
			Utils.log("AVISO: Falha ao atualizar [[" + title + "]] (possível conflicto)");
		else
			Utils.log("Números atualizados em [[" + title + "]]");
	}

	private String update99sText(String title, String skillName, int skillNum,
			ScoreType scType, long score, String text) throws IOException {
		Pattern skillPattern = Pattern.compile("\\| *" + skillName + "*= *([0-9,]*)[^|\\n]*");
		String[] toks = Utils.extract(skillPattern, text);
		if (toks == null) {
			Utils.log("AVISO: Não foi possível obter " + skillName + " de [[" + title + "]]");
			return text;
		}
		long guess = Utils.parseNum(toks[1], null, 0);
		long numRank = getLowest99sRank(skillNum, scType, score, guess);

		if (numRank > 0)
			text = text.replaceAll(skillPattern.toString(), "|" + skillName + "=" + Utils.formatNum(numRank));

		return text;
	}

	private long getLowest99sRank(int skillNum, ScoreType scType, long score, long guess) throws IOException {
		// NOTE: Need both matching score and score below on same page
		// to be sure got correct page, otherwise need page below too.
		int accel = 5;
		int pageNum = (int) ((guess - 1) / m_maxPageSize + 1);
		HSPage current = new HSPage(skillNum, pageNum, scType);
		if (!current.load())
			return -1;
		pageNum = current.getPageNum();

		// Get bounding range (if not already good hit)
		HSPage upper = null;
		HSPage lower = null;

		if (score > current.getHighestScore()) {
			lower = current;
			upper = current;
			current = null;

			int step = 1;
			while (upper.getPageNum() > 1 && score > upper.getHighestScore()) {
				lower = upper;
				upper = new HSPage(skillNum, pageNum - step, scType);
				if (!upper.load())
					return -1;
				step *= accel;
			}
		} else if (score <= current.getLowestScore()) {
			upper = current;
			lower = current;
			current = null;

			int step = 1;
			while (lower.getPageNum() < m_maxPageNum && score <= lower.getLowestScore()) {
				upper = lower;
				lower = new HSPage(skillNum, pageNum + step, scType);
				if (!lower.load())
					return -1;
				step *= accel;
			}
		}

		// Zoom into matching page (if not already good hit)
		while (current == null) {
			if (lower.getPageNum() <= upper.getPageNum() + 1) {
				current = (score > lower.getHighestScore() ? upper : lower);
				break;
			}

			pageNum = (upper.getPageNum() + lower.getPageNum()) / 2;
			HSPage mid = new HSPage(skillNum, pageNum, scType);
			if (!mid.load())
				return -1;

			if (score > mid.getHighestScore())
				lower = mid;
			else if (score <= mid.getLowestScore())
				upper = mid;
			else
				current = mid;
		}

		// Find lowest matching entry on page
		long rank = -1;
		List<HSEntry> entries = current.getEntries();
		for (HSEntry entry : entries) {
			if (entry.getScore(scType) >= score)
				rank = entry.getRank();
		}
		return rank;
	}

	private class HSPage {
		private int m_skillNum = 0;
		private int m_pageNum = 0;
		private int m_maxPage = 0;
		private ArrayList<HSEntry> m_entries = new ArrayList<HSEntry>();
		private ScoreType m_scoreType = ScoreType.LEVEL;
		private long m_highestScore = 0;
		private long m_lowestScore = 0;

		public HSPage(int skillNum, int pageNum, ScoreType scType) {
			m_skillNum = skillNum;
			if (m_skillNum < 0)
				m_skillNum = 0;

			m_pageNum = pageNum;
			if (m_pageNum < 1)
				m_pageNum = 1;
			if (m_maxPageNum > 0 && m_pageNum > m_maxPageNum)
				m_pageNum = m_maxPageNum;

			m_scoreType = scType;
		}

		public boolean load() throws IOException {
			if (!m_entries.isEmpty())
				return true; // already loaded

			try {
				Thread.sleep(HS_DELAY);
			} catch (Exception ex) { }

			if (m_debug)
				System.out.println("DEBUG: Obteve o nome " + m_pageNum + " para a habilidade " + m_skillNum);

			String url = HS_URL + "&table=" + m_skillNum + "&page=" + m_pageNum;
			String hsText = Utils.getInternetPage(url, null, null);
			if (hsText == null) {
				Utils.log("ERRO: Não foi possível obter a página de recordes " + m_pageNum + " para o número da habilidade " + m_skillNum);
				return false;
			}

			Matcher matcher = m_hsEntryPattern.matcher(hsText);
			while (matcher.find()) {
				HSEntry entry = new HSEntry(matcher.group(1), matcher.group(2),
						matcher.group(3), matcher.group(4));
				m_entries.add(entry);
			}

			if (m_entries.isEmpty()) {
				Utils.log("ERRO: Não foi possível obter os registos analizados na página de recordes " + m_pageNum + " para o número da habilidade " + m_skillNum);
				return false;
			}

			if (m_pageNum > 1 && m_entries.get(0).getRank() <= 1) {
				// server given first page again
				m_entries.clear();
			} else {
				m_highestScore = m_entries.get(0).getScore(m_scoreType);
				m_lowestScore = m_entries.get(m_entries.size() - 1).getScore(m_scoreType);
			}

			String[] toks = Utils.extract(m_hsMaxPagePattern, hsText);
			if (toks != null)
				m_maxPage = (int) Utils.parseNum(toks[1], null, 0);

			return true;
		}

		public int getSkillNum() {
			return m_skillNum;
		}

		public int getPageNum() {
			return m_pageNum;
		}

		public int getMaxPage() {
			return m_maxPage;
		}

		public List<HSEntry> getEntries() {
			return m_entries;
		}

		public long getHighestScore() {
			return m_highestScore;
		}

		public long getLowestScore() {
			return m_lowestScore;
		}
	}

	private class HSEntry {
		private long m_rank = 0;
		private String m_name = null;
		private long m_level = 0;
		private long m_xp = 0;

		public HSEntry(String rank, String name, String level, String xp) {
			m_rank = Utils.parseNum(rank, null, 0);
			m_name = Utils.decodeEntities(name).replace((char) 65533, ' ');
			m_level = Utils.parseNum(level, null, 0);
			m_xp = Utils.parseNum(xp, null, 0);
		}

		public long getRank() {
			return m_rank;
		}

		public String getName() {
			return m_name;
		}

		public long getLevel() {
			return m_level;
		}

		public long getXp() {
			return m_xp;
		}

		public long getScore(ScoreType scType) {
			switch (scType) {
			case XP:
				return m_xp;
			default:
				return m_level;
			}
		}

	}
}