├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Mastodon.API.Tests ├── ApiClientBaseTest.cs ├── Entities │ ├── AccountTest.cs │ ├── ApplicationTest.cs │ ├── AttachmentTest.cs │ ├── CardTest.cs │ ├── ErrorTest.cs │ ├── InstanceTest.cs │ ├── MastodonAppTest.cs │ ├── MentionTest.cs │ ├── OAuthAccessScopeTest.cs │ ├── RelationshipTest.cs │ ├── ReportTest.cs │ ├── StatusTest.cs │ ├── StatusVisibilityTest.cs │ ├── TagTest.cs │ └── TokenTest.cs ├── ExtensionsTest.cs ├── LinkTest.cs ├── Mastodon.API.Tests.csproj ├── MastodonApiTest.cs ├── Mocks │ ├── MockHttpClient.cs │ └── MockHttpMessageHandler.cs ├── Resources │ ├── get_account.json │ ├── get_application.json │ ├── get_attachment.json │ ├── get_card.json │ ├── get_error.json │ ├── get_instance.json │ ├── get_mastodon_app.json │ ├── get_mention.json │ ├── get_relationship.json │ ├── get_report.json │ ├── get_status.json │ ├── get_tag.json │ ├── get_token.json │ ├── next_and_prev_link │ ├── next_link │ └── prev_link ├── TestUtils.cs └── packages.config ├── Mastodon.API.sln ├── Mastodon.API ├── ApiClientBase.cs ├── Entities │ ├── Account.cs │ ├── Application.cs │ ├── Attachment.cs │ ├── Card.cs │ ├── Context.cs │ ├── Error.cs │ ├── Instance.cs │ ├── MastodonApp.cs │ ├── Mention.cs │ ├── Notification.cs │ ├── OAuthAccessScope.cs │ ├── Relationship.cs │ ├── Report.cs │ ├── Results.cs │ ├── Status.cs │ ├── StatusVisibility.cs │ ├── Tag.cs │ └── Token.cs ├── Extensions.cs ├── IMastodonApi.cs ├── Link.cs ├── Mastodon.API.csproj ├── MastodonApi.cs ├── MastodonApiConfig.cs ├── MastodonApiException.cs ├── MastodonAuthClient.cs ├── Properties │ └── AssemblyInfo.cs ├── Response.cs └── packages.config ├── README.md └── appveyor.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # nuget 5 | *.nupkg 6 | *.nuspec 7 | 8 | # build 9 | [Oo]bj/ 10 | [Bb]in/ 11 | packages/ 12 | TestResults/ 13 | 14 | # globs 15 | Makefile.in 16 | *.DS_Store 17 | *.sln.cache 18 | *.suo 19 | *.cache 20 | *.pidb 21 | *.userprefs 22 | *.usertasks 23 | config.log 24 | config.make 25 | config.status 26 | aclocal.m4 27 | install-sh 28 | autom4te.cache/ 29 | *.user 30 | *.tar.gz 31 | tarballs/ 32 | test-results/ 33 | Thumbs.db 34 | 35 | # Mac bundle stuff 36 | *.dmg 37 | *.app 38 | 39 | # resharper 40 | *_Resharper.* 41 | *.Resharper 42 | 43 | # dotCover 44 | *.dotCover 45 | 46 | # VS 2017 47 | .vs -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | solution: Mastodon.API.sln 3 | install: 4 | - nuget restore Mastodon.API.sln 5 | - nuget install NUnit.Runners -Version 2.6.4 -OutputDirectory testrunner 6 | script: 7 | - xbuild /p:Configuration=Release Mastodon.API.sln 8 | - mono ./testrunner/NUnit.Runners.2.6.4/tools/nunit-console.exe ./Mastodon.API.Tests/bin/Release/Mastodon.API.Tests.dll 9 | 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGE LOG 2 | 3 | ## 0.4.0 4 | ### BREAKING CHANGES 5 | 6 | * Add static factory method to `Tag` and `Token` entities. ([#37](https://github.com/pawotter/mastodon-api-cs/pull/37))$ 7 | * Rename all the static factory methods of entities from `create` to `Create`. ([#35](https://github.com/pawotter/mastodon-api-cs/pull/35))$ 8 | * Rename method `OAuthAccessScope.of` to `OAuthAccessScope.Of`. ([#36](https://github.com/pawotter/mastodon-api-cs/pull/36))$ 9 | 10 | ## 0.3.0 11 | ### BREAKING CHANGES 12 | 13 | * All error responses are thrown as the type MastodonApiException. ([#26](https://github.com/pawotter/mastodon-api-cs/pull/26))$ 14 | 15 | ## 0.2.1 16 | ### Features 17 | 18 | * The constructor parameter of MastodonAuthClient and MastodonApi had become optional. ([#23](https://github.com/pawotter/mastodon-api-cs/pull/23)) 19 | 20 | ## 0.2.0 21 | ### Anomalies 22 | 23 | * Fix a problem of deserializing Account entity. Avatar and AvatarStatic properties could not derived from JSON. But now fiexed. ([#20](https://github.com/pawotter/mastodon-api-cs/pull/20)) 24 | * Fix a problem of deserializing Header/HeaderStatic properties of Account entity. ([#20](https://github.com/pawotter/mastodon-api-cs/pull/20)) 25 | 26 | ### BREAKING CHANGES 27 | 28 | * All the contructors of entities had been replaced to static factory methods `Create`. ([#22](https://github.com/pawotter/mastodon-api-cs/pull/22)) 29 | * The type of Header/AvaterHeader properties of Account was changed from Uri to string. ([#20](https://github.com/pawotter/mastodon-api-cs/pull/20)) 30 | 31 | ## 0.1.4 32 | ### Features 33 | 34 | * Add Link property to Response class ([#8](https://github.com/pawotter/mastodon-api-cs/pull/8)) 35 | * this enabled getting next/prev link from array response easily. 36 | 37 | ## 0.1.3 38 | ### Anomalies 39 | 40 | * Regression bug fix for pull request [#2](https://github.com/pawotter/mastodon-api-cs/pull/2) ((#7)[https://github.com/pawotter/mastodon-api-cs/pull/7]) 41 | 42 | ## 0.1.2 43 | ### Anomalies 44 | 45 | * Fix a bug of endpoint url([#5](https://github.com/pawotter/mastodon-api-cs/pull/5)) 46 | 47 | ## 0.1.1 48 | ### BREAKING CHANGES 49 | 50 | * Use StatusVisibility instead of string in Visibility field of Status class. 51 | 52 | ## 0.1.0 53 | 54 | Release. 55 | 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 gomi_ningen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/ApiClientBaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Http; 6 | using Mastodon.API.Tests.Mocks; 7 | 8 | 9 | namespace Mastodon.API.Tests 10 | { 11 | [TestFixture] 12 | public class ApiClientBaseTest 13 | { 14 | [Test] 15 | public void CreateUriTestWithIListParams() 16 | { 17 | var expected = new Uri("https://friends.nico/api/v1/path/to/endpoint?id[]=1&id[]=2&id[]=3"); 18 | var baseUrl = new Uri("https://friends.nico/"); 19 | var parameters = new List> 20 | { 21 | new KeyValuePair("id[]", "1"), 22 | new KeyValuePair("id[]", "2"), 23 | new KeyValuePair("id[]", "3") 24 | }; 25 | var path = "/api/v1/path/to/endpoint"; 26 | var actual = ApiClientBase.CreateUrl(baseUrl, path, parameters); 27 | Assert.AreEqual(expected, actual); 28 | } 29 | 30 | [Test] 31 | public void CreateUriTestWithIDictionary() 32 | { 33 | var expected = new Uri("https://friends.nico/api/v1/path/to/endpoint?id=hoge&arg1=fuga&arg2=piyo"); 34 | var baseUrl = new Uri("https://friends.nico/"); 35 | var parameters = new Dictionary 36 | { 37 | { "id", "hoge" }, 38 | { "arg1", "fuga" }, 39 | { "arg2", "piyo" } 40 | }; 41 | var path = "/api/v1/path/to/endpoint"; 42 | var actual = ApiClientBase.CreateUrl(baseUrl, path, parameters); 43 | Assert.AreEqual(expected, actual); 44 | } 45 | 46 | [Test] 47 | public void CreateUriTestWithNoParams() 48 | { 49 | var expected = new Uri("https://friends.nico/api/v1/path/to/endpoint"); 50 | var baseUrl = new Uri("https://friends.nico/"); 51 | var path = "/api/v1/path/to/endpoint"; 52 | var actual = ApiClientBase.CreateUrl(baseUrl, path, null); 53 | Assert.AreEqual(expected, actual); 54 | } 55 | 56 | [Test] 57 | public void CreateUriTestWithNoPath() 58 | { 59 | var expected = new Uri("https://friends.nico/"); 60 | var baseUrl = new Uri("https://friends.nico/"); 61 | var actual = ApiClientBase.CreateUrl(baseUrl, null, null); 62 | Assert.AreEqual(expected, actual); 63 | } 64 | 65 | [Test] 66 | public async void CheckResponseWithStatusOKReturnsSameResponse() 67 | { 68 | var testResponse = new HttpResponseMessage() 69 | { 70 | StatusCode = HttpStatusCode.OK 71 | }; 72 | 73 | var result = await ApiClientBase.CheckResponse(testResponse); 74 | Assert.AreSame(testResponse, result); 75 | } 76 | 77 | [Test] 78 | public void CheckResponseWithFailStatusAndErrorBodyThrowsMastodonApiExceptionWithParsedError() 79 | { 80 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_error.json"); 81 | var expectedError = new Error("Record not found"); 82 | var testResponse = new HttpResponseMessage() 83 | { 84 | StatusCode = HttpStatusCode.NotFound, 85 | Content = new StringContent(jsonString) 86 | }; 87 | Assert.That(async () => await ApiClientBase.CheckResponse(testResponse), 88 | Throws.Exception.TypeOf() 89 | .With.Property("Error").EqualTo(expectedError) 90 | .With.Property("StatusCode").EqualTo(HttpStatusCode.NotFound)); 91 | } 92 | 93 | [Test] 94 | public void CheckResponseWithFailStatusButNoParsableErrorBodyThrowsMastodonApiExceptionWithBodyInMessage() 95 | { 96 | var errorMessage = "History eraser button pressed"; 97 | var testResponse = new HttpResponseMessage() 98 | { 99 | StatusCode = HttpStatusCode.BadRequest, 100 | Content = new StringContent(errorMessage) 101 | }; 102 | Assert.That(async () => await ApiClientBase.CheckResponse(testResponse), 103 | Throws.Exception.TypeOf() 104 | .With.Property("Message").EqualTo($"Unexpected error returned from server: {errorMessage}") 105 | .With.Property("StatusCode").EqualTo(HttpStatusCode.BadRequest)); 106 | } 107 | 108 | [Test] 109 | public async void GetAsyncWithStatusAndMessage() 110 | { 111 | var message = "Hello"; 112 | var statusCode = HttpStatusCode.OK; 113 | var mockHttp = MockHttpClient.Create(message, statusCode); 114 | var apiClient = new ApiClientBase(new Uri("http://example.com/"), mockHttp); 115 | 116 | var response = await apiClient.GetAsync("/test"); 117 | 118 | Assert.AreEqual(message, await response.Content.ReadAsStringAsync()); 119 | Assert.AreEqual(statusCode, response.StatusCode); 120 | } 121 | 122 | [Test] 123 | public void GetAsyncWithBadRequest() 124 | { 125 | var message = "This was bad"; 126 | var statusCode = HttpStatusCode.BadRequest; 127 | var mockHttp = MockHttpClient.Create(message, statusCode); 128 | var apiClient = new ApiClientBase(new Uri("http://example.com/"), mockHttp); 129 | 130 | Assert.Throws(async () => await apiClient.GetAsync("/test")); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/AccountTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using NUnit.Framework; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class AccountTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_account.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Account.Create( 16 | "29", 17 | "gomi_ningen", 18 | "gomi_ningen", 19 | "ゴミ人間 ✅", 20 | false, 21 | "2017-04-19T06:38:32.390Z", 22 | 147, 23 | 105, 24 | 165, 25 | "https://twitter.com/gomi_ningen 木組みの街のSIerラビットハウス社で住み込みバイトしているエンジニアです。", 26 | new Uri("https://friends.nico/@gomi_ningen"), 27 | new Uri("https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071"), 28 | new Uri("https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071"), 29 | "/headers/original/missing.png", 30 | "/headers/original/missing.png" 31 | ); 32 | Assert.AreEqual(expected, actual); 33 | actual.GetHashCode(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/ApplicationTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | using System; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class ApplicationTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_application.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Application.Create("Amaroq", new Uri("https://appsto.re/us/OfFxib.i")); 16 | Assert.AreEqual(expected, actual); 17 | actual.GetHashCode(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/AttachmentTest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using NUnit.Framework; 3 | using System; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class AttachmentTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_attachment.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Attachment.Create( 16 | "39071", 17 | "image", 18 | new Uri("https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/original/1a37136a4fe604b1.png?1492771668"), 19 | new Uri("https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/original/1a37136a4fe604b1.png?1492771668"), 20 | new Uri("https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/small/1a37136a4fe604b1.png?1492771668"), 21 | new Uri("https://friends.nico/media/NB4b8WwFtCxC2ynwZis") 22 | ); 23 | Assert.AreEqual(expected, actual); 24 | actual.GetHashCode(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/CardTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | using System; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class CardTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_card.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Card.Create( 16 | new Uri("http://seiga.nicovideo.jp/comic/20782"), 17 | "ご注文はうさぎですか? / Koi", 18 | "喫茶ラビットハウスにやってきたココア。 実は、そこが彼女が住み込むことになっていた喫茶店で…。 すべて…", 19 | new Uri("https://d2zoeobnny43zx.cloudfront.net/preview_cards/images/000/001/769/original/ogp_alternative.jpg?1492773335") 20 | ); 21 | Assert.AreEqual(expected, actual); 22 | actual.GetHashCode(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/ErrorTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | using System; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class ErrorTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_error.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = new Error("Record not found"); 16 | Assert.AreEqual(expected, actual); 17 | actual.GetHashCode(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/InstanceTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using Newtonsoft.Json; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class InstanceTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_instance.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Instance.Create("friends.nico", "friends.nico", "test", "test@test.nico"); 16 | Assert.AreEqual(expected, actual); 17 | actual.GetHashCode(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/MastodonAppTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using NUnit.Framework; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class MastodonAppTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_mastodon_app.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = MastodonApp.Create( 16 | "0", 17 | "client_id", 18 | "client_secret", 19 | new Uri("urn:ietf:wg:oauth:2.0:oob") 20 | ); 21 | Assert.AreEqual(expected, actual); 22 | actual.GetHashCode(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/MentionTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using NUnit.Framework; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class MentionTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_mention.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Mention.Create( 16 | new Uri("https://friends.nico/@gomi_ningen"), 17 | "gomi_ningen", 18 | "gomi_ningen", 19 | "29" 20 | ); 21 | Assert.AreEqual(expected, actual); 22 | actual.GetHashCode(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/OAuthAccessScopeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace Mastodon.API.Tests 5 | { 6 | [TestFixture] 7 | public class OAuthAccessScopeTest 8 | { 9 | [Test] 10 | public void ValueTest() 11 | { 12 | Assert.AreEqual("read", OAuthAccessScope.Of(OAtuhAccessScopeType.Read).Value); 13 | Assert.AreEqual("write", OAuthAccessScope.Of(OAtuhAccessScopeType.Write).Value); 14 | Assert.AreEqual("follow", OAuthAccessScope.Of(OAtuhAccessScopeType.Follow).Value); 15 | Assert.AreEqual("read", OAuthAccessScope.Of(OAtuhAccessScopeType.Read, OAtuhAccessScopeType.Read).Value); 16 | Assert.AreEqual("read write", OAuthAccessScope.Of(OAtuhAccessScopeType.Read, OAtuhAccessScopeType.Write).Value); 17 | Assert.AreEqual("read write follow", OAuthAccessScope.Of(OAtuhAccessScopeType.Read, OAtuhAccessScopeType.Write, OAtuhAccessScopeType.Follow).Value); 18 | Assert.AreEqual("read write follow", OAuthAccessScope.Of(OAtuhAccessScopeType.Read, OAtuhAccessScopeType.Write, OAtuhAccessScopeType.Follow, OAtuhAccessScopeType.Read).Value); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/RelationshipTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.IO; 3 | using System.Reflection; 4 | using Newtonsoft.Json; 5 | 6 | namespace Mastodon.API.Tests 7 | { 8 | [TestFixture] 9 | public class RelationshipTest 10 | { 11 | [Test] 12 | public void DeserializeTest() 13 | { 14 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_relationship.json"); 15 | var actual = JsonConvert.DeserializeObject(jsonString); 16 | var expected = Relationship.Create("29", true, true, true, true, true); 17 | Assert.AreEqual(expected, actual); 18 | actual.GetHashCode(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/ReportTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.IO; 3 | using System.Reflection; 4 | using Newtonsoft.Json; 5 | 6 | namespace Mastodon.API.Tests 7 | { 8 | [TestFixture] 9 | public class ReportTest 10 | { 11 | [Test] 12 | public void DeserializeTest() 13 | { 14 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_report.json"); 15 | var actual = JsonConvert.DeserializeObject(jsonString); 16 | var expected = Report.Create("101", true); 17 | Assert.AreEqual(expected, actual); 18 | actual.GetHashCode(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/StatusTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace Mastodon.API.Tests 7 | { 8 | [TestFixture] 9 | public class StatusTest 10 | { 11 | [Test] 12 | public void DeserializeTest() 13 | { 14 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_status.json"); 15 | var actual = JsonConvert.DeserializeObject(jsonString); 16 | var expected = Status.Create( 17 | "622216", 18 | "tag:friends.nico,2017-04-21:objectId=622216:objectType=Status", 19 | new Uri("https://friends.nico/@gomi_ningen/622216"), 20 | Account.Create( 21 | "29", 22 | "gomi_ningen", 23 | "gomi_ningen", 24 | "ゴミ人間 ✅", 25 | false, 26 | "2017-04-19T06:38:32.390Z", 27 | 165, 28 | 115, 29 | 204, 30 | "https://twitter.com/gomi_ningen 木組みの街のSIerラビットハウス社で住み込みバイトしているエンジニアです。", 31 | new Uri("https://friends.nico/@gomi_ningen"), 32 | new Uri("https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071"), 33 | new Uri("https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071"), 34 | "/headers/original/missing.png", 35 | "/headers/original/missing.png" 36 | ), 37 | null, 38 | null, 39 | null, 40 | "

