├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── generic-build.yml
│ └── nuget-release.yml
├── .gitignore
├── Directory.Build.props
├── LICENSE.txt
├── README.md
└── src
├── Wikipedia.Examples
├── Program.cs
└── Wikipedia.Examples.csproj
├── Wikipedia.Tests
├── WikiMediaRequestTests.cs
├── WikiSearchRequestTests.cs
└── Wikipedia.Tests.csproj
├── Wikipedia.sln
└── Wikipedia
├── Enums
├── WikiErrorFormat.cs
├── WikiInfo.cs
├── WikiLanguage.cs
├── WikiNamespace.cs
├── WikiProperty.cs
├── WikiQueryProfile.cs
├── WikiSortOrder.cs
└── WikiWhat.cs
├── Extensions
└── EnumExtensions.cs
├── Internal
├── LowerCasePolicy.cs
├── StringValueAttribute.cs
└── UrlHelper.cs
├── Objects
├── Continuation.cs
├── Error.cs
├── ModuleError.cs
├── QueryResult.cs
├── SearchInfo.cs
└── SearchResult.cs
├── WikiMediaRequest.cs
├── WikiSearchRequest.cs
├── WikiSearchResponse.cs
├── Wikipedia.csproj
└── WikipediaClient.cs
/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | [*.{appxmanifest,axml,build,config,csproj,dbml,discomap,dtd,jsproj,lsproj,njsproj,nuspec,proj,props,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
3 | indent_style = space
4 | indent_size = 2
5 | tab_width = 4
6 |
7 | [*.{c,c++,cc,cginc,compute,cp,cpp,cu,cuh,cxx,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,mpp,mq4,mq5,mqh,proto,tpp,usf,ush}]
8 | indent_style = space
9 | indent_size = 4
10 | tab_width = 4
11 |
12 | [*.{asax,ascx,aspx,axaml,cs,cshtml,css,htm,html,js,jsx,master,paml,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
13 | indent_style = space
14 | indent_size = 4
15 | tab_width = 4
16 |
17 | [*.{json,resjson}]
18 | indent_style = space
19 | indent_size = 2
20 | tab_width = 2
21 |
22 | [*]
23 |
24 | # Microsoft .NET properties
25 | csharp_new_line_before_members_in_object_initializers = false
26 | csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion
27 | csharp_space_after_cast = true
28 | csharp_style_var_elsewhere = false:suggestion
29 | csharp_style_var_for_built_in_types = false:suggestion
30 | csharp_style_var_when_type_is_apparent = false:suggestion
31 | dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
32 | dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
33 | dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
34 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
35 | dotnet_style_predefined_type_for_member_access = true:suggestion
36 | dotnet_style_qualification_for_event = false:suggestion
37 | dotnet_style_qualification_for_field = false:suggestion
38 | dotnet_style_qualification_for_method = false:suggestion
39 | dotnet_style_qualification_for_property = false:suggestion
40 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
41 | dotnet_style_object_initializer = false
42 |
43 | # ReSharper properties
44 | resharper_align_linq_query = true
45 | resharper_align_multiline_array_and_object_initializer = true
46 | resharper_align_multiline_binary_patterns = true
47 | resharper_align_multiline_calls_chain = true
48 | resharper_align_multiline_expression = true
49 | resharper_align_multiline_extends_list = true
50 | resharper_align_multiline_property_pattern = true
51 | resharper_align_multline_type_parameter_constrains = true
52 | resharper_align_multline_type_parameter_list = true
53 | resharper_align_tuple_components = true
54 | resharper_blank_lines_around_single_line_type = 0
55 | resharper_blank_lines_before_single_line_comment = 1
56 | resharper_continuous_indent_multiplier = 2
57 | resharper_csharp_align_multiline_parameter = true
58 | resharper_csharp_align_multiple_declaration = true
59 | resharper_csharp_blank_lines_inside_region = 0
60 | resharper_csharp_empty_block_style = together_same_line
61 | resharper_csharp_outdent_dots = true
62 | resharper_csharp_stick_comment = false
63 | resharper_csharp_use_indent_from_vs = false
64 | resharper_csharp_wrap_lines = false
65 | resharper_indent_anonymous_method_block = true
66 | resharper_indent_preprocessor_region = no_indent
67 | resharper_keep_existing_embedded_arrangement = false
68 | resharper_place_simple_embedded_statement_on_same_line = false
69 | resharper_space_within_single_line_array_initializer_braces = false
70 | resharper_use_continuous_indent_inside_initializer_braces = false
71 | resharper_wrap_before_eq = true
72 |
73 | # ReSharper inspection severities
74 | resharper_arrange_object_creation_when_type_evident_highlighting = none
75 | resharper_arrange_redundant_parentheses_highlighting = hint
76 | resharper_arrange_this_qualifier_highlighting = hint
77 | resharper_arrange_type_member_modifiers_highlighting = hint
78 | resharper_arrange_type_modifiers_highlighting = hint
79 | resharper_built_in_type_reference_style_for_member_access_highlighting = hint
80 | resharper_built_in_type_reference_style_highlighting = hint
81 | resharper_redundant_base_qualifier_highlighting = warning
82 | resharper_suggest_var_or_type_built_in_types_highlighting = hint
83 | resharper_suggest_var_or_type_elsewhere_highlighting = hint
84 | resharper_suggest_var_or_type_simple_types_highlighting = hint
85 | csharp_space_around_declaration_statements = false
86 | resharper_use_object_or_collection_initializer_highlighting = false
87 |
88 | # Analyzers
89 | roslynator_accessibility_modifiers = explicit
90 | roslynator_enum_has_flag_style = method
91 | roslynator_object_creation_type_style = explicit
92 | roslynator_use_var_instead_of_implicit_object_creation = false
93 | csharp_style_namespace_declarations = file_scoped:error
94 | dotnet_diagnostic.xunit2003.severity = error
95 | dotnet_diagnostic.xunit2004.severity = error
96 | dotnet_diagnostic.ca1018.severity = error
97 | dotnet_diagnostic.ca1041.severity = error
98 | dotnet_diagnostic.ca1056.severity = silent
99 | dotnet_diagnostic.ca1055.severity = silent
100 | dotnet_diagnostic.ca1054.severity = silent
101 | dotnet_diagnostic.ca1200.severity = warning
102 | dotnet_diagnostic.ca1309.severity = suggestion
103 | dotnet_diagnostic.ca1401.severity = warning
104 | dotnet_diagnostic.ca1507.severity = warning
105 | dotnet_diagnostic.ca1821.severity = error
106 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Linux scripts
2 | *.sh text eol=lf
3 | *.py text diff=python eol=lf
4 |
5 | # Linux configs
6 | *.conf text eol=lf
7 | *.yml text eol=lf
8 |
9 | # Windows scripts
10 | *.ps1 text eol=crlf
11 | *.cmd text eol=crlf
12 | *.bat text eol=crlf
13 |
14 | # Source code files
15 | *.cs text diff=csharp
16 | *.cshtml text
17 | *.json text
18 | *.xml text
19 | *.html text diff=html
20 | *.css text diff=css
21 | *.scss text diff=css
22 | *.js text
23 | *.csproj text
24 | *.targets text ident
25 | *.sln text
26 |
27 | # Images
28 | *.svg text
29 |
30 | # Text files
31 | *.txt text
32 | *.md text
33 | *.markdowm text
34 |
35 | ###############################
36 | # Git Large File System (LFS) #
37 | ###############################
38 |
39 | # Archives
40 | *.7z filter=lfs diff=lfs merge=lfs -text
41 | *.rar filter=lfs diff=lfs merge=lfs -text
42 | *.br filter=lfs diff=lfs merge=lfs -text
43 | *.gz filter=lfs diff=lfs merge=lfs -text
44 | *.tar filter=lfs diff=lfs merge=lfs -text
45 | *.zip filter=lfs diff=lfs merge=lfs -text
46 |
47 | # Documents
48 | *.pdf filter=lfs diff=lfs merge=lfs -text
49 | *.doc filter=lfs diff=lfs merge=lfs -text
50 | *.docx filter=lfs diff=lfs merge=lfs -text
51 |
52 | # Images
53 | *.gif filter=lfs diff=lfs merge=lfs -text
54 | *.ico filter=lfs diff=lfs merge=lfs -text
55 | *.jpg filter=lfs diff=lfs merge=lfs -text
56 | *.png filter=lfs diff=lfs merge=lfs -text
57 | *.psd filter=lfs diff=lfs merge=lfs -text
58 | *.webp filter=lfs diff=lfs merge=lfs -text
59 | *.bmp filter=lfs diff=lfs merge=lfs -text
60 |
61 | # Fonts
62 | *.woff2 filter=lfs diff=lfs merge=lfs -text
63 |
64 | # Other
65 | *.exe filter=lfs diff=lfs merge=lfs -text
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [Genbox]
2 | custom: http://paypal.me/IanQvist
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | * [ ] I hereby verify that I am a sponsor.
11 | Sponsorship is required before you can submit bug reports. See https://github.com/sponsors/Genbox
12 |
13 | **Describe the bug**
14 | What is the bug about?
15 |
16 | **How to reproduce?**
17 | Describe steps to reproduce the bug. Please include code to reproduce if possible.
18 |
19 | **Expected behavior**
20 | Describe what you expected to happen.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest a feature for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | * [ ] I hereby verify that I am a sponsor.
11 | Sponsorship is required before you can submit feature requests. See https://github.com/sponsors/Genbox
12 |
13 | **Describe the feature**
14 | Provide a short description of the feature. Why would you like the feature?
15 | If the feature makes changes to an existing or new API please provide an example.
16 |
--------------------------------------------------------------------------------
/.github/workflows/generic-build.yml:
--------------------------------------------------------------------------------
1 | name: Generic build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Setup .NET Core 6.0
12 | uses: actions/setup-dotnet@v1
13 | with:
14 | dotnet-version: '6.0.x'
15 | - name: Build free
16 | run: dotnet build -c Release src/Wikipedia.sln
--------------------------------------------------------------------------------
/.github/workflows/nuget-release.yml:
--------------------------------------------------------------------------------
1 | name: Nuget release
2 |
3 | on:
4 | push:
5 | tags:
6 | - '[0-9]+\.[0-9]+\.[0-9]+'
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | - name: Setup .NET Core 6.0
15 | uses: actions/setup-dotnet@v1
16 | with:
17 | dotnet-version: '6.0.x'
18 | - name: Pack release
19 | run: dotnet pack -c Release src/Wikipedia.sln -o Temp
20 | - name: Upload to nuget
21 | run: dotnet nuget push --skip-duplicate -k ${{secrets.NUGET_KEY}} -s https://api.nuget.org/v3/index.json Temp/*
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build results
2 | [Bb]in/
3 | [Oo]bj/
4 | [Ll]og/
5 |
6 | # NuGet Packages
7 | *.nupkg
8 | *.snupkg
9 |
10 | # Others
11 | *.pfx
12 | *.publishsettings
13 |
14 | # SQL Server files
15 | *.mdf
16 | *.ldf
17 | *.ndf
18 |
19 | # Visual Studio cache folder
20 | .vs/
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ian Qvist
6 | Copyright 2022, by Ian Qvist. All rights reserved.
7 | A simple .NET based search client for Wikipedia
8 |
9 |
10 |
11 |
12 |
13 | 2
14 | 500
15 |
16 |
17 | Genbox.$(MSBuildProjectName)
18 | Genbox.$(MSBuildProjectName)
19 |
20 |
21 | latest
22 | enable
23 |
24 |
25 | true
26 | false
27 |
28 |
29 | portable
30 |
31 |
32 | Git
33 | https://github.com/Genbox/$(MSBuildProjectName)
34 | MIT
35 |
36 |
37 | true
38 | true
39 | true
40 | snupkg
41 |
42 |
43 | 0
44 | false
45 | false
46 | false
47 | none
48 |
49 |
50 | none
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | true
73 | true
74 | true
75 | 5
76 | Default
77 | latest
78 |
79 |
80 |
81 | false
82 | true
83 |
84 |
85 |
86 | true
87 | false
88 |
89 |
90 |
91 |
92 |
93 | $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).0
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Ian Qvist
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Wikipedia - An implementation of the full text search API of Wikipedia
2 |
3 | [](https://www.nuget.org/packages/Genbox.Wikipedia/)
4 | [](https://github.com/Genbox/Wikipedia/actions)
5 | [](https://github.com/Genbox/Wikipedia/actions)
6 | [](https://github.com/Genbox/Wikipedia/blob/master/LICENSE.txt)
7 |
8 | ### Features
9 |
10 | * Support for all 283 languages on Wikipedia
11 | * Support for all search parameters as of MediaWiki v1.24
12 |
13 | ### Example
14 |
15 | Here is the simplest way of getting data from Wikipedia:
16 |
17 | ```csharp
18 | static async Task Main()
19 | {
20 | using WikipediaClient client = new WikipediaClient();
21 |
22 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
23 | req.Limit = 5; //We would like 5 results
24 |
25 | WikiSearchResponse resp = await client.SearchAsync(req);
26 |
27 | foreach (SearchResult s in resp.QueryResult.SearchResults)
28 | {
29 | Console.WriteLine($" - {s.Title}");
30 | }
31 | }
32 | ```
33 |
34 | Output:
35 | ```
36 | - Albert Einstein
37 | - Hans Albert Einstein
38 | - Einstein family
39 | - Albert Brooks
40 | - Albert Einstein College of Medicine
41 | ```
--------------------------------------------------------------------------------
/src/Wikipedia.Examples/Program.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Enums;
2 | using Genbox.Wikipedia.Objects;
3 |
4 | namespace Genbox.Wikipedia.Examples;
5 |
6 | internal static class Program
7 | {
8 | private static async Task Main()
9 | {
10 | //Default language is English
11 | using WikipediaClient client = new WikipediaClient();
12 |
13 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
14 | req.Limit = 5; //We would like 5 results
15 | req.WhatToSearch = WikiWhat.Text; //We would like to search inside the articles
16 |
17 | WikiSearchResponse resp = await client.SearchAsync(req).ConfigureAwait(false);
18 |
19 | Console.WriteLine($"Searching for {req.Query}");
20 | Console.WriteLine();
21 | Console.WriteLine($"Found {resp.QueryResult.SearchResults.Count} English results:");
22 |
23 | foreach (SearchResult s in resp.QueryResult.SearchResults)
24 | {
25 | Console.WriteLine($" - {s.Title}");
26 | }
27 |
28 | Console.WriteLine();
29 | Console.WriteLine();
30 |
31 | //We change the language to Spanish
32 | req.WikiLanguage = WikiLanguage.Spanish;
33 |
34 | resp = await client.SearchAsync(req).ConfigureAwait(false);
35 |
36 | Console.WriteLine($"Found {resp.QueryResult.SearchResults.Count} Spanish results:");
37 |
38 | foreach (SearchResult s in resp.QueryResult.SearchResults)
39 | {
40 | Console.WriteLine($" - {s.Title}");
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Wikipedia.Examples/Wikipedia.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/Wikipedia.Tests/WikiMediaRequestTests.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Enums;
2 | using Xunit;
3 |
4 | namespace Genbox.Wikipedia.Tests;
5 |
6 | public sealed class WikiMediaRequestTests : IDisposable
7 | {
8 | private readonly WikipediaClient _client;
9 |
10 | public WikiMediaRequestTests()
11 | {
12 | _client = new WikipediaClient();
13 | }
14 |
15 | [Fact]
16 | public async Task AssertTest()
17 | {
18 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
19 | req.Assert = "anon";
20 |
21 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
22 | Assert.Null(resp.Error); //We expect there to be no error since we are not logged in
23 |
24 | req.Assert = "user";
25 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
26 | Assert.NotNull(resp.Error); //We expect an error because we are not logged in
27 | Assert.Equal("assertuserfailed", resp.Error.Code);
28 | Assert.Equal("See https://en.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/> for notice of API deprecations and breaking changes.", resp.Error.DocumentReference);
29 | Assert.Equal("You are no longer logged in, so the action could not be completed.", resp.Error.Info);
30 | }
31 |
32 | [Fact]
33 | public async Task AssertUserTest()
34 | {
35 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
36 | req.AssertUser = "someone";
37 |
38 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
39 | Assert.Equal("assertnameduserfailed", resp.Error.Code);
40 | Assert.Equal("See https://en.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/postorius/lists/mediawiki-api-announce.lists.wikimedia.org/> for notice of API deprecations and breaking changes.", resp.Error.DocumentReference);
41 | Assert.Equal("You are no longer logged in as \"Someone\", so the action could not be completed.", resp.Error.Info);
42 | }
43 |
44 | [Fact]
45 | public async Task IncludeServedByTest()
46 | {
47 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
48 | req.IncludeServedBy = true;
49 |
50 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
51 | Assert.NotNull(resp.ServedBy);
52 | }
53 |
54 | [Fact]
55 | public async Task IncludeCurrentTimestampTest()
56 | {
57 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
58 | req.IncludeCurrentTimestamp = true;
59 |
60 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
61 | Assert.Equal(DateTime.UtcNow, resp.CurrentTimestamp.DateTime, TimeSpan.FromMinutes(1));
62 | }
63 |
64 | [Fact]
65 | public async Task IncludeLanguageUsedTest()
66 | {
67 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
68 | req.IncludeLanguageUsed = true;
69 |
70 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
71 | Assert.Equal("en", resp.ErrorLanguage);
72 | Assert.Equal("en", resp.Language);
73 | }
74 |
75 | [Fact]
76 | public async Task RequestIdTest()
77 | {
78 | Guid id = Guid.NewGuid();
79 |
80 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
81 | req.RequestId = id.ToString();
82 |
83 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
84 | Assert.Equal(id.ToString(), resp.RequestId);
85 | }
86 |
87 | [Fact]
88 | public async Task LanguageToUseAndVariantTest()
89 | {
90 | //Go to https://www.mediawiki.org/w/api.php?action=query&meta=siteinfo&siprop=languages&formatversion=2 to see languages
91 |
92 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
93 | req.LanguageToUse = "es";
94 | req.LanguageVariant = "es-419";
95 | req.IncludeLanguageUsed = true;
96 |
97 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
98 | Assert.Equal("es", resp.Language);
99 | }
100 |
101 | [Fact(Skip = "Unable to create a working test case")]
102 | public async Task ErrorLanguageToUseTest()
103 | {
104 | //Go to https://www.mediawiki.org/w/api.php?action=query&meta=siteinfo&siprop=languages&formatversion=2 to see languages
105 |
106 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
107 | req.WikiLanguage = WikiLanguage.Spanish;
108 | req.IncludeLanguageUsed = true;
109 |
110 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
111 | Assert.Equal("es", resp.ErrorLanguage);
112 | }
113 |
114 | [Fact]
115 | public async Task ErrorFormatTest()
116 | {
117 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
118 | req.AssertUser = "whatever"; //We use AssertUser to provoke an error
119 | req.ErrorFormat = WikiErrorFormat.Bc;
120 |
121 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
122 | Assert.Equal("You are no longer logged in as \"Whatever\", so the action could not be completed.", resp.Error.Info);
123 |
124 | req.ErrorFormat = WikiErrorFormat.Html;
125 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
126 | Assert.Equal("assertnameduserfailed", resp.ModuleErrors[0].Code);
127 | Assert.Equal("main", resp.ModuleErrors[0].Module);
128 | Assert.Equal("You are no longer logged in as \"Whatever\", so the action could not be completed.", resp.ModuleErrors[0].Html);
129 |
130 | req.ErrorFormat = WikiErrorFormat.Plaintext;
131 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
132 | Assert.Equal("assertnameduserfailed", resp.ModuleErrors[0].Code);
133 | Assert.Equal("main", resp.ModuleErrors[0].Module);
134 | Assert.Equal("You are no longer logged in as \"Whatever\", so the action could not be completed.", resp.ModuleErrors[0].Text);
135 |
136 | req.ErrorFormat = WikiErrorFormat.None;
137 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
138 | Assert.Equal("assertnameduserfailed", resp.ModuleErrors[0].Code);
139 | Assert.Equal("main", resp.ModuleErrors[0].Module);
140 | Assert.Null(resp.ModuleErrors[0].Text);
141 | Assert.Null(resp.ModuleErrors[0].Html);
142 |
143 | req.ErrorFormat = WikiErrorFormat.Raw;
144 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
145 | Assert.Equal("assertnameduserfailed", resp.ModuleErrors[0].Code);
146 | Assert.Equal("main", resp.ModuleErrors[0].Module);
147 | Assert.Equal("apierror-assertnameduserfailed", resp.ModuleErrors[0].Key);
148 | Assert.Equal("Whatever", resp.ModuleErrors[0].Params[0]);
149 |
150 | req.ErrorFormat = WikiErrorFormat.WikiText;
151 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
152 | Assert.Equal("assertnameduserfailed", resp.ModuleErrors[0].Code);
153 | Assert.Equal("main", resp.ModuleErrors[0].Module);
154 | Assert.Equal("You are no longer logged in as \"Whatever\", so the action could not be completed.", resp.ModuleErrors[0].Text);
155 | }
156 |
157 | public void Dispose()
158 | {
159 | _client.Dispose();
160 | }
161 | }
--------------------------------------------------------------------------------
/src/Wikipedia.Tests/WikiSearchRequestTests.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Enums;
2 | using Genbox.Wikipedia.Extensions;
3 | using Genbox.Wikipedia.Objects;
4 | using Xunit;
5 |
6 | namespace Genbox.Wikipedia.Tests;
7 |
8 | public sealed class WikiSearchRequestTests : IDisposable
9 | {
10 | private readonly WikipediaClient _client;
11 |
12 | public WikiSearchRequestTests()
13 | {
14 | _client = new WikipediaClient();
15 | }
16 |
17 | [Fact]
18 | public async Task NamespacesToIncludeTest()
19 | {
20 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
21 | req.NamespacesToInclude = WikiNamespace.Category | WikiNamespace.CategoryTalk;
22 | req.Limit = 100;
23 |
24 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
25 |
26 | //Ideally we should have results from both categories
27 | Assert.Contains(resp.QueryResult.SearchResults, x => x.Namespace.ToString() == WikiNamespace.Category.GetStringValue());
28 | Assert.Contains(resp.QueryResult.SearchResults, x => x.Namespace.ToString() == WikiNamespace.CategoryTalk.GetStringValue());
29 | }
30 |
31 | [Fact]
32 | public async Task LimitAndOffsetTest()
33 | {
34 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
35 | req.Limit = 1;
36 |
37 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
38 | SearchResult? item = Assert.Single(resp.QueryResult.SearchResults);
39 |
40 | req.Offset = 1;
41 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
42 |
43 | SearchResult? otherItem = Assert.Single(resp.QueryResult.SearchResults);
44 |
45 | Assert.NotEqual(item, otherItem);
46 | }
47 |
48 | [Fact]
49 | public async Task QueryIndependentProfileTest()
50 | {
51 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
52 | req.QueryIndependentProfile = WikiQueryProfile.Classic;
53 |
54 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
55 | Assert.NotNull(resp.QueryResult);
56 | }
57 |
58 | [Fact]
59 | public async Task WhatToSearchTest()
60 | {
61 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
62 | req.WhatToSearch = WikiWhat.Text;
63 |
64 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
65 | Assert.NotNull(resp.QueryResult);
66 | }
67 |
68 | [Fact]
69 | public async Task InfoToIncludeTest()
70 | {
71 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstei");
72 | req.InfoToInclude = WikiInfo.TotalHits | WikiInfo.Suggestion;
73 |
74 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
75 | Assert.Equal("albert einstein", resp.QueryResult.SearchInfo.Suggestion);
76 | Assert.Equal("albert einstein", resp.QueryResult.SearchInfo.SuggestionSnippet);
77 | Assert.NotEqual(0, resp.QueryResult.SearchInfo.TotalHits);
78 | }
79 |
80 | [Fact]
81 | public async Task PropertiesToIncludeTest()
82 | {
83 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
84 | req.PropertiesToInclude = WikiProperty.All;
85 |
86 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
87 |
88 | SearchResult s = resp.QueryResult.SearchResults[0];
89 |
90 | Assert.NotNull(s.CategorySnippet);
91 | Assert.NotNull(s.IsFileMatch);
92 | Assert.NotEqual(0, s.Size);
93 | Assert.NotEqual(0, s.PageId);
94 | Assert.NotNull(s.Snippet);
95 | Assert.NotNull(s.TimeStamp);
96 | Assert.NotNull(s.Title);
97 | Assert.NotNull(s.TitleSnippet);
98 | Assert.NotEqual(0, s.WordCount);
99 |
100 | req = new WikiSearchRequest("Albert Einstein");
101 | req.PropertiesToInclude = WikiProperty.Size;
102 |
103 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
104 |
105 | s = resp.QueryResult.SearchResults[0];
106 | Assert.NotEqual(0, s.Size);
107 | Assert.NotNull(s.Title);
108 | Assert.Null(s.CategorySnippet);
109 | Assert.Null(s.IsFileMatch);
110 | Assert.Null(s.Snippet);
111 | Assert.Null(s.TimeStamp);
112 | Assert.Null(s.TitleSnippet);
113 | Assert.Null(s.WordCount);
114 | }
115 |
116 | [Fact]
117 | public async Task IncludeInterWikiResultsTest()
118 | {
119 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstei");
120 | req.IncludeInterWikiResults = true;
121 |
122 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
123 | Assert.NotNull(resp.QueryResult);
124 | }
125 |
126 | [Fact]
127 | public async Task SortOrderTest()
128 | {
129 | WikiSearchRequest req = new WikiSearchRequest("Albert Einstein");
130 | req.SortOrder = WikiSortOrder.CreateTimestampAsc;
131 |
132 | WikiSearchResponse resp = await _client.SearchAsync(req).ConfigureAwait(false);
133 | SearchResult item = resp.QueryResult.SearchResults[0];
134 |
135 | req.SortOrder = WikiSortOrder.CreateTimestampDesc;
136 | resp = await _client.SearchAsync(req).ConfigureAwait(false);
137 |
138 | SearchResult otherItem = resp.QueryResult.SearchResults[0];
139 |
140 | Assert.NotEqual(item, otherItem);
141 | }
142 |
143 | public void Dispose()
144 | {
145 | _client.Dispose();
146 | }
147 | }
--------------------------------------------------------------------------------
/src/Wikipedia.Tests/Wikipedia.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Wikipedia.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 15.0.26124.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Items", "_Items", "{7FC54FC1-575A-4BD7-B93B-D30E2F3AA6DC}"
7 | ProjectSection(SolutionItems) = preProject
8 | ..\.editorconfig = ..\.editorconfig
9 | ..\.gitattributes = ..\.gitattributes
10 | ..\.gitignore = ..\.gitignore
11 | ..\Directory.Build.props = ..\Directory.Build.props
12 | ..\LICENSE.txt = ..\LICENSE.txt
13 | ..\README.md = ..\README.md
14 | EndProjectSection
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wikipedia", "Wikipedia\Wikipedia.csproj", "{5DAAD97E-41B7-4F9A-9CAE-1F44CB61E449}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wikipedia.Examples", "Wikipedia.Examples\Wikipedia.Examples.csproj", "{A8805B03-BFC9-4AD6-89C8-B52DF8FCE337}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wikipedia.Tests", "Wikipedia.Tests\Wikipedia.Tests.csproj", "{6CF44102-30B8-4B6C-86AD-CFA02FEE63A5}"
21 | EndProject
22 | Global
23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
24 | Debug|Any CPU = Debug|Any CPU
25 | Release|Any CPU = Release|Any CPU
26 | EndGlobalSection
27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
28 | {5DAAD97E-41B7-4F9A-9CAE-1F44CB61E449}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {5DAAD97E-41B7-4F9A-9CAE-1F44CB61E449}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {5DAAD97E-41B7-4F9A-9CAE-1F44CB61E449}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {5DAAD97E-41B7-4F9A-9CAE-1F44CB61E449}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {A8805B03-BFC9-4AD6-89C8-B52DF8FCE337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {A8805B03-BFC9-4AD6-89C8-B52DF8FCE337}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {A8805B03-BFC9-4AD6-89C8-B52DF8FCE337}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {A8805B03-BFC9-4AD6-89C8-B52DF8FCE337}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {6CF44102-30B8-4B6C-86AD-CFA02FEE63A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {6CF44102-30B8-4B6C-86AD-CFA02FEE63A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {6CF44102-30B8-4B6C-86AD-CFA02FEE63A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {6CF44102-30B8-4B6C-86AD-CFA02FEE63A5}.Release|Any CPU.Build.0 = Release|Any CPU
40 | EndGlobalSection
41 | GlobalSection(SolutionProperties) = preSolution
42 | HideSolutionNode = FALSE
43 | EndGlobalSection
44 | GlobalSection(ExtensibilityGlobals) = postSolution
45 | SolutionGuid = {5AAF35F0-620F-4CF1-981B-9FC7B6652049}
46 | EndGlobalSection
47 | EndGlobal
48 |
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiErrorFormat.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | public enum WikiErrorFormat
6 | {
7 | NotSet = 0,
8 |
9 | /// Format used prior to MediaWiki 1.29. and
10 | /// are ignored.
11 | [StringValue("bc")] Bc,
12 |
13 | /// HTML
14 | [StringValue("html")] Html,
15 |
16 | /// No text output, only the error codes.
17 | [StringValue("none")] None,
18 |
19 | /// Wikitext with HTML tags removed and entities replaced.
20 | [StringValue("plaintext")] Plaintext,
21 |
22 | /// Message key and parameters.
23 | [StringValue("raw")] Raw,
24 |
25 | /// Unparsed wikitext.
26 | [StringValue("wikitext")] WikiText
27 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiInfo.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | [Flags]
6 | public enum WikiInfo
7 | {
8 | NotSet = 0,
9 |
10 | /// The number of search results
11 | [StringValue("totalhits")] TotalHits = 1 << 0,
12 |
13 | /// A suggestion that might fit better than what you searched for.
14 | [StringValue("suggestion")] Suggestion = 1 << 1,
15 |
16 | [StringValue("rewrittenquery")] RewrittenQuery = 1 << 2
17 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiLanguage.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | /// All the supported languages from Wikipedia. Source: http://meta.wikimedia.org/wiki/List_of_Wikipedias
6 | public enum WikiLanguage
7 | {
8 | NotSet = 0,
9 | [StringValue("en")] English,
10 | [StringValue("de")] German,
11 | [StringValue("fr")] French,
12 | [StringValue("nl")] Dutch,
13 | [StringValue("it")] Italian,
14 | [StringValue("pl")] Polish,
15 | [StringValue("es")] Spanish,
16 | [StringValue("ru")] Russian,
17 | [StringValue("ja")] Japanese,
18 | [StringValue("pt")] Portuguese,
19 | [StringValue("zh")] Chinese,
20 | [StringValue("sv")] Swedish,
21 | [StringValue("vi")] Vietnamese,
22 | [StringValue("uk")] Ukrainian,
23 | [StringValue("ca")] Catalan,
24 | [StringValue("no")] NorwegianBokmål,
25 | [StringValue("fi")] Finnish,
26 | [StringValue("cs")] Czech,
27 | [StringValue("hu")] Hungarian,
28 | [StringValue("fa")] Persian,
29 | [StringValue("ko")] Korean,
30 | [StringValue("id")] Indonesian,
31 | [StringValue("tr")] Turkish,
32 | [StringValue("ar")] Arabic,
33 | [StringValue("ro")] Romanian,
34 | [StringValue("sk")] Slovak,
35 | [StringValue("eo")] Esperanto,
36 | [StringValue("da")] Danish,
37 | [StringValue("sr")] Serbian,
38 | [StringValue("lt")] Lithuanian,
39 | [StringValue("ms")] Malay,
40 | [StringValue("kk")] Kazakh,
41 | [StringValue("he")] Hebrew,
42 | [StringValue("eu")] Basque,
43 | [StringValue("bg")] Bulgarian,
44 | [StringValue("sl")] Slovenian,
45 | [StringValue("vo")] Volapük,
46 | [StringValue("hr")] Croatian,
47 | [StringValue("war")] WarayWaray,
48 | [StringValue("hi")] Hindi,
49 | [StringValue("et")] Estonian,
50 | [StringValue("az")] Azerbaijani,
51 | [StringValue("gl")] Galician,
52 | [StringValue("nn")] NorwegianNynorsk,
53 | [StringValue("simple")] SimpleEnglish,
54 | [StringValue("la")] Latin,
55 | [StringValue("el")] Greek,
56 | [StringValue("th")] Thai,
57 | [StringValue("new")] NewarNepalBhasa,
58 | [StringValue("sh")] SerboCroatian,
59 | [StringValue("roa-rup")] Aromanian,
60 | [StringValue("oc")] Occitan,
61 | [StringValue("ka")] Georgian,
62 | [StringValue("mk")] Macedonian,
63 | [StringValue("tl")] Tagalog,
64 | [StringValue("ht")] Haitian,
65 | [StringValue("pms")] Piedmontese,
66 | [StringValue("te(")] Telugu,
67 | [StringValue("ta")] Tamil,
68 | [StringValue("be-x-old")] BelarusianTaraškievica,
69 | [StringValue("be(")] Belarusian,
70 | [StringValue("br")] Breton,
71 | [StringValue("ceb")] Cebuano,
72 | [StringValue("lv")] Latvian,
73 | [StringValue("sq")] Albanian,
74 | [StringValue("jv")] Javanese,
75 | [StringValue("mg")] Malagasy,
76 | [StringValue("cy")] Welsh,
77 | [StringValue("mr")] Marathi,
78 | [StringValue("lb")] Luxembourgish,
79 | [StringValue("is")] Icelandic,
80 | [StringValue("bs")] Bosnian,
81 | [StringValue("yo")] Yoruba,
82 | [StringValue("an")] Aragonese,
83 | [StringValue("lmo")] Lombard,
84 | [StringValue("hy")] Armenian,
85 | [StringValue("fy")] WestFrisian,
86 | [StringValue("bpy")] BishnupriyaManipuri,
87 | [StringValue("ml")] Malayalam,
88 | [StringValue("pnb")] WesternPanjabi,
89 | [StringValue("sw")] Swahili,
90 | [StringValue("bn")] Bengali,
91 | [StringValue("io")] Ido,
92 | [StringValue("af")] Afrikaans,
93 | [StringValue("gu")] Gujarati,
94 | [StringValue("uz")] Uzbek,
95 | [StringValue("zh-yue")] Cantonese,
96 | [StringValue("ne")] Nepali,
97 | [StringValue("nds")] LowSaxon,
98 | [StringValue("ur")] Urdu,
99 | [StringValue("ku")] Kurdish,
100 | [StringValue("ast")] Asturian,
101 | [StringValue("scn")] Sicilian,
102 | [StringValue("su")] Sundanese,
103 | [StringValue("qu")] Quechua,
104 | [StringValue("diq")] Zazaki,
105 | [StringValue("ba")] Bashkir,
106 | [StringValue("tt")] Tatar,
107 | [StringValue("my")] Burmese,
108 | [StringValue("ga")] Irish,
109 | [StringValue("cv")] Chuvash,
110 | [StringValue("ia")] Interlingua,
111 | [StringValue("nap")] Neapolitan,
112 | [StringValue("bat-smg")] Samogitian,
113 | [StringValue("map-bms")] Banyumasan,
114 | [StringValue("wa")] Walloon,
115 | [StringValue("als")] Alemannic,
116 | [StringValue("am")] Amharic,
117 | [StringValue("kn")] Kannada,
118 | [StringValue("gd")] ScottishGaelic,
119 | [StringValue("bug")] Buginese,
120 | [StringValue("tg")] Tajik,
121 | [StringValue("zh-min-nan")] MinNan,
122 | [StringValue("yi")] Yiddish,
123 | [StringValue("sco")] Scots,
124 | [StringValue("vec")] Venetian,
125 | [StringValue("hif")] FijiHindi,
126 | [StringValue("roa-tara")] Tarantino,
127 | [StringValue("arz")] EgyptianArabic,
128 | [StringValue("os")] Ossetian,
129 | [StringValue("mzn")] Mazandarani,
130 | [StringValue("nah")] Nahuatl,
131 | [StringValue("ky")] Kirghiz,
132 | [StringValue("sah")] Sakha,
133 | [StringValue("mn")] Mongolian,
134 | [StringValue("sa")] Sanskrit,
135 | [StringValue("pam")] Kapampangan,
136 | [StringValue("hsb")] UpperSorbian,
137 | [StringValue("ckb")] Sorani,
138 | [StringValue("li")] Limburgian,
139 | [StringValue("mi")] Maori,
140 | [StringValue("si")] Sinhalese,
141 | [StringValue("co")] Corsican,
142 | [StringValue("gan")] Gan,
143 | [StringValue("glk")] Gilaki,
144 | [StringValue("bo")] Tibetan,
145 | [StringValue("fo")] Faroese,
146 | [StringValue("bar")] Bavarian,
147 | [StringValue("bcl")] CentralBicolano,
148 | [StringValue("ilo")] Ilokano,
149 | [StringValue("mrj")] HillMari,
150 | [StringValue("se")] NorthernSami,
151 | [StringValue("fiu-vro")] Võro,
152 | [StringValue("nds-nl")] DutchLowSaxon,
153 | [StringValue("tk")] Turkmen,
154 | [StringValue("vls")] WestFlemish,
155 | [StringValue("ps")] Pashto,
156 | [StringValue("gv")] Manx,
157 | [StringValue("rue")] Rusyn,
158 | [StringValue("dv")] Divehi,
159 | [StringValue("nrm")] Norman,
160 | [StringValue("pag")] Pangasinan,
161 | [StringValue("pa")] Punjabi,
162 | [StringValue("koi")] KomiPermyak,
163 | [StringValue("rm")] Romansh,
164 | [StringValue("km")] Khmer,
165 | [StringValue("kv")] Komi,
166 | [StringValue("xmf")] Mingrelian,
167 | [StringValue("csb")] Kashubian,
168 | [StringValue("udm")] Udmurt,
169 | [StringValue("mhr")] MeadowMari,
170 | [StringValue("fur")] Friulian,
171 | [StringValue("mt")] Maltese,
172 | [StringValue("zea")] Zeelandic,
173 | [StringValue("wuu")] Wu,
174 | [StringValue("lad")] Ladino,
175 | [StringValue("lij")] Ligurian,
176 | [StringValue("ug")] Uyghur,
177 | [StringValue("pi")] Pali,
178 | [StringValue("sc")] Sardinian,
179 | [StringValue("bh")] Bihari,
180 | [StringValue("zh-classical")] ClassicalChinese,
181 | [StringValue("or")] Oriya,
182 | [StringValue("nov")] Novial,
183 | [StringValue("ksh")] Ripuarian,
184 | [StringValue("frr")] NorthFrisian,
185 | [StringValue("ang")] AngloSaxon,
186 | [StringValue("so")] Somali,
187 | [StringValue("kw")] Cornish,
188 | [StringValue("stq")] SaterlandFrisian,
189 | [StringValue("nv")] Navajo,
190 | [StringValue("hak")] Hakka,
191 | [StringValue("ay")] Aymara,
192 | [StringValue("frp")] FrancoProvençalArpitan,
193 | [StringValue("ext")] Extremaduran,
194 | [StringValue("szl")] Silesian,
195 | [StringValue("pcd")] Picard,
196 | [StringValue("gag")] Gagauz,
197 | [StringValue("ie")] Interlingue,
198 | [StringValue("ln")] Lingala,
199 | [StringValue("haw")] Hawaiian,
200 | [StringValue("xal")] Kalmyk,
201 | [StringValue("rw")] Kinyarwanda,
202 | [StringValue("pdc")] PennsylvaniaGerman,
203 | [StringValue("vep")] Vepsian,
204 | [StringValue("pfl")] PalatinateGerman,
205 | [StringValue("krc")] KarachayBalkar,
206 | [StringValue("eml")] EmilianRomagnol,
207 | [StringValue("crh")] CrimeanTatar,
208 | [StringValue("gn")] Guarani,
209 | [StringValue("ace")] Acehnese,
210 | [StringValue("to")] Tongan,
211 | [StringValue("ce")] Chechen,
212 | [StringValue("kl")] Greenlandic,
213 | [StringValue("arc")] AssyrianNeoAramaic,
214 | [StringValue("myv")] Erzya,
215 | [StringValue("dsb")] LowerSorbian,
216 | [StringValue("as")] Assamese,
217 | [StringValue("bjn")] Banjar,
218 | [StringValue("pap")] Papiamentu,
219 | [StringValue("tpi")] TokPisin,
220 | [StringValue("lbe")] Lak,
221 | [StringValue("mdf")] Moksha,
222 | [StringValue("wo")] Wolof,
223 | [StringValue("jbo")] Lojban,
224 | [StringValue("sn")] Shona,
225 | [StringValue("kab")] Kabyle,
226 | [StringValue("av")] Avar,
227 | [StringValue("cbk-zam")] ZamboangaChavacano,
228 | [StringValue("ty")] Tahitian,
229 | [StringValue("srn")] Sranan,
230 | [StringValue("kbd")] KabardianCircassian,
231 | [StringValue("lez")] Lezgian,
232 | [StringValue("lo")] Lao,
233 | [StringValue("ab")] Abkhazian,
234 | [StringValue("tet")] Tetum,
235 | [StringValue("mwl")] Mirandese,
236 | [StringValue("ltg")] Latgalian,
237 | [StringValue("na")] Nauruan,
238 | [StringValue("ig")] Igbo,
239 | [StringValue("kg")] Kongo,
240 | [StringValue("za")] Zhuang,
241 | [StringValue("kaa")] Karakalpak,
242 | [StringValue("nso")] NorthernSotho,
243 | [StringValue("zu")] Zulu,
244 | [StringValue("rmy")] Romani,
245 | [StringValue("cu")] OldChurchSlavonic,
246 | [StringValue("tn")] Tswana,
247 | [StringValue("chy")] Cheyenne,
248 | [StringValue("chr")] Cherokee,
249 | [StringValue("got")] Gothic,
250 | [StringValue("sm")] Samoan,
251 | [StringValue("bi")] Bislama,
252 | [StringValue("mo")] Moldovan,
253 | [StringValue("iu")] Inuktitut,
254 | [StringValue("bm")] Bambara,
255 | [StringValue("pih")] Norfolk,
256 | [StringValue("ik")] Inupiak,
257 | [StringValue("ss")] Swati,
258 | [StringValue("sd")] Sindhi,
259 | [StringValue("pnt")] Pontic,
260 | [StringValue("cdo")] MinDong,
261 | [StringValue("ee")] Ewe,
262 | [StringValue("ha")] Hausa,
263 | [StringValue("ti")] Tigrinya,
264 | [StringValue("bxr")] BuryatRussia,
265 | [StringValue("ts")] Tsonga,
266 | [StringValue("om")] Oromo,
267 | [StringValue("ks")] Kashmiri,
268 | [StringValue("ki")] Kikuyu,
269 | [StringValue("ve(")] Venda,
270 | [StringValue("sg")] Sango,
271 | [StringValue("rn")] Kirundi,
272 | [StringValue("cr")] Cree,
273 | [StringValue("dz")] Dzongkha,
274 | [StringValue("lg")] Luganda,
275 | [StringValue("ak")] Akan,
276 | [StringValue("ff")] Fula,
277 | [StringValue("tum")] Tumbuka,
278 | [StringValue("fj")] Fijian,
279 | [StringValue("st")] Sesotho,
280 | [StringValue("tw")] Twi,
281 | [StringValue("xh")] Xhosa,
282 | [StringValue("ch")] Chamorro,
283 | [StringValue("ny")] Chichewa,
284 | [StringValue("ng")] Ndonga,
285 | [StringValue("ii")] SichuanYi,
286 | [StringValue("cho")] Choctaw,
287 | [StringValue("mh")] Marshallese,
288 | [StringValue("kj")] Kuanyama,
289 | [StringValue("ho")] HiriMotu,
290 | [StringValue("mus")] Muscogee,
291 | [StringValue("kr")] Kanuri,
292 | [StringValue("hz")] Herero
293 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiNamespace.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | /// The Wikipedia namespaces. See https://en.wikipedia.org/wiki/Wikipedia:Namespace
6 | [Flags]
7 | public enum WikiNamespace : ulong
8 | {
9 | NotSet = 0,
10 | [StringValue("*")] All = 1 << 0,
11 | [StringValue("0")] Main = 1 << 1,
12 | [StringValue("1")] Talk = 1 << 2,
13 | [StringValue("2")] User = 1 << 3,
14 | [StringValue("3")] UserTalk = 1 << 4,
15 | [StringValue("4")] Wikipedia = 1 << 5,
16 | [StringValue("5")] WikipediaTalk = 1 << 6,
17 | [StringValue("6")] File = 1 << 7,
18 | [StringValue("7")] FileTalk = 1 << 8,
19 | [StringValue("8")] MediaWiki = 1 << 9,
20 | [StringValue("9")] MediaWikiTalk = 1 << 10,
21 | [StringValue("10")] Template = 1 << 11,
22 | [StringValue("11")] TemplateTalk = 1 << 12,
23 | [StringValue("12")] Help = 1 << 13,
24 | [StringValue("13")] HelpTalk = 1 << 14,
25 | [StringValue("14")] Category = 1 << 15,
26 | [StringValue("15")] CategoryTalk = 1 << 16,
27 | [StringValue("100")] Portal = 1 << 17,
28 | [StringValue("101")] PortalTalk = 1 << 18,
29 | [StringValue("118")] Draft = 1 << 19,
30 | [StringValue("119")] DraftTalk = 1 << 20,
31 | [StringValue("710")] TimedText = 1 << 21,
32 | [StringValue("711")] TimedTextTalk = 1 << 22,
33 | [StringValue("828")] Module = 1 << 23,
34 | [StringValue("829")] ModuleTalk = 1 << 24
35 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiProperty.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | [Flags]
6 | public enum WikiProperty : uint
7 | {
8 | NotSet = 0,
9 |
10 | /// Adds the size of the page in bytes.
11 | [StringValue("size")] Size = 1 << 0,
12 |
13 | /// Adds the word count of the page.
14 | [StringValue("wordcount")] WordCount = 1 << 1,
15 |
16 | /// Adds the timestamp of when the page was last edited.
17 | [StringValue("timestamp")] Timestamp = 1 << 2,
18 |
19 | /// Adds a parsed snippet of the page.
20 | [StringValue("snippet")] Snippet = 1 << 3,
21 |
22 | /// Adds a parsed snippet of the page title.
23 | [StringValue("titlesnippet")] TitleSnippet = 1 << 4,
24 |
25 | /// Adds the title of the matching redirect.
26 | [StringValue("redirecttitle")] RedirectTitle = 1 << 5,
27 |
28 | /// Adds a parsed snippet of the redirect title.
29 | [StringValue("redirectsnippet")] RedirectSnippet = 1 << 6,
30 |
31 | /// Adds the title of the matching section.
32 | [StringValue("sectiontitle")] SectionTitle = 1 << 7,
33 |
34 | /// Adds a parsed snippet of the matching section title.
35 | [StringValue("sectionsnippet")] SectionSnippet = 1 << 8,
36 |
37 | /// Adds a boolean indicating if the search matched file content.
38 | [StringValue("isfilematch")] IsFileMatch = 1 << 9,
39 |
40 | /// Adds a parsed snippet of the matching category.
41 | [StringValue("categorysnippet")] CategorySnippet = 1 << 10,
42 |
43 | /// Adds extra data generated by extensions.
44 | [StringValue("extensiondata")] ExtensionData = 1 << 11,
45 |
46 | ///
47 | /// Include all properties
48 | ///
49 | All = uint.MaxValue
50 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiQueryProfile.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | [Flags]
6 | public enum WikiQueryProfile
7 | {
8 | NotSet = 0,
9 |
10 | /// Ranking based on the number of incoming links, some templates, page language and recency
11 | /// (templates/language/recency may not be activated on this wiki).
12 | [StringValue("classic")] Classic = 1 << 0,
13 |
14 | /// Ranking based on some templates, page language and recency when activated on this wiki.
15 | [StringValue("classic_noboostlinks")] ClassicNoBoostLinks = 1 << 1,
16 |
17 | /// Ranking based solely on query dependent features (for debug only).
18 | [StringValue("empty")] Empty = 1 << 2,
19 |
20 | /// Weighted sum based on incoming links
21 | [StringValue("wsum_inclinks")] WeightedSumIncomingLinks = 1 << 3,
22 |
23 | /// Weighted sum based on incoming links and weekly pageviews
24 | [StringValue("wsum_inclinks_pv")] WeightedSumIncomingLinksAndPageViews = 1 << 4,
25 |
26 | /// Ranking based primarily on page views
27 | [StringValue("popular_inclinks_pv")] PopularIncomingLinksAndPageViews = 1 << 5,
28 |
29 | /// Ranking based primarily on incoming link counts
30 | [StringValue("popular_inclinks")] PopularIncomingLinks = 1 << 6,
31 |
32 | /// Let the search engine decide on the best profile to use.
33 | [StringValue("engine_autoselect")] AutoSelect = 1 << 7
34 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiSortOrder.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | public enum WikiSortOrder
6 | {
7 | NotSet = 0,
8 | [StringValue("create_timestamp_asc")] CreateTimestampAsc,
9 | [StringValue("create_timestamp_desc")] CreateTimestampDesc,
10 | [StringValue("incoming_links_asc")] IncomingLinksAsc,
11 | [StringValue("incoming_links_desc")] IncomingLinksDesc,
12 | [StringValue("just_match")] JustMatch,
13 | [StringValue("last_edit_asc")] LastEditAsc,
14 | [StringValue("last_edit_desc")] LastEditDesc,
15 | [StringValue("none")] None,
16 | [StringValue("random")] Random,
17 | [StringValue("relevance")] Relevance,
18 | [StringValue("user_random")] UserRandom
19 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Enums/WikiWhat.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Internal;
2 |
3 | namespace Genbox.Wikipedia.Enums;
4 |
5 | public enum WikiWhat
6 | {
7 | NotSet = 0,
8 |
9 | /// Search in page titles (default) (if search engine doesn't support title searches, such as Lucene which is used
10 | /// by Wikipedia, then it falls back to text)
11 | [StringValue("title")] Title,
12 |
13 | /// Search in page text
14 | [StringValue("text")] Text,
15 |
16 | /// Search for titles that match exactly. Example: 'Microsoft' results in 'Microsoft', where 'Microsof' results in
17 | /// 'no results'
18 | [StringValue("nearmatch")] NearMatch
19 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Extensions/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Text;
3 | using Genbox.Wikipedia.Internal;
4 |
5 | namespace Genbox.Wikipedia.Extensions;
6 |
7 | public static class EnumExtensions
8 | {
9 | ///
10 | /// Convert a flags enum into a sequence of string values concatenated with |. E.g. Value1|Value2.
11 | ///
12 | public static string GetConcatValues(this Enum value)
13 | {
14 | StringBuilder sb = new StringBuilder();
15 |
16 | foreach (Enum e in Enum.GetValues(value.GetType()))
17 | {
18 | //This library follows a convention where all enums have a NotSet value. We do not want that in our output
19 | if (string.Equals(e.ToString(), "NotSet", StringComparison.OrdinalIgnoreCase))
20 | continue;
21 |
22 | //Same as above
23 | if (string.Equals(e.ToString(), "All", StringComparison.OrdinalIgnoreCase))
24 | continue;
25 |
26 | if (value.HasFlag(e))
27 | sb.Append(e.GetStringValue()).Append('|');
28 | }
29 |
30 | return sb.ToString().TrimEnd('|');
31 | }
32 |
33 | /// Will get the string value for a given enums value, this will only work if you assign the StringValue attribute
34 | /// to the items in your enum. Source:
35 | /// http://weblogs.asp.net/stefansedich/archive/2008/03/12/enum-with-string-values-in-c.aspx
36 | public static string GetStringValue(this Enum value)
37 | {
38 | // Get the type
39 | Type type = value.GetType();
40 |
41 | // Get fieldinfo for this type
42 | FieldInfo fieldInfo = type.GetField(value.ToString());
43 |
44 | // Get the stringvalue attributes
45 | StringValueAttribute[] attr = fieldInfo.GetCustomAttributes(false).ToArray();
46 |
47 | if (attr.Length == 0)
48 | throw new InvalidOperationException("Unable to find StringValue attribute on " + type.Name);
49 |
50 | // Return the first if there was a match.
51 | return attr[0].StringValue;
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Internal/LowerCasePolicy.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 |
3 | namespace Genbox.Wikipedia.Internal;
4 |
5 | internal class LowerCasePolicy : JsonNamingPolicy
6 | {
7 | public static readonly JsonNamingPolicy Instance = new LowerCasePolicy();
8 |
9 | private LowerCasePolicy() { }
10 |
11 | public override string ConvertName(string name)
12 | {
13 | return name.ToLowerInvariant();
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Internal/StringValueAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Genbox.Wikipedia.Internal;
2 |
3 | /// This attribute is used to represent a string value for a value in an enum.
4 | [AttributeUsage(AttributeTargets.Field)]
5 | internal class StringValueAttribute : Attribute
6 | {
7 | /// Constructor used to init a StringValue Attribute
8 | public StringValueAttribute(string value)
9 | {
10 | StringValue = value;
11 | }
12 |
13 | /// Holds the string value for a value in an enum.
14 | public string StringValue { get; }
15 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Internal/UrlHelper.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Text;
3 |
4 | namespace Genbox.Wikipedia.Internal;
5 |
6 | internal static class UrlHelper
7 | {
8 | //Valid URL characters according to RFC3986: https://tools.ietf.org/html/rfc3986#section-2.3
9 | private const string _validUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
10 |
11 | private static readonly HashSet _validUrlLookup = new HashSet(BuildLookup(_validUrlCharacters));
12 |
13 | private static IEnumerable BuildLookup(string charList)
14 | {
15 | foreach (char c in charList)
16 | {
17 | string escaped = Uri.EscapeUriString(c.ToString(CultureInfo.InvariantCulture));
18 | if (escaped.Length == 1 && escaped[0] == c)
19 | yield return (byte)c;
20 | }
21 | }
22 |
23 | public static string UrlPathEncode(string input)
24 | {
25 | string[] pathSegments = input.Split('/');
26 | return string.Join("/", pathSegments.Select(UrlEncode));
27 | }
28 |
29 | public static string UrlEncode(string data)
30 | {
31 | StringBuilder sb = new StringBuilder();
32 |
33 | foreach (byte symbol in Encoding.UTF8.GetBytes(data))
34 | {
35 | if (_validUrlLookup.Contains(symbol))
36 | sb.Append((char)symbol);
37 | else
38 | sb.Append('%').AppendFormat(CultureInfo.InvariantCulture, "{0:X2}", symbol);
39 | }
40 |
41 | return sb.ToString();
42 | }
43 |
44 | public static string CreateQueryString(IEnumerable> parameters, bool encode = true, bool outputEqualOnEmpty = false)
45 | {
46 | StringBuilder sb = new StringBuilder();
47 |
48 | foreach (KeyValuePair item in parameters)
49 | {
50 | if (sb.Length > 0)
51 | sb.Append('&');
52 |
53 | string encodedKey = encode ? UrlEncode(item.Key) : item.Key;
54 |
55 | if (string.IsNullOrEmpty(item.Value))
56 | {
57 | sb.Append(encodedKey);
58 |
59 | if (outputEqualOnEmpty)
60 | sb.Append('=');
61 | }
62 | else
63 | sb.Append(encodedKey).Append('=').Append(encode ? UrlEncode(item.Value) : item.Value);
64 | }
65 |
66 | return sb.ToString();
67 | }
68 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/Continuation.cs:
--------------------------------------------------------------------------------
1 | namespace Genbox.Wikipedia.Objects;
2 |
3 | public class Continuation
4 | {
5 | public int? SROffset { get; set; }
6 | public string? Continue { get; set; }
7 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/Error.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Genbox.Wikipedia.Objects;
4 |
5 | public class Error
6 | {
7 | public string? Code { get; set; }
8 |
9 | public string? Info { get; set; }
10 |
11 | [JsonPropertyName("docref")]
12 | public string? DocumentReference { get; set; }
13 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/ModuleError.cs:
--------------------------------------------------------------------------------
1 | namespace Genbox.Wikipedia.Objects;
2 |
3 | public class ModuleError
4 | {
5 | public string? Code { get; set; }
6 |
7 | public string? Module { get; set; }
8 |
9 | public string? Html { get; set; }
10 |
11 | public string? Text { get; set; }
12 |
13 | public string? Key { get; set; }
14 |
15 | public IList? Params { get; set; }
16 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/QueryResult.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Genbox.Wikipedia.Objects;
4 |
5 | public class QueryResult
6 | {
7 | public QueryResult()
8 | {
9 | SearchResults = new List(0);
10 | }
11 |
12 | [JsonPropertyName("search")]
13 | public List SearchResults { get; set; }
14 | public SearchInfo? SearchInfo { get; set; }
15 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/SearchInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Genbox.Wikipedia.Objects;
2 |
3 | public class SearchInfo
4 | {
5 | public int TotalHits { get; set; }
6 |
7 | public string? Suggestion { get; set; }
8 |
9 | public string? SuggestionSnippet { get; set; }
10 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Objects/SearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Genbox.Wikipedia.Objects;
4 |
5 | public class SearchResult
6 | {
7 | //Default properties
8 | [JsonPropertyName("ns")]
9 | public int Namespace { get; set; }
10 |
11 | public string Title { get; set; } = string.Empty;
12 |
13 | public int PageId { get; set; }
14 |
15 | public int Size { get; set; }
16 |
17 | //Other properties
18 | public int? WordCount { get; set; }
19 |
20 | public DateTimeOffset? TimeStamp { get; set; }
21 |
22 | public string? Snippet { get; set; }
23 |
24 | public string? TitleSnippet { get; set; }
25 |
26 | public string? RedirectTitle { get; set; }
27 |
28 | public string? RedirectSnippet { get; set; }
29 |
30 | public string? SectionTitle { get; set; }
31 |
32 | public string? SectionSnippet { get; set; }
33 |
34 | public bool? IsFileMatch { get; set; }
35 |
36 | public string? CategorySnippet { get; set; }
37 |
38 | public string? ExtensionData { get; set; }
39 |
40 | /// The URI that points to the wikipedia page that contains the title. Note: Normalization of the title occurs
41 | /// automatically.
42 | public Uri Url { get; set; } = null!;
43 | }
--------------------------------------------------------------------------------
/src/Wikipedia/WikiMediaRequest.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Enums;
2 |
3 | namespace Genbox.Wikipedia;
4 |
5 | public abstract class WikiMediaRequest
6 | {
7 | /// Verify that the user is logged in if set to user, not logged in if set to anon, or has the bot user right if
8 | /// bot.
9 | public string? Assert { get; set; }
10 |
11 | /// Verify the current user is the named user.
12 | public string? AssertUser { get; set; }
13 |
14 | /// Include the hostname that served the request in the results.
15 | public bool IncludeServedBy { get; set; }
16 |
17 | /// Include the current timestamp in the result.
18 | public bool IncludeCurrentTimestamp { get; set; }
19 |
20 | /// Include the languages used for uselang and errorlang in the result.
21 | public bool IncludeLanguageUsed { get; set; }
22 |
23 | /// Any value given here will be included in the response. May be used to distinguish requests.
24 | public string? RequestId { get; set; }
25 |
26 | /// Language to use for message translations. action=query&meta=siteinfo with siprop=languages returns a list of
27 | /// language codes, or specify user to use the current user's language preference, or specify content to use this wiki's
28 | /// content language.
29 | public string? LanguageToUse { get; set; }
30 |
31 | /// Variant of the language. Only works if the base language supports variant conversion.
32 | public string? LanguageVariant { get; set; }
33 |
34 | /// Format to use for warning and error text output
35 | public WikiErrorFormat ErrorFormat { get; set; }
36 |
37 | /// Language to use for warnings and errors. action=query&meta=siteinfo with siprop=languages returns a list of
38 | /// language codes, or specify content to use this wiki's content language, or specify uselang to use the same value as the
39 | /// uselang parameter.
40 | public string? ErrorLanguageToUse { get; set; }
41 |
42 | /// If given, error texts will use locally-customized messages from the MediaWiki namespace.
43 | public bool ErrorUseLocalLanguage { get; set; }
44 | }
--------------------------------------------------------------------------------
/src/Wikipedia/WikiSearchRequest.cs:
--------------------------------------------------------------------------------
1 | using Genbox.Wikipedia.Enums;
2 |
3 | namespace Genbox.Wikipedia;
4 |
5 | public class WikiSearchRequest : WikiMediaRequest
6 | {
7 | //See https://www.mediawiki.org/wiki/API:Search
8 |
9 | public WikiSearchRequest(string query)
10 | {
11 | Query = query;
12 | }
13 |
14 | /// Search for page titles or content matching this value. You can use the search string to invoke special search
15 | /// features, depending on what the wiki's search backend implements.
16 | public string Query { get; set; }
17 |
18 | /// The namespace(s) to enumerate. Defaults to .
19 | public WikiNamespace NamespacesToInclude { get; set; }
20 |
21 | /// How many total pages to return. Default: 10, Max: 500
22 | public int Limit { get; set; }
23 |
24 | /// When more results are available, use this to continue. Default: 0
25 | public int Offset { get; set; }
26 |
27 | /// Query independent profile to use (affects ranking algorithm). Defaults to
28 | ///
29 | public WikiQueryProfile QueryIndependentProfile { get; set; }
30 |
31 | /// Which type of search to perform.
32 | public WikiWhat WhatToSearch { get; set; }
33 |
34 | /// What metadata to return. Default: TotalHits, Suggestion
35 | public WikiInfo InfoToInclude { get; set; }
36 |
37 | /// What property to include in the results. Defaults to a combination of snippet, size, word count and timestamp
38 | public WikiProperty PropertiesToInclude { get; set; }
39 |
40 | /// Include InterWiki results in the search, if available.
41 | public bool IncludeInterWikiResults { get; set; }
42 |
43 | /// Enable internal query rewriting. Some search backends can rewrite the query into another which is thought to
44 | /// provide better results, for instance by correcting spelling errors.
45 | public bool EnableRewrites { get; set; }
46 |
47 | /// Set the sort order of returned results. Defaults to Relevance.
48 | public WikiSortOrder SortOrder { get; set; }
49 |
50 | /// What language to use. Default: English (en)
51 | public WikiLanguage WikiLanguage { get; set; }
52 |
53 | public bool TryValidate(out string? message)
54 | {
55 | if (Limit > 500)
56 | {
57 | message = nameof(Limit) + " must be between 1 and 500";
58 | return false;
59 | }
60 |
61 | if (string.IsNullOrEmpty(Query))
62 | {
63 | message = nameof(Query) + " must be set to a value";
64 | return false;
65 | }
66 |
67 | if (WikiLanguage == WikiLanguage.NotSet)
68 | {
69 | message = nameof(WikiLanguage) + " must be set to a valid value";
70 | return false;
71 | }
72 |
73 | message = null;
74 | return true;
75 | }
76 | }
--------------------------------------------------------------------------------
/src/Wikipedia/WikiSearchResponse.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using Genbox.Wikipedia.Objects;
3 |
4 | namespace Genbox.Wikipedia;
5 |
6 | public class WikiSearchResponse
7 | {
8 | public bool BatchComplete { get; set; }
9 | public Continuation? Continue { get; set; }
10 |
11 | [JsonPropertyName("query")]
12 | public QueryResult? QueryResult { get; set; }
13 | public Error? Error { get; set; }
14 |
15 | [JsonPropertyName("errors")]
16 | public IList? ModuleErrors { get; set; }
17 | public string? ServedBy { get; set; }
18 | public string? RequestId { get; set; }
19 |
20 | [JsonPropertyName("errorlang")]
21 | public string? ErrorLanguage { get; set; }
22 |
23 | [JsonPropertyName("uselang")]
24 | public string? Language { get; set; }
25 |
26 | [JsonPropertyName("curtimestamp")]
27 | public DateTimeOffset CurrentTimestamp { get; set; }
28 | }
--------------------------------------------------------------------------------
/src/Wikipedia/Wikipedia.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Wikipedia/WikipediaClient.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using Genbox.Wikipedia.Enums;
3 | using Genbox.Wikipedia.Extensions;
4 | using Genbox.Wikipedia.Internal;
5 | using Genbox.Wikipedia.Objects;
6 |
7 | namespace Genbox.Wikipedia;
8 |
9 | public class WikipediaClient : IDisposable
10 | {
11 | private readonly HttpClient? _httpClient;
12 | private readonly JsonSerializerOptions _options;
13 | private readonly HttpClient? _userClient;
14 |
15 | public WikipediaClient(HttpClient? client = null)
16 | {
17 | _userClient = client;
18 |
19 | if (_userClient == null)
20 | _httpClient = new HttpClient();
21 |
22 | _options = new JsonSerializerOptions();
23 | _options.PropertyNamingPolicy = LowerCasePolicy.Instance;
24 | }
25 |
26 | ///Set to true to use HTTPS instead of HTTP. Defaults to true.
27 | public bool UseTls { get; set; } = true;
28 |
29 | ///The default language to use. Can be overriden on each request.
30 | public WikiLanguage DefaultLanguage { get; set; } = WikiLanguage.English;
31 |
32 | public void Dispose()
33 | {
34 | Dispose(true);
35 | GC.SuppressFinalize(this);
36 | }
37 |
38 | protected virtual void Dispose(bool disposing)
39 | {
40 | if (disposing)
41 | _httpClient?.Dispose();
42 | }
43 |
44 | public async Task SearchAsync(string query, CancellationToken token = default)
45 | {
46 | return await SearchAsync(new WikiSearchRequest(query), token).ConfigureAwait(false);
47 | }
48 |
49 | public async Task SearchAsync(WikiSearchRequest request, CancellationToken token = default)
50 | {
51 | if (request.WikiLanguage == WikiLanguage.NotSet)
52 | request.WikiLanguage = DefaultLanguage;
53 |
54 | HttpClient? client = _userClient ?? _httpClient;
55 |
56 | if (client == null)
57 | throw new InvalidOperationException("Bug check: HttpClient is null");
58 |
59 | using HttpRequestMessage httpReq = CreateHttpRequest(request);
60 | using HttpResponseMessage? httpResp = await client.SendAsync(httpReq, token).ConfigureAwait(false);
61 | using Stream? contentStream = await httpResp.Content.ReadAsStreamAsync().ConfigureAwait(false);
62 |
63 | WikiSearchResponse? searchResp = await JsonSerializer.DeserializeAsync(contentStream, _options, token).ConfigureAwait(false);
64 |
65 | if (searchResp == null)
66 | throw new InvalidOperationException("Unable to read query response");
67 |
68 | QueryResult? query = searchResp.QueryResult;
69 |
70 | //We do this to ensure users are not bothered with nullable responses
71 | if (query == null)
72 | query = new QueryResult();
73 |
74 | //For convenience, we autocreate uris that point directly to the wiki page.
75 | string prefix = UseTls ? "https://" : "http://";
76 |
77 | foreach (SearchResult? search in query.SearchResults)
78 | search.Url = new Uri(prefix + request.WikiLanguage.GetStringValue() + ".wikipedia.org/wiki/" + search.Title);
79 |
80 | return searchResp;
81 | }
82 |
83 | private HttpRequestMessage CreateHttpRequest(WikiSearchRequest searchRequest)
84 | {
85 | if (!searchRequest.TryValidate(out string? message))
86 | throw new ArgumentException(message, nameof(searchRequest));
87 |
88 | //Required
89 | Dictionary queryParams = new Dictionary
90 | {
91 | {"action", "query"},
92 | {"list", "search"}, //See https://www.mediawiki.org/w/api.php?action=help&modules=query%2Bsearch
93 | {"srsearch", searchRequest.Query},
94 | {"format", "json"},
95 | {"formatversion", "2"} //See https://www.mediawiki.org/wiki/API:JSON_version_2
96 | };
97 |
98 | MapWikiMediaRequest(searchRequest, queryParams);
99 |
100 | //Optional
101 | if (searchRequest.NamespacesToInclude != WikiNamespace.NotSet)
102 | queryParams.Add("srnamespace", searchRequest.NamespacesToInclude.GetConcatValues());
103 |
104 | if (searchRequest.Limit != 0)
105 | queryParams.Add("srlimit", searchRequest.Limit.ToString());
106 |
107 | if (searchRequest.Offset != 0)
108 | queryParams.Add("sroffset", searchRequest.Offset.ToString());
109 |
110 | if (searchRequest.QueryIndependentProfile != WikiQueryProfile.NotSet)
111 | queryParams.Add("srqiprofile", searchRequest.QueryIndependentProfile.GetStringValue());
112 |
113 | if (searchRequest.WhatToSearch != WikiWhat.NotSet)
114 | queryParams.Add("srwhat", searchRequest.WhatToSearch.GetStringValue());
115 |
116 | if (searchRequest.InfoToInclude != WikiInfo.NotSet)
117 | queryParams.Add("srinfo", searchRequest.InfoToInclude.GetConcatValues());
118 |
119 | if (searchRequest.PropertiesToInclude != WikiProperty.NotSet)
120 | queryParams.Add("srprop", searchRequest.PropertiesToInclude.GetConcatValues());
121 |
122 | if (searchRequest.IncludeInterWikiResults)
123 | queryParams.Add("srinterwiki", "true");
124 |
125 | if (searchRequest.EnableRewrites)
126 | queryParams.Add("srenablerewrites", "true");
127 |
128 | if (searchRequest.SortOrder != WikiSortOrder.NotSet)
129 | queryParams.Add("srsort", searchRequest.SortOrder.GetStringValue());
130 |
131 | //API example: http://en.wikipedia.org/w/api.php?action=query&list=search&srsearch=wikipedia&srprop=timestamp
132 | Uri baseUrl = new Uri(string.Format(UseTls ? "https://{0}.wikipedia.org/w/" : "http://{0}.wikipedia.org/w/", searchRequest.WikiLanguage.GetStringValue()));
133 |
134 | return new HttpRequestMessage(HttpMethod.Get, new Uri(baseUrl, "api.php?" + UrlHelper.CreateQueryString(queryParams)));
135 | }
136 |
137 | private void MapWikiMediaRequest(WikiMediaRequest request, Dictionary queryParams)
138 | {
139 | if (request.Assert != null)
140 | queryParams.Add("assert", request.Assert);
141 |
142 | if (request.AssertUser != null)
143 | queryParams.Add("assertuser", request.AssertUser);
144 |
145 | if (request.ErrorFormat != WikiErrorFormat.NotSet)
146 | queryParams.Add("errorformat", request.ErrorFormat.GetStringValue());
147 |
148 | if (request.ErrorLanguageToUse != null)
149 | queryParams.Add("errorlang", request.ErrorLanguageToUse);
150 |
151 | if (request.ErrorUseLocalLanguage)
152 | queryParams.Add("errorsuselocal", "true");
153 |
154 | if (request.IncludeCurrentTimestamp)
155 | queryParams.Add("curtimestamp", "true");
156 |
157 | if (request.IncludeLanguageUsed)
158 | queryParams.Add("responselanginfo", "true");
159 |
160 | if (request.IncludeServedBy)
161 | queryParams.Add("servedby", "true");
162 |
163 | if (request.LanguageToUse != null)
164 | queryParams.Add("uselang", request.LanguageToUse);
165 |
166 | if (request.LanguageVariant != null)
167 | queryParams.Add("variant", request.LanguageVariant);
168 |
169 | if (request.RequestId != null)
170 | queryParams.Add("requestid", request.RequestId);
171 | }
172 | }
--------------------------------------------------------------------------------