User:A2569875-bot/Code/Util.cs

维基百科,自由的百科全书
public class Site
{
    public Site(string address, string userName, 
		string userPass, string userDomain) : address(address), userName(userName),
        userPass(userPass), userDomain(userDomain) {}

    public List<string> getPageNamesFromCategory(string category, int limit)
    {
        List<string> page_list = new List<string>();

        int category_count = getCategoryCount(category);
        if(category_count <= 0) return page_list;
        string post_data_url = apiPath + "?action=query&list=categorymembers&cmtitle=Category:" + category + "&cmlimit=" +
            (limit <= 0 ? 1: limit) + "&format=xml";
        if(limit == 0) post_data_url = apiPath + "?action=query&list=categorymembers&cmtitle=Category:" + category + "&cmlimit=" + category_count + "&format=xml";
        string tokenXmlSrc = PostDataAndGetResult(post_data_url, "", true, true);
        XElement tokenXml, categoryToken;
        try
        {
            tokenXml = XElement.Parse(tokenXmlSrc);
            categoryToken = tokenXml.Element("query").Element("categorymembers");
            foreach (var page_it in categoryToken.Elements())
            {
                //只作主名字空間
                if (page_it.Attribute("ns").Value != "0") continue;
                page_list.Add(page_it.Attribute("title").Value);
            }
        }
        catch
        {

        }
        return page_list;
    }

    public int getCategoryCount(string category)
    {
        List<string> page_list = new List<string>();

        //參考[[mw:API:Categoryinfo]]
        string post_data_url = apiPath + "?action=query&prop=categoryinfo&titles=Category:" + category + "&format=xml";
        string tokenXmlSrc = PostDataAndGetResult(post_data_url, "", true, true);
        XElement tokenXml, categoryToken;
        try
        {
            tokenXml = XElement.Parse(tokenXmlSrc);
            categoryToken = (tokenXml.Element("query").Element("pages").Elements().First<XElement>())
                .Element("categoryinfo");
            return Int32.Parse(categoryToken.Attribute("pages").Value);
        }
        catch
        {
            //反正等一下就return 0了
        }
        return 0;
    }