アプリから投稿テスト

", 41 | "2017-04-21T11:47:31.293Z", 42 | 0, 43 | 2, 44 | false, 45 | false, 46 | null, 47 | "", 48 | StatusVisibility.Unlisted, 49 | new List(), 50 | new List(), 51 | new List(), 52 | Application.Create( 53 | "Amaroq", 54 | new Uri("https://appsto.re/us/OfFxib.i")) 55 | ); 56 | Assert.AreEqual(expected, actual); 57 | actual.GetHashCode(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/StatusVisibilityTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using Newtonsoft.Json; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | struct StatusVisibilityTestStruct 8 | { 9 | [JsonProperty(PropertyName = "v")] 10 | public StatusVisibility V { get; set; } 11 | } 12 | 13 | [TestFixture] 14 | public class StatusVisibilityTest 15 | { 16 | [Test] 17 | public void Deserialize() 18 | { 19 | var vPrivate = JsonConvert.DeserializeObject("{\"v\":\"private\"}"); 20 | Assert.AreEqual(StatusVisibility.Private, vPrivate.V); 21 | var vPublic = JsonConvert.DeserializeObject("{\"v\":\"public\"}"); 22 | Assert.AreEqual(StatusVisibility.Public, vPublic.V); 23 | var vUnlisted = JsonConvert.DeserializeObject("{\"v\":\"unlisted\"}"); 24 | Assert.AreEqual(StatusVisibility.Unlisted, vUnlisted.V); 25 | var vDirect = JsonConvert.DeserializeObject("{\"v\":\"direct\"}"); 26 | Assert.AreEqual(StatusVisibility.Direct, vDirect.V); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/TagTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | using System; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class TagTest 9 | { 10 | [Test] 11 | public void DeserializeTest() 12 | { 13 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_tag.json"); 14 | var actual = JsonConvert.DeserializeObject(jsonString); 15 | var expected = Tag.Create("test1", new Uri("https://friends.nico/tags/test1")); 16 | Assert.AreEqual(expected, actual); 17 | actual.GetHashCode(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Entities/TokenTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API.Tests 5 | { 6 | [TestFixture] 7 | public class TokenTest 8 | { 9 | [Test] 10 | public void DeserializeTest() 11 | { 12 | var jsonString = TestUtils.GetResource("Mastodon.API.Tests.Resources.get_token.json"); 13 | var actual = JsonConvert.DeserializeObject(jsonString); 14 | var expected = Token.Create("aiueo", "bearer", "read", "1492791762"); 15 | Assert.AreEqual(expected, actual); 16 | actual.GetHashCode(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/ExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System.Collections.Generic; 3 | 4 | namespace Mastodon.API.Tests 5 | { 6 | [TestFixture] 7 | public class ExtensionsTest 8 | { 9 | [Test] 10 | public void AsQueryStringTest() 11 | { 12 | var ps = new Dictionary 13 | { 14 | {"x1", "y1"}, 15 | {"x2", "y2"}, 16 | {"x3", "y3"} 17 | }; 18 | var expected = "?x1=y1&x2=y2&x3=y3"; 19 | Assert.AreEqual(expected, ps.AsQueryString()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/LinkTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | using System.Net.Http.Headers; 4 | using System.Net; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text.RegularExpressions; 8 | using System.Reflection; 9 | 10 | namespace Mastodon.API.Tests 11 | { 12 | [TestFixture] 13 | public class LinkTest 14 | { 15 | [Test] 16 | public void CreateNextAndPrevLinkTest() 17 | { 18 | var str = TestUtils.GetResource("Mastodon.API.Tests.Resources.next_and_prev_link"); 19 | var link = Link.CreateLinkFromHeaderLinkValue(str); 20 | Assert.NotNull(link); 21 | Assert.AreEqual(1860868, link.Value.MaxId); 22 | Assert.AreEqual(1874433, link.Value.SinceId); 23 | } 24 | 25 | [Test] 26 | public void CreateNextLinkTest() 27 | { 28 | var str = TestUtils.GetResource("Mastodon.API.Tests.Resources.next_link"); 29 | var link = Link.CreateLinkFromHeaderLinkValue(str); 30 | Assert.NotNull(link); 31 | Assert.AreEqual(1860868, link.Value.MaxId); 32 | Assert.Null(link.Value.SinceId); 33 | } 34 | 35 | [Test] 36 | public void CreatePrevLinkTest() 37 | { 38 | var str = TestUtils.GetResource("Mastodon.API.Tests.Resources.prev_link"); 39 | var link = Link.CreateLinkFromHeaderLinkValue(str); 40 | Assert.NotNull(link); 41 | Assert.Null(link.Value.MaxId); 42 | Assert.AreEqual(1874433, link.Value.SinceId); 43 | } 44 | 45 | [Test] 46 | public void CreateLinkTestWithEmptyString() 47 | { 48 | var str = ""; 49 | var link = Link.CreateLinkFromHeaderLinkValue(str); 50 | Assert.Null(link); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Mastodon.API.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {080A956E-17EC-444C-9716-205E11D7B4D9} 7 | Library 8 | Mastodon.API.Tests 9 | Mastodon.API.Tests 10 | v4.5 11 | 0.4.0 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug 18 | DEBUG; 19 | prompt 20 | 4 21 | 22 | 23 | true 24 | bin\Release 25 | prompt 26 | 4 27 | 28 | 29 | 30 | 31 | ..\packages\NUnit.2.6.4\lib\nunit.framework.dll 32 | 33 | 34 | ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {BBF68570-AF0D-4440-85C4-0094876580BF} 68 | Mastodon.API 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/MastodonApiTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using NUnit.Framework; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | [TestFixture] 8 | public class MastodonApiTest 9 | { 10 | //[Test] 11 | //public void Test() 12 | //{ 13 | // var http = new HttpClient(); 14 | // var config = new MastodonApiConfig(new Uri("https://friends.nico"), ""); 15 | // var api = new MastodonApi(config, http); 16 | // var account = api.GetFollowers("29", limit: 13).Result; 17 | // Console.WriteLine(account.Length); 18 | //} 19 | 20 | [Test] 21 | public void GetDefaultHttpClient_DoesNotReturnNull() 22 | { 23 | Assert.NotNull(MastodonApi.GetDefaultHttpClient()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Mocks/MockHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Net; 7 | using System.Net.Http; 8 | 9 | namespace Mastodon.API.Tests.Mocks 10 | { 11 | class MockHttpClient 12 | { 13 | /// 14 | /// Will create an HttpClient using the MockHttpMessageHandler to force the client to ALWAYS return the 15 | /// same message. Can be good for testing single calls to force certain execution paths without having 16 | /// to touch the real web server 17 | /// 18 | /// Message that will be returned in the response 19 | /// The standard HTTP Status Code that will be returned 20 | /// 21 | public static HttpClient Create(string message, HttpStatusCode statusCode) 22 | { 23 | return new HttpClient(new MockHttpMessageHandler(message, statusCode)); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Mocks/MockHttpMessageHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Net; 7 | using System.Net.Http; 8 | using System.Threading; 9 | 10 | namespace Mastodon.API.Tests.Mocks 11 | { 12 | class MockHttpMessageHandler : HttpClientHandler 13 | { 14 | private readonly HttpResponseMessage _response; 15 | 16 | public MockHttpMessageHandler(string message, HttpStatusCode statusCode) 17 | { 18 | _response = new HttpResponseMessage() 19 | { 20 | Content = new StringContent(message), 21 | StatusCode = statusCode 22 | }; 23 | } 24 | 25 | protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 26 | { 27 | return Task.FromResult(_response); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 29, 3 | "username": "gomi_ningen", 4 | "acct": "gomi_ningen", 5 | "display_name": "ゴミ人間 ✅", 6 | "locked": false, 7 | "created_at": "2017-04-19T06:38:32.390Z", 8 | "followers_count": 147, 9 | "following_count": 105, 10 | "statuses_count": 165, 11 | "note": "https://twitter.com/gomi_ningen 木組みの街のSIerラビットハウス社で住み込みバイトしているエンジニアです。", 12 | "url": "https://friends.nico/@gomi_ningen", 13 | "avatar": "https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071", 14 | "avatar_static": "https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071", 15 | "header": "/headers/original/missing.png", 16 | "header_static": "/headers/original/missing.png", 17 | "nico_url": "http://www.nicovideo.jp/user/2316023" 18 | } 19 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Amaroq", 3 | "website": "https://appsto.re/us/OfFxib.i" 4 | } 5 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_attachment.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 39071, 3 | "remote_url": "https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/original/1a37136a4fe604b1.png?1492771668", 4 | "type": "image", 5 | "url": "https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/original/1a37136a4fe604b1.png?1492771668", 6 | "preview_url": "https://d2zoeobnny43zx.cloudfront.net/media_attachments/files/000/039/071/small/1a37136a4fe604b1.png?1492771668", 7 | "text_url": "https://friends.nico/media/NB4b8WwFtCxC2ynwZis" 8 | } 9 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_card.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "http://seiga.nicovideo.jp/comic/20782", 3 | "title": "ご注文はうさぎですか? / Koi", 4 | "description": "喫茶ラビットハウスにやってきたココア。 実は、そこが彼女が住み込むことになっていた喫茶店で…。 すべて…", 5 | "image": "https://d2zoeobnny43zx.cloudfront.net/preview_cards/images/000/001/769/original/ogp_alternative.jpg?1492773335" 6 | } 7 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_error.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": "Record not found" 3 | } 4 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_instance.json: -------------------------------------------------------------------------------- 1 | { 2 | "uri": "friends.nico", 3 | "title": "friends.nico", 4 | "description": "test", 5 | "email": "test@test.nico" 6 | } 7 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_mastodon_app.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 0, 3 | "redirect_uri": "urn:ietf:wg:oauth:2.0:oob", 4 | "client_id": "client_id", 5 | "client_secret": "client_secret" 6 | } 7 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_mention.json: -------------------------------------------------------------------------------- 1 | { 2 | "url": "https://friends.nico/@gomi_ningen", 3 | "acct": "gomi_ningen", 4 | "id": 29, 5 | "username": "gomi_ningen" 6 | } 7 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_relationship.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 29, 3 | "following": true, 4 | "followed_by": true, 5 | "blocking": true, 6 | "muting": true, 7 | "requested": true 8 | } 9 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_report.json: -------------------------------------------------------------------------------- 1 |  { 2 | "id": 101, 3 | "action_taken": true 4 | } 5 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 622216, 3 | "created_at": "2017-04-21T11:47:31.293Z", 4 | "in_reply_to_id": null, 5 | "in_reply_to_account_id": null, 6 | "sensitive": null, 7 | "spoiler_text": "", 8 | "visibility": "unlisted", 9 | "application": { 10 | "name": "Amaroq", 11 | "website": "https://appsto.re/us/OfFxib.i" 12 | }, 13 | "account": { 14 | "id": 29, 15 | "username": "gomi_ningen", 16 | "acct": "gomi_ningen", 17 | "display_name": "ゴミ人間 ✅", 18 | "locked": false, 19 | "created_at": "2017-04-19T06:38:32.390Z", 20 | "followers_count": 165, 21 | "following_count": 115, 22 | "statuses_count": 204, 23 | "note": "https://twitter.com/gomi_ningen 木組みの街のSIerラビットハウス社で住み込みバイトしているエンジニアです。", 24 | "url": "https://friends.nico/@gomi_ningen", 25 | "avatar": "https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071", 26 | "avatar_static": "https://d2zoeobnny43zx.cloudfront.net/accounts/avatars/000/000/029/original/bc840deef1c57f8f.png?1492587071", 27 | "header": "/headers/original/missing.png", 28 | "header_static": "/headers/original/missing.png", 29 | "nico_url": "http://www.nicovideo.jp/user/2316023" 30 | }, 31 | "media_attachments": [], 32 | "mentions": [], 33 | "tags": [], 34 | "uri": "tag:friends.nico,2017-04-21:objectId=622216:objectType=Status", 35 | "content": "

