Creating a C# Script to login to WikiMedia API

huangapple go评论146阅读模式
英文:

Creating a C# Script to login to WikiMedia Api

问题

我正在尝试创建一个脚本来登录到一个私人维基站,以便我可以进行更改。以下是我用来实现这个目的的代码。当我发送登录令牌请求时,我成功地获得了一个登录令牌。然而,当我使用该令牌发送登录请求时,我得到了一个"失败"的结果,并附带一个原因,说明"无法继续登录。您的会话很可能已经超时。"

apiUrl被设置为维基百科的URL,但在我的私人维基站上也存在这个问题。

// 代码部分

我应该收到这个:

{
    "login": {
        "result": "Success",
        "lguserid": 1,
        "lgusername": "UserName"
    }
}

但实际上我收到了这个:

{
    "login": {
        "result": "Failed",
        "reason": "无法继续登录。您的会话很可能已经超时。"
    }
}

无论我做什么,我都会得到这条消息。即使我输入了错误的用户名和密码。我尽我所能地去判断,这实际上并不是超时的问题,还有其他的情况发生。

我在这里缺少了什么?

英文:

I am trying to create a script to login to a private Wiki so that I can make changes. This is the code I created to do this. When I send this login token request I successfully get a login token. However when I use that token and send a request to login I get a result of "Failed" with a reason stating "Unable to continue login. Your session most likely timed out."

The apiUrl is set to the Wikipedia url, but the problem exists on my private wiki as well.

public static class WikiAccess
{
    static string apiUrl = "https://en.wikipedia.org/w/api.php";
    
    // I am calling this with a working bot username and password
    public static async Task<bool> LoginToWikiAsync(string userName, string password)
    {
        // Create the request parameters to get a login token
        var tokenParameters = new Dictionary<string, string>
        {
            { "action", "query" },
            { "format", "json" },
            { "meta", "tokens" },
            { "formatversion", "2" },
            { "type", "login" },
        };

        // Send the login Token Request
        var tokenResponse = await SendMessageToWikiGet(tokenParameters);
        JObject tokenJsonResponse = ParseString(tokenResponse);

        // this gets the login token
        string loginToken = tokenJsonResponse["query"]["tokens"]["logintoken"].Value<string>();

        var loginParameters = new Dictionary<string, string>
        {
            { "action", "login" },
            { "format", "json" },
            { "lgname", userName },
            { "lgpassword", password },
            { "lgtoken", loginToken },
            { "formatversion", "2" },
        };
        
        // Send login request
        var loginResponse = await SendMessageToWikiPost(loginParameters);

        JObject loginJsonResponse = ParseString(loginResponse);
        
        Debug.Log("Login Result: " + loginJsonResponse);
         /*
              Login Result: {
              "login": {
                "result": "Failed",
                "reason": "Unable to continue login. Your session most likely timed out."
              }
            }
         */
        
        string loginResult = loginJsonResponse["login"]["result"].Value<string>();
        // The login result returns "Failed"

        return loginResult == "Success";
    }
    
    static JObject ParseString(string value)
    {
        try
        {
            return JObject.Parse(value);
        }
        catch (Exception ex)
        {
            Debug.LogError(value);
            throw ex;
        }
    }

    public static Task<string> SendMessageToWikiGet(Dictionary<string, string> parameters)
    {
        // Build the query string and send the request to the url via Get
        string url = apiUrl + "?" + BuildQueryString(parameters);
        Debug.Log(url);
        return SendMessageToUrlGet(url);
    }

    public static Task<string> SendMessageToWikiPost(Dictionary<string, string> parameters)
    {
        // Build the query string and send the request to the url via Post
        return SendMessageToUrlPost(apiUrl, BuildQueryString(parameters));
    }

    static WebClient client;

    public async static Task<string> SendMessageToUrlPost(string url, string parameters)
    {
        client ??= new();
        client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        var response = await client.UploadStringTaskAsync(url, parameters);
        return response;
    }

    public async static Task<string> SendMessageToUrlGet(string url)
    {
        client ??= new();
        var tokenResponse = await client.DownloadStringTaskAsync(url);
        return tokenResponse;
    }

    static string BuildQueryString(Dictionary<string, string> parameters)
    {
        StringBuilder queryBuilder = new StringBuilder();

        foreach (var parameter in parameters)
        {
            queryBuilder.Append($"{Uri.EscapeDataString(parameter.Key)}={Uri.EscapeDataString(parameter.Value)}&");
        }

        string returnValue = queryBuilder.ToString().TrimEnd('&');
        //Debug.Log(returnValue);

        return returnValue;
    }
}

I should be receiving this:

{
    "login": {
        "result": "Success",
        "lguserid": 1,
        "lgusername": "UserName"
    }
}

but instead I am receiving this:

