/// <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); } }
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()); }
/// <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; } }