From a330a683c8affd978fe77a935f029c402010f79c Mon Sep 17 00:00:00 2001 From: marihachi Date: Tue, 17 Jan 2023 16:49:34 +0900 Subject: [PATCH] Selectable misskey instance (#48) * update iroiro * support multi-version for misskey instance * :rocket: * add Misq project (fork) --- LegatoNowPlaying.sln | 10 +- LegatoNowPlaying/Accounts.cs | 27 +++- LegatoNowPlaying/App.config | 8 ++ LegatoNowPlaying/ErrorLogger.cs | 39 ++++++ LegatoNowPlaying/LegatoNowPlaying.csproj | 10 +- LegatoNowPlaying/MainForm.cs | 2 + LegatoNowPlaying/Program.cs | 5 +- .../Services/Misskey/AuthForm.Designer.cs | 38 +++++- LegatoNowPlaying/Services/Misskey/AuthForm.cs | 63 ++++++++- .../Services/Misskey/AuthForm.resx | 120 +++++++++++++++++ LegatoNowPlaying/Services/Misskey/Service.cs | 60 +++++---- .../Services/Misskey/SettingForm.Designer.cs | 1 - .../Services/Misskey/SettingForm.cs | 24 ++-- LegatoNowPlaying/SettingWindow.ja.resx | 57 +++----- LegatoNowPlaying/SettingWindow.resx | 5 +- LegatoNowPlaying/packages.config | 1 - Misq/App.cs | 122 ++++++++++++++++++ Misq/Core.cs | 58 +++++++++ Misq/Entities/User.cs | 43 ++++++ Misq/Me.cs | 97 ++++++++++++++ Misq/Misq.csproj | 66 ++++++++++ Misq/Misq.nuspec | 20 +++ Misq/Properties/AssemblyInfo.cs | 36 ++++++ Misq/README.md | 3 + Misq/packages.config | 4 + 25 files changed, 817 insertions(+), 102 deletions(-) create mode 100644 LegatoNowPlaying/ErrorLogger.cs create mode 100644 Misq/App.cs create mode 100644 Misq/Core.cs create mode 100644 Misq/Entities/User.cs create mode 100644 Misq/Me.cs create mode 100644 Misq/Misq.csproj create mode 100644 Misq/Misq.nuspec create mode 100644 Misq/Properties/AssemblyInfo.cs create mode 100644 Misq/README.md create mode 100644 Misq/packages.config diff --git a/LegatoNowPlaying.sln b/LegatoNowPlaying.sln index 8cb8c17..0f4ef7c 100644 --- a/LegatoNowPlaying.sln +++ b/LegatoNowPlaying.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.10 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31624.102 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LegatoNowPlaying", "LegatoNowPlaying\LegatoNowPlaying.csproj", "{98ED99F2-1323-4153-B1D1-44874270FD31}" EndProject @@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Misq", "Misq\Misq.csproj", "{79333B32-8A49-4937-9732-DBAC9BE770FF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,6 +22,10 @@ Global {98ED99F2-1323-4153-B1D1-44874270FD31}.Debug|Any CPU.Build.0 = Debug|Any CPU {98ED99F2-1323-4153-B1D1-44874270FD31}.Release|Any CPU.ActiveCfg = Release|Any CPU {98ED99F2-1323-4153-B1D1-44874270FD31}.Release|Any CPU.Build.0 = Release|Any CPU + {79333B32-8A49-4937-9732-DBAC9BE770FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79333B32-8A49-4937-9732-DBAC9BE770FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79333B32-8A49-4937-9732-DBAC9BE770FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79333B32-8A49-4937-9732-DBAC9BE770FF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/LegatoNowPlaying/Accounts.cs b/LegatoNowPlaying/Accounts.cs index 20a3713..adc2a02 100644 --- a/LegatoNowPlaying/Accounts.cs +++ b/LegatoNowPlaying/Accounts.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Drawing; using System.Threading.Tasks; +using System.Windows.Forms; namespace LegatoNowPlaying { @@ -11,18 +12,34 @@ public class Accounts public async void Init() { - Services.Add(new Services.Misskey.Service()); - Services.Add(new Services.Twitter.Service()); + this.Services.Add(new Services.Misskey.Service()); + this.Services.Add(new Services.Twitter.Service()); - foreach (var service in Services) + for (var i = 0; i < this.Services.Count; i++) { - await service.Setup(); + try + { + await this.Services[i].Setup(); + } + catch + { + MessageBox.Show( + $"サービス「{this.Services[i].Name}」の設定ファイル読込み時にエラーが発生しました。\r\n" + + $"サービス「{this.Services[i].Name}」は無効化されます。\r\n" + + "設定ファイルが古い形式で保存されている等が考えられるため、設定ファイルを削除して再設定すると問題が解決する可能性があります。", + "エラー", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + + this.Services.RemoveAt(i); + i--; + } } } public async Task Post(string text, Image albumArt) { - foreach (var service in Services) + foreach (var service in this.Services) { if (service.Enabled && service.IsInstalled) { diff --git a/LegatoNowPlaying/App.config b/LegatoNowPlaying/App.config index be1f062..5688b7e 100644 --- a/LegatoNowPlaying/App.config +++ b/LegatoNowPlaying/App.config @@ -5,4 +5,12 @@ + + + + + + + + diff --git a/LegatoNowPlaying/ErrorLogger.cs b/LegatoNowPlaying/ErrorLogger.cs new file mode 100644 index 0000000..a97a03b --- /dev/null +++ b/LegatoNowPlaying/ErrorLogger.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace LegatoNowPlaying +{ + public static class ErrorLogger + { + public static async Task LogException(Exception ex, bool isInnerException = false) + { + using (var log = new StreamWriter("errors.log", true, Encoding.UTF8)) + { + if (!isInnerException) + { + await log.WriteLineAsync("------------------------------"); + await log.WriteLineAsync(""); + await log.WriteLineAsync("# Exception"); + await log.WriteLineAsync($"{DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}"); + } + else + { + await log.WriteLineAsync("# Inner Exception"); + } + + await log.WriteLineAsync(""); + await log.WriteLineAsync($"[Message] {ex.Message}"); + await log.WriteLineAsync("[StackTrace]"); + await log.WriteLineAsync($"{ex.StackTrace}"); + await log.WriteLineAsync(""); + } + + if (ex.InnerException != null) + { + await LogException(ex.InnerException, true); + } + } + } +} diff --git a/LegatoNowPlaying/LegatoNowPlaying.csproj b/LegatoNowPlaying/LegatoNowPlaying.csproj index 66f22d3..ccc619d 100644 --- a/LegatoNowPlaying/LegatoNowPlaying.csproj +++ b/LegatoNowPlaying/LegatoNowPlaying.csproj @@ -47,9 +47,6 @@ ..\packages\Legato.1.1.2\lib\net461\Legato.dll - - ..\packages\Misq.6.0.0\lib\net461\Misq.dll - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll @@ -67,6 +64,7 @@ + Form @@ -155,6 +153,12 @@ + + + {79333b32-8a49-4937-9732-dbac9be770ff} + Misq + + diff --git a/LegatoNowPlaying/MainForm.cs b/LegatoNowPlaying/MainForm.cs index 7d90654..2b13757 100644 --- a/LegatoNowPlaying/MainForm.cs +++ b/LegatoNowPlaying/MainForm.cs @@ -155,6 +155,8 @@ private async Task _PostAsync() var albumArt = this._GetAlbumArt(); + + await _Accounts.Post(text, this.checkBoxNeedAlbumArt.Checked ? albumArt : null); } diff --git a/LegatoNowPlaying/Program.cs b/LegatoNowPlaying/Program.cs index 81300c2..c68e6a9 100644 --- a/LegatoNowPlaying/Program.cs +++ b/LegatoNowPlaying/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows.Forms; namespace LegatoNowPlaying @@ -19,7 +19,8 @@ static void Main() } catch (Exception ex) { - MessageBox.Show($"内容:\r\n{ex.Message}\r\n\r\nスタックトレース:\r\n{ex.StackTrace}", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); + ErrorLogger.LogException(ex).Wait(); + MessageBox.Show($"エラーが発生しました。\r\n詳細についてはerrors.logを確認してください。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } diff --git a/LegatoNowPlaying/Services/Misskey/AuthForm.Designer.cs b/LegatoNowPlaying/Services/Misskey/AuthForm.Designer.cs index 214457b..9ee63d9 100644 --- a/LegatoNowPlaying/Services/Misskey/AuthForm.Designer.cs +++ b/LegatoNowPlaying/Services/Misskey/AuthForm.Designer.cs @@ -30,6 +30,11 @@ private void InitializeComponent() { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AuthForm)); this.label5 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.panel1 = new System.Windows.Forms.Panel(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.panel1.SuspendLayout(); this.SuspendLayout(); // // label5 @@ -37,16 +42,43 @@ private void InitializeComponent() resources.ApplyResources(this.label5, "label5"); this.label5.Name = "label5"; // + // button1 + // + resources.ApplyResources(this.button1, "button1"); + this.button1.Name = "button1"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // panel1 + // + this.panel1.Controls.Add(this.label1); + this.panel1.Controls.Add(this.textBox1); + this.panel1.Controls.Add(this.button1); + resources.ApplyResources(this.panel1, "panel1"); + this.panel1.Name = "panel1"; + // + // textBox1 + // + resources.ApplyResources(this.textBox1, "textBox1"); + this.textBox1.Name = "textBox1"; + // + // label1 + // + resources.ApplyResources(this.label1, "label1"); + this.label1.Name = "label1"; + // // AuthForm // resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.panel1); this.Controls.Add(this.label5); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "AuthForm"; - this.Load += new System.EventHandler(this.AuthForm_Load); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -54,5 +86,9 @@ private void InitializeComponent() #endregion private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox textBox1; } } \ No newline at end of file diff --git a/LegatoNowPlaying/Services/Misskey/AuthForm.cs b/LegatoNowPlaying/Services/Misskey/AuthForm.cs index 0731969..f5228a7 100644 --- a/LegatoNowPlaying/Services/Misskey/AuthForm.cs +++ b/LegatoNowPlaying/Services/Misskey/AuthForm.cs @@ -1,11 +1,13 @@ +using Newtonsoft.Json.Linq; using System; +using System.Collections.Generic; using System.Windows.Forms; namespace LegatoNowPlaying.Services.Misskey { public partial class AuthForm : Form { - public delegate void OnComplete(Misq.Me me); + public delegate void OnComplete(Misq.Me me, Misq.App app); private OnComplete onComplete; @@ -15,15 +17,66 @@ public AuthForm(OnComplete onComplete) InitializeComponent(); } - private async void AuthForm_Load(object sender, EventArgs e) + private async void button1_Click(object sender, EventArgs e) { - var app = new Misq.App("https://misskey.xyz", Misskey.Service.appKey); + try + { + new Uri(textBox1.Text); + } + catch(UriFormatException ex) + { + MessageBox.Show( + $"url of target instance is invalid", + "Error", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + return; + } + + panel1.Visible = false; + + JToken metaRes = await Misq.Core.Request(textBox1.Text, "meta", new Dictionary { }); + var versionSource = metaRes.Value("version"); + var versionMajorStr = versionSource.Split('.')[0]; + + if (!int.TryParse(versionMajorStr, out int version)) + { + MessageBox.Show( + $"invalid format of the Misskey version", + "Error", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + return; + } + + var permissionsList = new List(); + + if (version == 10) + { + permissionsList.AddRange(new[] { "note-write", "drive-write" }); + } + else if (version >= 11) + { + permissionsList.AddRange(new[] { "write:notes", "write:drive" }); + } + else + { + MessageBox.Show( + $"Unsupported the Misskey instance version", + "Error", + MessageBoxButtons.OK, + MessageBoxIcon.Exclamation); + return; + } + + var app = await Misq.App.Register( + textBox1.Text, "Legato Nowplaying", "A NowPlaying App for AIMP4", permissionsList); var me = await app.Authorize(); this.Close(); - this.onComplete(me); + this.onComplete(me, app); MessageBox.Show( - $"WELCOME {me.Username}", + $"WELCOME @{me.Username}", "Done", MessageBoxButtons.OK, MessageBoxIcon.Information); diff --git a/LegatoNowPlaying/Services/Misskey/AuthForm.resx b/LegatoNowPlaying/Services/Misskey/AuthForm.resx index 994977b..8797c97 100644 --- a/LegatoNowPlaying/Services/Misskey/AuthForm.resx +++ b/LegatoNowPlaying/Services/Misskey/AuthForm.resx @@ -147,6 +147,126 @@ $this + 1 + + + 218, 54 + + + 80, 23 + + + 6 + + + Authorize + + + button1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 2 + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 0 + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 1 + + + 12, 12 + + + 321, 117 + + + 7 + + + panel1 + + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + 24, 56 + + + 188, 19 + + + 7 + + + https://misskey.io + + + textBox1 + + + System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + + 1 + + + True + + + 22, 40 + + + 87, 12 + + + 8 + + + Target Instance: + + + label1 + + + System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panel1 + + 0 diff --git a/LegatoNowPlaying/Services/Misskey/Service.cs b/LegatoNowPlaying/Services/Misskey/Service.cs index 0803747..135cec9 100644 --- a/LegatoNowPlaying/Services/Misskey/Service.cs +++ b/LegatoNowPlaying/Services/Misskey/Service.cs @@ -7,27 +7,26 @@ namespace LegatoNowPlaying.Services.Misskey { public class Service : Services.Service { - public const string appKey = "z31SlkbuIonQ5G1tdx4j7xvGRL7XS51y"; + private Misq.Me Me; - private Misq.Me me; - - private CredentialsJsonFile _Config { get; set; } + private CredentialsJsonFile Config { get; set; } public override string Name { get; } = "Misskey"; - public override bool IsInstalled => me != null && me.UserToken != null; + public override bool IsInstalled => this.Me != null && this.Me.UserToken != null; public override bool HasSetting { get; } = true; public override Task Install() { var s = new TaskCompletionSource(); - - var form = new Services.Misskey.AuthForm(async (Misq.Me me) => { - _Config.Token = me.UserToken; - _Config.Host = me.Host; - _Config.AccountName = "@" + me.Username; - await _Config.SaveAsync(); + + var form = new Services.Misskey.AuthForm(async (Misq.Me me, Misq.App app) => { + this.Config.Token = me.UserToken; + this.Config.Secret = app.Secret; + this.Config.Host = me.Host; + this.Config.AccountName = "@" + me.Username; + await this.Config.SaveAsync(); await Setup(); s.SetResult(true); @@ -39,19 +38,22 @@ public override Task Install() public override async Task Setup() { - _Config = await CredentialsJsonFile.LoadAsync(); + this.Config = await CredentialsJsonFile.LoadAsync(); - this.me = new Misq.Me(_Config.Host, _Config.Token, appKey); - Enabled = _Config.Enabled; - AccountName = _Config.AccountName; + if (this.Config.Host != null && this.Config.Token != null && this.Config.Secret != null) + { + this.Me = new Misq.Me(this.Config.Host, this.Config.Token, this.Config.Secret); + } + this.Enabled = this.Config.Enabled; + this.AccountName = this.Config.AccountName; } public override async Task ToggleEnable() { - Enabled = !Enabled; + this.Enabled = !this.Enabled; - _Config.Enabled = Enabled; - await _Config.SaveAsync(); + this.Config.Enabled = this.Enabled; + await this.Config.SaveAsync(); } public override async Task Post(string text, Image albumArt) @@ -61,7 +63,7 @@ public override async Task Post(string text, Image albumArt) { "text", text } }; - if (!_Config.PostToLtl) + if (!this.Config.PostToLtl) { ps.Add("visibility", "home"); } @@ -80,17 +82,17 @@ public override async Task Post(string text, Image albumArt) { new ByteArrayContent(img), "file", "cover.jpg" } }; - var file = await this.me.RequestWithBinary("drive/files/create", form); + var file = await this.Me.RequestWithBinary("drive/files/create", form); ps.Add("mediaIds", new string[] { file.id }); } - await this.me.Request("notes/create", ps); + await this.Me.Request("notes/create", ps); } public override Task Setting() { - var form = new Services.Misskey.SettingForm(); + var form = new Services.Misskey.SettingForm(this.Config); form.Show(); return Task.CompletedTask; @@ -106,6 +108,8 @@ public class CredentialsJsonFile : JsonFile public string Token { get; set; } + public string Secret { get; set; } + public string Host { get; set; } public bool PostToLtl { get; set; } @@ -119,9 +123,17 @@ public class CredentialsJsonFile : JsonFile /// /// misskey.json からアカウント情報を読み込みます /// - public static Task LoadAsync() + public static async Task LoadAsync() { - return LoadAsync("misskey.json"); + var setting = await LoadAsync("misskey.json"); + + if (setting.Token == null || setting.Secret == null) + { + setting.Token = null; + setting.Secret = null; + } + + return setting; } /// diff --git a/LegatoNowPlaying/Services/Misskey/SettingForm.Designer.cs b/LegatoNowPlaying/Services/Misskey/SettingForm.Designer.cs index 4d7d414..ea89a74 100644 --- a/LegatoNowPlaying/Services/Misskey/SettingForm.Designer.cs +++ b/LegatoNowPlaying/Services/Misskey/SettingForm.Designer.cs @@ -58,7 +58,6 @@ private void InitializeComponent() this.Name = "SettingForm"; this.ShowIcon = false; this.ShowInTaskbar = false; - this.Load += new System.EventHandler(this.SettingForm_Load); this.ResumeLayout(false); this.PerformLayout(); diff --git a/LegatoNowPlaying/Services/Misskey/SettingForm.cs b/LegatoNowPlaying/Services/Misskey/SettingForm.cs index 4472067..9ae2fb3 100644 --- a/LegatoNowPlaying/Services/Misskey/SettingForm.cs +++ b/LegatoNowPlaying/Services/Misskey/SettingForm.cs @@ -1,33 +1,27 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace LegatoNowPlaying.Services.Misskey { public partial class SettingForm : Form { - public SettingForm() + public SettingForm(CredentialsJsonFile config) { InitializeComponent(); - } - private async void SettingForm_Load(object sender, EventArgs e) - { - var config = await CredentialsJsonFile.LoadAsync(); + this.Config = config; + this.checkBox1.Checked = config.PostToLtl; } + private CredentialsJsonFile Config; + + // save button private async void button1_Click(object sender, EventArgs e) { - var config = await CredentialsJsonFile.LoadAsync(); - config.PostToLtl = this.checkBox1.Checked; - await config.SaveAsync(); + this.Config.PostToLtl = this.checkBox1.Checked; + await this.Config.SaveAsync(); + this.Close(); } } diff --git a/LegatoNowPlaying/SettingWindow.ja.resx b/LegatoNowPlaying/SettingWindow.ja.resx index a476bb4..06b55b3 100644 --- a/LegatoNowPlaying/SettingWindow.ja.resx +++ b/LegatoNowPlaying/SettingWindow.ja.resx @@ -130,26 +130,14 @@ 通知の時間 : - - 77, 18 - - - 投稿時の音 : - - - 367, 25 - - - 6, 301 - - - 77, 18 + + 527, 368 - - 終了時の音 : + + 519, 337 - - 367, 25 + + 投稿 284, 223 @@ -215,11 +203,11 @@ 20 - + 519, 337 - - 投稿 + + 全般 123, 22 @@ -227,17 +215,11 @@ 常に最前面に表示 - - 467, 269 - - - 467, 298 - - + 519, 337 - - 全般 + + アカウント 設定... @@ -257,11 +239,11 @@ アカウント名 - + 519, 337 - - アカウント + + バージョン情報 42, 24 @@ -269,15 +251,6 @@ よお - - 519, 337 - - - バージョン情報 - - - 527, 368 - 50, 12 diff --git a/LegatoNowPlaying/SettingWindow.resx b/LegatoNowPlaying/SettingWindow.resx index a38d9e4..927120d 100644 --- a/LegatoNowPlaying/SettingWindow.resx +++ b/LegatoNowPlaying/SettingWindow.resx @@ -807,7 +807,10 @@ 6, 12 - 539, 431 + 539, 432 + + + NoControl 1024, 470 diff --git a/LegatoNowPlaying/packages.config b/LegatoNowPlaying/packages.config index 11e5ad3..82cec32 100644 --- a/LegatoNowPlaying/packages.config +++ b/LegatoNowPlaying/packages.config @@ -3,6 +3,5 @@ - \ No newline at end of file diff --git a/Misq/App.cs b/Misq/App.cs new file mode 100644 index 0000000..12e1bdc --- /dev/null +++ b/Misq/App.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Misq +{ + /// + /// Misskeyアプリクラス + /// + public class App + { + /// + /// アプリケーションの属するインスタンスURLを取得または設定します。 + /// + public string Host + { + get; + set; + } + + /// + /// アプリケーションのシークレット・キーを取得または設定します。 + /// + public string Secret + { + get; + set; + } + + /// + /// Appインスタンスを初期化します。 + /// + /// アプリケーションの属するインスタンスURL + /// アプリケーションのシークレットキー + public App(string host, string secret) + { + this.Host = host; + this.Secret = secret; + } + + /// + /// 認証セッションを開始し、フォームを既定のブラウザーで表示します。 + /// + /// ユーザー + public async Task Authorize() + { + var obj = await this.Request("auth/session/generate"); + + var token = obj.token.Value; + var url = obj.url.Value; + + // 規定のブラウザで表示 + System.Diagnostics.Process.Start(url); + + Func> check = async () => { + var a = await this.Request("auth/session/userkey", new Dictionary { + { "token", token } + }); + + return a.accessToken != null ? a : null; + }; + + dynamic x = null; + + while (x == null) + { + x = await check(); + await Task.Delay(1000); + } + + var accessToken = x.accessToken.Value; + var userData = x.user; + + return new Me(this.Host, accessToken, this.Secret, userData); + } + + /// + /// このアプリからAPIにリクエストします。 + /// + /// エンドポイント名 + /// レスポンス + public async Task Request(string endpoint) + { + return await Core.Request(this.Host, endpoint, new Dictionary { + { "appSecret", this.Secret } + }); + } + + /// + /// このアプリからAPIにリクエストします。 + /// + /// エンドポイント名 + /// パラメーター + /// レスポンス + public async Task Request(string endpoint, Dictionary ps) + { + ps.Add("appSecret", this.Secret); + return await Core.Request(this.Host, endpoint, ps); + } + + /// + /// Misskeyアプリを登録します。 + /// + /// アプリの属するインスタンスURL + /// アプリの名前 + /// アプリの説明文 + /// アプリから利用可能な権限 + /// 登録されたアプリ + public static async Task Register(string host, string appName, string appDescription, IEnumerable permissions) + { + var ps = new Dictionary + { + ["name"] = appName, + ["description"] = appDescription, + ["permission"] = permissions + }; + var app = await Core.Request(host, "app/create", ps); + + return new App(host, app.secret.Value); + } + } +} diff --git a/Misq/Core.cs b/Misq/Core.cs new file mode 100644 index 0000000..eb074b0 --- /dev/null +++ b/Misq/Core.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Misq +{ + public static class Core + { + static Lazy _client = new Lazy(); + /// + /// APIにリクエストします。 + /// + /// MisskeyインスタンスのURL + /// エンドポイント名 + /// パラメーター + /// レスポンス + public static async Task Request(string host, string endpoint, Dictionary ps) + { + var client = _client.Value; + + var ep = $"{host}/api/{endpoint}"; + + var content = new StringContent(JsonConvert.SerializeObject(ps), + Encoding.UTF8, "application/json"); + + var res = await client.PostAsync(ep, content); + + var obj = JsonConvert.DeserializeObject( + await res.Content.ReadAsStringAsync()); + + return obj; + } + + /// + /// APIにリクエストします。 + /// + /// MisskeyインスタンスのURL + /// エンドポイント名 + /// パラメーター + /// レスポンス + public static async Task RequestWithBinary(string host, string endpoint, MultipartFormDataContent ps) + { + var client = _client.Value; + + var ep = $"{host}/api/{endpoint}"; + + var res = await client.PostAsync(ep, ps); + + var obj = JsonConvert.DeserializeObject( + await res.Content.ReadAsStringAsync()); + + return obj; + } + } +} diff --git a/Misq/Entities/User.cs b/Misq/Entities/User.cs new file mode 100644 index 0000000..ffc6c7b --- /dev/null +++ b/Misq/Entities/User.cs @@ -0,0 +1,43 @@ +namespace Misq.Entities +{ + public class User + { + /// + /// ID + /// + public string ID + { + get; + } + + /// + /// 名前 + /// + public string Name + { + get; + } + + /// + /// ユーザー名 + /// + public string Username + { + get; + } + + /// + /// データソースを与えてユーザーを初期化します。 + /// + /// データソース + public User(dynamic source) + { + if (source != null) + { + this.ID = source.id; + this.Name = source.name; + this.Username = source.username; + } + } + } +} diff --git a/Misq/Me.cs b/Misq/Me.cs new file mode 100644 index 0000000..1641f2d --- /dev/null +++ b/Misq/Me.cs @@ -0,0 +1,97 @@ +using System.Security.Cryptography; +using System.Collections.Generic; +using System.Threading.Tasks; +using System; +using System.Text; +using System.Linq; +using System.Net.Http; + +namespace Misq +{ + public class Me : Entities.User + { + /// + /// アクセストークン + /// + public string Token + { + get; + } + + /// + /// ユーザートークン + /// + public string UserToken + { + get; + } + + /// + /// インスタンスURL + /// + public string Host + { + get; + } + + public Me(string Host, string userToken, string appSecret) : base(null) + { + this.Host = Host; + this.UserToken = userToken; + this.Token = this.GenerateAccessToken(userToken, appSecret); + } + + public Me(string Host, string userToken, string appSecret, dynamic user) : base((object)user) + { + this.Host = Host; + this.UserToken = userToken; + this.Token = this.GenerateAccessToken(userToken, appSecret); + } + + private string GenerateAccessToken(string userToken, string appSecret) + { + using (var hash = SHA256Managed.Create()) + { + return String.Concat(hash + .ComputeHash(Encoding.UTF8.GetBytes(userToken + appSecret)) + .Select(item => item.ToString("x2"))); + } + } + + /// + /// このユーザーからAPIにリクエストします。 + /// + /// エンドポイント名 + /// レスポンス + public async Task Request(string endpoint) + { + return await Core.Request(this.Host, endpoint, new Dictionary { + { "i", this.Token } + }); + } + + /// + /// このユーザーからAPIにリクエストします。 + /// + /// エンドポイント名 + /// パラメーター + /// レスポンス + public async Task Request(string endpoint, Dictionary ps) + { + ps.Add("i", this.Token); + return await Core.Request(this.Host, endpoint, ps); + } + + /// + /// このユーザーからAPIにリクエストします。 + /// + /// エンドポイント名 + /// パラメーター + /// レスポンス + public async Task RequestWithBinary(string endpoint, MultipartFormDataContent ps) + { + ps.Add(new StringContent(this.Token), "i"); + return await Core.RequestWithBinary(this.Host, endpoint, ps); + } + } +} diff --git a/Misq/Misq.csproj b/Misq/Misq.csproj new file mode 100644 index 0000000..5dff9f4 --- /dev/null +++ b/Misq/Misq.csproj @@ -0,0 +1,66 @@ + + + + + Debug + AnyCPU + {79333B32-8A49-4937-9732-DBAC9BE770FF} + Library + Properties + Misq + Misq + v4.6.1 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Misq.xml + + + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Misq/Misq.nuspec b/Misq/Misq.nuspec new file mode 100644 index 0000000..9283f2c --- /dev/null +++ b/Misq/Misq.nuspec @@ -0,0 +1,20 @@ + + + + Misq + 6.1.0 + Misq + syuilo + syuilo + MIT + https://github.com/syuilo/Misq + false + Misskey library + Yo + Copyright (c) syuilo 2019 + Misskey + + + + + diff --git a/Misq/Properties/AssemblyInfo.cs b/Misq/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..18f45ef --- /dev/null +++ b/Misq/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("Misq")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Misq")] +[assembly: AssemblyCopyright("Copyright © syuilo 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから +// 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(true)] + +// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります +[assembly: Guid("79333b32-8a49-4937-9732-dbac9be770ff")] + +// アセンブリのバージョン情報は次の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// Revision +// +// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を +// 既定値にすることができます: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("5.0.1.0")] +[assembly: AssemblyFileVersion("5.0.1.0")] diff --git a/Misq/README.md b/Misq/README.md new file mode 100644 index 0000000..cb56113 --- /dev/null +++ b/Misq/README.md @@ -0,0 +1,3 @@ +# Misq +A fork of Misq +https://github.com/syuilo/Misq diff --git a/Misq/packages.config b/Misq/packages.config new file mode 100644 index 0000000..9d64bf3 --- /dev/null +++ b/Misq/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file