mirror of
https://github.com/osukey/osukey.git
synced 2025-04-29 02:37:25 +09:00
魔術
This commit is contained in:
parent
646d2a0e1d
commit
35d12d7b1c
@ -17,7 +17,7 @@ indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
#license header
|
||||
file_header_template = Copyright (c) ppy Pty Ltd <contact@ppy.sh>, sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
|
||||
file_header_template = Copyright (c) sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.\nSee the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#Roslyn naming styles
|
||||
|
||||
|
11
.idea/.idea.osu/.idea/aws.xml
generated
11
.idea/.idea.osu/.idea/aws.xml
generated
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="accountSettings">
|
||||
<option name="activeRegion" value="us-east-1" />
|
||||
<option name="recentlyUsedRegions">
|
||||
<list>
|
||||
<option value="us-east-1" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -65,8 +65,8 @@ namespace osu.Android
|
||||
|
||||
Debug.Assert(Window != null);
|
||||
|
||||
Window.AddFlags(WindowManagerFlags.Fullscreen);
|
||||
Window.AddFlags(WindowManagerFlags.KeepScreenOn);
|
||||
// Window.AddFlags(WindowManagerFlags.Fullscreen);
|
||||
// Window.AddFlags(WindowManagerFlags.KeepScreenOn);
|
||||
|
||||
Debug.Assert(WindowManager?.DefaultDisplay != null);
|
||||
Debug.Assert(Resources?.DisplayMetrics != null);
|
||||
@ -76,7 +76,7 @@ namespace osu.Android
|
||||
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
|
||||
bool isTablet = smallestWidthDp >= 600f;
|
||||
|
||||
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
|
||||
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.User;
|
||||
}
|
||||
|
||||
protected override void OnNewIntent(Intent intent) => handleIntent(intent);
|
||||
|
@ -163,6 +163,8 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.DiscordRichPresence, DiscordRichPresenceMode.Full);
|
||||
|
||||
SetDefault(OsuSetting.EditorWaveformOpacity, 0.25f);
|
||||
|
||||
SetDefault(OsuSetting.MisskeyToken, string.Empty);
|
||||
}
|
||||
|
||||
public IDictionary<OsuSetting, string> GetLoggableState() =>
|
||||
@ -358,5 +360,9 @@ namespace osu.Game.Configuration
|
||||
DiscordRichPresence,
|
||||
AutomaticallyDownloadWhenSpectating,
|
||||
ShowOnlineExplicitContent,
|
||||
|
||||
//Misskey
|
||||
|
||||
MisskeyToken,
|
||||
}
|
||||
}
|
||||
|
483
osu.Game/Online/MisskeyAPI/APIAccess.cs
Normal file
483
osu.Game/Online/MisskeyAPI/APIAccess.cs
Normal file
@ -0,0 +1,483 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>, sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.ExceptionExtensions;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Online.MisskeyAPI.Requests;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class APIAccess : Component, IAPIProvider
|
||||
{
|
||||
private readonly OsuConfigManager config;
|
||||
|
||||
private readonly string versionHash;
|
||||
|
||||
private readonly Auth authentication;
|
||||
|
||||
private readonly Queue<APIRequest> queue = new Queue<APIRequest>();
|
||||
|
||||
public string APIEndpointUrl { get; }
|
||||
|
||||
public int APIVersion => 20220217; // We may want to pull this from the game version eventually.
|
||||
|
||||
public Exception LastLoginError { get; private set; }
|
||||
|
||||
public string ProvidedUsername { get; private set; }
|
||||
|
||||
private string password;
|
||||
|
||||
public IBindable<Requests.Responses.I> LocalUser => localUser;
|
||||
//public IBindableList<I> Friends => friends;
|
||||
public IBindable<UserActivity> Activity => activity;
|
||||
|
||||
private Bindable<Requests.Responses.I> localUser { get; } = new Bindable<Requests.Responses.I>(createGuestUser());
|
||||
|
||||
//private BindableList<I> friends { get; } = new BindableList<I>();
|
||||
|
||||
private Bindable<UserActivity> activity { get; } = new Bindable<UserActivity>();
|
||||
|
||||
protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password));
|
||||
|
||||
private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource();
|
||||
|
||||
private readonly Logger log;
|
||||
|
||||
public APIAccess(OsuConfigManager config, EndpointConfiguration endpointConfiguration, string versionHash)
|
||||
{
|
||||
this.config = config;
|
||||
this.versionHash = versionHash;
|
||||
|
||||
APIEndpointUrl = endpointConfiguration.APIEndpointUrl;
|
||||
|
||||
authentication = new Auth(APIEndpointUrl);
|
||||
log = Logger.GetLogger(LoggingTarget.Network);
|
||||
|
||||
ProvidedUsername = config.Get<string>(OsuSetting.Username);
|
||||
|
||||
authentication.TokenString = config.Get<string>(OsuSetting.MisskeyToken);
|
||||
authentication.Token.ValueChanged += onTokenChanged;
|
||||
|
||||
var thread = new Thread(run)
|
||||
{
|
||||
Name = "APIAccess",
|
||||
IsBackground = true
|
||||
};
|
||||
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
private void onTokenChanged(ValueChangedEvent<AuthToken> e) => config.SetValue(OsuSetting.MisskeyToken, config.Get<bool>(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty);
|
||||
|
||||
internal new void Schedule(Action action) => base.Schedule(action);
|
||||
|
||||
public string AccessToken => authentication.RequestAccessToken();
|
||||
|
||||
/// <summary>
|
||||
/// Number of consecutive requests which failed due to network issues.
|
||||
/// </summary>
|
||||
private int failureCount;
|
||||
|
||||
private void run()
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
switch (State.Value)
|
||||
{
|
||||
case APIState.Failing:
|
||||
//todo: replace this with a ping request.
|
||||
log.Add(@"In a failing state, waiting a bit before we try again...");
|
||||
Thread.Sleep(5000);
|
||||
|
||||
if (!IsLoggedIn) goto case APIState.Connecting;
|
||||
|
||||
if (queue.Count == 0)
|
||||
{
|
||||
log.Add(@"Queueing a ping request");
|
||||
Queue(new Requests.I());
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case APIState.Offline:
|
||||
case APIState.Connecting:
|
||||
// work to restore a connection...
|
||||
if (!HasLogin)
|
||||
{
|
||||
state.Value = APIState.Offline;
|
||||
Thread.Sleep(50);
|
||||
continue;
|
||||
}
|
||||
|
||||
state.Value = APIState.Connecting;
|
||||
|
||||
// save the username at this point, if the user requested for it to be.
|
||||
config.SetValue(OsuSetting.Username, config.Get<bool>(OsuSetting.SaveUsername) ? ProvidedUsername : string.Empty);
|
||||
|
||||
if (!authentication.HasValidAccessToken)
|
||||
{
|
||||
LastLoginError = null;
|
||||
|
||||
try
|
||||
{
|
||||
authentication.AuthenticateWithLogin(ProvidedUsername, password);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//todo: this fails even on network-related issues. we should probably handle those differently.
|
||||
LastLoginError = e;
|
||||
log.Add(@"Login failed!");
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var userReq = new Requests.I();
|
||||
|
||||
userReq.Failure += ex =>
|
||||
{
|
||||
if (ex is WebException webException && webException.Message == @"Unauthorized")
|
||||
{
|
||||
log.Add(@"Login no longer valid");
|
||||
Logout();
|
||||
}
|
||||
else
|
||||
failConnectionProcess();
|
||||
};
|
||||
userReq.Success += u =>
|
||||
{
|
||||
localUser.Value = u;
|
||||
|
||||
// todo: save/pull from settings
|
||||
// localUser.Value.Status.Value = new UserStatusOnline();
|
||||
|
||||
failureCount = 0;
|
||||
};
|
||||
|
||||
if (!handleRequest(userReq))
|
||||
{
|
||||
failConnectionProcess();
|
||||
continue;
|
||||
}
|
||||
|
||||
// getting user's friends is considered part of the connection process.
|
||||
// var friendsReq = new GetFriendsRequest();
|
||||
//
|
||||
// friendsReq.Failure += _ => failConnectionProcess();
|
||||
// friendsReq.Success += res =>
|
||||
// {
|
||||
// friends.AddRange(res);
|
||||
//
|
||||
// //we're connected!
|
||||
// state.Value = APIState.Online;
|
||||
// };
|
||||
//
|
||||
// if (!handleRequest(friendsReq))
|
||||
// {
|
||||
// failConnectionProcess();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// The Success callback event is fired on the main thread, so we should wait for that to run before proceeding.
|
||||
// Without this, we will end up circulating this Connecting loop multiple times and queueing up many web requests
|
||||
// before actually going online.
|
||||
while (State.Value > APIState.Offline && State.Value < APIState.Online)
|
||||
Thread.Sleep(500);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// hard bail if we can't get a valid access token.
|
||||
if (authentication.RequestAccessToken() == null)
|
||||
{
|
||||
Logout();
|
||||
continue;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
APIRequest req;
|
||||
|
||||
lock (queue)
|
||||
{
|
||||
if (queue.Count == 0) break;
|
||||
|
||||
req = queue.Dequeue();
|
||||
}
|
||||
|
||||
handleRequest(req);
|
||||
}
|
||||
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
void failConnectionProcess()
|
||||
{
|
||||
// if something went wrong during the connection process, we want to reset the state (but only if still connecting).
|
||||
if (State.Value == APIState.Connecting)
|
||||
state.Value = APIState.Failing;
|
||||
}
|
||||
}
|
||||
|
||||
public void Perform(APIRequest request)
|
||||
{
|
||||
try
|
||||
{
|
||||
request.Perform(this);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// todo: fix exception handling
|
||||
request.Fail(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Task PerformAsync(APIRequest request) =>
|
||||
Task.Factory.StartNew(() => Perform(request), TaskCreationOptions.LongRunning);
|
||||
|
||||
public void Login(string username, string password)
|
||||
{
|
||||
Debug.Assert(State.Value == APIState.Offline);
|
||||
|
||||
ProvidedUsername = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
// public IHubClientConnector GetHubConnector(string clientName, string endpoint, bool preferMessagePack) =>
|
||||
// new HubClientConnector(clientName, endpoint, this, versionHash, preferMessagePack);
|
||||
|
||||
public IHubClientConnector GetHubConnector(string clientName, string endpoint, bool preferMessagePack = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
|
||||
{
|
||||
Debug.Assert(State.Value == APIState.Offline);
|
||||
|
||||
var req = new RegistrationRequest
|
||||
{
|
||||
Url = $@"{APIEndpointUrl}/users",
|
||||
Method = HttpMethod.Post,
|
||||
Username = username,
|
||||
Email = email,
|
||||
Password = password
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
req.Perform();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
try
|
||||
{
|
||||
return JObject.Parse(req.GetResponseString().AsNonNull()).SelectToken("form_error", true).AsNonNull().ToObject<RegistrationRequest.RegistrationRequestErrors>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// if we couldn't deserialize the error message let's throw the original exception outwards.
|
||||
e.Rethrow();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single API request.
|
||||
/// Ensures all exceptions are caught and dealt with correctly.
|
||||
/// </summary>
|
||||
/// <param name="req">The request.</param>
|
||||
/// <returns>true if the request succeeded.</returns>
|
||||
private bool handleRequest(APIRequest req)
|
||||
{
|
||||
try
|
||||
{
|
||||
req.Perform(this);
|
||||
|
||||
if (req.CompletionState != APIRequestCompletionState.Completed)
|
||||
return false;
|
||||
|
||||
// we could still be in initialisation, at which point we don't want to say we're Online yet.
|
||||
if (IsLoggedIn) state.Value = APIState.Online;
|
||||
failureCount = 0;
|
||||
return true;
|
||||
}
|
||||
catch (HttpRequestException re)
|
||||
{
|
||||
log.Add($"{nameof(HttpRequestException)} while performing request {req}: {re.Message}");
|
||||
handleFailure();
|
||||
return false;
|
||||
}
|
||||
catch (SocketException se)
|
||||
{
|
||||
log.Add($"{nameof(SocketException)} while performing request {req}: {se.Message}");
|
||||
handleFailure();
|
||||
return false;
|
||||
}
|
||||
catch (WebException we)
|
||||
{
|
||||
log.Add($"{nameof(WebException)} while performing request {req}: {we.Message}");
|
||||
handleWebException(we);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(ex, "Error occurred while handling an API request.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Bindable<APIState> state = new Bindable<APIState>();
|
||||
|
||||
/// <summary>
|
||||
/// The current connectivity state of the API.
|
||||
/// </summary>
|
||||
public IBindable<APIState> State => state;
|
||||
|
||||
private void handleWebException(WebException we)
|
||||
{
|
||||
HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode
|
||||
?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout);
|
||||
|
||||
// special cases for un-typed but useful message responses.
|
||||
switch (we.Message)
|
||||
{
|
||||
case "Unauthorized":
|
||||
case "Forbidden":
|
||||
statusCode = HttpStatusCode.Unauthorized;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (statusCode)
|
||||
{
|
||||
case HttpStatusCode.Unauthorized:
|
||||
Logout();
|
||||
break;
|
||||
|
||||
case HttpStatusCode.RequestTimeout:
|
||||
handleFailure();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleFailure()
|
||||
{
|
||||
failureCount++;
|
||||
log.Add($@"API failure count is now {failureCount}");
|
||||
|
||||
if (failureCount >= 3 && State.Value == APIState.Online)
|
||||
{
|
||||
state.Value = APIState.Failing;
|
||||
flushQueue();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLoggedIn => localUser.Value.Id != "system" && localUser.Value.Id != "guest"; // TODO: should this also be true if attempting to connect?
|
||||
|
||||
public void Queue(APIRequest request)
|
||||
{
|
||||
lock (queue)
|
||||
{
|
||||
if (state.Value == APIState.Offline)
|
||||
{
|
||||
request.Fail(new WebException(@"User not logged in"));
|
||||
return;
|
||||
}
|
||||
|
||||
queue.Enqueue(request);
|
||||
}
|
||||
}
|
||||
|
||||
private void flushQueue(bool failOldRequests = true)
|
||||
{
|
||||
lock (queue)
|
||||
{
|
||||
var oldQueueRequests = queue.ToArray();
|
||||
|
||||
queue.Clear();
|
||||
|
||||
if (failOldRequests)
|
||||
{
|
||||
foreach (var req in oldQueueRequests)
|
||||
req.Fail(new WebException($@"Request failed from flush operation (state {state.Value})"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Logout()
|
||||
{
|
||||
password = null;
|
||||
authentication.Clear();
|
||||
|
||||
// Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present
|
||||
Schedule(() =>
|
||||
{
|
||||
localUser.Value = createGuestUser();
|
||||
// friends.Clear();
|
||||
});
|
||||
|
||||
state.Value = APIState.Offline;
|
||||
flushQueue();
|
||||
}
|
||||
|
||||
private static Requests.Responses.I createGuestUser() => new GuestUser();
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
flushQueue();
|
||||
cancellationToken.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
internal class GuestUser : Requests.Responses.I
|
||||
{
|
||||
public GuestUser()
|
||||
{
|
||||
Username = @"Guest";
|
||||
Id = SYSTEM_USER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
public enum APIState
|
||||
{
|
||||
/// <summary>
|
||||
/// We cannot login (not enough credentials).
|
||||
/// </summary>
|
||||
Offline,
|
||||
|
||||
/// <summary>
|
||||
/// We are having connectivity issues.
|
||||
/// </summary>
|
||||
Failing,
|
||||
|
||||
/// <summary>
|
||||
/// We are in the process of (re-)connecting.
|
||||
/// </summary>
|
||||
Connecting,
|
||||
|
||||
/// <summary>
|
||||
/// We are online.
|
||||
/// </summary>
|
||||
Online
|
||||
}
|
||||
}
|
@ -1,13 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class Login
|
||||
public class APIException : InvalidOperationException
|
||||
{
|
||||
public void login(string username, string password)
|
||||
public APIException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
236
osu.Game/Online/MisskeyAPI/APIRequest.cs
Normal file
236
osu.Game/Online/MisskeyAPI/APIRequest.cs
Normal file
@ -0,0 +1,236 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using JetBrains.Annotations;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
/// <summary>
|
||||
/// An API request with a well-defined response type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the response (used for deserialisation).</typeparam>
|
||||
public abstract class APIRequest<T> : APIRequest where T : class
|
||||
{
|
||||
protected override WebRequest CreateWebRequest() => new JsonWebRequest<T>(Uri);
|
||||
|
||||
/// <summary>
|
||||
/// The deserialised response object. May be null if the request or deserialisation failed.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public T Response { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked on successful completion of an API request.
|
||||
/// This will be scheduled to the API's internal scheduler (run on update thread automatically).
|
||||
/// </summary>
|
||||
public new event APISuccessHandler<T> Success;
|
||||
|
||||
protected APIRequest()
|
||||
{
|
||||
base.Success += () => Success?.Invoke(Response);
|
||||
}
|
||||
|
||||
protected override void PostProcess()
|
||||
{
|
||||
base.PostProcess();
|
||||
|
||||
if (WebRequest != null)
|
||||
{
|
||||
Response = ((JsonWebRequest<T>)WebRequest).ResponseObject;
|
||||
Logger.Log($"{GetType()} finished with response size of {WebRequest.ResponseStream.Length:#,0} bytes", LoggingTarget.Network);
|
||||
}
|
||||
}
|
||||
|
||||
internal void TriggerSuccess(T result)
|
||||
{
|
||||
if (Response != null)
|
||||
throw new InvalidOperationException("Attempted to trigger success more than once");
|
||||
|
||||
Response = result;
|
||||
|
||||
TriggerSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AN API request with no specified response type.
|
||||
/// </summary>
|
||||
public abstract class APIRequest
|
||||
{
|
||||
protected abstract string Target { get; }
|
||||
|
||||
protected virtual WebRequest CreateWebRequest() => new WebRequest(Uri);
|
||||
|
||||
protected virtual string Uri => $@"{API.APIEndpointUrl}/api/{Target}";
|
||||
|
||||
protected APIAccess API;
|
||||
protected WebRequest WebRequest;
|
||||
|
||||
//// <summary>
|
||||
//// The currently logged in user. Note that this will only be populated during <see cref="Perform"/>.
|
||||
//// </summary>
|
||||
// protected APIUser User { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Invoked on successful completion of an API request.
|
||||
/// This will be scheduled to the API's internal scheduler (run on update thread automatically).
|
||||
/// </summary>
|
||||
public event APISuccessHandler Success;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked on failure to complete an API request.
|
||||
/// This will be scheduled to the API's internal scheduler (run on update thread automatically).
|
||||
/// </summary>
|
||||
public event APIFailureHandler Failure;
|
||||
|
||||
private readonly object completionStateLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// The state of this request, from an outside perspective.
|
||||
/// This is used to ensure correct notification events are fired.
|
||||
/// </summary>
|
||||
public APIRequestCompletionState CompletionState { get; private set; }
|
||||
|
||||
public void Perform(IAPIProvider api)
|
||||
{
|
||||
if (!(api is APIAccess apiAccess))
|
||||
{
|
||||
Fail(new NotSupportedException($"A {nameof(APIAccess)} is required to perform requests."));
|
||||
return;
|
||||
}
|
||||
|
||||
API = apiAccess;
|
||||
//User = apiAccess.LocalUser.Value;
|
||||
|
||||
if (isFailing) return;
|
||||
|
||||
WebRequest = CreateWebRequest();
|
||||
WebRequest.Failed += Fail;
|
||||
WebRequest.AllowRetryOnTimeout = false;
|
||||
|
||||
WebRequest.AddHeader("x-api-version", API.APIVersion.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
if (!string.IsNullOrEmpty(API.AccessToken))
|
||||
// WebRequest.AddRaw("i", $"{API.AccessToken}");
|
||||
|
||||
if (isFailing) return;
|
||||
|
||||
Logger.Log($@"Performing request {this}", LoggingTarget.Network);
|
||||
WebRequest.Perform();
|
||||
|
||||
if (isFailing) return;
|
||||
|
||||
PostProcess();
|
||||
|
||||
TriggerSuccess();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any post-processing actions after a successful request.
|
||||
/// </summary>
|
||||
protected virtual void PostProcess()
|
||||
{
|
||||
}
|
||||
|
||||
internal void TriggerSuccess()
|
||||
{
|
||||
lock (completionStateLock)
|
||||
{
|
||||
if (CompletionState != APIRequestCompletionState.Waiting)
|
||||
return;
|
||||
|
||||
CompletionState = APIRequestCompletionState.Completed;
|
||||
}
|
||||
|
||||
if (API == null)
|
||||
Success?.Invoke();
|
||||
else
|
||||
API.Schedule(() => Success?.Invoke());
|
||||
}
|
||||
|
||||
internal void TriggerFailure(Exception e)
|
||||
{
|
||||
lock (completionStateLock)
|
||||
{
|
||||
if (CompletionState != APIRequestCompletionState.Waiting)
|
||||
return;
|
||||
|
||||
CompletionState = APIRequestCompletionState.Failed;
|
||||
}
|
||||
|
||||
if (API == null)
|
||||
Failure?.Invoke(e);
|
||||
else
|
||||
API.Schedule(() => Failure?.Invoke(e));
|
||||
}
|
||||
|
||||
public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled"));
|
||||
|
||||
public void Fail(Exception e)
|
||||
{
|
||||
lock (completionStateLock)
|
||||
{
|
||||
if (CompletionState != APIRequestCompletionState.Waiting)
|
||||
return;
|
||||
|
||||
WebRequest?.Abort();
|
||||
|
||||
// in the case of a cancellation we don't care about whether there's an error in the response.
|
||||
if (!(e is OperationCanceledException))
|
||||
{
|
||||
string responseString = WebRequest?.GetResponseString();
|
||||
|
||||
// naive check whether there's an error in the response to avoid unnecessary JSON deserialisation.
|
||||
if (!string.IsNullOrEmpty(responseString) && responseString.Contains(@"""error"""))
|
||||
{
|
||||
try
|
||||
{
|
||||
// attempt to decode a displayable error string.
|
||||
var error = JsonConvert.DeserializeObject<DisplayableError>(responseString);
|
||||
if (error != null)
|
||||
e = new APIException(error.ErrorMessage, e);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network);
|
||||
TriggerFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this request is in a failing or failed state.
|
||||
/// </summary>
|
||||
private bool isFailing
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (completionStateLock)
|
||||
return CompletionState == APIRequestCompletionState.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
private class DisplayableError
|
||||
{
|
||||
[JsonProperty("error")]
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void APIFailureHandler(Exception e);
|
||||
|
||||
public delegate void APISuccessHandler();
|
||||
|
||||
public delegate void APIProgressHandler(long current, long total);
|
||||
|
||||
public delegate void APISuccessHandler<in T>(T content);
|
||||
}
|
23
osu.Game/Online/MisskeyAPI/APIRequestCompletionState.cs
Normal file
23
osu.Game/Online/MisskeyAPI/APIRequestCompletionState.cs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public enum APIRequestCompletionState
|
||||
{
|
||||
/// <summary>
|
||||
/// Not yet run or currently waiting on response.
|
||||
/// </summary>
|
||||
Waiting,
|
||||
|
||||
/// <summary>
|
||||
/// Ran to completion.
|
||||
/// </summary>
|
||||
Completed,
|
||||
|
||||
/// <summary>
|
||||
/// Cancelled or failed due to error.
|
||||
/// </summary>
|
||||
Failed
|
||||
}
|
||||
}
|
216
osu.Game/Online/MisskeyAPI/Auth.cs
Normal file
216
osu.Game/Online/MisskeyAPI/Auth.cs
Normal file
@ -0,0 +1,216 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class Auth
|
||||
{
|
||||
// private readonly string clientId;
|
||||
private readonly string endpoint;
|
||||
|
||||
public readonly Bindable<AuthToken> Token = new Bindable<AuthToken>();
|
||||
|
||||
public string TokenString
|
||||
{
|
||||
get => Token.Value?.ToString();
|
||||
set => Token.Value = string.IsNullOrEmpty(value) ? null : AuthToken.Parse(value);
|
||||
}
|
||||
|
||||
internal Auth(
|
||||
// string clientId,
|
||||
string endpoint)
|
||||
{
|
||||
// Debug.Assert(clientId != null);
|
||||
Debug.Assert(endpoint != null);
|
||||
|
||||
// this.clientId = clientId;
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
internal void AuthenticateWithLogin(string username, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username)) throw new ArgumentException("Missing username.");
|
||||
if (string.IsNullOrEmpty(password)) throw new ArgumentException("Missing password.");
|
||||
|
||||
var accessTokenRequest = new AccessTokenRequestPassword(username, password)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/token",
|
||||
Method = HttpMethod.Post,
|
||||
// ClientId = clientId
|
||||
};
|
||||
|
||||
using (accessTokenRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
accessTokenRequest.Perform();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Token.Value = null;
|
||||
|
||||
var throwableException = ex;
|
||||
|
||||
try
|
||||
{
|
||||
// attempt to decode a displayable error string.
|
||||
var error = JsonConvert.DeserializeObject<OAuthError>(accessTokenRequest.GetResponseString() ?? string.Empty);
|
||||
if (error != null)
|
||||
throwableException = new APIException(error.UserDisplayableError, ex);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
throw throwableException;
|
||||
}
|
||||
|
||||
Token.Value = accessTokenRequest.ResponseObject;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool AuthenticateWithRefresh(string refresh)
|
||||
{
|
||||
try
|
||||
{
|
||||
var refreshRequest = new AccessTokenRequestRefresh(refresh)
|
||||
{
|
||||
Url = $@"{endpoint}/oauth/token",
|
||||
Method = HttpMethod.Post,
|
||||
// ClientId = clientId
|
||||
};
|
||||
|
||||
using (refreshRequest)
|
||||
{
|
||||
refreshRequest.Perform();
|
||||
|
||||
Token.Value = refreshRequest.ResponseObject;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
//todo: potentially only kill the refresh token on certain exception types.
|
||||
Token.Value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly object access_token_retrieval_lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Should be run before any API request to make sure we have a valid key.
|
||||
/// </summary>
|
||||
private bool ensureAccessToken()
|
||||
{
|
||||
// if we already have a valid access token, let's use it.
|
||||
if (accessTokenValid) return true;
|
||||
|
||||
// we want to ensure only a single authentication update is happening at once.
|
||||
lock (access_token_retrieval_lock)
|
||||
{
|
||||
// re-check if valid, in case another request completed and revalidated our access.
|
||||
if (accessTokenValid) return true;
|
||||
|
||||
// if not, let's try using our refresh token to request a new access token.
|
||||
if (!string.IsNullOrEmpty(Token.Value?.RefreshToken))
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
AuthenticateWithRefresh(Token.Value.RefreshToken);
|
||||
|
||||
return accessTokenValid;
|
||||
}
|
||||
}
|
||||
|
||||
private bool accessTokenValid => Token.Value?.IsValid ?? false;
|
||||
|
||||
internal bool HasValidAccessToken => RequestAccessToken() != null;
|
||||
|
||||
internal string RequestAccessToken()
|
||||
{
|
||||
if (!ensureAccessToken()) return null;
|
||||
|
||||
return Token.Value.AccessToken;
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
Token.Value = null;
|
||||
}
|
||||
|
||||
private class AccessTokenRequestRefresh : AccessTokenRequest
|
||||
{
|
||||
internal readonly string RefreshToken;
|
||||
|
||||
internal AccessTokenRequestRefresh(string refreshToken)
|
||||
{
|
||||
RefreshToken = refreshToken;
|
||||
GrantType = @"refresh_token";
|
||||
}
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
AddParameter("refresh_token", RefreshToken);
|
||||
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessTokenRequestPassword : AccessTokenRequest
|
||||
{
|
||||
internal readonly string Username;
|
||||
internal readonly string Password;
|
||||
|
||||
internal AccessTokenRequestPassword(string username, string password)
|
||||
{
|
||||
Username = username;
|
||||
Password = password;
|
||||
GrantType = @"password";
|
||||
}
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
AddParameter("username", Username);
|
||||
AddParameter("password", Password);
|
||||
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
|
||||
private class AccessTokenRequest : JsonWebRequest<AuthToken>
|
||||
{
|
||||
protected string GrantType;
|
||||
|
||||
// internal string ClientId;
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
AddParameter("grant_type", GrantType);
|
||||
// AddParameter("client_id", ClientId);
|
||||
AddParameter("scope", "*");
|
||||
|
||||
base.PrePerform();
|
||||
}
|
||||
}
|
||||
|
||||
private class OAuthError
|
||||
{
|
||||
public string UserDisplayableError => !string.IsNullOrEmpty(Hint) ? Hint : ErrorIdentifier;
|
||||
|
||||
[JsonProperty("error")]
|
||||
public string ErrorIdentifier { get; set; }
|
||||
|
||||
[JsonProperty("hint")]
|
||||
public string Hint { get; set; }
|
||||
|
||||
[JsonProperty("message")]
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
57
osu.Game/Online/MisskeyAPI/AuthToken.cs
Normal file
57
osu.Game/Online/MisskeyAPI/AuthToken.cs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
[Serializable]
|
||||
public class AuthToken
|
||||
{
|
||||
/// <summary>
|
||||
/// OAuth 2.0 access token.
|
||||
/// </summary>
|
||||
[JsonProperty(@"access_token")]
|
||||
public string AccessToken;
|
||||
|
||||
[JsonProperty(@"expires_in")]
|
||||
public long ExpiresIn
|
||||
{
|
||||
get => AccessTokenExpiry - DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
set => AccessTokenExpiry = DateTimeOffset.Now.AddSeconds(value).ToUnixTimeSeconds();
|
||||
}
|
||||
|
||||
public bool IsValid => !string.IsNullOrEmpty(AccessToken) && ExpiresIn > 30;
|
||||
|
||||
public long AccessTokenExpiry;
|
||||
|
||||
/// <summary>
|
||||
/// OAuth 2.0 refresh token.
|
||||
/// </summary>
|
||||
[JsonProperty(@"refresh_token")]
|
||||
public string RefreshToken;
|
||||
|
||||
public override string ToString() => $@"{AccessToken}|{AccessTokenExpiry.ToString(NumberFormatInfo.InvariantInfo)}|{RefreshToken}";
|
||||
|
||||
public static AuthToken Parse(string value)
|
||||
{
|
||||
try
|
||||
{
|
||||
string[] parts = value.Split('|');
|
||||
return new AuthToken
|
||||
{
|
||||
AccessToken = parts[0],
|
||||
AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo),
|
||||
RefreshToken = parts[2]
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
13
osu.Game/Online/MisskeyAPI/DefaultEndpointConfigration.cs
Normal file
13
osu.Game/Online/MisskeyAPI/DefaultEndpointConfigration.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class DefaultEndpointConfigration : EndpointConfiguration
|
||||
{
|
||||
public DefaultEndpointConfigration()
|
||||
{
|
||||
APIEndpointUrl = "https://misskey.io";
|
||||
}
|
||||
}
|
||||
}
|
13
osu.Game/Online/MisskeyAPI/EndpointConfiguration.cs
Normal file
13
osu.Game/Online/MisskeyAPI/EndpointConfiguration.cs
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright (c) sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class EndpointConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// The endpoint for the main (osu-web) API.
|
||||
/// </summary>
|
||||
public string APIEndpointUrl { get; set; }
|
||||
}
|
||||
}
|
@ -1,15 +1,123 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public interface IAPIProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The local user.
|
||||
/// This is not thread-safe and should be scheduled locally if consumed from a drawable component.
|
||||
/// </summary>
|
||||
IBindable<Requests.Responses.I> LocalUser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current user's activity.
|
||||
/// This is not thread-safe and should be scheduled locally if consumed from a drawable component.
|
||||
/// </summary>
|
||||
IBindable<UserActivity> Activity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the OAuth access token.
|
||||
/// </summary>
|
||||
string AccessToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the local user is logged in.
|
||||
/// </summary>
|
||||
bool IsLoggedIn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The last username provided by the end-user.
|
||||
/// May not be authenticated.
|
||||
/// </summary>
|
||||
string ProvidedUsername { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL endpoint for this API. Does not include a trailing slash.
|
||||
/// </summary>
|
||||
string APIEndpointUrl { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// The root URL of of the website, excluding the trailing slash.
|
||||
// /// </summary>
|
||||
// string WebsiteRootUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of the API.
|
||||
/// </summary>
|
||||
int APIVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The last login error that occurred, if any.
|
||||
/// </summary>
|
||||
Exception? LastLoginError { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current connection state of the API.
|
||||
/// This is not thread-safe and should be scheduled locally if consumed from a drawable component.
|
||||
/// </summary>
|
||||
IBindable<APIState> State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Queue a new request.
|
||||
/// </summary>
|
||||
/// <param name="request">The request to perform.</param>
|
||||
void Queue(APIRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Perform a request immediately, bypassing any API state checks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be used to run requests as a guest user.
|
||||
/// </remarks>
|
||||
/// <param name="request">The request to perform.</param>
|
||||
void Perform(APIRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Perform a request immediately, bypassing any API state checks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be used to run requests as a guest user.
|
||||
/// </remarks>
|
||||
/// <param name="request">The request to perform.</param>
|
||||
Task PerformAsync(APIRequest request);
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to login using the provided credentials. This is a non-blocking operation.
|
||||
/// </summary>
|
||||
/// <param name="username">The user's username.</param>
|
||||
/// <param name="password">The user's password.</param>
|
||||
void Login(string username, string password);
|
||||
|
||||
/// <summary>
|
||||
/// Log out the current user.
|
||||
/// </summary>
|
||||
void Logout();
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="IHubClientConnector"/>. May be null if not supported.
|
||||
/// </summary>
|
||||
/// <param name="clientName">The name of the client this connector connects for, used for logging.</param>
|
||||
/// <param name="endpoint">The endpoint to the hub.</param>
|
||||
/// <param name="preferMessagePack">Whether to use MessagePack for serialisation if available on this platform.</param>
|
||||
IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack = true);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new user account. This is a blocking operation.
|
||||
/// </summary>
|
||||
/// <param name="email">The email to create the account with.</param>
|
||||
/// <param name="username">The username to create the account with.</param>
|
||||
/// <param name="password">The password to create the account with.</param>
|
||||
/// <returns>Any errors encoutnered during account creation.</returns>
|
||||
RegistrationRequest.RegistrationRequestErrors? CreateAccount(string email, string username, string password);
|
||||
}
|
||||
}
|
||||
|
41
osu.Game/Online/MisskeyAPI/RegistrationRequest.cs
Normal file
41
osu.Game/Online/MisskeyAPI/RegistrationRequest.cs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
{
|
||||
public class RegistrationRequest : WebRequest
|
||||
{
|
||||
internal string Username;
|
||||
internal string Email;
|
||||
internal string Password;
|
||||
|
||||
protected override void PrePerform()
|
||||
{
|
||||
AddParameter("user[username]", Username);
|
||||
AddParameter("user[user_email]", Email);
|
||||
AddParameter("user[password]", Password);
|
||||
|
||||
base.PrePerform();
|
||||
}
|
||||
|
||||
public class RegistrationRequestErrors
|
||||
{
|
||||
public UserErrors User;
|
||||
|
||||
public class UserErrors
|
||||
{
|
||||
[JsonProperty("username")]
|
||||
public string[] Username;
|
||||
|
||||
[JsonProperty("user_email")]
|
||||
public string[] Email;
|
||||
|
||||
[JsonProperty("password")]
|
||||
public string[] Password;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
osu.Game/Online/MisskeyAPI/Requests/I/I.cs
Normal file
24
osu.Game/Online/MisskeyAPI/Requests/I/I.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>, sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests
|
||||
{
|
||||
public class I : APIRequest<MisskeyAPI.Requests.Responses.I>
|
||||
{
|
||||
public I()
|
||||
{
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Post;
|
||||
return req;
|
||||
}
|
||||
|
||||
protected override string Target => @"meta";
|
||||
}
|
||||
}
|
28
osu.Game/Online/MisskeyAPI/Requests/Meta.cs
Normal file
28
osu.Game/Online/MisskeyAPI/Requests/Meta.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>, sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Net.Http;
|
||||
using osu.Framework.IO.Network;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests
|
||||
{
|
||||
public class Meta : APIRequest<MisskeyAPI.Requests.Responses.Meta>
|
||||
{
|
||||
public string instanceUrl { get; }
|
||||
|
||||
public Meta(string instanceUrl)
|
||||
{
|
||||
this.instanceUrl = instanceUrl;
|
||||
}
|
||||
|
||||
protected override WebRequest CreateWebRequest()
|
||||
{
|
||||
var req = base.CreateWebRequest();
|
||||
req.Method = HttpMethod.Post;
|
||||
return req;
|
||||
}
|
||||
|
||||
public string APIEndpointUrl => $"https://{instanceUrl}";
|
||||
protected override string Target => @"meta";
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
public class UsersShow
|
||||
{
|
||||
public partial class Emojis
|
||||
{
|
||||
[CanBeNull]
|
||||
public List<Emoji> Emoji { get; set; }
|
||||
}
|
||||
|
||||
public partial class Emoji
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Uri Url { get; set; }
|
||||
}
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("name")]
|
||||
[CanBeNull]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username;
|
||||
|
||||
[JsonProperty("host")]
|
||||
[CanBeNull]
|
||||
public string Host;
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
[CanBeNull]
|
||||
public Uri AvatarUrl;
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
[CanBeNull]
|
||||
public string AvatarBlurhash;
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
[CanBeNull]
|
||||
public string AvatarColor;
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool isAdmin;
|
||||
|
||||
[JsonProperty("isModerator")]
|
||||
public bool isModerator;
|
||||
|
||||
[JsonProperty("isBot")]
|
||||
public bool isBot;
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool isCat;
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public Emojis emojis;
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
[CanBeNull]
|
||||
public string onlineStatus;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
29
osu.Game/Online/MisskeyAPI/Responses/Emoji.cs
Normal file
29
osu.Game/Online/MisskeyAPI/Responses/Emoji.cs
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
public class Emoji : IEquatable<Emoji>
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("aliases")]
|
||||
public List<string> Aliases;
|
||||
|
||||
[JsonProperty("category")]
|
||||
public string Category;
|
||||
|
||||
[JsonProperty("host")]
|
||||
public string Host;
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
|
||||
public bool Equals(Emoji other) => Id == other?.Id;
|
||||
}
|
||||
}
|
574
osu.Game/Online/MisskeyAPI/Responses/I/I.cs
Normal file
574
osu.Game/Online/MisskeyAPI/Responses/I/I.cs
Normal file
@ -0,0 +1,574 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class I //: IEquatable<I>
|
||||
{
|
||||
/// <summary>
|
||||
/// A user ID which can be used to represent any system user which is not attached to a user profile.
|
||||
/// </summary>
|
||||
public const string SYSTEM_USER_ID = "system";
|
||||
|
||||
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
|
||||
public class Channel
|
||||
{
|
||||
}
|
||||
|
||||
public class Emoji
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
}
|
||||
|
||||
public class Field
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("value")]
|
||||
public string Value;
|
||||
}
|
||||
|
||||
public class File
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt;
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type;
|
||||
|
||||
[JsonProperty("md5")]
|
||||
public string Md5;
|
||||
|
||||
[JsonProperty("size")]
|
||||
public int Size;
|
||||
|
||||
[JsonProperty("isSensitive")]
|
||||
public bool IsSensitive;
|
||||
|
||||
[JsonProperty("blurhash")]
|
||||
public string Blurhash;
|
||||
|
||||
[JsonProperty("properties")]
|
||||
public Properties Properties;
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
|
||||
[JsonProperty("thumbnailUrl")]
|
||||
public string ThumbnailUrl;
|
||||
|
||||
[JsonProperty("comment")]
|
||||
public string Comment;
|
||||
|
||||
[JsonProperty("folderId")]
|
||||
public string FolderId;
|
||||
|
||||
[JsonProperty("folder")]
|
||||
[CanBeNull]
|
||||
public Folder Folder;
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId;
|
||||
|
||||
[JsonProperty("user")]
|
||||
[CanBeNull]
|
||||
public User User;
|
||||
}
|
||||
|
||||
public class Folder
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt;
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("foldersCount")]
|
||||
public int? FoldersCount;
|
||||
|
||||
[JsonProperty("filesCount")]
|
||||
public int? FilesCount;
|
||||
|
||||
[JsonProperty("parentId")]
|
||||
public string ParentId;
|
||||
|
||||
[JsonProperty("parent")]
|
||||
[CanBeNull]
|
||||
public Parent Parent;
|
||||
}
|
||||
|
||||
public class Integration
|
||||
{
|
||||
}
|
||||
|
||||
public class MyReaction
|
||||
{
|
||||
}
|
||||
|
||||
public class Parent
|
||||
{
|
||||
}
|
||||
|
||||
public class PinnedNote
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt;
|
||||
|
||||
[JsonProperty("text")]
|
||||
public string Text;
|
||||
|
||||
[JsonProperty("cw")]
|
||||
[CanBeNull]
|
||||
public string Cw;
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId;
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User;
|
||||
|
||||
[JsonProperty("replyId")]
|
||||
[CanBeNull]
|
||||
public string ReplyId;
|
||||
|
||||
[JsonProperty("renoteId")]
|
||||
[CanBeNull]
|
||||
public string RenoteId;
|
||||
|
||||
[JsonProperty("reply")]
|
||||
[CanBeNull]
|
||||
public Reply Reply;
|
||||
|
||||
[JsonProperty("renote")]
|
||||
[CanBeNull]
|
||||
public Renote Renote;
|
||||
|
||||
[JsonProperty("isHidden")]
|
||||
public bool? IsHidden;
|
||||
|
||||
[JsonProperty("visibility")]
|
||||
public string Visibility;
|
||||
|
||||
[JsonProperty("mentions")]
|
||||
[CanBeNull]
|
||||
public List<string> Mentions;
|
||||
|
||||
[JsonProperty("visibleUserIds")]
|
||||
[CanBeNull]
|
||||
public List<string> VisibleUserIds;
|
||||
|
||||
[JsonProperty("fileIds")]
|
||||
[CanBeNull]
|
||||
public List<string> FileIds;
|
||||
|
||||
[JsonProperty("files")]
|
||||
[CanBeNull]
|
||||
public List<File> Files;
|
||||
|
||||
[JsonProperty("tags")]
|
||||
[CanBeNull]
|
||||
public List<string> Tags;
|
||||
|
||||
[JsonProperty("poll")]
|
||||
[CanBeNull]
|
||||
public Poll Poll;
|
||||
|
||||
[JsonProperty("channelId")]
|
||||
[CanBeNull]
|
||||
public string ChannelId;
|
||||
|
||||
[JsonProperty("channel")]
|
||||
[CanBeNull]
|
||||
public Channel Channel;
|
||||
|
||||
[JsonProperty("localOnly")]
|
||||
public bool? LocalOnly;
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis;
|
||||
|
||||
[JsonProperty("reactions")]
|
||||
public Reactions Reactions;
|
||||
|
||||
[JsonProperty("renoteCount")]
|
||||
public int RenoteCount;
|
||||
|
||||
[JsonProperty("repliesCount")]
|
||||
public int RepliesCount;
|
||||
|
||||
[JsonProperty("uri")]
|
||||
[CanBeNull]
|
||||
public string Uri;
|
||||
|
||||
[JsonProperty("url")]
|
||||
[CanBeNull]
|
||||
public string Url;
|
||||
|
||||
[JsonProperty("myReaction")]
|
||||
[CanBeNull]
|
||||
public MyReaction MyReaction;
|
||||
}
|
||||
|
||||
public class PinnedPages
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt;
|
||||
|
||||
[JsonProperty("updatedAt")]
|
||||
public DateTime UpdatedAt;
|
||||
|
||||
[JsonProperty("title")]
|
||||
public string Title;
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("summary")]
|
||||
public string Summary;
|
||||
|
||||
[JsonProperty("content")]
|
||||
public List<object> Content;
|
||||
|
||||
[JsonProperty("variables")]
|
||||
public List<object> Variables;
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId;
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User;
|
||||
}
|
||||
|
||||
public class Poll
|
||||
{
|
||||
}
|
||||
|
||||
public class Properties
|
||||
{
|
||||
[JsonProperty("width")]
|
||||
public int? Width;
|
||||
|
||||
[JsonProperty("height")]
|
||||
public int? Height;
|
||||
|
||||
[JsonProperty("orientation")]
|
||||
public int? Orientation;
|
||||
|
||||
[JsonProperty("avgColor")]
|
||||
[CanBeNull]
|
||||
public string AvgColor;
|
||||
}
|
||||
|
||||
public class Reactions
|
||||
{
|
||||
}
|
||||
|
||||
public class Renote
|
||||
{
|
||||
}
|
||||
|
||||
public class Reply
|
||||
{
|
||||
}
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; } = "guest";
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username;
|
||||
|
||||
[JsonProperty("host")]
|
||||
public string Host;
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
public string AvatarUrl;
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
public object AvatarBlurhash;
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
public object AvatarColor;
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool? IsAdmin;
|
||||
|
||||
[JsonProperty("isModerator")]
|
||||
public bool? IsModerator;
|
||||
|
||||
[JsonProperty("isBot")]
|
||||
public bool? IsBot;
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool? IsCat;
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis;
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
public string OnlineStatus;
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url;
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri;
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt;
|
||||
|
||||
[JsonProperty("updatedAt")]
|
||||
public DateTime UpdatedAt;
|
||||
|
||||
[JsonProperty("lastFetchedAt")]
|
||||
public DateTime LastFetchedAt;
|
||||
|
||||
[JsonProperty("bannerUrl")]
|
||||
public string BannerUrl;
|
||||
|
||||
[JsonProperty("bannerBlurhash")]
|
||||
public object BannerBlurhash;
|
||||
|
||||
[JsonProperty("bannerColor")]
|
||||
public object BannerColor;
|
||||
|
||||
[JsonProperty("isLocked")]
|
||||
public bool IsLocked;
|
||||
|
||||
[JsonProperty("isSilenced")]
|
||||
public bool IsSilenced;
|
||||
|
||||
[JsonProperty("isSuspended")]
|
||||
public bool IsSuspended;
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description;
|
||||
|
||||
[JsonProperty("location")]
|
||||
public string Location;
|
||||
|
||||
[JsonProperty("birthday")]
|
||||
public string Birthday;
|
||||
|
||||
[JsonProperty("lang")]
|
||||
public string Lang;
|
||||
|
||||
[JsonProperty("fields")]
|
||||
public List<Field> Fields;
|
||||
|
||||
[JsonProperty("followersCount")]
|
||||
public int FollowersCount;
|
||||
|
||||
[JsonProperty("followingCount")]
|
||||
public int FollowingCount;
|
||||
|
||||
[JsonProperty("notesCount")]
|
||||
public int NotesCount;
|
||||
|
||||
[JsonProperty("pinnedNoteIds")]
|
||||
public List<string> PinnedNoteIds;
|
||||
|
||||
[JsonProperty("pinnedNotes")]
|
||||
public List<PinnedNote> PinnedNotes;
|
||||
|
||||
[JsonProperty("pinnedPageId")]
|
||||
public string PinnedPageId;
|
||||
|
||||
[JsonProperty("pinnedPage")]
|
||||
public PinnedPages PinnedPage;
|
||||
|
||||
[JsonProperty("publicReactions")]
|
||||
public bool PublicReactions;
|
||||
|
||||
[JsonProperty("twoFactorEnabled")]
|
||||
public bool TwoFactorEnabled;
|
||||
|
||||
[JsonProperty("usePasswordLessLogin")]
|
||||
public bool UsePasswordLessLogin;
|
||||
|
||||
[JsonProperty("securityKeys")]
|
||||
public bool SecurityKeys;
|
||||
|
||||
[JsonProperty("isFollowing")]
|
||||
public bool? IsFollowing;
|
||||
|
||||
[JsonProperty("isFollowed")]
|
||||
public bool? IsFollowed;
|
||||
|
||||
[JsonProperty("hasPendingFollowRequestFromYou")]
|
||||
public bool? HasPendingFollowRequestFromYou;
|
||||
|
||||
[JsonProperty("hasPendingFollowRequestToYou")]
|
||||
public bool? HasPendingFollowRequestToYou;
|
||||
|
||||
[JsonProperty("isBlocking")]
|
||||
public bool? IsBlocking;
|
||||
|
||||
[JsonProperty("isBlocked")]
|
||||
public bool? IsBlocked;
|
||||
|
||||
[JsonProperty("isMuted")]
|
||||
public bool? IsMuted;
|
||||
|
||||
[JsonProperty("avatarId")]
|
||||
public string AvatarId;
|
||||
|
||||
[JsonProperty("bannerId")]
|
||||
public string BannerId;
|
||||
|
||||
[JsonProperty("injectFeaturedNote")]
|
||||
public bool InjectFeaturedNote;
|
||||
|
||||
[JsonProperty("receiveAnnouncementEmail")]
|
||||
public bool ReceiveAnnouncementEmail;
|
||||
|
||||
[JsonProperty("alwaysMarkNsfw")]
|
||||
public bool AlwaysMarkNsfw;
|
||||
|
||||
[JsonProperty("carefulBot")]
|
||||
public bool CarefulBot;
|
||||
|
||||
[JsonProperty("autoAcceptFollowed")]
|
||||
public bool AutoAcceptFollowed;
|
||||
|
||||
[JsonProperty("noCrawle")]
|
||||
public bool NoCrawle;
|
||||
|
||||
[JsonProperty("isExplorable")]
|
||||
public bool IsExplorable;
|
||||
|
||||
[JsonProperty("isDeleted")]
|
||||
public bool IsDeleted;
|
||||
|
||||
[JsonProperty("hideOnlineStatus")]
|
||||
public bool HideOnlineStatus;
|
||||
|
||||
[JsonProperty("hasUnreadSpecifiedNotes")]
|
||||
public bool HasUnreadSpecifiedNotes;
|
||||
|
||||
[JsonProperty("hasUnreadMentions")]
|
||||
public bool HasUnreadMentions;
|
||||
|
||||
[JsonProperty("hasUnreadAnnouncement")]
|
||||
public bool HasUnreadAnnouncement;
|
||||
|
||||
[JsonProperty("hasUnreadAntenna")]
|
||||
public bool HasUnreadAntenna;
|
||||
|
||||
[JsonProperty("hasUnreadChannel")]
|
||||
public bool HasUnreadChannel;
|
||||
|
||||
[JsonProperty("hasUnreadMessagingMessage")]
|
||||
public bool HasUnreadMessagingMessage;
|
||||
|
||||
[JsonProperty("hasUnreadNotification")]
|
||||
public bool HasUnreadNotification;
|
||||
|
||||
[JsonProperty("hasPendingReceivedFollowRequest")]
|
||||
public bool HasPendingReceivedFollowRequest;
|
||||
|
||||
[JsonProperty("integrations")]
|
||||
public Integration Integrations;
|
||||
|
||||
[JsonProperty("mutedWords")]
|
||||
public List<List<string>> MutedWords;
|
||||
|
||||
[JsonProperty("mutedInstances")]
|
||||
public List<string> MutedInstances;
|
||||
|
||||
[JsonProperty("mutingNotificationTypes")]
|
||||
public List<string> MutingNotificationTypes;
|
||||
|
||||
[JsonProperty("emailNotificationTypes")]
|
||||
public List<string> EmailNotificationTypes;
|
||||
|
||||
[JsonProperty("email")]
|
||||
[CanBeNull]
|
||||
public string Email;
|
||||
|
||||
[JsonProperty("emailVerified")]
|
||||
public bool? EmailVerified;
|
||||
|
||||
[JsonProperty("securityKeysList")]
|
||||
[CanBeNull]
|
||||
public List<SecurityKeysLists> SecurityKeysList;
|
||||
|
||||
public class SecurityKeysLists
|
||||
{
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username;
|
||||
|
||||
[JsonProperty("host")]
|
||||
public string Host;
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
public string AvatarUrl;
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
public object AvatarBlurhash;
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
public object AvatarColor;
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool IsAdmin;
|
||||
|
||||
[JsonProperty("isModerator")]
|
||||
public bool IsModerator;
|
||||
|
||||
[JsonProperty("isBot")]
|
||||
public bool IsBot;
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool IsCat;
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis;
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
public string OnlineStatus;
|
||||
|
||||
//public bool Equals(I other) => this.MatchesOnlineID(other);
|
||||
}
|
||||
}
|
||||
}
|
110
osu.Game/Online/MisskeyAPI/Responses/Meta.cs
Normal file
110
osu.Game/Online/MisskeyAPI/Responses/Meta.cs
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public class Meta //: IEquatable<Meta>
|
||||
{
|
||||
public class Feature
|
||||
{
|
||||
[JsonProperty("registration")]
|
||||
public bool Registration;
|
||||
|
||||
[JsonProperty("localTimeLine")]
|
||||
public bool LocalTimeLine;
|
||||
|
||||
[JsonProperty("globalTimeLine")]
|
||||
public bool GlobalTimeLine;
|
||||
|
||||
[JsonProperty("hcaptcha")]
|
||||
public bool Hcaptcha;
|
||||
|
||||
[JsonProperty("recaptcha")]
|
||||
public bool Recaptcha;
|
||||
|
||||
[JsonProperty("serviceWorker")]
|
||||
public bool ServiceWorker;
|
||||
|
||||
[JsonProperty("miauth")]
|
||||
public bool? Miauth;
|
||||
}
|
||||
|
||||
[JsonProperty("maintainerName")]
|
||||
public string MaintainerName;
|
||||
|
||||
[JsonProperty("maintainerEmail")]
|
||||
public string MaintainerEmail;
|
||||
|
||||
[JsonProperty("version")]
|
||||
public string Version;
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public string Uri;
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description;
|
||||
|
||||
[JsonProperty("langs")]
|
||||
public List<string> Langs;
|
||||
|
||||
[JsonProperty("tosUrl")]
|
||||
public string TosUrl;
|
||||
|
||||
[JsonProperty("defaultDarkTheme")]
|
||||
public string DefaultDarkTheme;
|
||||
|
||||
[JsonProperty("defaultLightTheme")]
|
||||
public string DefaultLightTheme;
|
||||
|
||||
[JsonProperty("driveCapacityPerLocalUserMb")]
|
||||
public int DriveCapacityPerLocalUserMb;
|
||||
|
||||
[JsonProperty("emailRequiredForSignup")]
|
||||
public bool EmailRequiredForSignup;
|
||||
|
||||
[JsonProperty("recaptchaSiteKey")]
|
||||
public string RecaptchaSiteKey;
|
||||
|
||||
[JsonProperty("swPublickey")]
|
||||
public string SwPublickey;
|
||||
|
||||
[JsonProperty("mascotImageUrl")]
|
||||
public string MascotImageUrl;
|
||||
|
||||
[JsonProperty("bannerUrl")]
|
||||
public string BannerUrl;
|
||||
|
||||
[JsonProperty("iconUrl")]
|
||||
public string IconUrl;
|
||||
|
||||
[JsonProperty("maxNoteTextLength")]
|
||||
public int MaxNoteTextLength;
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis;
|
||||
|
||||
[JsonProperty("requireSetup")]
|
||||
public bool RequireSetup;
|
||||
|
||||
[JsonProperty("enableEmail")]
|
||||
public bool EnableEmail;
|
||||
|
||||
[JsonProperty("enableServiceWorker")]
|
||||
public bool EnableServiceWorker;
|
||||
|
||||
[JsonProperty("translatorAvailable")]
|
||||
public bool TranslatorAvailable;
|
||||
|
||||
[JsonProperty("features")]
|
||||
public Feature Features;
|
||||
}
|
||||
}
|
@ -7,13 +7,10 @@ namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
public class Signin
|
||||
{
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id;
|
||||
|
||||
[JsonProperty("i")]
|
||||
public string i;
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
public class OAuth
|
||||
public class UsersNotes
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
381
osu.Game/Online/MisskeyAPI/Responses/UsersShow.cs
Normal file
381
osu.Game/Online/MisskeyAPI/Responses/UsersShow.cs
Normal file
@ -0,0 +1,381 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Online.MisskeyAPI.Requests.Responses
|
||||
{
|
||||
public class UsersShow
|
||||
{
|
||||
// Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
|
||||
public class Emoji
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
}
|
||||
|
||||
public class Field
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("value")]
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public class File
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonProperty("md5")]
|
||||
public string Md5 { get; set; }
|
||||
|
||||
[JsonProperty("size")]
|
||||
public int Size { get; set; }
|
||||
|
||||
[JsonProperty("isSensitive")]
|
||||
public bool IsSensitive { get; set; }
|
||||
|
||||
[JsonProperty("blurhash")]
|
||||
public object Blurhash { get; set; }
|
||||
|
||||
[JsonProperty("properties")]
|
||||
public Properties Properties { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public string Url { get; set; }
|
||||
|
||||
[JsonProperty("thumbnailUrl")]
|
||||
public object ThumbnailUrl { get; set; }
|
||||
|
||||
[JsonProperty("comment")]
|
||||
public object Comment { get; set; }
|
||||
|
||||
[JsonProperty("folderId")]
|
||||
public object FolderId { get; set; }
|
||||
|
||||
[JsonProperty("folder")]
|
||||
public object Folder { get; set; }
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public object UserId { get; set; }
|
||||
|
||||
[JsonProperty("user")]
|
||||
public object User { get; set; }
|
||||
}
|
||||
|
||||
public class Integrations
|
||||
{
|
||||
}
|
||||
|
||||
public class PinnedNote
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("userId")]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[JsonProperty("user")]
|
||||
public User User { get; set; }
|
||||
|
||||
[JsonProperty("text")]
|
||||
public string Text { get; set; }
|
||||
|
||||
[JsonProperty("cw")]
|
||||
public object Cw { get; set; }
|
||||
|
||||
[JsonProperty("visibility")]
|
||||
public string Visibility { get; set; }
|
||||
|
||||
[JsonProperty("renoteCount")]
|
||||
public int RenoteCount { get; set; }
|
||||
|
||||
[JsonProperty("repliesCount")]
|
||||
public int RepliesCount { get; set; }
|
||||
|
||||
[JsonProperty("reactions")]
|
||||
public Reactions Reactions { get; set; }
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis { get; set; }
|
||||
|
||||
[JsonProperty("fileIds")]
|
||||
public List<string> FileIds { get; set; }
|
||||
|
||||
[JsonProperty("files")]
|
||||
public List<File> Files { get; set; }
|
||||
|
||||
[JsonProperty("replyId")]
|
||||
public object ReplyId { get; set; }
|
||||
|
||||
[JsonProperty("renoteId")]
|
||||
public object RenoteId { get; set; }
|
||||
}
|
||||
|
||||
public class Properties
|
||||
{
|
||||
}
|
||||
|
||||
public class Reactions
|
||||
{
|
||||
[JsonProperty(":thinkhappy@miss.nem.one:")]
|
||||
public int ThinkhappyMissNemOne { get; set; }
|
||||
}
|
||||
|
||||
public class Root
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonProperty("host")]
|
||||
public object Host { get; set; }
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
public string AvatarUrl { get; set; }
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
public string AvatarBlurhash { get; set; }
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
public object AvatarColor { get; set; }
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool IsAdmin { get; set; }
|
||||
|
||||
[JsonProperty("isModerator")]
|
||||
public bool IsModerator { get; set; }
|
||||
|
||||
[JsonProperty("isBot")]
|
||||
public bool IsBot { get; set; }
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool IsCat { get; set; }
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis { get; set; }
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
public string OnlineStatus { get; set; }
|
||||
|
||||
[JsonProperty("url")]
|
||||
public object Url { get; set; }
|
||||
|
||||
[JsonProperty("uri")]
|
||||
public object Uri { get; set; }
|
||||
|
||||
[JsonProperty("createdAt")]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[JsonProperty("updatedAt")]
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
|
||||
[JsonProperty("lastFetchedAt")]
|
||||
public object LastFetchedAt { get; set; }
|
||||
|
||||
[JsonProperty("bannerUrl")]
|
||||
public string BannerUrl { get; set; }
|
||||
|
||||
[JsonProperty("bannerBlurhash")]
|
||||
public string BannerBlurhash { get; set; }
|
||||
|
||||
[JsonProperty("bannerColor")]
|
||||
public object BannerColor { get; set; }
|
||||
|
||||
[JsonProperty("isLocked")]
|
||||
public bool IsLocked { get; set; }
|
||||
|
||||
[JsonProperty("isSilenced")]
|
||||
public bool IsSilenced { get; set; }
|
||||
|
||||
[JsonProperty("isSuspended")]
|
||||
public bool IsSuspended { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonProperty("location")]
|
||||
public string Location { get; set; }
|
||||
|
||||
[JsonProperty("birthday")]
|
||||
public string Birthday { get; set; }
|
||||
|
||||
[JsonProperty("lang")]
|
||||
public object Lang { get; set; }
|
||||
|
||||
[JsonProperty("fields")]
|
||||
public List<Field> Fields { get; set; }
|
||||
|
||||
[JsonProperty("followersCount")]
|
||||
public int FollowersCount { get; set; }
|
||||
|
||||
[JsonProperty("followingCount")]
|
||||
public int FollowingCount { get; set; }
|
||||
|
||||
[JsonProperty("notesCount")]
|
||||
public int NotesCount { get; set; }
|
||||
|
||||
[JsonProperty("pinnedNoteIds")]
|
||||
public List<string> PinnedNoteIds { get; set; }
|
||||
|
||||
[JsonProperty("pinnedNotes")]
|
||||
public List<PinnedNote> PinnedNotes { get; set; }
|
||||
|
||||
[JsonProperty("pinnedPageId")]
|
||||
public object PinnedPageId { get; set; }
|
||||
|
||||
[JsonProperty("pinnedPage")]
|
||||
public object PinnedPage { get; set; }
|
||||
|
||||
[JsonProperty("publicReactions")]
|
||||
public bool PublicReactions { get; set; }
|
||||
|
||||
[JsonProperty("ffVisibility")]
|
||||
public string FfVisibility { get; set; }
|
||||
|
||||
[JsonProperty("twoFactorEnabled")]
|
||||
public bool TwoFactorEnabled { get; set; }
|
||||
|
||||
[JsonProperty("usePasswordLessLogin")]
|
||||
public bool UsePasswordLessLogin { get; set; }
|
||||
|
||||
[JsonProperty("securityKeys")]
|
||||
public bool SecurityKeys { get; set; }
|
||||
|
||||
[JsonProperty("avatarId")]
|
||||
public string AvatarId { get; set; }
|
||||
|
||||
[JsonProperty("bannerId")]
|
||||
public string BannerId { get; set; }
|
||||
|
||||
[JsonProperty("injectFeaturedNote")]
|
||||
public bool InjectFeaturedNote { get; set; }
|
||||
|
||||
[JsonProperty("receiveAnnouncementEmail")]
|
||||
public bool ReceiveAnnouncementEmail { get; set; }
|
||||
|
||||
[JsonProperty("alwaysMarkNsfw")]
|
||||
public bool AlwaysMarkNsfw { get; set; }
|
||||
|
||||
[JsonProperty("carefulBot")]
|
||||
public bool CarefulBot { get; set; }
|
||||
|
||||
[JsonProperty("autoAcceptFollowed")]
|
||||
public bool AutoAcceptFollowed { get; set; }
|
||||
|
||||
[JsonProperty("noCrawle")]
|
||||
public bool NoCrawle { get; set; }
|
||||
|
||||
[JsonProperty("isExplorable")]
|
||||
public bool IsExplorable { get; set; }
|
||||
|
||||
[JsonProperty("isDeleted")]
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
[JsonProperty("hideOnlineStatus")]
|
||||
public bool HideOnlineStatus { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadSpecifiedNotes")]
|
||||
public bool HasUnreadSpecifiedNotes { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadMentions")]
|
||||
public bool HasUnreadMentions { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadAnnouncement")]
|
||||
public bool HasUnreadAnnouncement { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadAntenna")]
|
||||
public bool HasUnreadAntenna { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadChannel")]
|
||||
public bool HasUnreadChannel { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadMessagingMessage")]
|
||||
public bool HasUnreadMessagingMessage { get; set; }
|
||||
|
||||
[JsonProperty("hasUnreadNotification")]
|
||||
public bool HasUnreadNotification { get; set; }
|
||||
|
||||
[JsonProperty("hasPendingReceivedFollowRequest")]
|
||||
public bool HasPendingReceivedFollowRequest { get; set; }
|
||||
|
||||
[JsonProperty("integrations")]
|
||||
public Integrations Integrations { get; set; }
|
||||
|
||||
[JsonProperty("mutedWords")]
|
||||
public List<object> MutedWords { get; set; }
|
||||
|
||||
[JsonProperty("mutedInstances")]
|
||||
public List<object> MutedInstances { get; set; }
|
||||
|
||||
[JsonProperty("mutingNotificationTypes")]
|
||||
public List<object> MutingNotificationTypes { get; set; }
|
||||
|
||||
[JsonProperty("emailNotificationTypes")]
|
||||
public List<string> EmailNotificationTypes { get; set; }
|
||||
|
||||
[JsonProperty("showTimelineReplies")]
|
||||
public bool ShowTimelineReplies { get; set; }
|
||||
}
|
||||
|
||||
public class User
|
||||
{
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("username")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[JsonProperty("host")]
|
||||
public object Host { get; set; }
|
||||
|
||||
[JsonProperty("avatarUrl")]
|
||||
public string AvatarUrl { get; set; }
|
||||
|
||||
[JsonProperty("avatarBlurhash")]
|
||||
public string AvatarBlurhash { get; set; }
|
||||
|
||||
[JsonProperty("avatarColor")]
|
||||
public object AvatarColor { get; set; }
|
||||
|
||||
[JsonProperty("isAdmin")]
|
||||
public bool IsAdmin { get; set; }
|
||||
|
||||
[JsonProperty("isCat")]
|
||||
public bool IsCat { get; set; }
|
||||
|
||||
[JsonProperty("emojis")]
|
||||
public List<Emoji> Emojis { get; set; }
|
||||
|
||||
[JsonProperty("onlineStatus")]
|
||||
public string OnlineStatus { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Net.Mime;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.LocalisationExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -15,12 +17,14 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.Misskey;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Overlays.Login
|
||||
{
|
||||
public class LoginForm : FillFlowContainer
|
||||
{
|
||||
private TextBox hostname;
|
||||
private TextBox username;
|
||||
private TextBox password;
|
||||
private ShakeContainer shakeSignIn;
|
||||
@ -33,8 +37,8 @@ namespace osu.Game.Overlays.Login
|
||||
private void performLogin()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(username.Text) && !string.IsNullOrEmpty(password.Text))
|
||||
// api?.Login(username.Text, password.Text);
|
||||
shakeSignIn.Shake();
|
||||
api?.Login(username.Text, password.Text);
|
||||
//shakeSignIn.Shake();
|
||||
else
|
||||
shakeSignIn.Shake();
|
||||
}
|
||||
@ -51,6 +55,12 @@ namespace osu.Game.Overlays.Login
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
hostname = new OsuTextBox()
|
||||
{
|
||||
PlaceholderText = "instanceHostName",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
TabbableContentContainer = this,
|
||||
},
|
||||
username = new OsuTextBox
|
||||
{
|
||||
PlaceholderText = UsersStrings.LoginUsername.ToLower(),
|
||||
|
@ -112,7 +112,7 @@ namespace osu.Game.Screens.Menu
|
||||
OnSolo = loadSoloSongSelect,
|
||||
OnMultiplayer = () => this.Push(new Multiplayer()),
|
||||
OnPlaylists = () => this.Push(new Playlists()),
|
||||
OnMisskey = () => this.Push(new MisskeyLogin()),
|
||||
OnMisskey = () => this.Push(new MisskeyScreenSelector()),
|
||||
OnExit = confirmAndExit,
|
||||
}
|
||||
}
|
||||
|
115
osu.Game/Screens/Misskey/MisskeyInstanceSelect.cs
Normal file
115
osu.Game/Screens/Misskey/MisskeyInstanceSelect.cs
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>, sim1222 <kokt@sim1222.com>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using DiffPlex;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.MisskeyAPI;
|
||||
using osu.Game.Online.MisskeyAPI.Requests;
|
||||
using osu.Game.Online.MisskeyAPI.Requests.Responses;
|
||||
using osuTK;
|
||||
using Meta = osu.Game.Online.MisskeyAPI.Requests.Meta;
|
||||
|
||||
namespace osu.Game.Screens.Misskey
|
||||
{
|
||||
public class MisskeyInstanceSelect : OsuScreen
|
||||
{
|
||||
private Container contentContainer;
|
||||
|
||||
private SeekLimitedSearchTextBox searchTextBox;
|
||||
// private OsuButton submitButton;
|
||||
|
||||
// private string instanceName = String.Empty;
|
||||
|
||||
// [Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
// private Meta getMeta;
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = contentContainer = new Container
|
||||
{
|
||||
Masking = true,
|
||||
CornerRadius = 10,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(0.5f, 0.4f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Masking = true,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Height = 3,
|
||||
Colour = colours.Yellow,
|
||||
Alpha = 1,
|
||||
},
|
||||
searchTextBox = new SeekLimitedSearchTextBox()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.TopCentre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Text = "misskey.io"
|
||||
},
|
||||
new OsuButton()
|
||||
{
|
||||
Text = "Submit",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 15f,
|
||||
Width = 0.6f,
|
||||
Action = insetanceFetch,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// insetanceFetch();
|
||||
//
|
||||
// searchTextBox.Current.ValueChanged += _ => insetanceFetch();
|
||||
}
|
||||
|
||||
private void insetanceFetch()
|
||||
{
|
||||
var getMeta = new Meta(searchTextBox.Text);
|
||||
|
||||
getMeta.Success += response =>
|
||||
{
|
||||
Logger.Log($"{response}");
|
||||
};
|
||||
|
||||
api.Queue(getMeta);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,15 +4,22 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Overlays.Login;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Misskey
|
||||
{
|
||||
public class MisskeyLogin : OsuScreen
|
||||
{
|
||||
private LoginPanel panel;
|
||||
|
||||
private const float transition_time = 400;
|
||||
|
||||
public override bool HideOverlaysOnEnter => false;
|
||||
|
||||
private Container contentContainer;
|
||||
@ -23,9 +30,6 @@ namespace osu.Game.Screens.Misskey
|
||||
|
||||
//private LoginPanel loginPanel;
|
||||
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
@ -44,13 +48,34 @@ namespace osu.Game.Screens.Misskey
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = new Color4(255, 255, 255, 255),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(0.5f, 0.4f),
|
||||
Anchor = Anchor.Centre,
|
||||
Colour = Color4.Black,
|
||||
Alpha = 0.6f,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Masking = true,
|
||||
AutoSizeDuration = transition_time,
|
||||
AutoSizeEasing = Easing.OutQuint,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
panel = new LoginPanel
|
||||
{
|
||||
Padding = new MarginPadding(10),
|
||||
RequestHide = Hide,
|
||||
},
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Height = 3,
|
||||
Colour = colours.Yellow,
|
||||
Alpha = 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
79
osu.Game/Screens/Misskey/MisskeyScreenSelector.cs
Normal file
79
osu.Game/Screens/Misskey/MisskeyScreenSelector.cs
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Misskey
|
||||
{
|
||||
public class MisskeyScreenSelector : OsuScreen
|
||||
{
|
||||
private Container contentContainer;
|
||||
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = contentContainer = new Container()
|
||||
{
|
||||
CornerRadius = 10,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(0.9f, 0.8f),
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.GreySeaFoamDark,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
//Width = 0.35f,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new BasicButton()
|
||||
{
|
||||
Text = "MisskeyLogin",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 60f,
|
||||
Width = 0.9f,
|
||||
Action = () => this.Push(new MisskeyLogin())
|
||||
},
|
||||
new BasicButton()
|
||||
{
|
||||
Text = "MisskeyInstanceSelect",
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 60f,
|
||||
Width = 0.9f,
|
||||
Action = () => this.Push(new MisskeyInstanceSelect())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user