/// <summary>
        /// Asynchronously uploads a file in this title.
        /// </summary>
        /// <param name="site"></param>
        /// <param name="title"></param>
        /// <param name="source">Source of the file.</param>
        /// <param name="comment">Comment of the upload, as well as the page content if it doesn't exist.</param>
        /// <param name="ignoreWarnings">Ignore any warnings. This must be set to upload a new version of an existing image.</param>
        /// <param name="watch">Whether to add the file into your watchlist.</param>
        /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned task.</param>
        /// <exception cref="UnauthorizedAccessException">You do not have the permission to upload the file.</exception>
        /// <exception cref="OperationFailedException">
        /// There's an general failure while uploading the file.
        /// - or -
        /// Since MW 1.31, if you are uploading the exactly same content to the same title
        /// with <paramref name="ignoreWarnings"/> set to <c>true</c>,
        /// you will reveive this exception with <see cref="OperationFailedException.ErrorCode"/>
        /// set to <c>fileexists-no-change</c>. See https://gerrit.wikimedia.org/r/378702 .
        /// </exception>
        /// <exception cref="TimeoutException">Timeout specified in <see cref="WikiClient.Timeout"/> has been reached.</exception>
        /// <returns>An <see cref="UploadResult"/>. You need to check <see cref="UploadResult.ResultCode"/> for further action.</returns>
        public static async Task <UploadResult> UploadAsync(this WikiSite site, string title, WikiUploadSource source, string comment, bool ignoreWarnings,
                                                            AutoWatchBehavior watch, CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            Debug.Assert(source != null);
            var link = WikiLink.Parse(site, title, BuiltInNamespaces.File);

            using (site.BeginActionScope(null, title, source))
            {
                var requestFields = new Dictionary <string, object>
                {
                    { "action", "upload" },
                    { "watchlist", watch },
                    { "token", WikiSiteToken.Edit },
                    { "filename", link.Title },
                    { "comment", comment },
                    { "ignorewarnings", ignoreWarnings },
                };
                foreach (var p in source.GetUploadParameters(site.SiteInfo))
                {
                    requestFields[p.Key] = p.Value;
                }
                var request = new MediaWikiFormRequestMessage(requestFields, true);
                site.Logger.LogDebug("Start uploading.");
                var jresult = await site.InvokeMediaWikiApiAsync(request, cancellationToken);

                var result = jresult["upload"].ToObject <UploadResult>(Utility.WikiJsonSerializer);
                site.Logger.LogInformation("Uploaded. Result={Result}.", result.ResultCode);
                return(result);
            }
        }
Example #2
0
        public static async Task <IList <string> > SearchItemsAsync(this WikiSite site, string keywords)
        {
            var request = new MediaWikiFormRequestMessage(
                new
            {
                action   = "wbsearchentities",
                search   = keywords,
                language = "en",
                type     = "item",
                limit    = 5
            });
            var response = await site.InvokeMediaWikiApiAsync(request, CancellationToken.None);

            return(response["search"].Select(item => (string)item["id"]).ToList());
        }