アプリから投稿テスト

", 36 | "url": "https://friends.nico/@gomi_ningen/622216", 37 | "reblogs_count": 0, 38 | "favourites_count": 2, 39 | "reblog": null, 40 | "favourited": false, 41 | "reblogged": false 42 | } 43 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test1", 3 | "url": "https://friends.nico/tags/test1" 4 | } 5 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/get_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token":"aiueo", 3 | "token_type":"bearer", 4 | "scope":"read", 5 | "created_at":1492791762 6 | } 7 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/next_and_prev_link: -------------------------------------------------------------------------------- 1 | ; rel="next", ; rel="prev" 2 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/next_link: -------------------------------------------------------------------------------- 1 | ; rel="next" 2 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/Resources/prev_link: -------------------------------------------------------------------------------- 1 | ; rel="prev" 2 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace Mastodon.API.Tests 6 | { 7 | static class TestUtils 8 | { 9 | internal static string GetResource(string resourceName) 10 | { 11 | var assembly = typeof(AccountTest).GetTypeInfo().Assembly; 12 | var stream = assembly.GetManifestResourceStream(resourceName); 13 | string text = ""; 14 | using (var reader = new StreamReader(stream)) 15 | { 16 | text = reader.ReadToEnd(); 17 | } 18 | return text; 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Mastodon.API.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Mastodon.API.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mastodon.API", "Mastodon.API\Mastodon.API.csproj", "{BBF68570-AF0D-4440-85C4-0094876580BF}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mastodon.API.Tests", "Mastodon.API.Tests\Mastodon.API.Tests.csproj", "{080A956E-17EC-444C-9716-205E11D7B4D9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {BBF68570-AF0D-4440-85C4-0094876580BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BBF68570-AF0D-4440-85C4-0094876580BF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BBF68570-AF0D-4440-85C4-0094876580BF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BBF68570-AF0D-4440-85C4-0094876580BF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {080A956E-17EC-444C-9716-205E11D7B4D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {080A956E-17EC-444C-9716-205E11D7B4D9}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {080A956E-17EC-444C-9716-205E11D7B4D9}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {080A956E-17EC-444C-9716-205E11D7B4D9}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(MonoDevelopProperties) = preSolution 24 | Policies = $0 25 | $0.TextStylePolicy = $1 26 | $1.FileWidth = 500 27 | $1.TabWidth = 2 28 | $1.IndentWidth = 2 29 | $1.NoTabsAfterNonTabs = True 30 | $1.scope = application/json 31 | $1.TabsToSpaces = True 32 | $0.DotNetNamingPolicy = $2 33 | $0.TextStylePolicy = $3 34 | $3.FileWidth = 200 35 | $3.NoTabsAfterNonTabs = True 36 | $3.scope = text/x-csharp 37 | $3.TabsToSpaces = True 38 | $0.CSharpFormattingPolicy = $4 39 | $4.scope = text/x-csharp 40 | $0.TextStylePolicy = $5 41 | $5.inheritsSet = null 42 | $5.scope = application/xml 43 | $0.XmlFormattingPolicy = $6 44 | $6.scope = application/xml 45 | $0.StandardHeader = $7 46 | $0.NameConventionPolicy = $8 47 | $8.Rules = $9 48 | $9.NamingRule = $10 49 | $10.Name = Namespaces 50 | $10.AffectedEntity = Namespace 51 | $10.VisibilityMask = VisibilityMask 52 | $10.NamingStyle = PascalCase 53 | $10.IncludeInstanceMembers = True 54 | $10.IncludeStaticEntities = True 55 | $9.NamingRule = $11 56 | $11.Name = Types 57 | $11.AffectedEntity = Class, Struct, Enum, Delegate 58 | $11.VisibilityMask = Public 59 | $11.NamingStyle = PascalCase 60 | $11.IncludeInstanceMembers = True 61 | $11.IncludeStaticEntities = True 62 | $9.NamingRule = $12 63 | $12.Name = Interfaces 64 | $12.RequiredPrefixes = $13 65 | $13.String = I 66 | $12.AffectedEntity = Interface 67 | $12.VisibilityMask = Public 68 | $12.NamingStyle = PascalCase 69 | $12.IncludeInstanceMembers = True 70 | $12.IncludeStaticEntities = True 71 | $9.NamingRule = $14 72 | $14.Name = Attributes 73 | $14.RequiredSuffixes = $15 74 | $15.String = Attribute 75 | $14.AffectedEntity = CustomAttributes 76 | $14.VisibilityMask = Public 77 | $14.NamingStyle = PascalCase 78 | $14.IncludeInstanceMembers = True 79 | $14.IncludeStaticEntities = True 80 | $9.NamingRule = $16 81 | $16.Name = Event Arguments 82 | $16.RequiredSuffixes = $17 83 | $17.String = EventArgs 84 | $16.AffectedEntity = CustomEventArgs 85 | $16.VisibilityMask = Public 86 | $16.NamingStyle = PascalCase 87 | $16.IncludeInstanceMembers = True 88 | $16.IncludeStaticEntities = True 89 | $9.NamingRule = $18 90 | $18.Name = Exceptions 91 | $18.RequiredSuffixes = $19 92 | $19.String = Exception 93 | $18.AffectedEntity = CustomExceptions 94 | $18.VisibilityMask = VisibilityMask 95 | $18.NamingStyle = PascalCase 96 | $18.IncludeInstanceMembers = True 97 | $18.IncludeStaticEntities = True 98 | $9.NamingRule = $20 99 | $20.Name = Methods 100 | $20.AffectedEntity = Methods 101 | $20.VisibilityMask = Protected, Public 102 | $20.NamingStyle = PascalCase 103 | $20.IncludeInstanceMembers = True 104 | $20.IncludeStaticEntities = True 105 | $9.NamingRule = $21 106 | $21.Name = Static Readonly Fields 107 | $21.AffectedEntity = ReadonlyField 108 | $21.VisibilityMask = Protected, Public 109 | $21.NamingStyle = PascalCase 110 | $21.IncludeInstanceMembers = False 111 | $21.IncludeStaticEntities = True 112 | $9.NamingRule = $22 113 | $22.Name = Fields 114 | $22.AffectedEntity = Field 115 | $22.VisibilityMask = Protected, Public 116 | $22.NamingStyle = PascalCase 117 | $22.IncludeInstanceMembers = True 118 | $22.IncludeStaticEntities = True 119 | $9.NamingRule = $23 120 | $23.Name = ReadOnly Fields 121 | $23.AffectedEntity = ReadonlyField 122 | $23.VisibilityMask = Protected, Public 123 | $23.NamingStyle = PascalCase 124 | $23.IncludeInstanceMembers = True 125 | $23.IncludeStaticEntities = False 126 | $9.NamingRule = $24 127 | $24.Name = Constant Fields 128 | $24.AffectedEntity = ConstantField 129 | $24.VisibilityMask = Protected, Public 130 | $24.NamingStyle = PascalCase 131 | $24.IncludeInstanceMembers = True 132 | $24.IncludeStaticEntities = True 133 | $9.NamingRule = $25 134 | $25.Name = Properties 135 | $25.AffectedEntity = Property 136 | $25.VisibilityMask = Protected, Public 137 | $25.NamingStyle = PascalCase 138 | $25.IncludeInstanceMembers = True 139 | $25.IncludeStaticEntities = True 140 | $9.NamingRule = $26 141 | $26.Name = Events 142 | $26.AffectedEntity = Event 143 | $26.VisibilityMask = Protected, Public 144 | $26.NamingStyle = PascalCase 145 | $26.IncludeInstanceMembers = True 146 | $26.IncludeStaticEntities = True 147 | $9.NamingRule = $27 148 | $27.Name = Enum Members 149 | $27.AffectedEntity = EnumMember 150 | $27.VisibilityMask = VisibilityMask 151 | $27.NamingStyle = PascalCase 152 | $27.IncludeInstanceMembers = True 153 | $27.IncludeStaticEntities = True 154 | $9.NamingRule = $28 155 | $28.Name = Parameters 156 | $28.AffectedEntity = Parameter 157 | $28.VisibilityMask = VisibilityMask 158 | $28.NamingStyle = CamelCase 159 | $28.IncludeInstanceMembers = True 160 | $28.IncludeStaticEntities = True 161 | $9.NamingRule = $29 162 | $29.Name = Type Parameters 163 | $29.RequiredPrefixes = $30 164 | $30.String = T 165 | $29.AffectedEntity = TypeParameter 166 | $29.VisibilityMask = VisibilityMask 167 | $29.NamingStyle = PascalCase 168 | $29.IncludeInstanceMembers = True 169 | $29.IncludeStaticEntities = True 170 | $0.VersionControlPolicy = $31 171 | version = 0.4.0 172 | EndGlobalSection 173 | EndGlobal 174 | -------------------------------------------------------------------------------- /Mastodon.API/ApiClientBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Runtime.InteropServices; 7 | using Newtonsoft.Json; 8 | 9 | namespace Mastodon.API 10 | { 11 | class ApiClientBase : IDisposable 12 | { 13 | readonly Uri baseUrl; 14 | readonly HttpClient http; 15 | 16 | internal ApiClientBase(Uri baseUrl, HttpClient http) 17 | { 18 | this.baseUrl = baseUrl; 19 | this.http = http; 20 | } 21 | 22 | internal async Task GetAsync(string path, IDictionary parameters = null, Dictionary headers = null, CancellationToken? token = null) 23 | { 24 | var url = CreateUrl(baseUrl, path, parameters); 25 | var request = CreateRequest(HttpMethod.Get, url, headers); 26 | var response = token.HasValue ? await http.SendAsync(request, token.Value) : await http.SendAsync(request); 27 | return await CheckResponse(response); 28 | } 29 | 30 | internal async Task GetAsyncWithArrayParams(string path, IEnumerable> parameters = null, Dictionary headers = null, CancellationToken? token = null) 31 | { 32 | var url = CreateUrl(baseUrl, path, parameters); 33 | var request = CreateRequest(HttpMethod.Get, url, headers); 34 | var response = token.HasValue ? await http.SendAsync(request, token.Value) : await http.SendAsync(request); 35 | return await CheckResponse(response); 36 | } 37 | 38 | internal async Task PostAsync(string path, Dictionary parameters = null, Dictionary headers = null, CancellationToken? token = null) 39 | { 40 | var url = CreateUrl(baseUrl, path, null); 41 | var request = CreateRequest(HttpMethod.Post, url, headers); 42 | if (parameters != null) request.Content = new FormUrlEncodedContent(parameters); 43 | var response = token.HasValue ? await http.SendAsync(request, token.Value) : await http.SendAsync(request); 44 | return await CheckResponse(response); 45 | } 46 | 47 | internal async Task PatchAsync(string path, Dictionary parameters = null, Dictionary headers = null, CancellationToken? token = null) 48 | { 49 | var url = CreateUrl(baseUrl, path, null); 50 | var request = CreateRequest(new HttpMethod("PATCH"), url, headers); 51 | if (parameters != null) request.Content = new FormUrlEncodedContent(parameters); 52 | var response = token.HasValue ? await http.SendAsync(request, token.Value) : await http.SendAsync(request); 53 | return await CheckResponse(response); 54 | } 55 | 56 | internal async Task DeleteAsync(string path, Dictionary parameters = null, Dictionary headers = null, CancellationToken? token = null) 57 | { 58 | var url = CreateUrl(baseUrl, path, null); 59 | var request = CreateRequest(HttpMethod.Delete, url, headers); 60 | if (parameters != null) request.Content = new FormUrlEncodedContent(parameters); 61 | var response = token.HasValue ? await http.SendAsync(request, token.Value) : await http.SendAsync(request); 62 | return await CheckResponse(response); 63 | } 64 | 65 | HttpRequestMessage CreateRequest(HttpMethod method, Uri url, Dictionary headers = null) 66 | { 67 | var request = new HttpRequestMessage(method, url); 68 | if (headers == null) return request; 69 | foreach (var header in headers) 70 | { 71 | request.Headers.Add(header.Key, header.Value); 72 | } 73 | return request; 74 | } 75 | 76 | internal static Uri CreateUrl(Uri baseUri, string path, IEnumerable> parameters) 77 | { 78 | var p = path ?? ""; 79 | var q = parameters?.AsQueryString() ?? ""; 80 | return new Uri(baseUri, $"{p}{q}"); 81 | } 82 | 83 | public override string ToString() 84 | { 85 | return string.Format("[ApiClientBase: baseUrl={0}, http={1}]", baseUrl, http); 86 | } 87 | 88 | public override bool Equals(object obj) 89 | { 90 | var o = obj as ApiClientBase; 91 | if (o == null) return false; 92 | return Equals(baseUrl, o.baseUrl) && 93 | Equals(http, o.http); 94 | } 95 | 96 | public override int GetHashCode() 97 | { 98 | return Object.GetHashCode(baseUrl, http); 99 | } 100 | 101 | internal static async Task CheckResponse(HttpResponseMessage response) 102 | { 103 | if (!response.IsSuccessStatusCode) 104 | { 105 | var responseBody = await response.Content.ReadAsStringAsync(); 106 | try 107 | { 108 | var error = JsonConvert.DeserializeObject(responseBody); 109 | throw new MastodonApiException(response.StatusCode, error); 110 | } 111 | catch (JsonReaderException) 112 | { 113 | // There is a possibility that the object could not be deserialized 114 | throw new MastodonApiException(response.StatusCode, $"Unexpected error returned from server: {responseBody}"); 115 | } 116 | } 117 | return response; 118 | } 119 | 120 | public void Dispose() 121 | { 122 | http.Dispose(); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Account. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#account 9 | /// 10 | public class Account 11 | { 12 | [JsonProperty(PropertyName = "id")] 13 | public string Id { get; set; } 14 | [JsonProperty(PropertyName = "username")] 15 | public string Username { get; set; } 16 | [JsonProperty(PropertyName = "acct")] 17 | public string Acct { get; set; } 18 | [JsonProperty(PropertyName = "display_name")] 19 | public string DisplayName { get; set; } 20 | [JsonProperty(PropertyName = "locked")] 21 | public bool IsLocked { get; set; } 22 | [JsonProperty(PropertyName = "created_at")] 23 | public string CreatedAt { get; set; } 24 | [JsonProperty(PropertyName = "followers_count")] 25 | public int FollowersCount { get; set; } 26 | [JsonProperty(PropertyName = "following_count")] 27 | public int FollowingCount { get; set; } 28 | [JsonProperty(PropertyName = "statuses_count")] 29 | public int StatusesCount { get; set; } 30 | [JsonProperty(PropertyName = "note")] 31 | public string Note { get; set; } 32 | [JsonProperty(PropertyName = "url")] 33 | public Uri Url { get; set; } 34 | [JsonProperty(PropertyName = "avatar")] 35 | public Uri Avatar { get; set; } 36 | [JsonProperty(PropertyName = "avatar_static")] 37 | public Uri AvatarStatic { get; set; } 38 | [JsonProperty(PropertyName = "header")] 39 | public string Header { get; set; } 40 | [JsonProperty(PropertyName = "header_static")] 41 | public string HeaderStatic { get; set; } 42 | 43 | // 44 | /// 45 | /// Initializes for JSON.NET 46 | /// 47 | internal Account() { } 48 | 49 | public Account(string id, string username, string acct, string displayName, bool locked, string createdAt, int followersCount, int followingCount, int statusesCount, string note, Uri url, Uri avater, Uri avatarStatic, string header, string headerStatic) 50 | { 51 | Id = id; 52 | Username = username; 53 | Acct = acct; 54 | DisplayName = displayName; 55 | IsLocked = locked; 56 | CreatedAt = createdAt; 57 | FollowersCount = followersCount; 58 | FollowingCount = followingCount; 59 | StatusesCount = statusesCount; 60 | Note = note; 61 | Url = url; 62 | Avatar = avater; 63 | AvatarStatic = avatarStatic; 64 | Header = header; 65 | HeaderStatic = headerStatic; 66 | } 67 | 68 | public static Account Create(string id, string username, string acct, string displayName, bool locked, string createdAt, int followersCount, int followingCount, int statusesCount, string note, Uri url, Uri avater, Uri avatarStatic, string header, string headerStatic) 69 | { 70 | return new Account(id, username, acct, displayName, locked, createdAt, followersCount, followingCount, statusesCount, note, url, avater, avatarStatic, header, headerStatic); 71 | } 72 | 73 | public override string ToString() 74 | { 75 | return string.Format("[Account: Id={0}, Username={1}, Acct={2}, DisplayName={3}, isLocked={4}, CreatedAt={5}, FollowersCount={6}, FollowingCount={7}, StatusesCount={8}, Note={9}, Url={10}, Avatar={11}, AvatarStatic={12}, Header={13}, HeaderStatic={14}]", Id, Username, Acct, DisplayName, IsLocked, CreatedAt, FollowersCount, FollowingCount, StatusesCount, Note, Url, Avatar, AvatarStatic, Header, HeaderStatic); 76 | } 77 | 78 | public override bool Equals(object obj) 79 | { 80 | var o = obj as Account; 81 | if (o == null) return false; 82 | return Equals(Id, o.Id) && 83 | Equals(Username, o.Username) && 84 | Equals(Acct, o.Acct) && 85 | Equals(DisplayName, o.DisplayName) && 86 | Equals(IsLocked, o.IsLocked) && 87 | Equals(CreatedAt, o.CreatedAt) && 88 | Equals(FollowingCount, o.FollowingCount) && 89 | Equals(FollowersCount, o.FollowersCount) && 90 | Equals(StatusesCount, o.StatusesCount) && 91 | Equals(Note, o.Note) && 92 | Equals(Url, o.Url) && 93 | Equals(Avatar, o.Avatar) && 94 | Equals(AvatarStatic, o.AvatarStatic) && 95 | Equals(Header, o.Header) && 96 | Equals(HeaderStatic, o.HeaderStatic); 97 | } 98 | 99 | public override int GetHashCode() 100 | { 101 | return Object.GetHashCode(Id, Username, Acct, DisplayName, IsLocked, CreatedAt, FollowingCount, FollowersCount, StatusesCount, Note, Url, Avatar, AvatarStatic, Header, HeaderStatic); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Application.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Application. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#application 9 | /// 10 | public class Application 11 | { 12 | [JsonProperty(PropertyName = "name")] 13 | public string Name { get; set; } 14 | [JsonProperty(PropertyName = "website")] 15 | public Uri Website { get; set; } 16 | 17 | internal Application() { } 18 | 19 | Application(string name, Uri webSite) 20 | { 21 | Name = name; 22 | Website = webSite; 23 | } 24 | 25 | public static Application Create(string name, Uri webSite) 26 | { 27 | return new Application(name, webSite); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return string.Format("[Application: Name={0}, Website={1}]", Name, Website); 33 | } 34 | 35 | public override bool Equals(object obj) 36 | { 37 | var o = obj as Application; 38 | if (o == null) return false; 39 | return Equals(Name, o.Name) && 40 | Equals(Website, o.Website); 41 | } 42 | 43 | public override int GetHashCode() 44 | { 45 | return Object.GetHashCode(Name, Website); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Attachment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Attachment. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#attachment 9 | /// 10 | public class Attachment 11 | { 12 | [JsonProperty(PropertyName = "id")] 13 | public string Id { get; set; } 14 | [JsonProperty(PropertyName = "type")] 15 | public string Type { get; set; } 16 | [JsonProperty(PropertyName = "url")] 17 | public Uri Url { get; set; } 18 | [JsonProperty(PropertyName = "remote_url")] 19 | public Uri RemoteUrl { get; set; } 20 | [JsonProperty(PropertyName = "preview_url")] 21 | public Uri PreviewUrl { get; set; } 22 | [JsonProperty(PropertyName = "text_url")] 23 | public Uri TextUrl { get; set; } 24 | 25 | internal Attachment() { } 26 | 27 | Attachment(string id, string type, Uri url, Uri remoteUrl, Uri previewUrl, Uri textUrl) 28 | { 29 | Id = id; 30 | Type = type; 31 | Url = url; 32 | RemoteUrl = remoteUrl; 33 | PreviewUrl = previewUrl; 34 | TextUrl = textUrl; 35 | } 36 | 37 | public static Attachment Create(string id, string type, Uri url, Uri remoteUrl, Uri previewUrl, Uri textUrl) 38 | { 39 | return new Attachment(id, type, url, remoteUrl, previewUrl, textUrl); 40 | } 41 | 42 | public override string ToString() 43 | { 44 | return string.Format("[Attachment: Id={0}, Type={1}, Url={2}, RemoteUrl={3}, PreviewUrl={4}, TextUrl={5}]", Id, Type, Url, RemoteUrl, PreviewUrl, TextUrl); 45 | } 46 | 47 | public override bool Equals(object obj) 48 | { 49 | var o = obj as Attachment; 50 | if (o == null) return false; 51 | return Equals(Id, o.Id) && 52 | Equals(Type, o.Type) && 53 | Equals(Url, o.Url) && 54 | Equals(RemoteUrl, o.RemoteUrl) && 55 | Equals(PreviewUrl, o.PreviewUrl) && 56 | Equals(TextUrl, o.TextUrl); 57 | } 58 | 59 | public override int GetHashCode() 60 | { 61 | return Object.GetHashCode(Id, Type, Url, RemoteUrl, PreviewUrl, TextUrl); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Card.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Card. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#card 9 | /// 10 | public class Card 11 | { 12 | [JsonProperty(PropertyName = "url")] 13 | public Uri Url { get; set; } 14 | [JsonProperty(PropertyName = "title")] 15 | public string Title { get; set; } 16 | [JsonProperty(PropertyName = "description")] 17 | public string Description { get; set; } 18 | [JsonProperty(PropertyName = "image")] 19 | public Uri Image { get; set; } 20 | 21 | internal Card() { } 22 | 23 | Card(Uri url, string title, string description, Uri image) 24 | { 25 | Url = url; 26 | Title = title; 27 | Description = description; 28 | Image = image; 29 | } 30 | 31 | public static Card Create(Uri url, string title, string description, Uri image) 32 | { 33 | return new Card(url, title, description, image); 34 | } 35 | 36 | 37 | public override string ToString() 38 | { 39 | return string.Format("[Card: Url={0}, Title={1}, Description={2}, Image={3}]", Url, Title, Description, Image); 40 | } 41 | 42 | public override bool Equals(object obj) 43 | { 44 | var o = obj as Card; 45 | if (o == null) return false; 46 | return Equals(Url, o.Url) && 47 | Equals(Title, o.Title) && 48 | Equals(Description, o.Description) && 49 | Equals(Image, o.Image); 50 | } 51 | 52 | public override int GetHashCode() 53 | { 54 | return Object.GetHashCode(Url, Title, Description, Image); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Context.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Serialization; 5 | namespace Mastodon.API 6 | { 7 | /// 8 | /// Context. 9 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#card 10 | /// 11 | public class Context 12 | { 13 | [JsonProperty(PropertyName = "ancestors")] 14 | public IList Ancestors { get; set; } 15 | [JsonProperty(PropertyName = "descendants")] 16 | public IList Descendants { get; set; } 17 | 18 | internal Context() { } 19 | 20 | Context(IList ancestors, IList descendants) 21 | { 22 | Ancestors = ancestors; 23 | Descendants = descendants; 24 | } 25 | 26 | public static Context Create(IList ancestors, IList descendants) 27 | { 28 | return new Context(ancestors, descendants); 29 | } 30 | 31 | public override string ToString() 32 | { 33 | return string.Format("[Context: Ancestors={0}, Descendants={1}]", Ancestors, Descendants); 34 | } 35 | 36 | public override bool Equals(object obj) 37 | { 38 | var o = obj as Context; 39 | if (o == null) return false; 40 | return Object.SequenceEqual(Ancestors, o.Ancestors) && 41 | Object.SequenceEqual(Descendants, o.Descendants); 42 | } 43 | 44 | public override int GetHashCode() 45 | { 46 | return Object.GetHashCode(Ancestors, Descendants); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Error.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Error. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#error 9 | /// 10 | public class Error 11 | { 12 | [JsonProperty(PropertyName = "error")] 13 | public string Description { get; set; } 14 | 15 | public Error(string error) 16 | { 17 | Description = error; 18 | } 19 | 20 | public override string ToString() 21 | { 22 | return string.Format("[Error: Description={0}]", Description); 23 | } 24 | 25 | public override bool Equals(object obj) 26 | { 27 | var o = obj as Error; 28 | if (o == null) return false; 29 | return Equals(Description, o.Description); 30 | } 31 | 32 | public override int GetHashCode() 33 | { 34 | return Object.GetHashCode(Description); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Instance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Instance. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#instance 9 | /// 10 | public class Instance 11 | { 12 | [JsonProperty(PropertyName = "uri")] 13 | public string Uri { get; set; } 14 | [JsonProperty(PropertyName = "title")] 15 | public string Title { get; set; } 16 | [JsonProperty(PropertyName = "description")] 17 | public string Description { get; set; } 18 | [JsonProperty(PropertyName = "email")] 19 | public string Email { get; set; } 20 | 21 | internal Instance() { } 22 | 23 | Instance(string uri, string title, string description, string email) 24 | { 25 | Uri = uri; 26 | Title = title; 27 | Description = description; 28 | Email = email; 29 | } 30 | 31 | public static Instance Create(string uri, string title, string description, string email) 32 | { 33 | return new Instance(uri, title, description, email); 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return string.Format("[Instance: Uri={0}, Title={1}, Description={2}, Email={3}]", Uri, Title, Description, Email); 39 | } 40 | 41 | public override bool Equals(object obj) 42 | { 43 | var o = obj as Instance; 44 | if (o == null) return false; 45 | return Equals(Uri, o.Uri) && 46 | Equals(Title, o.Title) && 47 | Equals(Description, o.Description) && 48 | Equals(Email, o.Email); 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return Object.GetHashCode(Uri) ^ 54 | Object.GetHashCode(Title) ^ 55 | Object.GetHashCode(Description) ^ 56 | Object.GetHashCode(Email); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/MastodonApp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | public class MastodonApp 7 | { 8 | [JsonProperty(PropertyName = "id")] 9 | public string Id { get; set; } 10 | [JsonProperty(PropertyName = "client_id")] 11 | public string ClientId { get; set; } 12 | [JsonProperty(PropertyName = "client_secret")] 13 | public string ClientSecret { get; set; } 14 | [JsonProperty(PropertyName = "redirect_uri")] 15 | public Uri RedirectUri { get; set; } 16 | 17 | internal MastodonApp() { } 18 | 19 | MastodonApp(string id, string clientId, string clientSecret, Uri redirectUri) 20 | { 21 | Id = id; 22 | ClientId = clientId; 23 | ClientSecret = clientSecret; 24 | RedirectUri = redirectUri; 25 | } 26 | 27 | public static MastodonApp Create(string id, string clientId, string clientSecret, Uri redirectUri) 28 | { 29 | return new MastodonApp(id, clientId, clientSecret, redirectUri); 30 | } 31 | 32 | public override bool Equals(object obj) 33 | { 34 | var o = obj as MastodonApp; 35 | if (o == null) return false; 36 | return Equals(Id, o.Id) && 37 | Equals(ClientId, o.ClientId) && 38 | Equals(ClientSecret, o.ClientSecret) && 39 | Equals(RedirectUri, o.RedirectUri); 40 | } 41 | 42 | public override int GetHashCode() 43 | { 44 | return Object.GetHashCode(Id, ClientId, ClientSecret, RedirectUri); 45 | } 46 | 47 | public override string ToString() 48 | { 49 | return string.Format("[MastodonApp: Id={0}, ClientId={1}, ClientSecret={2}, RedirectUri={3}]", Id, ClientId, ClientSecret, RedirectUri); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Mention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Mention. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#mention 9 | /// 10 | public class Mention 11 | { 12 | [JsonProperty(PropertyName = "url")] 13 | public Uri Url { get; set; } 14 | [JsonProperty(PropertyName = "username")] 15 | public string Username { get; set; } 16 | [JsonProperty(PropertyName = "acct")] 17 | public string Acct { get; set; } 18 | [JsonProperty(PropertyName = "id")] 19 | public string Id { get; set; } 20 | 21 | internal Mention() { } 22 | 23 | Mention(Uri url, string username, string acct, string id) 24 | { 25 | Url = url; 26 | Username = username; 27 | Acct = acct; 28 | Id = id; 29 | } 30 | 31 | public static Mention Create(Uri url, string username, string acct, string id) 32 | { 33 | return new Mention(url, username, acct, id); 34 | } 35 | 36 | public override string ToString() 37 | { 38 | return string.Format("[Mention: Url={0}, Username={1}, Acct={2}, Id={3}]", Url, Username, Acct, Id); 39 | } 40 | 41 | public override bool Equals(object obj) 42 | { 43 | var o = obj as Mention; 44 | if (o == null) return false; 45 | return Equals(Url, o.Url) && 46 | Equals(Username, o.Username) && 47 | Equals(Acct, o.Acct) && 48 | Equals(Id, o.Id); 49 | } 50 | 51 | public override int GetHashCode() 52 | { 53 | return Object.GetHashCode(Url, Username, Acct, Id); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Notification.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Notification. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#notification 9 | /// 10 | public class Notification 11 | { 12 | [JsonProperty(PropertyName = "id")] 13 | public string Id { get; set; } 14 | [JsonProperty(PropertyName = "type")] 15 | public string Type { get; set; } 16 | [JsonProperty(PropertyName = "created_at")] 17 | public string CreatedAt { get; set; } 18 | [JsonProperty(PropertyName = "account")] 19 | public Account Account { get; set; } 20 | [JsonProperty(PropertyName = "status")] 21 | public Status Status { get; set; } 22 | 23 | internal Notification() { } 24 | 25 | Notification(string id, string type, string createdAt, Account account, Status status) 26 | { 27 | Id = id; 28 | Type = type; 29 | CreatedAt = createdAt; 30 | Account = account; 31 | Status = status; 32 | } 33 | 34 | public static Notification Create(string id, string type, string createdAt, Account account, Status status) 35 | { 36 | return new Notification(id, type, createdAt, account, status); 37 | } 38 | 39 | public override string ToString() 40 | { 41 | return string.Format("[Notification: Id={0}, Type={1}, CreatedAt={2}, Account={3}, Status={4}]", Id, Type, CreatedAt, Account, Status); 42 | } 43 | 44 | public override bool Equals(object obj) 45 | { 46 | var o = obj as Notification; 47 | if (o == null) return false; 48 | return Equals(Id, o.Id) && 49 | Equals(Type, o.Type) && 50 | Equals(CreatedAt, o.CreatedAt) && 51 | Equals(Account, o.Account) && 52 | Equals(Status, o.Status); 53 | } 54 | 55 | public override int GetHashCode() 56 | { 57 | return Object.GetHashCode(Id, Type, CreatedAt, Account, Status); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/OAuthAccessScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Mastodon.API 7 | { 8 | public class OAuthAccessScope 9 | { 10 | IEnumerable types; 11 | 12 | public static OAuthAccessScope Of(params OAtuhAccessScopeType[] types) 13 | { 14 | return new OAuthAccessScope(types); 15 | } 16 | 17 | OAuthAccessScope(params OAtuhAccessScopeType[] types) 18 | { 19 | this.types = types.ToList(); 20 | } 21 | 22 | internal string Value 23 | { 24 | get 25 | { 26 | var ts = types.Distinct().Select(x => value(x)); 27 | return string.Join(" ", ts); 28 | } 29 | } 30 | 31 | static string value(OAtuhAccessScopeType type) 32 | { 33 | switch (type) 34 | { 35 | case OAtuhAccessScopeType.Read: return "read"; 36 | case OAtuhAccessScopeType.Write: return "write"; 37 | case OAtuhAccessScopeType.Follow: return "follow"; 38 | } 39 | return ""; 40 | } 41 | } 42 | 43 | public enum OAtuhAccessScopeType 44 | { 45 | Read, 46 | Write, 47 | Follow 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Relationship.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Relationship. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#relationship 9 | /// 10 | public class Relationship 11 | { 12 | [JsonProperty(PropertyName = "id")] 13 | public string Id { get; set; } 14 | [JsonProperty(PropertyName = "following")] 15 | public bool IsFollowing { get; set; } 16 | [JsonProperty(PropertyName = "followed_by")] 17 | public bool IsFollowedBy { get; set; } 18 | [JsonProperty(PropertyName = "blocking")] 19 | public bool IsBlocking { get; set; } 20 | [JsonProperty(PropertyName = "muting")] 21 | public bool IsMuting { get; set; } 22 | [JsonProperty(PropertyName = "requested")] 23 | public bool IsRequested { get; set; } 24 | 25 | internal Relationship() { } 26 | 27 | Relationship(string id, bool following, bool followedBy, bool blocking, bool muting, bool requested) 28 | { 29 | Id = id; 30 | IsFollowing = following; 31 | IsFollowedBy = followedBy; 32 | IsBlocking = blocking; 33 | IsMuting = muting; 34 | IsRequested = requested; 35 | } 36 | 37 | public static Relationship Create(string id, bool following, bool followedBy, bool blocking, bool muting, bool requested) 38 | 39 | { 40 | return new Relationship(id, following, followedBy, blocking, muting, requested); 41 | } 42 | 43 | 44 | public override string ToString() 45 | { 46 | return string.Format("[Relationship: Id={0}, IsFollowing={1}, IsFollowedBy={2}, IsBlocking={3}, IsMuting={4}, IsRequested={5}]", Id, IsFollowing, IsFollowedBy, IsBlocking, IsMuting, IsRequested); 47 | } 48 | 49 | public override bool Equals(object obj) 50 | { 51 | var o = obj as Relationship; 52 | if (o == null) return false; 53 | return Equals(Id, o.Id) && 54 | Equals(IsFollowing, o.IsFollowing) && 55 | Equals(IsFollowedBy, o.IsFollowedBy) && 56 | Equals(IsBlocking, o.IsBlocking) && 57 | Equals(IsMuting, o.IsMuting) && 58 | Equals(IsRequested, o.IsRequested); 59 | } 60 | 61 | public override int GetHashCode() 62 | { 63 | return Object.GetHashCode(Id, IsFollowing, IsFollowedBy, IsBlocking, IsMuting, IsRequested); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Report.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Report. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#report 9 | /// 10 | public class Report 11 | { 12 | [JsonProperty(PropertyName = "id")] 13 | public string Id { get; set; } 14 | [JsonProperty(PropertyName = "action_taken")] 15 | public bool ActionTaken { get; set; } 16 | 17 | internal Report() { } 18 | 19 | Report(string id, bool actionTaken) 20 | { 21 | Id = id; 22 | ActionTaken = actionTaken; 23 | } 24 | 25 | public static Report Create(string id, bool actionTaken) 26 | { 27 | return new Report(id, actionTaken); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return string.Format("[Report: Id={0}, ActionTaken={1}]", Id, ActionTaken); 33 | } 34 | 35 | public override bool Equals(object obj) 36 | { 37 | var o = obj as Report; 38 | if (o == null) return false; 39 | return Equals(Id, o.Id) && Equals(ActionTaken, o.ActionTaken); 40 | } 41 | 42 | public override int GetHashCode() 43 | { 44 | return Object.GetHashCode(Id, ActionTaken); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Results.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | 5 | namespace Mastodon.API 6 | { 7 | /// 8 | /// Results. 9 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#results 10 | /// 11 | public class Results 12 | { 13 | [JsonProperty(PropertyName = "accounts")] 14 | public IList Accounts { get; set; } 15 | [JsonProperty(PropertyName = "statuses")] 16 | public IList Statuses { get; set; } 17 | [JsonProperty(PropertyName = "hashtags")] 18 | public IList Hashtags { get; set; } 19 | 20 | internal Results() { } 21 | 22 | Results(IList accounts, IList statuses, IList hashtags) 23 | { 24 | Accounts = accounts; 25 | Statuses = statuses; 26 | Hashtags = hashtags; 27 | } 28 | 29 | public static Results Create(IList accounts, IList statuses, IList hashtags) 30 | { 31 | return new Results(accounts, statuses, hashtags); 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return string.Format("[Results: Accounts={0}, Statuses={1}, Hashtags={2}]", Accounts, Statuses, Hashtags); 37 | } 38 | 39 | public override bool Equals(object obj) 40 | { 41 | var o = obj as Results; 42 | if (o == null) return false; 43 | return 44 | Object.SequenceEqual(Accounts, o.Accounts) && 45 | Object.SequenceEqual(Statuses, o.Statuses) && 46 | Object.SequenceEqual(Hashtags, o.Hashtags); 47 | } 48 | 49 | public override int GetHashCode() 50 | { 51 | return Object.GetHashCode(Accounts, Statuses, Hashtags); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Status.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using System.Linq; 5 | 6 | namespace Mastodon.API 7 | { 8 | /// 9 | /// Status. 10 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#status 11 | /// 12 | public class Status 13 | { 14 | [JsonProperty(PropertyName = "id")] 15 | public string Id { get; set; } 16 | [JsonProperty(PropertyName = "uri")] 17 | public string Uri { get; set; } 18 | [JsonProperty(PropertyName = "url")] 19 | public Uri Url { get; set; } 20 | [JsonProperty(PropertyName = "account")] 21 | public Account Account { get; set; } 22 | [JsonProperty(PropertyName = "in_reply_to_id")] 23 | public string InReplyToId { get; set; } 24 | [JsonProperty(PropertyName = "in_reply_to_account_id")] 25 | public string InReplyToAccountId { get; set; } 26 | [JsonProperty(PropertyName = "reblog")] 27 | public Status Reblog { get; set; } 28 | [JsonProperty(PropertyName = "content")] 29 | public string Content { get; set; } 30 | [JsonProperty(PropertyName = "created_at")] 31 | public string CreatedAt { get; set; } 32 | [JsonProperty(PropertyName = "reblogs_count")] 33 | public int ReblogsCount { get; set; } 34 | [JsonProperty(PropertyName = "favourites_count")] 35 | public int FavouritesCount { get; set; } 36 | [JsonProperty(PropertyName = "reblogged")] 37 | public bool IsReblogged { get; set; } 38 | [JsonProperty(PropertyName = "favourited")] 39 | public bool IsFavourited { get; set; } 40 | [JsonProperty(PropertyName = "sensitive", NullValueHandling = NullValueHandling.Ignore)] 41 | public bool IsSensitive { get; set; } 42 | [JsonProperty(PropertyName = "spoiler_text")] 43 | public string SpoilerText { get; set; } 44 | [JsonProperty(PropertyName = "visibility")] 45 | public StatusVisibility Visibility { get; set; } 46 | [JsonProperty(PropertyName = "media_attachments")] 47 | public IList MediaAttachments { get; set; } 48 | [JsonProperty(PropertyName = "mentions")] 49 | public IList Mentions { get; set; } 50 | [JsonProperty(PropertyName = "tags")] 51 | public IList Tags { get; set; } 52 | [JsonProperty(PropertyName = "application")] 53 | public Application Application { get; set; } 54 | 55 | internal Status() { } 56 | 57 | Status(string id, string uri, Uri url, Account account, string inReplyToId, string inReplyToAccountId, Status reblog, string content, string createdAt, int reblogsCount, int favouritesCount, bool? reblogged, bool? favourited, bool? sensitive, string spoilerText, StatusVisibility visibility, IList mediaAttachments, IList mentions, IList tags, Application application) 58 | { 59 | Id = id; 60 | Uri = uri; 61 | Url = url; 62 | Account = account; 63 | InReplyToId = inReplyToId; 64 | InReplyToAccountId = inReplyToAccountId; 65 | Reblog = reblog; 66 | Content = content; 67 | CreatedAt = createdAt; 68 | ReblogsCount = reblogsCount; 69 | FavouritesCount = favouritesCount; 70 | IsReblogged = reblogged ?? false; 71 | IsFavourited = favourited ?? false; 72 | IsSensitive = sensitive ?? false; 73 | SpoilerText = spoilerText; 74 | Visibility = visibility; 75 | MediaAttachments = mediaAttachments; 76 | Mentions = mentions; 77 | Tags = tags; 78 | Application = application; 79 | } 80 | 81 | public static Status Create(string id, string uri, Uri url, Account account, string inReplyToId, string inReplyToAccountId, Status reblog, string content, string createdAt, int reblogsCount, int favouritesCount, bool? reblogged, bool? favourited, bool? sensitive, string spoilerText, StatusVisibility visibility, IList mediaAttachments, IList mentions, IList tags, Application application) 82 | { 83 | return new Status(id, uri, url, account, inReplyToId, inReplyToAccountId, reblog, content, createdAt, reblogsCount, favouritesCount, reblogged, favourited, sensitive, spoilerText, visibility, mediaAttachments, mentions, tags, application); 84 | } 85 | 86 | public override string ToString() 87 | { 88 | return string.Format("[Status: Id={0}, Uri={1}, Url={2}, Account={3}, InReplyToId={4}, InReplyToAccountId={5}, Reblog={6}, Content={7}, CreatedAt={8}, ReblogsCount={9}, FavouritesCount={10}, IsReblogged={11}, IsFavourited={12}, IsSensitive={13}, SpoilerText={14}, Visibility={15}, MediaAttachments={16}, Mentions={17}, Tags={18}, Application={19}]", Id, Uri, Url, Account, InReplyToId, InReplyToAccountId, Reblog, Content, CreatedAt, ReblogsCount, FavouritesCount, IsReblogged, IsFavourited, IsSensitive, SpoilerText, Visibility, MediaAttachments, Mentions, Tags, Application); 89 | } 90 | 91 | public override bool Equals(object obj) 92 | { 93 | var o = obj as Status; 94 | if (o == null) return false; 95 | return Equals(Id, o.Id) && 96 | Equals(Uri, o.Uri) && 97 | Equals(Url, o.Url) && 98 | Equals(Account, o.Account) && 99 | Equals(InReplyToId, o.InReplyToId) && 100 | Equals(InReplyToAccountId, o.InReplyToAccountId) && 101 | Equals(Reblog, o.Reblog) && 102 | Equals(Content, o.Content) && 103 | Equals(CreatedAt, o.CreatedAt) && 104 | Equals(ReblogsCount, o.ReblogsCount) && 105 | Equals(FavouritesCount, o.FavouritesCount) && 106 | Equals(IsReblogged, o.IsReblogged) && 107 | Equals(IsFavourited, o.IsFavourited) && 108 | Equals(SpoilerText, o.SpoilerText) && 109 | Equals(Visibility, o.Visibility) && 110 | Object.SequenceEqual(MediaAttachments, o.MediaAttachments) && 111 | Object.SequenceEqual(Mentions, o.Mentions) && 112 | Object.SequenceEqual(Tags, o.Tags) && 113 | Equals(Application, o.Application); 114 | } 115 | 116 | public override int GetHashCode() 117 | { 118 | return Object.GetHashCode(Id, Uri, Url, Account, InReplyToId, InReplyToAccountId, Reblog, Content, CreatedAt, ReblogsCount, FavouritesCount, IsReblogged, IsFavourited, IsSensitive, SpoilerText, Visibility, MediaAttachments, Mentions, Tags, Application); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/StatusVisibility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Mastodon.API 3 | { 4 | public enum StatusVisibility 5 | { 6 | Direct, 7 | Private, 8 | Unlisted, 9 | Public 10 | } 11 | 12 | public static class StatusVisibilities 13 | { 14 | public static string Value(this StatusVisibility visibility) 15 | { 16 | switch (visibility) 17 | { 18 | case StatusVisibility.Direct: return "direct"; 19 | case StatusVisibility.Private: return "private"; 20 | case StatusVisibility.Public: return "public"; 21 | case StatusVisibility.Unlisted: return "unlisted"; 22 | } 23 | return ""; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Tag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace Mastodon.API 5 | { 6 | /// 7 | /// Tag. 8 | /// https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md#tag 9 | /// 10 | public class Tag 11 | { 12 | [JsonProperty(PropertyName = "name")] 13 | public string Name { get; set; } 14 | [JsonProperty(PropertyName = "url")] 15 | public Uri Url { get; set; } 16 | 17 | internal Tag() { } 18 | 19 | Tag(string name, Uri url) 20 | { 21 | Name = name; 22 | Url = url; 23 | } 24 | 25 | public static Tag Create(string name, Uri url) 26 | { 27 | return new Tag(name, url); 28 | } 29 | 30 | 31 | public override string ToString() 32 | { 33 | return string.Format("[Tag: Name={0}, Url={1}]", Name, Url); 34 | } 35 | 36 | public override bool Equals(object obj) 37 | { 38 | var o = obj as Tag; 39 | if (o == null) return false; 40 | return Equals(Name, o.Name) && 41 | Equals(Url, o.Url); 42 | } 43 | 44 | public override int GetHashCode() 45 | { 46 | return Object.GetHashCode(Name, Url); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Mastodon.API/Entities/Token.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | namespace Mastodon.API 5 | { 6 | public class Token 7 | { 8 | [JsonProperty(PropertyName = "access_token")] 9 | public string AccessToken { get; set; } 10 | [JsonProperty(PropertyName = "token_type")] 11 | public string TokenType { get; set; } 12 | [JsonProperty(PropertyName = "scope")] 13 | public string Scope { get; set; } 14 | [JsonProperty(PropertyName = "created_at")] 15 | public string CreatedAt { get; set; } 16 | 17 | internal Token() { } 18 | 19 | Token(string accessToken, string tokenType, string scope, string createdAt) 20 | { 21 | AccessToken = accessToken; 22 | TokenType = tokenType; 23 | Scope = scope; 24 | CreatedAt = createdAt; 25 | } 26 | 27 | public static Token Create(string accessToken, string tokenType, string scope, string createdAt) 28 | { 29 | return new Token(accessToken, tokenType, scope, createdAt); 30 | } 31 | 32 | public override bool Equals(object obj) 33 | { 34 | var o = obj as Token; 35 | if (o == null) return false; 36 | return Equals(AccessToken, o.AccessToken) && 37 | Equals(TokenType, o.TokenType) && 38 | Equals(Scope, o.Scope) && 39 | Equals(CreatedAt, o.CreatedAt); 40 | } 41 | 42 | public override int GetHashCode() 43 | { 44 | return Object.GetHashCode(AccessToken, TokenType, Scope, CreatedAt); 45 | } 46 | 47 | public override string ToString() 48 | { 49 | return string.Format("[Token: AccessToken={0}, TokenType={1}, Scope={2}, CreatedAt={3}]", AccessToken, TokenType, Scope, CreatedAt); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Mastodon.API/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Net; 4 | 5 | namespace Mastodon.API 6 | { 7 | static class Object 8 | { 9 | internal static bool SequenceEqual(IList left, IList right) 10 | { 11 | if (left == null && right == null) return true; 12 | if (ReferenceEquals(left, right)) return true; 13 | return left.SequenceEqual(right); 14 | } 15 | 16 | internal static int GetHashCode(object o) 17 | { 18 | return o == null ? 0 : o.GetHashCode(); 19 | } 20 | 21 | internal static int GetHashCode(params object[] objects) 22 | { 23 | var hashCode = 0; 24 | foreach (object obj in objects) 25 | { 26 | hashCode = hashCode ^ GetHashCode(obj); 27 | } 28 | return hashCode; 29 | } 30 | 31 | internal static string AsQueryString(this IDictionary parameters) 32 | { 33 | if (parameters == null || !parameters.Any()) return ""; 34 | var enumerable = parameters.Select(x => new KeyValuePair(x.Key, x.Value)); 35 | return enumerable.AsQueryString(); 36 | } 37 | 38 | internal static string AsQueryString(this IEnumerable> parameters) 39 | { 40 | if (parameters == null | !parameters.Any()) return ""; 41 | var strings = parameters 42 | .Select(param => string.Format("{0}={1}", param.Key.UrlEncoded(), param.Value.UrlEncoded())); 43 | return "?" + string.Join("&", strings); 44 | } 45 | 46 | internal static string UrlEncoded(this object obj) 47 | { 48 | return WebUtility.UrlEncode(obj.ToString()); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Mastodon.API/IMastodonApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Collections.Generic; 5 | 6 | namespace Mastodon.API 7 | { 8 | public interface IMastodonApi 9 | { 10 | /// 11 | /// Returns an Account. 12 | /// 13 | /// The account. 14 | /// Identifier. 15 | /// Token. 16 | Task GetAccount(string id, CancellationToken? token = null); 17 | 18 | /// 19 | /// Returns the authenticated user's Account. 20 | /// 21 | /// The current account. 22 | /// Token. 23 | Task GetCurrentAccount(CancellationToken? token = null); 24 | 25 | /// 26 | /// Updating the current user 27 | /// 28 | /// The current account. 29 | /// The name to display in the user's profile. 30 | /// A new biography for the user. 31 | /// A base64 encoded image to display as the user's avatar. 32 | /// A base64 encoded image to display as the user's header image. 33 | /// Token. 34 | Task UpdateCurrentAccount(string displayName = null, string note = null, string base64EncodedAvater = null, string base64EncodedHeader = null, CancellationToken? token = null); 35 | 36 | /// 37 | /// Getting an account's followers. 38 | /// 39 | /// The followers. 40 | /// Identifier. 41 | /// Maximum number of accounts to get (Default 40, Max 80) 42 | /// MaxId and SinceId are usually get from the Link header. 43 | /// Token. 44 | Task> GetFollowers(string id, int? limit = null, Link? link = null, CancellationToken? token = null); 45 | 46 | /// 47 | /// Getting an account's following. 48 | /// 49 | /// The follwing. 50 | /// Identifier. 51 | /// Maximum number of accounts to get (Default 40, Max 80) 52 | /// MaxId and SinceId are usually get from the Link header. 53 | /// Token. 54 | Task> GetFollowing(string id, int? limit = null, Link? link = null, CancellationToken? token = null); 55 | 56 | /// 57 | /// Getting an account's statuses. 58 | /// 59 | /// The status. 60 | /// Identifier. 61 | /// Limit. 62 | /// MaxId and SinceId are usually get from the Link header. 63 | /// Token. 64 | Task> GetStatuses(string id, bool isOnlyMedia = false, bool isExcludeReplies = false, int? limit = null, Link? link = null, CancellationToken? token = null); 65 | 66 | /// 67 | /// Following an account: 68 | /// 69 | /// Relationship. 70 | /// Identifier. 71 | /// Token. 72 | Task Follow(string id, CancellationToken? token = null); 73 | 74 | /// 75 | /// Unfollowing an account: 76 | /// 77 | /// Relationship. 78 | /// Identifier. 79 | /// Token. 80 | Task Unfollow(string id, CancellationToken? token = null); 81 | 82 | /// 83 | /// Blocking an account 84 | /// 85 | /// Relationship. 86 | /// Identifier. 87 | /// Token. 88 | Task Block(string id, CancellationToken? token = null); 89 | 90 | 91 | /// 92 | /// Unblocking an account 93 | /// 94 | /// Relationship. 95 | /// Identifier. 96 | /// Token. 97 | Task Unblock(string id, CancellationToken? token = null); 98 | 99 | /// 100 | /// Mute an account 101 | /// 102 | /// Relationship. 103 | /// Identifier. 104 | /// Token. 105 | Task Mute(string id, CancellationToken? token = null); 106 | 107 | /// 108 | /// Unmute an account 109 | /// 110 | /// Relationship. 111 | /// Identifier. 112 | /// Token. 113 | Task Unmute(string id, CancellationToken? token = null); 114 | 115 | /// 116 | /// Getting an account's relationship. 117 | /// 118 | /// The relationships 119 | /// Identifier. 120 | /// Token. 121 | Task GetRelationship(string id, CancellationToken? token = null); 122 | 123 | /// 124 | /// Getting an account's relationships 125 | /// 126 | /// The relationships. 127 | /// Identifiers. 128 | /// Token. 129 | Task GetRelationships(string[] ids, CancellationToken? token = null); 130 | 131 | /// 132 | /// Searching for accounts. 133 | /// 134 | /// Accounts. 135 | /// Query. 136 | /// Maximum number of accounts to get (Default 40, Max 80) 137 | /// MaxId and SinceId are usually get from the Link header. 138 | /// Token. 139 | Task> SearchAccounts(string query, int? limit = null, Link? link = null, CancellationToken? token = null); 140 | 141 | /// 142 | /// Fetching a user's blocks. 143 | /// 144 | /// Accounts. 145 | /// MaxId and SinceId are usually get from the Link header. 146 | /// Token. 147 | Task> GetBlocks(Link? link = null, CancellationToken? token = null); 148 | 149 | /// 150 | /// Fetching a user's favourites. 151 | /// 152 | /// Accounts. 153 | /// MaxId and SinceId are usually get from the Link header. 154 | /// Token. 155 | Task> GetFavourites(Link? link = null, CancellationToken? token = null); 156 | 157 | /// 158 | /// Fetching a list of follow requests. 159 | /// 160 | /// The follow requests. 161 | /// MaxId and SinceId are usually get from the Link header. 162 | /// Token. 163 | Task> GetFollowRequests(Link? link = null, CancellationToken? token = null); 164 | 165 | /// 166 | /// Authorizing follow requests. 167 | /// 168 | /// Nothing. 169 | /// Identifier. 170 | /// Token. 171 | Task AuthorizeFollowRequests(string id, CancellationToken? token = null); 172 | 173 | /// 174 | /// Rejecting follow requests. 175 | /// 176 | /// Nothing. 177 | /// Identifier. 178 | /// Token. 179 | Task RejectFollowRequests(string id, CancellationToken? token = null); 180 | 181 | /// 182 | /// Following a remote user. 183 | /// 184 | /// The local representation of the followed account, as an Account. 185 | /// URI. 186 | /// Token. 187 | Task FollowRemoteUser(string uri, CancellationToken? token = null); 188 | 189 | /// 190 | /// Getting instance information. 191 | /// 192 | /// The current Instance. 193 | /// Token. 194 | Task GetInstance(CancellationToken? token = null); 195 | 196 | /// 197 | /// Uploading a media attachment. 198 | /// 199 | /// An Attachment that can be used when creating a status. 200 | /// Base64 encoded media. 201 | /// Token. 202 | Task Upload(string base64EncodedMedia, CancellationToken? token = null); 203 | 204 | /// 205 | /// Fetching a user's mutes. 206 | /// 207 | /// An array of Accounts muted by the authenticated user. 208 | /// MaxId and SinceId are usually get from the Link header. 209 | /// Token. 210 | Task> GetMutes(Link? link = null, CancellationToken? token = null); 211 | 212 | 213 | /// 214 | /// Fetching a user's notification. 215 | /// 216 | /// The Notification for the authenticated user. 217 | /// Identifier. 218 | /// Token. 219 | Task GetNotification(string id, CancellationToken? token = null); 220 | 221 | /// 222 | /// Fetching a user's notifications. 223 | /// 224 | /// A list of Notifications for the authenticated user. 225 | /// MaxId and SinceId are usually get from the Link header. 226 | /// Token. 227 | Task> GetNotifications(Link? link = null, CancellationToken? token = null); 228 | 229 | /// 230 | /// Clearing notifications. 231 | /// Deletes all notifications from the Mastodon server for the authenticated user. Returns an empty object. 232 | /// 233 | /// Nothing. 234 | /// Token. 235 | Task ClearNotifications(CancellationToken? token = null); 236 | 237 | /// 238 | /// Fetching a user's reports. 239 | /// 240 | /// A list of Reports made by the authenticated user. 241 | /// MaxId and SinceId are usually get from the Link header. 242 | /// Token. 243 | Task> GetReports(Link? link = null, CancellationToken? token = null); 244 | 245 | /// 246 | /// Reporting a user. 247 | /// 248 | /// The report. 249 | /// Account identifier. 250 | /// Status identifier. 251 | /// Comment. 252 | /// Token. 253 | Task Report(string accountId, string statusId, string comment, CancellationToken? token = null); 254 | 255 | /// 256 | /// Searching for content. 257 | /// 258 | /// If q is a URL, Mastodon will attempt to fetch the provided account or status. Otherwise, it will do a local account and hashtag search. 259 | /// Query. 260 | /// If set to true resolve. 261 | /// Limit. 262 | /// Link. 263 | /// Token. 264 | Task> Search(string query, bool resolve = true, int? limit = null, Link? link = null, CancellationToken? token = null); 265 | 266 | /// 267 | /// Fetching a status. 268 | /// 269 | /// Status. 270 | /// Identifier. 271 | /// Token. 272 | Task GetStatus(string id, CancellationToken? token = null); 273 | 274 | /// 275 | /// Getting status context. 276 | /// 277 | /// The context. 278 | /// Identifier. 279 | /// Token. 280 | Task GetContext(string id, CancellationToken? token = null); 281 | 282 | /// 283 | /// Getting a card associated with a status. 284 | /// 285 | /// The card. 286 | /// Identifier. 287 | /// Token. 288 | Task GetCard(string id, CancellationToken? token = null); 289 | 290 | /// 291 | /// Getting who reblogged a status. 292 | /// 293 | /// Accounts. 294 | /// Identifier. 295 | /// Token. 296 | Task> GetRebloggedBy(string id, CancellationToken? token = null); 297 | 298 | /// 299 | /// Getting who favourited a status. 300 | /// 301 | /// Accounts. 302 | /// Identifier. 303 | /// Token. 304 | Task> GetFavouritedBy(string id, CancellationToken? token = null); 305 | 306 | /// 307 | /// Posting a new status. 308 | /// 309 | /// The status. 310 | /// The text of the status. 311 | /// Local ID of the status you want to reply to. 312 | /// Media ID to attach to the status. 313 | /// Text to be shown as a warning before the actual content. 314 | /// Visibility 315 | /// set this to mark the media of the status as NSFW. 316 | /// Token. 317 | Task PostStatus(string status, string inReplyTo = null, string mediaId = null, string spoilerText = null, StatusVisibility? visibility = null, bool? isSensitive = null, CancellationToken? token = null); 318 | 319 | /// 320 | /// Deleting a status. 321 | /// 322 | /// Nothing. 323 | /// Identifier. 324 | /// Token. 325 | Task DeleteStatus(string id, CancellationToken? token = null); 326 | 327 | /// 328 | /// Reblogging a status: 329 | /// 330 | /// Status. 331 | /// Identifier. 332 | /// Token. 333 | Task Reblog(string id, CancellationToken? token = null); 334 | 335 | /// 336 | /// Unreblogging a status: 337 | /// 338 | /// Status. 339 | /// Identifier. 340 | /// Token. 341 | Task Unreblog(string id, CancellationToken? token = null); 342 | 343 | /// 344 | /// Favouriting/ a status. 345 | /// 346 | /// Status. 347 | /// Identifier. 348 | /// Token. 349 | Task Favourite(string id, CancellationToken? token = null); 350 | 351 | 352 | /// 353 | /// Unfavouriting a status. 354 | /// 355 | /// Status. 356 | /// Identifier. 357 | /// Token. 358 | Task Unfavourite(string id, CancellationToken? token = null); 359 | 360 | 361 | /// 362 | /// Retrieving a home timeline. 363 | /// 364 | /// The home timelines. 365 | /// Link. 366 | /// Token. 367 | Task> GetHomeTimelines(Link? link = null, CancellationToken? token = null); 368 | 369 | /// 370 | /// Retrieving a public timeline. 371 | /// 372 | /// The public timelines. 373 | /// Is local. 374 | /// Link. 375 | /// Token. 376 | Task> GetPublicTimelines(bool? isLocal = null, Link? link = null, CancellationToken? token = null); 377 | 378 | /// 379 | /// Retrieving a tag timeline. 380 | /// 381 | /// The tag timelines. 382 | /// Hashtag. 383 | /// Is local. 384 | /// Link. 385 | /// Token. 386 | Task> GetTagTimelines(string hashtag, bool? isLocal = null, Link? link = null, CancellationToken? token = null); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /Mastodon.API/Link.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Net.Http.Headers; 3 | using System.Text.RegularExpressions; 4 | using System.Collections.Generic; 5 | 6 | namespace Mastodon.API 7 | { 8 | public struct Link 9 | { 10 | /// 11 | /// Get a list with ID less than or equal this value 12 | /// 13 | /// The max identifier. 14 | public int? MaxId { get; } 15 | 16 | /// 17 | /// Get a list with ID greater than this value 18 | /// 19 | /// The since identifier. 20 | public int? SinceId { get; } 21 | 22 | public static Link? Create(int? maxId = null, int? sinceId = null) 23 | { 24 | if (maxId.HasValue || sinceId.HasValue) return new Link(maxId, sinceId); 25 | return null; 26 | } 27 | 28 | public static Link? Create(HttpHeaders headers) 29 | { 30 | var linkHeader = GetValueFromHeaders("Link", headers); 31 | if (linkHeader == null) return null; 32 | return CreateLinkFromHeaderLinkValue(linkHeader); 33 | } 34 | 35 | Link(int? maxId, int? sinceId) 36 | { 37 | MaxId = maxId; 38 | SinceId = sinceId; 39 | } 40 | 41 | public override bool Equals(object obj) 42 | { 43 | if (!(obj is Link)) return false; 44 | var o = (Link)obj; 45 | return Equals(MaxId, o.MaxId) && 46 | Equals(SinceId, o.SinceId); 47 | } 48 | 49 | public override int GetHashCode() 50 | { 51 | return Object.GetHashCode(MaxId, SinceId); 52 | } 53 | 54 | public override string ToString() 55 | { 56 | return string.Format("[Link: MaxId={0}, SinceId={1}]", MaxId, SinceId); 57 | } 58 | 59 | internal static string GetValueFromHeaders(string key, HttpHeaders headers) 60 | { 61 | if (headers == null) return null; 62 | return headers 63 | .Where(x => x.Key.Equals(key)) 64 | .Select(x => x.Value) 65 | .First().First(); 66 | } 67 | 68 | internal static Link? CreateLinkFromHeaderLinkValue(string headerLinkValue) 69 | { 70 | if (headerLinkValue == null) return null; 71 | var kvs = headerLinkValue.Split(',') 72 | .Select(x => Regex.Match(x, ";\\s*rel=\"(next|prev)\"").Groups) 73 | .Select(x => new KeyValuePair(x[1].ToString(), x[2].ToString())); 74 | int? maxId = null; 75 | int? sinceId = null; 76 | foreach (var kv in kvs) 77 | { 78 | if (kv.Key.Equals("max_id")) maxId = int.Parse(kv.Value); 79 | if (kv.Key.Equals("since_id")) sinceId = int.Parse(kv.Value); 80 | } 81 | return Create(maxId, sinceId); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Mastodon.API/Mastodon.API.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {BBF68570-AF0D-4440-85C4-0094876580BF} 7 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 8 | true 9 | Library 10 | Mastodon.API 11 | Mastodon.API 12 | v4.5 13 | Profile111 14 | 0.4.0 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug 21 | DEBUG; 22 | prompt 23 | 4 24 | 25 | 26 | true 27 | bin\Release 28 | prompt 29 | 4 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | ..\packages\Newtonsoft.Json.10.0.2\lib\portable-net45+win8+wpa81+wp8\Newtonsoft.Json.dll 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Mastodon.API/MastodonApi.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | using System; 5 | using Newtonsoft.Json; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace Mastodon.API 10 | { 11 | public class MastodonApi : IMastodonApi, IDisposable 12 | { 13 | readonly ApiClientBase apiBase; 14 | readonly MastodonApiConfig config; 15 | readonly Dictionary authorizationHeader; 16 | 17 | public MastodonApi(MastodonApiConfig config, HttpClient httpClient = null) 18 | { 19 | this.config = config; 20 | apiBase = new ApiClientBase(config.InstanceUrl, httpClient ?? MastodonApi.GetDefaultHttpClient()); 21 | authorizationHeader = new Dictionary { { "Authorization", $"Bearer {config.AccessToken}" } }; 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return $"[MastodonApi: config={config}]"; 27 | } 28 | 29 | public override bool Equals(object obj) 30 | { 31 | var o = obj as MastodonApi; 32 | if (o == null) return false; 33 | return Equals(apiBase, o.apiBase) && 34 | Equals(config, o.config); 35 | } 36 | 37 | public override int GetHashCode() 38 | { 39 | return Object.GetHashCode(apiBase, config); 40 | } 41 | 42 | public async Task GetAccount(string id, CancellationToken? token = null) 43 | { 44 | var path = $"/api/v1/accounts/{id}"; 45 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 46 | return await response 47 | .Content.ReadAsStringAsync() 48 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 49 | } 50 | 51 | public async Task GetCurrentAccount(CancellationToken? token = null) 52 | { 53 | var path = "/api/v1/accounts/verify_credentials"; 54 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 55 | return await response 56 | .Content.ReadAsStringAsync() 57 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 58 | } 59 | 60 | public async Task UpdateCurrentAccount(string displayName = null, string note = null, string base64EncodedAvater = null, string base64EncodedHeader = null, CancellationToken? token = null) 61 | { 62 | var parameters = new Dictionary(); 63 | if (displayName != null) parameters.Add("display_name", displayName); 64 | if (note != null) parameters.Add("note", note); 65 | if (base64EncodedAvater != null) parameters.Add("avater", base64EncodedAvater); 66 | if (base64EncodedHeader != null) parameters.Add("header", base64EncodedHeader); 67 | var path = "/api/v1/accounts/update_credentials"; 68 | var response = await apiBase.PatchAsync(path, parameters, authorizationHeader, token); 69 | return await response 70 | .Content.ReadAsStringAsync() 71 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 72 | } 73 | 74 | public async Task> GetFollowers(string id, int? limit = null, Link? link = null, CancellationToken? token = null) 75 | { 76 | var parameters = new Dictionary(); 77 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 78 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 79 | if (limit != null) parameters.Add("limit", limit); 80 | var path = $"/api/v1/accounts/{id}/followers"; 81 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 82 | var resource = await response 83 | .Content.ReadAsStringAsync() 84 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 85 | return new Response(resource, response); 86 | } 87 | 88 | public async Task> GetFollowing(string id, int? limit = null, Link? link = null, CancellationToken? token = null) 89 | { 90 | var parameters = new Dictionary(); 91 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 92 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 93 | if (limit != null) parameters.Add("limit", limit); 94 | var path = $"/api/v1/accounts/{id}/following"; 95 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 96 | var resource = await response 97 | .Content.ReadAsStringAsync() 98 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 99 | return new Response(resource, response); 100 | } 101 | 102 | public async Task> GetStatuses(string id, bool isOnlyMedia = false, bool isExcludeReplies = false, int? limit = null, Link? link = null, CancellationToken? token = null) 103 | { 104 | var parameters = new Dictionary(); 105 | if (isOnlyMedia) parameters.Add("only_media", "1"); 106 | if (isExcludeReplies) parameters.Add("exclude_replies", "1"); 107 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 108 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 109 | if (limit != null) parameters.Add("limit", limit); 110 | var path = $"/api/v1/accounts/{id}/statuses"; 111 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 112 | var resource = await response 113 | .Content.ReadAsStringAsync() 114 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 115 | return new Response(resource, response); 116 | } 117 | 118 | public async Task Follow(string id, CancellationToken? token = null) 119 | { 120 | var path = $"/api/v1/accounts/{id}/follow"; 121 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 122 | return await response 123 | .Content.ReadAsStringAsync() 124 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 125 | } 126 | 127 | public async Task Unfollow(string id, CancellationToken? token = null) 128 | { 129 | var path = $"/api/v1/accounts/{id}/unfollow"; 130 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 131 | return await response 132 | .Content.ReadAsStringAsync() 133 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 134 | } 135 | 136 | public async Task Block(string id, CancellationToken? token = null) 137 | { 138 | var path = $"/api/v1/accounts/{id}/block"; 139 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 140 | return await response 141 | .Content.ReadAsStringAsync() 142 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 143 | } 144 | 145 | public async Task Unblock(string id, CancellationToken? token = null) 146 | { 147 | var path = $"/api/v1/accounts/{id}/unblock"; 148 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 149 | return await response 150 | .Content.ReadAsStringAsync() 151 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 152 | } 153 | 154 | public async Task Mute(string id, CancellationToken? token = null) 155 | { 156 | var path = $"/api/v1/accounts/{id}/mute"; 157 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 158 | return await response 159 | .Content.ReadAsStringAsync() 160 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 161 | } 162 | 163 | public async Task Unmute(string id, CancellationToken? token = null) 164 | { 165 | var path = $"/api/v1/accounts/{id}/unmute"; 166 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 167 | return await response 168 | .Content.ReadAsStringAsync() 169 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 170 | } 171 | 172 | public async Task GetRelationship(string id, CancellationToken? token = null) 173 | { 174 | return await GetRelationships(new string[] { id }, token) 175 | .ContinueWith((task) => task.Result.First()); 176 | } 177 | 178 | public async Task GetRelationships(string[] ids, CancellationToken? token = null) 179 | { 180 | var idArray = ids ?? new string[] { }; 181 | var parameters = idArray.Select(x => new KeyValuePair("id[]", x)); 182 | var path = "/api/v1/accounts/relationships"; 183 | var response = await apiBase.GetAsyncWithArrayParams(path, parameters, authorizationHeader, token); 184 | return await response 185 | .Content.ReadAsStringAsync() 186 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 187 | } 188 | 189 | public async Task> SearchAccounts(string query, int? limit = null, Link? link = null, CancellationToken? token = null) 190 | { 191 | var parameters = new Dictionary(); 192 | parameters.Add("q", query); 193 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 194 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 195 | if (limit != null) parameters.Add("limit", limit); 196 | var path = "/api/v1/accounts/search"; 197 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 198 | var resource = await response 199 | .Content.ReadAsStringAsync() 200 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 201 | return new Response(resource, response); 202 | } 203 | 204 | public async Task> GetBlocks(Link? link = null, CancellationToken? token = null) 205 | { 206 | var parameters = new Dictionary(); 207 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 208 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 209 | var path = "/api/v1/blocks"; 210 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 211 | var resource = await response 212 | .Content.ReadAsStringAsync() 213 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 214 | return new Response(resource, response); 215 | } 216 | 217 | public async Task> GetFavourites(Link? link = null, CancellationToken? token = null) 218 | { 219 | var parameters = new Dictionary(); 220 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 221 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 222 | var path = "/api/v1/favourites"; 223 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 224 | var resource = await response 225 | .Content.ReadAsStringAsync() 226 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 227 | return new Response(resource, response); 228 | } 229 | 230 | public async Task> GetFollowRequests(Link? link = null, CancellationToken? token = null) 231 | { 232 | var parameters = new Dictionary(); 233 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 234 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 235 | var path = "/api/v1/follow_requests"; 236 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 237 | var resource = await response 238 | .Content.ReadAsStringAsync() 239 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 240 | return new Response(resource, response); 241 | } 242 | 243 | public async Task AuthorizeFollowRequests(string id, CancellationToken? token = null) 244 | { 245 | var path = $"/api/v1/follow_requests/{id}/authorize"; 246 | await apiBase.PostAsync(path, null, authorizationHeader, token); 247 | } 248 | 249 | public async Task RejectFollowRequests(string id, CancellationToken? token = null) 250 | { 251 | var path = $"/api/v1/follow_requests/{id}/reject"; 252 | await apiBase.PostAsync(path, null, authorizationHeader, token); 253 | } 254 | 255 | public async Task FollowRemoteUser(string uri, CancellationToken? token = null) 256 | { 257 | var parameters = new Dictionary { { "uri", uri } }; 258 | var path = "/api/v1/follows"; 259 | var response = await apiBase.PostAsync(path, parameters, authorizationHeader, token); 260 | return await response 261 | .Content.ReadAsStringAsync() 262 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 263 | } 264 | 265 | public async Task GetInstance(CancellationToken? token = null) 266 | { 267 | var path = "/api/v1/instance"; 268 | // Does not require authentication. 269 | var response = await apiBase.GetAsync(path, null, null, token); 270 | return await response 271 | .Content.ReadAsStringAsync() 272 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 273 | } 274 | 275 | public async Task Upload(string base64EncodedMedia, CancellationToken? token = null) 276 | { 277 | var parameters = new Dictionary { { "file", base64EncodedMedia } }; 278 | var path = "/api/v1/media"; 279 | var response = await apiBase.PostAsync(path, parameters, authorizationHeader, token); 280 | return await response 281 | .Content.ReadAsStringAsync() 282 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 283 | } 284 | 285 | public async Task> GetMutes(Link? link = null, CancellationToken? token = null) 286 | { 287 | var parameters = new Dictionary(); 288 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 289 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 290 | var path = "/api/v1/mutes"; 291 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 292 | var resource = await response 293 | .Content.ReadAsStringAsync() 294 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 295 | return new Response(resource, response); 296 | } 297 | 298 | public async Task GetNotification(string id, CancellationToken? token = null) 299 | { 300 | var path = $"/api/v1/notifications/{id}"; 301 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 302 | return await response 303 | .Content.ReadAsStringAsync() 304 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 305 | } 306 | 307 | public async Task> GetNotifications(Link? link = null, CancellationToken? token = null) 308 | { 309 | var parameters = new Dictionary(); 310 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 311 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 312 | var path = "/api/v1/notifications"; 313 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 314 | var resource = await response 315 | .Content.ReadAsStringAsync() 316 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 317 | return new Response(resource, response); 318 | } 319 | 320 | public async Task ClearNotifications(CancellationToken? token = null) 321 | { 322 | var path = "/api/v1/notifications/clear"; 323 | await apiBase.PostAsync(path, null, authorizationHeader, token); 324 | } 325 | 326 | public async Task> GetReports(Link? link = null, CancellationToken? token = null) 327 | { 328 | var parameters = new Dictionary(); 329 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 330 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 331 | var path = "/api/v1/reports"; 332 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 333 | var resource = await response 334 | .Content.ReadAsStringAsync() 335 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 336 | return new Response(resource, response); 337 | } 338 | 339 | public async Task Report(string accountId, string statusId, string comment, CancellationToken? token = null) 340 | { 341 | var parameters = new Dictionary 342 | { 343 | { "account_id", accountId }, 344 | { "status_ids", statusId }, 345 | { "comment", comment } 346 | }; 347 | var path = "/api/v1/reports"; 348 | var response = await apiBase.PostAsync(path, parameters, authorizationHeader, token); 349 | return await response 350 | .Content.ReadAsStringAsync() 351 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 352 | } 353 | 354 | public async Task> Search(string query, bool resolve = true, int? limit = null, Link? link = null, CancellationToken? token = null) 355 | { 356 | var parameters = new Dictionary { { "q", query } }; 357 | if (resolve) parameters.Add("resolve", "1"); 358 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 359 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 360 | if (limit != null) parameters.Add("limit", limit); 361 | var path = "/api/v1/search"; 362 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 363 | var resource = await response 364 | .Content.ReadAsStringAsync() 365 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 366 | return new Response(resource, response); 367 | } 368 | 369 | public async Task GetStatus(string id, CancellationToken? token = null) 370 | { 371 | var path = $"/api/v1/statuses/{id}"; 372 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 373 | return await response 374 | .Content.ReadAsStringAsync() 375 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 376 | } 377 | 378 | public async Task GetContext(string id, CancellationToken? token = null) 379 | { 380 | var path = $"/api/v1/statuses/{id}/context"; 381 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 382 | return await response 383 | .Content.ReadAsStringAsync() 384 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 385 | } 386 | 387 | public async Task GetCard(string id, CancellationToken? token = null) 388 | { 389 | var path = $"/api/v1/statuses/{id}/card"; 390 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 391 | return await response 392 | .Content.ReadAsStringAsync() 393 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 394 | } 395 | 396 | public async Task> GetRebloggedBy(string id, CancellationToken? token = null) 397 | { 398 | var path = $"/api/v1/statuses/{id}/reblogged_by"; 399 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 400 | var resource = await response 401 | .Content.ReadAsStringAsync() 402 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 403 | return new Response(resource, response); 404 | } 405 | 406 | public async Task> GetFavouritedBy(string id, CancellationToken? token = null) 407 | { 408 | var path = $"/api/v1/statuses/{id}/favourited_by"; 409 | var response = await apiBase.GetAsync(path, null, authorizationHeader, token); 410 | var resource = await response 411 | .Content.ReadAsStringAsync() 412 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 413 | return new Response(resource, response); 414 | } 415 | 416 | public async Task PostStatus(string status, string inReplyTo = null, string mediaId = null, string spoilerText = null, StatusVisibility? visibility = null, bool? isSensitive = null, CancellationToken? token = null) 417 | { 418 | var parameters = new Dictionary { { "status", status } }; 419 | if (inReplyTo != null) parameters.Add("in_reply_to", inReplyTo); 420 | if (mediaId != null) parameters.Add("media_ids", mediaId); 421 | if (isSensitive ?? false) parameters.Add("sensitive", "1"); 422 | if (spoilerText != null) parameters.Add("spoiler_text", spoilerText); 423 | if (visibility != null) parameters.Add("visibility", visibility.Value.Value()); 424 | var path = "/api/v1/statuses"; 425 | var response = await apiBase.PostAsync(path, parameters, authorizationHeader, token); 426 | return await response 427 | .Content.ReadAsStringAsync() 428 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 429 | } 430 | 431 | public async Task DeleteStatus(string id, CancellationToken? token = null) 432 | { 433 | var path = $"/api/v1/statuses/{id}"; 434 | await apiBase.DeleteAsync(path, null, authorizationHeader, token); 435 | } 436 | 437 | public async Task Reblog(string id, CancellationToken? token = null) 438 | { 439 | var path = $"/api/v1/statuses/{id}/reblog"; 440 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 441 | return await response 442 | .Content.ReadAsStringAsync() 443 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 444 | } 445 | 446 | public async Task Unreblog(string id, CancellationToken? token = null) 447 | { 448 | var path = $"/api/v1/statuses/{id}/unreblog"; 449 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 450 | return await response 451 | .Content.ReadAsStringAsync() 452 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 453 | } 454 | 455 | public async Task Favourite(string id, CancellationToken? token = null) 456 | { 457 | var path = $"/api/v1/statuses/{id}/favourite"; 458 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 459 | return await response 460 | .Content.ReadAsStringAsync() 461 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 462 | } 463 | 464 | public async Task Unfavourite(string id, CancellationToken? token = null) 465 | { 466 | var path = $"/api/v1/statuses/{id}/unfavourite"; 467 | var response = await apiBase.PostAsync(path, null, authorizationHeader, token); 468 | return await response 469 | .Content.ReadAsStringAsync() 470 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 471 | } 472 | 473 | public async Task> GetHomeTimelines(Link? link = null, CancellationToken? token = null) 474 | { 475 | var parameters = new Dictionary(); 476 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 477 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 478 | var path = "/api/v1/timelines/home"; 479 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 480 | var resource = await response 481 | .Content.ReadAsStringAsync() 482 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 483 | return new Response(resource, response); 484 | } 485 | 486 | public async Task> GetPublicTimelines(bool? isLocal = null, Link? link = null, CancellationToken? token = null) 487 | { 488 | var parameters = new Dictionary(); 489 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 490 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 491 | if (isLocal ?? false) parameters.Add("local", "1"); 492 | var path = "/api/v1/timelines/public"; 493 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 494 | var resource = await response 495 | .Content.ReadAsStringAsync() 496 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 497 | return new Response(resource, response); 498 | } 499 | 500 | public async Task> GetTagTimelines(string hashtag, bool? isLocal = null, Link? link = null, CancellationToken? token = null) 501 | { 502 | var parameters = new Dictionary(); 503 | if (link?.MaxId != null) parameters.Add("max_id", link?.MaxId.Value); 504 | if (link?.SinceId != null) parameters.Add("since_id", link?.SinceId.Value); 505 | if (isLocal ?? false) parameters.Add("local", "1"); 506 | var path = $"/api/v1/timelines/tag/{hashtag}"; 507 | var response = await apiBase.GetAsync(path, parameters, authorizationHeader, token); 508 | var resource = await response 509 | .Content.ReadAsStringAsync() 510 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 511 | return new Response(resource, response); 512 | } 513 | 514 | internal static HttpClient GetDefaultHttpClient() 515 | { 516 | return new HttpClient(); 517 | } 518 | 519 | public void Dispose() 520 | { 521 | apiBase.Dispose(); 522 | } 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /Mastodon.API/MastodonApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Mastodon.API 3 | { 4 | public class MastodonApiConfig 5 | { 6 | public Uri InstanceUrl { get; } 7 | public string AccessToken { get; } 8 | 9 | public MastodonApiConfig(Uri instanceUrl, string accessToken) 10 | { 11 | InstanceUrl = instanceUrl; 12 | AccessToken = accessToken; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return string.Format("[MastodonApiConfig: InstanceBaseUrl={0}, AccessToken={1}]", InstanceUrl, AccessToken); 18 | } 19 | 20 | public override bool Equals(object obj) 21 | { 22 | var o = obj as MastodonApiConfig; 23 | if (o == null) return false; 24 | return Equals(InstanceUrl, o.InstanceUrl) && 25 | Equals(AccessToken, o.AccessToken); 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return Object.GetHashCode(InstanceUrl); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Mastodon.API/MastodonApiException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Net; 7 | 8 | namespace Mastodon.API 9 | { 10 | /// 11 | /// This exception indicates an error code from the MastodonAPI (i.e., 400, 404, 500, etc). 12 | /// It is up to the caller to catch these exceptions and take appropriate action within their application. 13 | /// 14 | public class MastodonApiException : Exception 15 | { 16 | /// 17 | /// The StatusCode returned from the Http call to the API 18 | /// 19 | public HttpStatusCode StatusCode { get; } 20 | /// 21 | /// Deserialized representation of the error message from the API 22 | /// 23 | public Error Error { get; } 24 | 25 | public MastodonApiException(HttpStatusCode statusCode, Error error) 26 | { 27 | StatusCode = statusCode; 28 | Error = error; 29 | } 30 | 31 | public MastodonApiException(HttpStatusCode statusCode, string message) 32 | : base(message) 33 | { 34 | StatusCode = statusCode; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Mastodon.API/MastodonAuthClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using System.Net.Http.Headers; 8 | 9 | namespace Mastodon.API 10 | { 11 | public class MastodonAuthClient : IDisposable 12 | { 13 | readonly ApiClientBase apiBase; 14 | 15 | public MastodonAuthClient(Uri instanceUrl, HttpClient httpClient = null) 16 | { 17 | apiBase = new ApiClientBase(instanceUrl, httpClient ?? MastodonApi.GetDefaultHttpClient()); 18 | } 19 | 20 | /// 21 | /// Creates the app on Mastodon instance. 22 | /// 23 | /// MastodonApp. 24 | /// Client name. 25 | /// Redirect uris. 26 | /// Scope. 27 | /// Token. 28 | public async Task CreateApp(string clientName, Uri redirectUris, OAuthAccessScope scope, CancellationToken? token = null) 29 | { 30 | var data = new Dictionary { 31 | { "client_name", clientName }, 32 | { "redirect_uris", redirectUris.AbsoluteUri }, 33 | { "scope", scope.Value }, 34 | }; 35 | var response = await apiBase.PostAsync("/api/v1/apps", data, null, token); 36 | return await response 37 | .Content.ReadAsStringAsync() 38 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 39 | } 40 | 41 | /// 42 | /// Return an OAuth token. 43 | /// DO NOT USE IN APP. 44 | /// 45 | /// OAuth token. 46 | /// Username. 47 | /// Password. 48 | /// Scope. 49 | /// Token. 50 | public async Task GetOAuthToken(string clientId, string clientSecret, string username, string password, OAuthAccessScope scope, CancellationToken? token = null) 51 | { 52 | var data = new Dictionary { 53 | { "client_id", clientId }, 54 | { "client_secret", clientSecret }, 55 | { "scope", scope.Value }, 56 | { "grant_type", "password" }, 57 | { "username", username }, 58 | { "password", password } 59 | }; 60 | var response = await apiBase.PostAsync("/oauth/token", data, null, token); 61 | return await response 62 | .Content.ReadAsStringAsync() 63 | .ContinueWith((task) => JsonConvert.DeserializeObject(task.Result)); 64 | } 65 | 66 | public void Dispose() 67 | { 68 | apiBase.Dispose(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Mastodon.API/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("Mastodon.API")] 8 | [assembly: AssemblyDescription("The Mastodon API Client Library for C#")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("(c) gomi_ningen")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("0.4.0")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | [assembly: InternalsVisibleTo("Mastodon.API.Tests")] 29 | -------------------------------------------------------------------------------- /Mastodon.API/Response.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | namespace Mastodon.API 3 | { 4 | public class Response 5 | { 6 | public T Resource { get; } 7 | public HttpResponseMessage Message { get; } 8 | public Link? Link { get; } 9 | 10 | public Response(T resource, HttpResponseMessage message) 11 | { 12 | Resource = resource; 13 | Message = message; 14 | Link = API.Link.Create(message.Headers); 15 | } 16 | 17 | public override string ToString() 18 | { 19 | return string.Format("[Response: Resource={0}, Message={1}]", Resource, Message); 20 | } 21 | 22 | public override bool Equals(object obj) 23 | { 24 | var o = obj as Response; 25 | if (o == null) return false; 26 | return Equals(Resource, o.Resource) && 27 | Equals(Message, o.Message); 28 | } 29 | 30 | public override int GetHashCode() 31 | { 32 | return Object.GetHashCode(Resource, Message); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Mastodon.API/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NuGet](https://img.shields.io/nuget/v/Mastodon.API.svg)](https://www.nuget.org/packages/Mastodon.API) [![License](https://img.shields.io/cocoapods/l/BadgeSwift.svg?style=flat)](/LICENSE) 2 | 3 | Mastodon API client library for C# 4 | ====== 5 | 6 | The Mastodon API Client Library for C# (PCL 4.5, Profile 111) 7 | 8 | Pull reqeusts are always welcome! 9 | 10 | #### master branch 11 | 12 | [![AppVeyor status](https://ci.appveyor.com/api/projects/status/qvfycjbddgbn2oxk?svg=true)](https://ci.appveyor.com/project/53ningen/mastodon-api-cs) [![Travis Status](https://travis-ci.org/pawotter/mastodon-api-cs.svg?branch=master)](https://travis-ci.org/pawotter/mastodon-api-cs) 13 | 14 | #### develop branch 15 | 16 | [![AppVeyor status](https://ci.appveyor.com/api/projects/status/qvfycjbddgbn2oxk/branch/develop?svg=true)](https://ci.appveyor.com/project/53ningen/mastodon-api-cs/branch/develop) [![Travis Status](https://travis-ci.org/pawotter/mastodon-api-cs.svg?branch=develop)](https://travis-ci.org/pawotter/mastodon-api-cs) 17 | 18 | 19 | # Install 20 | 21 | ``` 22 | Install-Package Mastodon.API 23 | ``` 24 | 25 | # Registering an app to Mastodon instance 26 | 27 | ```csharp 28 | var authClient = new MastodonAuthClient(new Uri("https://friends.nico")); 29 | var redirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob"); 30 | var scope = OAuthAccessScope.of(OAtuhAccessScopeType.Read); 31 | var app = await authClient.CreateApp("client_name", redirectUri, scope); 32 | ``` 33 | 34 | A registered OAuth application is assigned a unique Client ID and Client Secret. 35 | 36 | # Login with email address and password 37 | 38 | Not recommended to use in service. The Authorization Code Grant flow is recommended for applications. 39 | 40 | ```csharp 41 | var token = await authClient.GetOAuthToken(app.ClientId, app.ClientSecret, "username", "password", scope); 42 | ``` 43 | 44 | # Making API Calls 45 | 46 | ```csharp 47 | var config = new MastodonApiConfig(instanceUrl, token.AccessToken); 48 | var api = new MastodonApi(config); 49 | var account = await api.GetCurrentAccount(); 50 | ``` 51 | 52 | # Error Handling 53 | 54 | All error responses are thrown as the type MastodonApiException. Please be sure to catch this in your code and respond to any error condtions appropriately. 55 | 56 | ```csharp 57 | try 58 | { 59 | // api calls here 60 | } 61 | catch (MastodonApiException e) 62 | { 63 | // error handling here (i.e., no results found, login failed, server error, etc) 64 | } 65 | ``` 66 | 67 | # License 68 | 69 | MIT 70 | 71 | # Author 72 | 73 | gomi_ningen (@53ningen) 74 | 75 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | before_build: 3 | - cmd: nuget restore 4 | build: 5 | verbosity: minimal 6 | 7 | --------------------------------------------------------------------------------