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

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

Atualiza o preço de todas as páginas do Mercado.

/*--------------------------------------------------------
 * 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.Date;
import java.util.Locale;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.regex.Pattern;

@Description(summary = "Atualiza o preço de todas as páginas do Mercado.")
public class ExchangeTask implements BotTask
{
    private WikiSession m_wiki = null;
    private DateFormat m_dateFmt = null;
    private int m_numUnexpected = 0;
    private GEItems m_GEItems = null;

    private final static int MIN_UPDATE_HOURS = 36;
    private final static int MAX_UPDATE_HOURS = 14 * 24;
    private final static int MAX_UPDATE_ITEMS = 10000;
    private final static int MAX_REPORT_UNEXPECTED = 10;

    private final Pattern m_itemIdPattern = Pattern.compile("\\| *ItemId *= *(\\d+)[^|\\n]*");
    private final Pattern m_pricePattern = Pattern.compile("\\| *Preço *= *([0-9,]*)[^|\\n]*");
    private final Pattern m_lastPattern = Pattern.compile("\\| *ÚltimoPreço *= *([0-9,]*)[^|\\n]*");
    private final Pattern m_datePattern = Pattern.compile("\\| *Data *= *([^|\\n]*)");
    private final Pattern m_lastDatePattern = Pattern.compile("\\| *ÚltimaData *= *([^|\\n]*)");

    private final String singleNoBarComment = "(?:<!--[^|\\n]*?-->)?";
    private final String multiNoBarComment = "(?:<!--[^|]*?-->)?";
    private final Pattern m_expectedPattern = Pattern.compile(
            "^"+multiNoBarComment+"\\{\\{(Mercado Item(?:Nat)?) *\n" +
            "\\| *Ver *= *\\{\\{\\{Ver\\}\\}\\} *"+singleNoBarComment+" *\n" +
            "\\| *Ícone *= *[^|\\n]* *"+singleNoBarComment+" *\n" +
            "\\| *Item *= *[^|\\n]* *"+singleNoBarComment+" *\n" +
            "\\| *ItemId *= *\\d+ *"+singleNoBarComment+" *\n" +
            "\\| *Preço *= *[0-9,]{0,20} *"+singleNoBarComment+" *\n" +
            "\\| *ÚltimoPreço *= *[0-9,]{0,20} *"+singleNoBarComment+" *\n" +
            "\\| *Data *= *[^|\\n]* *"+singleNoBarComment+" *\n" +
            "\\| *ÚltimaData *= *[^|\\n]* *"+singleNoBarComment+" *\n" +
            "(?:\\| *\\w+ *= *[^|\\n]* *"+singleNoBarComment+" *\n)*" +
            "\\}\\}(?:<noinclude>(?:.|\n)*</noinclude>)?\n*$");

	public ExchangeTask(WikiSession wiki) {
		m_wiki = wiki;

		m_dateFmt = new SimpleDateFormat("HH:mm, MMMM d, yyyy (z)",
				Locale.ENGLISH);
		m_dateFmt.setTimeZone(TimeZone.getTimeZone("UTC"));

		m_GEItems = new GEItems(m_wiki);
	}

	public void perform() throws IOException {
		m_GEItems.load();

		SortedSet<String> allTitles = m_wiki.getAllPages(112, null,	MAX_UPDATE_ITEMS);

		long numFixed = 0;
		long numChecked = 0;
		long numFail = 0;
		for (String title : allTitles) {
			if (Utils.excessiveLog())
				break;

			if (title.contains("/") || title.equals("Mercado:Teste") || title.equals("0"))
				continue;

			numChecked++;
			try {
				if (checkItemPrice(title))
					numFixed++;
			} catch (Exception ex) {
				if (numFail++ > 5)
					return;
				Utils.log("ERRO: Problema com " + title + ": " + ex.toString());
				ex.printStackTrace();
			}
		}

		long numNew = 0;
		if (numChecked > 0)
			numNew = m_GEItems.save();

		Utils.log(numChecked + " preços do Mercado verificados, e "+ numFixed + " atualizados" + (numNew > 0 ? " <small>([[" + m_GEItems.getListPage() + "|" + numNew + " novos]])</small>" : ""));
	}

	private boolean checkItemPrice(String title) throws IOException {
		ExchPage page = new ExchPage(title);
		if (!page.load())
			return false;

		long id = page.getId();
		if (!m_GEItems.checkName(id, title.replaceFirst("^Mercado:", ""))) {
			Utils.log("ERRO: [[" + title + "]] não coincide com o nome esperado para o item do MG " + id);
			return false;
		}

		// check if different from recent price (for stable prices)
		long prevPrice = m_GEItems.getPrevPrice(id);
		if (page.getAgeHours() < MAX_UPDATE_HOURS && prevPrice > 0 && page.getPrice() == prevPrice)
			return false;

		// get current price
		long gePrice = m_GEItems.getPrice(id);
		if (gePrice <= 0) {
			Utils.log("ERRO: Não foi possível obter o preço do item " + id + " ([[" + title + "]]) do Mercado Geral");
			return false;
		}

		// check if recent update and price not vandalised (against current price)
		boolean vandal = page.isVandalised(gePrice);
		if (page.getAgeHours() < MIN_UPDATE_HOURS && !vandal)
			return false;

		// if vandalised, then report
		if (vandal) {
			Utils.log("AVISO: O preço em [[" + title + "]] parece ter sido vandalizado");
		}

		// update if price changed or long enough since last update
		if (page.getPrice() != gePrice || page.getAgeHours() >= MAX_UPDATE_HOURS) {
			page.setPrice(gePrice, !vandal);
		}

		boolean changed = page.saveChanges();

		return changed;
	}

	private class ExchPage {
		private String m_title = null;
		private String m_text = null;
		private int m_id = 0;
		private long m_price = 0;
		private String m_date = null;
		private long m_ageHours = 0;
		private boolean m_updated = false;

		public ExchPage(String title) {
			m_title = title;
		}

		public boolean load() throws IOException {
			m_text = m_wiki.getText(m_title);
			if (m_text == null) {
				Utils.log("ERRO: Não foi possível obter o conteúdo de [[" + m_title + "]]");
				return false;
			}

			Date histDate = m_wiki.getLastTimestamp();
			m_ageHours = (histDate == null ? MIN_UPDATE_HOURS : (System.currentTimeMillis() - histDate.getTime()) / 3600000);

			String[] toks = Utils.extract(m_itemIdPattern, m_text);
			if (toks == null) {
				Utils.log("ERRO: Não foi possível obter o ItemId de [[" + m_title + "]]");
				return false;
			}
			m_id = Integer.parseInt(toks[1]);

			toks = Utils.extract(m_pricePattern, m_text);
			if (toks == null) {
				Utils.log("ERRO: Não foi possível obter o Preço de [[" + m_title + "]]");
				return false;
			}
			m_price = Utils.parseNum(toks[1], null, 0);

            toks = Utils.extract(m_expectedPattern, m_text);
            /*if (toks == null)
            {
                m_numUnexpected ++;
                if (m_numUnexpected <= MAX_REPORT_UNEXPECTED)
                    Utils.log("NOTA: Formato não-padrão em [[" + m_title + "]]");
            }
            else if (toks[1].equals("Mercado ItemNat") != m_title.equals("Mercado:Runa_da_natureza"))
            {
                // Should use ExchangeItemNat if and only if Exchange:Nature rune
                m_numUnexpected ++;
                if (m_numUnexpected <= MAX_REPORT_UNEXPECTED)
                    Utils.log("NOTA: Predefinição incorreta usada em [[" + m_title + "]]");
            }*/
            
            
            toks = Utils.extract(m_datePattern, m_text);
            m_date = (toks != null ? toks[1] : "");

            return true;
        }

		public boolean saveChanges() throws IOException {
			if (!m_updated)
				return false;

			if (!m_wiki.editText(m_title, m_text, "Atualizando preço", false)) {
				Utils.log("AVISO: Falha ao atualizar [[" + m_title + "]] (possível conflicto)");
				return false;
			}
			return true;
		}

		public int getId() {
			return m_id;
		}

		public long getPrice() {
			return m_price;
		}

		public long getAgeHours() {
			return m_ageHours;
		}

		public boolean isVandalised(long knownPrice) {
			if (knownPrice <= 0)
				return false; // don't know price

			if (m_price <= 0)
				return true; // price too low

			if (Math.abs(m_price - knownPrice) >= knownPrice / 2 + 4)
				return true; // price changed too much

			return false;
		}

		public void setPrice(long newPrice, boolean updateLast) {
			String newDate = m_dateFmt.format(new Date());

			if (updateLast) {
				m_text = m_text.replaceAll(m_lastPattern.toString(), "|ÚltimoPreço=" + Utils.formatNum(m_price))
						.replaceAll(m_lastDatePattern.toString(), "|ÚltimaData=" + m_date);
			}

			m_text = m_text.replaceAll(m_pricePattern.toString(), "|Preço=" + Utils.formatNum(newPrice))
					.replaceAll(m_datePattern.toString(), "|Data=" + newDate);

			m_price = newPrice;
			m_date = newDate;
			m_updated = true;
		}
	}
}