Example #3
0
        /// <summary>
        /// Invokes MediaWiki API and gets JSON result.
        /// </summary>
        /// <param name="message">The request message.</param>
        /// <param name="responseParser">The parser that checks and parses the API response into <see cref="JToken"/>.</param>
        /// <param name="suppressAccountAssertion">Whether to temporarily disable account assertion as set in <see cref="SiteOptions.AccountAssertion"/>.</param>
        /// <param name="cancellationToken">The cancellation token that will be checked prior to completing the returned task.</param>
        /// <exception cref="InvalidActionException">Specified action is not supported.</exception>
        /// <exception cref="OperationFailedException">There is "error" node in returned JSON. Instances of derived types may be thrown.</exception>
        /// <exception cref="AccountAssertionFailureException">You enabled account assertion, the assertion failed, and it also failed to retry logging in.</exception>
        /// <returns>A task that returns the JSON response when completed.</returns>
        /// <remarks>
        /// Some enhancements are available only if <paramref name="message"/> is <see cref="MediaWikiFormRequestMessage"/>, including
        /// <list type="bullet">
        /// <item><description>
        /// Account assertion, as specified in <see cref="SiteOptions.AccountAssertion"/>.
        /// </description></item>
        /// <item><description>
        /// Automatic token-refreshing on <c>badtoken</c> error. This requires you to set all your <c>token</c>
        /// fields in the <paramref name="message"/> to a placeholder of type <see cref="WikiSiteToken"/>,
        /// instead of the actual token string.
        /// </description></item>
        /// </list>
        /// </remarks>
        public async Task <T> InvokeMediaWikiApiAsync <T>(WikiRequestMessage message, IWikiResponseMessageParser <T> responseParser,
                                                          bool suppressAccountAssertion, CancellationToken cancellationToken)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }
            if (responseParser == null)
            {
                throw new ArgumentNullException(nameof(responseParser));
            }
            var form            = message as MediaWikiFormRequestMessage;
            var localRequest    = message;
            var badTokenRetries = 0;

RETRY:
            if (form != null)
            {
                // Apply tokens
                var newFields = new List <KeyValuePair <string, object> >(form.Fields.Count + 3)
                {
                    new KeyValuePair <string, object>("format", "json")
                };
                foreach (var tokenField in form.Fields)
                {
                    var value = tokenField.Value;
                    if (value is WikiSiteToken)
                    {
                        value = await tokensManager.GetTokenAsync(
                            ((WikiSiteToken)tokenField.Value).Type, false, cancellationToken);
                    }
                    newFields.Add(new KeyValuePair <string, object>(tokenField.Key, value));
                }
                // Apply account assertions
                if (!suppressAccountAssertion && _AccountInfo != null)
                {
                    if ((options.AccountAssertion & AccountAssertionBehavior.AssertBot) ==
                        AccountAssertionBehavior.AssertBot && _AccountInfo.IsBot)
                    {
                        newFields.Add(new KeyValuePair <string, object>("assert", "bot"));
                    }
                    else if ((options.AccountAssertion & AccountAssertionBehavior.AssertUser) ==
                             AccountAssertionBehavior.AssertUser && _AccountInfo.IsUser)
                    {
                        newFields.Add(new KeyValuePair <string, object>("assert", "user"));
                    }
                }
                localRequest = new MediaWikiFormRequestMessage(form.Id, newFields, form.AsMultipartFormData);
            }
            Logger.LogDebug("Sending request {Request}, SuppressAccountAssertion={SuppressAccountAssertion}",
                            localRequest, suppressAccountAssertion);
            try
            {
                return(await WikiClient.InvokeAsync(options.ApiEndpoint, localRequest, responseParser, cancellationToken));
            }
            catch (AccountAssertionFailureException)
            {
                if (AccountAssertionFailureHandler != null)
                {
                    // ISSUE Relogin might be called multiple times.
                    if (reLoginTask == null)
                    {
                        Logger.LogWarning("Account assertion failed. Try to relogin.");
                        Volatile.Write(ref reLoginTask, Relogin());
                    }
                    else
                    {
                        Logger.LogWarning("Account assertion failed. Waiting for relongin.");
                    }
                    var result = await reLoginTask;
                    if (result)
                    {
                        goto RETRY;
                    }
                }
                throw;
            }
            catch (BadTokenException)
            {
                // Allows retrying once.
                if (form == null || badTokenRetries >= 1)
                {
                    throw;
                }
                string invalidatedToken = null;
                foreach (var tokenField in form.Fields.Where(p => p.Value is WikiSiteToken))
                {
                    invalidatedToken = ((WikiSiteToken)tokenField.Value).Type;
                    tokensManager.ClearCache(invalidatedToken);
                }
                if (invalidatedToken == null)
                {
                    throw;
                }
                Logger.LogWarning("BadTokenException: {Request}. Will retry after invalidating the token: {Token}.",
                                  message, invalidatedToken);
                badTokenRetries++;
                goto RETRY;
            }
        }