	//Bug 大魔王: "沒使用Https ,HttpS,httpssssss,Ssssssssssssssss!!!"
	//Api-User-Agent = "A2569875 / 1.0(http://website, [email protected])"
    private void LogIn()
    {
        if (!useApi)
        {
            string loginPageSrc = PostDataAndGetResult(indexPath +
                "?title=Special:Userlogin", "", true, true);
            string loginToken = "";
            int loginTokenPos = loginPageSrc.IndexOf(
                "<input type=\"hidden\" name=\"wpLoginToken\" value=\"");
            if (loginTokenPos != -1)
                loginToken = loginPageSrc.Substring(loginTokenPos + 48, 32);

            string postData = string.Format("wpName={0}&wpPassword={1}&wpDomain={2}" +
                "&wpLoginToken={3}&wpRemember=1&wpLoginattempt=Log+in",
                Bot.UrlEncode(userName), Bot.UrlEncode(userPass),
                Bot.UrlEncode(userDomain), Bot.UrlEncode(loginToken));
            string respStr = PostDataAndGetResult(indexPath +
                "?title=Special:Userlogin&action=submitlogin&type=login",
                postData, true, false);
            if (respStr.Contains("<div class=\"errorbox\">"))
                throw new WikiBotException(
                    "\n\n" + Bot.Msg("Login failed. Check your username and password.") + "\n");
            Console.WriteLine(Bot.Msg("Logged in as {0}."), userName);
        }
        else
        {
            string postData =
                string.Format("lgname={0}&lgpassword={1}",
                 Bot.UrlEncode(userName), Bot.UrlEncode(userPass));
				 
			//Debug = 
            //string.Format("lgname={0}&lgpassword={1}&lgdomain={2}",
            //     Bot.UrlEncode(userName), Bot.UrlEncode(userPass),
            //    Bot.UrlEncode(userDomain));

            //string postData = string.Format("lgname={0}&lgpassword={1}",
            //        Bot.UrlEncode(userName), Bot.UrlEncode(userPass));


            string post_data_url = apiPath + "?action=query&meta=tokens&type=login&format=xml";
            string tokenXmlSrc = PostDataAndGetResult(post_data_url, "", true, 
				//允許重定向
				true);
            XElement tokenXml;
            try
            {
                tokenXml = XElement.Parse(tokenXmlSrc);
            }
            catch
            {
				//try to init
				//執行到這裏的話,tokenXml將缺少初值
				//這裡其實是為了設中斷點
            }
            string respStr = "", loginToken = "";
            try
            {
                tokenXml = XElement.Parse(tokenXmlSrc);
				//[[WP:製作機器人]]肯定已陳舊,需要更新
				//不然照做只會Error、Error、Error、Error、Error、Error
                loginToken = tokenXml.Element("query").Element("tokens")
                    .Attribute("logintoken").Value;
            }
            catch
            {
                // 和藍桌討論這裡貌似非問題
                respStr = PostDataAndGetResult(apiPath +
                    "?action=login&format=xml", postData, true, true);
                if (respStr.Contains("result=\"Success\""))
                {
                    Console.WriteLine(Bot.Msg("Logged in as {0}."), userName);
                    return;
                }
                int tokenPos = respStr.IndexOf("token=\"");
                if (tokenPos < 1)
                    throw new WikiBotException(
                        "\n\n" + Bot.Msg("Login failed. Check your username and password.") + "\n");
                loginToken = respStr.Substring(tokenPos + 7, 32);
            }

            postData += "&lgtoken=" + Bot.UrlEncode(loginToken);


            //postData += "&lgtoken=" + loginToken;
            post_data_url = apiPath + "?action=login&format=xml";//&format=xml";
            respStr = PostDataAndGetResult(post_data_url, postData, true, true);
            if (!respStr.Contains("result=\"Success\""))
                throw new WikiBotException(
                    "\n\n" + Bot.Msg("Login failed. Check your username and password.") + "\n");
            Console.WriteLine(Bot.Msg("Logged in as {0}."), userName);
        }
    }