{
                  "login": {
                    "result": "Failed",
                    "reason": "Unable to continue login. Your session most likely timed out."
                  }
                }

I will get this message no matter what I do. Even if I put in an incorrect user name and password. As best as I can tell this is not actually timing out and something else is happening.

What am I missing here?

答案1

得分: 1

我找到了解决方法。在某个时候,MediaWiki开始要求使用cookie才能正常工作。所以我将这个部分更改为:

static CookieContainer cookieContainer;
public async static Task<string> PostUsingHttpCLient(string path, Dictionary<string, string> parameters)
{
    cookieContainer ??= new CookieContainer();
            
    using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
    using (var client = new HttpClient(handler))
    {
        var tokenResponse = await client.PostAsync(path, new FormUrlEncodedContent(parameters));
        return await tokenResponse.Content.ReadAsStringAsync();
    }
}

现在它可以正常工作了!

英文:

I ended up figuring it out. At some point MediaWiki started requiring cookies in order to work properly. So I changed this:

static WebClient client;

    public async static Task&lt;string&gt; SendMessageToUrlPost(string url, string parameters)
    {

        client ??= new();
        client.Headers[HttpRequestHeader.ContentType] = &quot;application/x-www-form-urlencoded&quot;;
        var response = await client.UploadStringTaskAsync(url, parameters);
        return response;
    }
    public async static Task&lt;string&gt; SendMessageToUrlGet(string url)
    {
        client ??= new();
        var tokenResponse = await client.DownloadStringTaskAsync(url);
        return tokenResponse;
    }
    static string BuildQueryString(Dictionary&lt;string, string&gt; parameters)
    {
        StringBuilder queryBuilder = new StringBuilder();

        foreach (var parameter in parameters)
        {
            queryBuilder.Append($&quot;{Uri.EscapeDataString(parameter.Key)}={Uri.EscapeDataString(parameter.Value)}&amp;&quot;);
        }
        string returnValue = queryBuilder.ToString().TrimEnd(&#39;&amp;&#39;);
        //Debug.Log(returnValue);
        return returnValue;
    }

To this:

static CookieContainer cookieContainer;
    public async static Task&lt;string&gt; PostUsingHttpCLient(string path, Dictionary&lt;string, string&gt; parameters)
    {
        cookieContainer ??= new CookieContainer();
        
        using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
        using (var client = new HttpClient(handler))
        {
            var tokenResponse = await client.PostAsync(path, new FormUrlEncodedContent(parameters));
            return await tokenResponse.Content.ReadAsStringAsync();
        }
    }

Now it works just fine!

Edit:
Here is the full script with some basic functions and the infrastructure to expand on this if anyone is interested.

    using Newtonsoft.Json.Linq;
    using Sirenix.OdinInspector;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Threading.Tasks;
    #if UNITY_EDITOR
    using UnityEditor;
    #endif
    using UnityEngine;
    
    
    public class WikiAccess : MonoBehaviour
    {
        static string apiUrl = https://www.Your_Wiki_URL.com/w/api.php&quot;;
    
        [ShowInInspector] static bool isLoggedIn;
        [ShowInInspector] static string csrftoken;
        
        [Button]
        public static async Task CheckCredentials()
        {
    #if UNITY_EDITOR
            string userName = &quot;bot_UserName&quot;;
            string password = &quot;bot_password&quot;;
            if (!isLoggedIn) await LoginToWikiAsync();
            if (string.IsNullOrEmpty(csrftoken)) await Get_CSRF_Token();
    
            async Task LoginToWikiAsync()
            {
    
                // Create the token parameters to get a login token
                var tokenParameters = new Dictionary&lt;string, string&gt;
                {
                    { &quot;action&quot;, &quot;query&quot; },
                    { &quot;format&quot;, &quot;json&quot; },
                    { &quot;meta&quot;, &quot;tokens&quot; },
                    { &quot;type&quot;, &quot;login&quot; },
    
                };
    
                // Send the login Token Request
                var tokenResponse = await PostMesssageToURL(tokenParameters);
    
                // Get the login token
                string loginToken = tokenResponse[&quot;query&quot;][&quot;tokens&quot;][&quot;logintoken&quot;].Value&lt;string&gt;();
    
                var loginParameters = new Dictionary&lt;string, string&gt;
                {
                    { &quot;action&quot;, &quot;login&quot; },
                    { &quot;format&quot;, &quot;json&quot; },
                    { &quot;lgname&quot;, userName },
                    { &quot;lgpassword&quot;, password },
                    { &quot;lgtoken&quot;, loginToken },
                };
    
                // Send login request
                isLoggedIn = (await PostMesssageToURL(loginParameters))[&quot;login&quot;][&quot;result&quot;].Value&lt;string&gt;() == &quot;Success&quot;;
    
    
            }
            async Task Get_CSRF_Token()
            {
                var tokenParameters = new Dictionary&lt;string, string&gt;
                {
                    { &quot;action&quot;, &quot;query&quot; },
                    { &quot;format&quot;, &quot;json&quot; },
                    { &quot;meta&quot;, &quot;tokens&quot; },
                    { &quot;formatversion&quot;, &quot;2&quot; },
    
                };
                csrftoken = (await PostMesssageToURL(tokenParameters))[&quot;query&quot;][&quot;tokens&quot;][&quot;csrftoken&quot;].Value&lt;string&gt;();
            }
    #endif
        }
        public static async Task EditPage(string pageTitle, string content)
        {
            await CheckCredentials();
            var parameters = new Dictionary&lt;string, string&gt;
                {
                    { &quot;action&quot;, &quot;edit&quot; },
                    { &quot;format&quot;, &quot;json&quot; },
                    { &quot;title&quot;, pageTitle },
                    { &quot;text&quot;, content },
                    { &quot;token&quot;, csrftoken }
    
                };
            var returnValue = await PostMesssageToURL(parameters);
            Debug.Log(returnValue);
    
        }
        /// &lt;summary&gt;
        ///  Uploads a Unity Asset
        /// &lt;/summary&gt;
        public static Task UploadFile(UnityEngine.Object obj)
        {
    #if UNITY_EDITOR
            return UploadFile(AssetDatabase.GetAssetPath(obj).Replace(&quot;Assets/&quot;, Application.dataPath + &quot;/&quot;), obj.name);
    #endif
        }
        /// &lt;summary&gt;
        ///  Upload a File
        /// &lt;/summary&gt;
        public static async Task UploadFile(string path, string fileName)
        {
            if (!File.Exists(path)) throw new FileNotFoundException();
            await CheckCredentials();
    
            var parameters = new Dictionary&lt;string, string&gt;
                {
                    { &quot;action&quot;, &quot;upload&quot; },
                    { &quot;format&quot;, &quot;json&quot; },
                    { &quot;filename&quot;, fileName },
                    { &quot;ignorewarnings&quot;, &quot;1&quot; },
                    { &quot;token&quot;, csrftoken }
    
                };
            // Read file content
            var bytes =  await File.ReadAllBytesAsync(path);
            
            // Create Byte Array content
            var fileContent = new ByteArrayContent(bytes);
            fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(&quot;form-data&quot;)
            {
                Name = &quot;file&quot;,
                FileName = fileName
            };
    
            // send the File
            Debug.Log(&quot;Uploading File...&quot;);
            var returnValue = await PostMesssageToURL(apiUrl, parameters, new List&lt;HttpContent&gt;() 
            {
             fileContent
            });
    
            Debug.Log(&quot;File uploaded.&quot;);
            Debug.Log(returnValue);
        }
    
        [ShowInInspector] static CookieContainer cookieContainer;
        /// &lt;summary&gt;
        /// Send a simple message and recieve a JObject Back
        /// &lt;/summary&gt;
        public static Task&lt;JObject&gt; PostMesssageToURL(Dictionary&lt;string, string&gt; parameters) =&gt; PostMesssageToURL(apiUrl, parameters, new());
        /// &lt;summary&gt;
        /// Sends a post message to the url with json prameters and any additional Http Content
        /// &lt;/summary&gt;
        /// &lt;param name=&quot;url&quot;&gt;apiUrl&lt;/param&gt;
        /// &lt;param name=&quot;parameters&quot;&gt;POST Parameters&lt;/param&gt;
        /// &lt;param name=&quot;additionalContent&quot;&gt;Use this to add HttpContent to the MultipartFormDataContent&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        public async static  Task&lt;JObject&gt; PostMesssageToURL(string url, Dictionary&lt;string, string&gt; parameters, List&lt;HttpContent&gt; additionalContent)
        {
            // Stores cookies needed to login
            cookieContainer ??= new CookieContainer();
    
            using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
            using (var client = new HttpClient(handler))
            {
                var multiPartContent = new MultipartFormDataContent();
    
                // Add Parameters
                foreach (var kvp in parameters) multiPartContent.Add(new StringContent(kvp.Value), kvp.Key);
                // Add Additional http content
                foreach (var content in additionalContent) multiPartContent.Add(content);
    
                // Post Message
                var httpResponse = await client.PostAsync(url, multiPartContent);
                string response = await httpResponse.Content.ReadAsStringAsync();
                multiPartContent.Dispose();
    
                // return JObject
                try
                {
                    return JObject.Parse(response);
                }
                catch (Exception ex)
                {
                    Debug.LogError(response);
                    throw ex;
                }
            }
        }
    }

huangapple
  • 本文由 发表于 2023年6月13日 05:16:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76460389.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定