	//Api-User-Agent = "A2569875 / 1.0(http://website, [email protected])"
    public string PostDataAndGetResult(string pageURL, string postData, bool getCookies,
        bool allowRedirect)
    {
        if (string.IsNullOrEmpty(pageURL))
            throw new ArgumentNullException("pageURL", Bot.Msg("No URL specified."));
        if (pageURL.StartsWith("/") && !pageURL.StartsWith("//"))
            pageURL = address + pageURL;

        int retryDelaySeconds = 60;
        HttpWebResponse webResp = null;
        for (int errorCounter = 0; true; errorCounter++)
        {
            HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(pageURL);
            webReq.Proxy.Credentials = CredentialCache.DefaultCredentials;
            webReq.UseDefaultCredentials = true;
            webReq.ContentType = "application/x-www-form-urlencoded";
            webReq.Headers.Add("Cache-Control", "no-cache, must-revalidate");

            webReq.Headers.Add("Api-User-Agent", "A2569875 / 1.0(http://website, [email protected])");
            //MyCoolTool/1.1 (https://example.org/MyCoolTool/; [email protected]) BasedOnSuperLib/1.4
            webReq.UserAgent = "A2569875 / 1.0(http://website, [email protected])";
			
			//Debug....
            //webReq.UserAgent = "MyCoolTool/1.1 (https://example.org/MyCoolTool/; [email protected]) BasedOnSuperLib/1.4";

            // webReq.Headers.Add("Api-User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36");
            //webReq.Headers.Add("Api-User-Agent", "MyCoolTool/1.1 (https://example.org/MyCoolTool/; [email protected]) BasedOnSuperLib/1.4");

            webReq.AllowAutoRedirect = allowRedirect;
            if (cookies.Count == 0)
                webReq.CookieContainer = new CookieContainer();
            else
                webReq.CookieContainer = cookies;
            if (Bot.unsafeHttpHeaderParsingUsed == 0)
            {
                webReq.ProtocolVersion = HttpVersion.Version10;
                webReq.KeepAlive = false;
            }
            if (!Bot.isRunningOnMono) webReq.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

			//Debug
            //webReq.Method = "POST";
            if (!string.IsNullOrEmpty(postData))
            {
                webReq.Method = WebRequestMethods.Http.Post;
				//don't need.
                //webReq.Timeout = 180000;
                postData += "&maxlag=" + maxLag;

				//Debug (和藍桌在IRC上討論的)
				//Try any Encoding
                //Encoding.UTF8
                //Encoding.ASCII
                byte[] postBytes = Encoding.UTF8.GetBytes(postData);
                webReq.ContentLength = postBytes.Length;

                Stream reqStrm = webReq.GetRequestStream();
                reqStrm.Write(postBytes, 0, postBytes.Length);

                reqStrm.Close();
            }
            try
            {
                webResp = (HttpWebResponse)webReq.GetResponse();
                if (webResp.Headers["Retry-After"] != null)
                    throw new WebException("Service is unavailable due to high load.");
                // API can return HTTP code 200 (OK) along with "Retry-After"
                break;
            }
            catch (WebException e)
            {

                if (webResp == null)
                    throw;

                if (webReq.AllowAutoRedirect == false &&
                    webResp.StatusCode == HttpStatusCode.Redirect)    // Mono bug 636219 evasion
                    return "";

                if (e.Message.Contains("Section=ResponseStatusLine"))
                {   // Known Squid problem
                    Bot.SwitchUnsafeHttpHeaderParsing(true);
                    return PostDataAndGetResult(pageURL, postData, getCookies, allowRedirect);
                }

                if (webResp.Headers["Retry-After"] != null)
                {    // Server is very busy
                    if (errorCounter > retryTimes)
                        throw;
                    // See https://www.mediawiki.org/wiki/Manual:Maxlag_parameter
                    int seconds;
                    Int32.TryParse(webResp.Headers["Retry-After"], out seconds);
                    if (seconds > 0)
                        retryDelaySeconds = seconds;
                    Console.Error.WriteLine(e.Message);
                    Console.Error.WriteLine(string.Format(Bot.Msg(
                        "Retrying in {0} seconds..."), retryDelaySeconds));
                    Bot.Wait(retryDelaySeconds);
                }
                else if (e.Status == WebExceptionStatus.ProtocolError)
                {
                    int code = (int)webResp.StatusCode;
                    if (code == 500 || code == 502 || code == 503 || code == 504)
                    {
                        // Remote server problem, retry
                        if (errorCounter > retryTimes)
                            throw;
                        Console.Error.WriteLine(e.Message);
                        Console.Error.WriteLine(string.Format(Bot.Msg(
                            "Retrying in {0} seconds..."), retryDelaySeconds));
                        Bot.Wait(retryDelaySeconds);

                    }
                    else
                        throw;
                }
                else
                    throw;
            }
        }
        Stream respStream = webResp.GetResponseStream();
        if (webResp.ContentEncoding.ToLower().Contains("gzip"))
            respStream = new GZipStream(respStream, CompressionMode.Decompress);
        else if (webResp.ContentEncoding.ToLower().Contains("deflate"))
            respStream = new DeflateStream(respStream, CompressionMode.Decompress);
        if (getCookies == true)
        {
            Uri siteUri = new Uri(address);
            foreach (Cookie cookie in webResp.Cookies)
            {
                if (cookie.Domain[0] == '.' &&
                    cookie.Domain.Substring(1) == siteUri.Host)
                    cookie.Domain = cookie.Domain.TrimStart(new char[] { '.' });
                cookies.Add(cookie);
            }
        }
        StreamReader strmReader = new StreamReader(respStream, Encoding.UTF8);
        string respStr = strmReader.ReadToEnd();
        strmReader.Close();
        webResp.Close();
        return respStr;
    }
}