s&box Package Code Search
Search C# source code, UI razor templates, shaders, and configs across s&box packages.
link Raw Facepunch API Link: https://public.facepunch.com/sbox/code/search/1/?ident=brycpt.lichessnetport&take=20
Showing code results for query:
*
(242 total matches found)
Game
library
#nullable enable annotations
using LichessNET.Entities;
using LichessNET.Entities.Account;
using LichessNET.Entities.Social;
using LichessNET.Entities.Social.Timeline;
namespace LichessNET.API;
public partial class LichessApiClient
{
/// <summary>
/// Retrieves the email address of the authenticated user.
/// </summary>
/// <returns>
/// A string representing the email address of the authenticated user.
/// </returns>
public async Task<string> GetAccountEmail()
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold("api/account/email");
var response = await SendRequest(request);
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, string>>();
return content["email"];
}
/// <summary>
/// Retrieves the profile information of the authenticated user from the Lichess platform.
/// </summary>
/// <returns>
/// An instance of <see cref="LichessUser"/> representing the profile details of the user.
/// </returns>
public async Task<LichessUser> GetOwnProfile()
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold("api/account");
var response = await SendRequest(request);
return await response.Content.ReadFromJsonAsync<LichessUser>();
}
/// <summary>
/// Retrieves the account preferences of the authenticated user.
/// </summary>
/// <returns>
/// An instance of <see cref="AccountPreferences"/> representing the user's account preferences.
/// </returns>
public async Task<AccountPreferences> GetAccountPreferences()
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold("api/account/preferences");
var response = await SendRequest(request);
var preferences = await response.Content.ReadFromJsonAsync<AccountPreferences>();
return preferences;
}
/// <summary>
/// Determines whether the authenticated user's account is in kid mode.
/// </summary>
/// <returns>
/// A boolean value indicating if the user's account is set to kid mode.
/// </returns>
public async Task<bool> GetKidModeStatus()
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold("api/account/kid");
var response = await SendRequest(request);
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();
return content["kid"];
}
/// <summary>
/// Sets the kid mode status for the authenticated account.
/// </summary>
/// <param name="enable">
/// A boolean value indicating whether to enable or disable kid mode.
/// </param>
/// <returns>
/// A boolean indicating whether the operation was successful.
/// </returns>
public async Task<bool> SetKidModeStatus(bool enable)
{
var request = GetRequestScaffold("api/account/kid", Tuple.Create("v", enable.ToString()));
var response = await SendRequest(request, "POST");
var result = LichessJson.Deserialize<Dictionary<string, bool>>(await response.Content.ReadAsStringAsync());
return result != null && result.TryGetValue("ok", out var ok) && ok;
}
/// <summary>
/// Follows a player with the specified username on Lichess.
/// </summary>
/// <param name="username">
/// The username of the player to follow.
/// </param>
/// <returns>
/// A boolean value indicating whether the operation was successful.
/// </returns>
public async Task<bool> FollowPlayerAsync(string username)
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold($"api/rel/follow/{username}");
var response = await SendRequest(request, "POST");
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();
return content["ok"];
}
/// <summary>
/// Unfollows a specified player using their username.
/// </summary>
/// <param name="username">
/// The username of the player to unfollow.
/// </param>
/// <returns>
/// A boolean value indicating whether the operation was successful.
/// </returns>
public async Task<bool> UnfollowPlayerAsync(string username)
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold($"api/rel/unfollow/{username}");
var response = await SendRequest(request, "POST");
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();
return content["ok"];
}
/// <summary>
/// Blocks a player on Lichess based on their username.
/// </summary>
/// <param name="username">
/// The username of the player to be blocked.
/// </param>
/// <returns>
/// A boolean indicating whether the player was successfully blocked.
/// </returns>
public async Task<bool> BlockPlayerAsync(string username)
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold($"api/rel/block/{username}");
var response = await SendRequest(request, "POST");
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();
return content["ok"];
}
/// <summary>
/// Unblocks a player specified by username.
/// </summary>
/// <param name="username">
/// The username of the player to unblock.
/// </param>
/// <returns>
/// A boolean value indicating whether the player was successfully unblocked.
/// </returns>
public async Task<bool> UnblockPlayerAsync(string username)
{
await _ratelimitController.Consume("api/account", false);
var request = GetRequestScaffold($"api/rel/unblock/{username}");
var response = await SendRequest(request, "POST");
var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();
return content["ok"];
}
/// <summary>
/// Gets the timeline of the authenticated user.
/// </summary>
/// <param name="since">When the timeline should start</param>
/// <param name="nb">The number of timeline events that should be loaded. Defaults to 15, the maximum is 50.</param>
/// <returns>A Timeline object</returns>
public async Task<Timeline?> GetTimelineAsync(DateTime since, int nb = 15)
{
await _ratelimitController.Consume("api/timeline", false);
var unixTimestamp = new DateTimeOffset(since).ToUnixTimeMilliseconds();
var request = GetRequestScaffold($"api/timeline",
new Tuple<string, string>("since", unixTimestamp.ToString()),
new Tuple<string, string>("nb", nb.ToString()));
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var root = LichessJson.Parse(content);
var timeline = new Timeline
{
Entries = new List<TimelineEntry>(),
Users = root.TryGetProperty("users", out var users)
? users.Deserialize<Dictionary<string, TimelineUser>>(LichessJson.Options) ?? new Dictionary<string, TimelineUser>()
: new Dictionary<string, TimelineUser>()
};
if (root.TryGetProperty("entries", out var entries))
{
foreach (var entryElement in entries.EnumerateArray())
{
var type = entryElement.TryGetProperty("type", out var typeElement) ? typeElement.GetString() : null;
var date = entryElement.TryGetProperty("date", out var dateElement) ? dateElement.GetInt64() : 0;
var data = entryElement.TryGetProperty("data", out var dataElement)
? TimelineEventDataConverter.DeserializeData(type, dataElement, LichessJson.Options)
: new UnknownEventData();
timeline.Entries.Add(new TimelineEntry
{
Type = type,
Date = date,
Data = data
});
}
}
return timeline;
}
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Enumerations;
public enum Speed
{
UltraBullet,
Bullet,
Blitz,
Rapid,
Classical,
Correspondence,
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Social.Timeline
{
public abstract class TimelineEventData
{
}
public class FollowEventData : TimelineEventData
{
public string U1 { get; set; }
public string U2 { get; set; }
}
public class UnknownEventData : TimelineEventData
{
}
public class TeamJoinEventData : TimelineEventData
{
public string UserId { get; set; }
public string TeamId { get; set; }
}
public class TeamCreateEventData : TimelineEventData
{
public string UserId { get; set; }
public string TeamId { get; set; }
}
public class ForumPostEventData : TimelineEventData
{
public string UserId { get; set; }
public string TopicId { get; set; }
public string TopicName { get; set; }
public string PostId { get; set; }
}
public class UblogPostEventData : TimelineEventData
{
public string UserId { get; set; }
public string Id { get; set; }
public string Slug { get; set; }
public string Title { get; set; }
}
public class TourJoinEventData : TimelineEventData
{
public string UserId { get; set; }
public string TourId { get; set; }
public string TourName { get; set; }
}
public class GameEndEventData : TimelineEventData
{
public string FullId { get; set; }
public string Perf { get; set; }
public string Opponent { get; set; }
public bool Win { get; set; }
}
public class SimulCreateEventData : TimelineEventData
{
public string UserId { get; set; }
public string SimulId { get; set; }
public string SimulName { get; set; }
}
public class SimulJoinEventData : TimelineEventData
{
public string UserId { get; set; }
public string SimulId { get; set; }
public string SimulName { get; set; }
}
public class StudyLikeEventData : TimelineEventData
{
public string UserId { get; set; }
public string StudyId { get; set; }
public string StudyName { get; set; }
}
public class PlanStartEventData : TimelineEventData
{
public string UserId { get; set; }
}
public class PlanRenewEventData : TimelineEventData
{
public string UserId { get; set; }
public int Months { get; set; }
}
public class BlogPostEventData : TimelineEventData
{
public string Id { get; set; }
public string Slug { get; set; }
public string Title { get; set; }
}
public class UblogPostLikeEventData : TimelineEventData
{
public string UserId { get; set; }
public string Id { get; set; }
public string Title { get; set; }
}
public class StreamStartEventData : TimelineEventData
{
public string Id { get; set; }
public string Title { get; set; }
}
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Social.Timeline;
internal class TimelineEventDataConverter : JsonConverter<TimelineEventData>
{
public override TimelineEventData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var document = JsonDocument.ParseValue(ref reader);
return DeserializeData(null, document.RootElement, options);
}
public override void Write(Utf8JsonWriter writer, TimelineEventData value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, (object)value, options);
}
internal static TimelineEventData DeserializeData(string type, JsonElement data, JsonSerializerOptions options)
{
if (string.IsNullOrWhiteSpace(type) && data.TryGetProperty("type", out var embeddedType))
type = embeddedType.GetString();
if (data.TryGetProperty("data", out var nestedData))
data = nestedData;
TimelineEventData result = type switch
{
"follow" => data.Deserialize<FollowEventData>(options),
"team-join" => data.Deserialize<TeamJoinEventData>(options),
"team-create" => data.Deserialize<TeamCreateEventData>(options),
"forum-post" => data.Deserialize<ForumPostEventData>(options),
"ublog-post" => data.Deserialize<UblogPostEventData>(options),
"tour-join" => data.Deserialize<TourJoinEventData>(options),
"game-end" => data.Deserialize<GameEndEventData>(options),
"simul-create" => data.Deserialize<SimulCreateEventData>(options),
"simul-join" => data.Deserialize<SimulJoinEventData>(options),
"study-like" => data.Deserialize<StudyLikeEventData>(options),
"plan-start" => data.Deserialize<PlanStartEventData>(options),
"plan-renew" => data.Deserialize<PlanRenewEventData>(options),
"blog-post" => data.Deserialize<BlogPostEventData>(options),
"ublog-post-like" => data.Deserialize<UblogPostLikeEventData>(options),
"stream-start" => data.Deserialize<StreamStartEventData>(options),
_ => null
};
return result ?? new UnknownEventData();
}
}
Game
library
#nullable enable annotations
using System.Text.Json;
using System.Text.Json.Serialization;
using LichessNET.Entities.Enumerations;
namespace LichessNET.Converters;
public class PermissionJsonConverter : JsonConverter<List<TokenPermission>>
{
public override List<TokenPermission>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var permissions = reader.GetString();
string[] permissionsList = permissions.Split(',');
List<TokenPermission> tokenPermissions = new List<TokenPermission>();
foreach (string permission in permissionsList)
{
switch (permission)
{
case "email:read":
tokenPermissions.Add(TokenPermission.ReadEmail);
break;
case "preference:read":
tokenPermissions.Add(TokenPermission.ReadPreferences);
break;
case "preference:write":
tokenPermissions.Add(TokenPermission.WritePreferences);
break;
case "follow:read":
tokenPermissions.Add(TokenPermission.ReadFollows);
break;
case "follow:write":
tokenPermissions.Add(TokenPermission.WriteFollows);
break;
case "msg:write":
tokenPermissions.Add(TokenPermission.WriteMessages);
break;
case "challenge:read":
tokenPermissions.Add(TokenPermission.ReadChallenges);
break;
case "challenge:write":
tokenPermissions.Add(TokenPermission.WriteChallenges);
break;
case "challenge:bulk":
tokenPermissions.Add(TokenPermission.BulkChallenges);
break;
case "tournament:write":
tokenPermissions.Add(TokenPermission.WriteTournaments);
break;
case "team:read":
tokenPermissions.Add(TokenPermission.ReadTeams);
break;
case "team:write":
tokenPermissions.Add(TokenPermission.WriteTeams);
break;
case "team:lead":
tokenPermissions.Add(TokenPermission.ManageTeams);
break;
case "puzzle:read":
tokenPermissions.Add(TokenPermission.ReadPuzzleActivity);
break;
case "racer:write":
tokenPermissions.Add(TokenPermission.WriteRaces);
break;
case "study:read":
tokenPermissions.Add(TokenPermission.ReadStudies);
break;
case "study:write":
tokenPermissions.Add(TokenPermission.WriteStudies);
break;
case "board:play":
tokenPermissions.Add(TokenPermission.PlayGames);
break;
case "engine:read":
tokenPermissions.Add(TokenPermission.ReadEngines);
break;
case "engine:write":
tokenPermissions.Add(TokenPermission.ManageEngines);
break;
}
}
return tokenPermissions;
}
public override void Write(Utf8JsonWriter writer, List<TokenPermission> value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
Game
library
#nullable enable annotations
using LichessNET.API;
namespace LichessNET.Entities.Game;
/// <summary>
/// Represents a stream for a Lichess game, handling real-time updates of moves and game information.
/// </summary>
public class GameStream
{
public delegate void GameInfoFetchedHandler(object sender, OngoingGame game);
public delegate void MoveUpdateHandler(object sender, Move move);
private readonly Dictionary<string, OngoingGame> _games = new Dictionary<string, OngoingGame>();
private readonly LichessStream _stream;
public GameStream(string requestUri, string method = "GET")
: this(requestUri, method, CancellationToken.None)
{
}
public GameStream(string requestUri, string method, CancellationToken cancellationToken)
{
_stream = new LichessStream(requestUri, method);
_stream.GameUpdateReceived += ProcessData;
_ = _stream.StreamGameAsync(cancellationToken);
}
public event MoveUpdateHandler? OnMoveMade;
public event GameInfoFetchedHandler? OnGameInfoFetched;
private void ProcessData(object sender, JsonElement data)
{
FetchDataForNewGame(data);
FetchDataForMove(data);
}
private void FetchDataForNewGame(JsonElement data)
{
if (!data.TryGetProperty("id", out var idProperty))
return;
var gameId = idProperty.GetString();
if (string.IsNullOrWhiteSpace(gameId))
return;
var game = new OngoingGame
{
GameId = gameId,
PlysAtInitFen = data.TryGetProperty("turns", out var turns) ? turns.GetInt32() : 0
};
_games[gameId] = game;
OnGameInfoFetched?.Invoke(this, game);
}
private void FetchDataForMove(JsonElement data)
{
if (!data.TryGetProperty("lm", out var lastMoveProperty))
return;
var gameId = _games.Count == 1 ? _games.First().Key : string.Empty;
if (string.IsNullOrWhiteSpace(gameId) || !_games.TryGetValue(gameId, out var game))
return;
game.Moves ??= new List<Move>();
var fen = data.TryGetProperty("fen", out var fenProperty) ? fenProperty.GetString() : string.Empty;
var move = new Move
{
Notation = lastMoveProperty.GetString(),
IsWhite = fen?.Contains(" w ") ?? false,
GameID = gameId,
MoveNumber = (game.Moves.Count / 2) + 1
};
game.Moves.Add(move);
OnMoveMade?.Invoke(this, move);
}
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Game;
public class TimeControl
{
public string Type { get; set; }
public int Limit { get; set; }
public int Increment { get; set; }
public string Show { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Puzzle.Dashboard;
public class PuzzleResults
{
public int FirstWins { get; set; }
public int Nb { get; set; }
public int Performance { get; set; }
public int PuzzleRatingAvg { get; set; }
public int ReplayWins { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Social.Stream;
public class Streamer
{
public string Name { get; set; }
public string Headline { get; set; }
public string Description { get; set; }
public string Twitch { get; set; }
public string YouTube { get; set; }
public string Image { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Account.Performance;
public class Glicko
{
public double Rating { get; set; }
public double Deviation { get; set; }
public bool Provisional { get; set; }
}
Game
library
#nullable enable annotations
using System.Text.Json.Serialization;
namespace LichessNET.Entities.Account.Performance;
public class PerformanceStats
{
public string User { get; set; }
public Perf Perf { get; set; }
public int Rank { get; set; }
public double Percentile { get; set; }
public Stat Stat { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Account.Performance;
public class PerfType
{
public string Key { get; set; }
public string Name { get; set; }
}
public class Highest
{
public int Int { get; set; }
public DateTime At { get; set; }
public string GameId { get; set; }
}
public class BestWins
{
public List<Result> Results { get; set; }
}
public class WorstLosses
{
public List<Result> Results { get; set; }
}
public class Result
{
public int OpRating { get; set; }
public OpId OpId { get; set; }
public DateTime At { get; set; }
public string GameId { get; set; }
}
public class OpId
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Count
{
public int All { get; set; }
public int Rated { get; set; }
public int Win { get; set; }
public int Loss { get; set; }
public int Draw { get; set; }
public int Tour { get; set; }
public int Berserk { get; set; }
public double OpAvg { get; set; }
public int Seconds { get; set; }
public int Disconnects { get; set; }
}
public class ResultStreak
{
public Streak Win { get; set; }
public Streak Loss { get; set; }
}
public class Streak
{
public Cur Cur { get; set; }
public Max Max { get; set; }
}
public class Cur
{
public int V { get; set; }
}
public class Max
{
public int V { get; set; }
public From From { get; set; }
public To To { get; set; }
}
public class From
{
public DateTime At { get; set; }
public string GameId { get; set; }
}
public class To
{
public DateTime At { get; set; }
public string GameId { get; set; }
}
public class UserId
{
public string Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
}
public class PlayStreak
{
public Nb Nb { get; set; }
public Time Time { get; set; }
public DateTime LastDate { get; set; }
}
public class Nb
{
public Cur Cur { get; set; }
public Max Max { get; set; }
}
public class Time
{
public Cur Cur { get; set; }
public Max Max { get; set; }
}
Game
library
#nullable enable annotations
using LichessNET.Entities.Social;
namespace LichessNET.Entities.Teams;
/// <summary>
/// Represents a team on Lichess, containing information such as its ID, name, description, and members.
/// </summary>
public class LichessTeam
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Flair { get; set; }
public TeamLeader Leader { get; set; }
public List<TeamLeader> Leaders { get; set; }
public int NbMembers { get; set; }
public bool Open { get; set; }
public bool Joined { get; set; }
public bool Requested { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Entities.Social;
/// <summary>
/// Represents a leader of a team on Lichess, providing properties for flair, identification, name, and patron status.
/// </summary>
public class TeamLeader
{
public string Flair { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public bool Patron { get; set; }
}
Game
library
#nullable enable annotations
namespace LichessNET.Internal;
internal sealed class LichessLog
{
private readonly string _name;
private readonly bool _enabled;
public LichessLog( string name, bool enabled = true )
{
_name = name;
_enabled = enabled;
}
public void Debug( string message )
{
if ( _enabled )
Log.Info( $"[{_name}] {message}" );
}
public void Information( string message )
{
if ( _enabled )
Log.Info( $"[{_name}] {message}" );
}
public void Warning( string message )
{
if ( _enabled )
Log.Warning( $"[{_name}] {message}" );
}
public void Error( string message )
{
if ( _enabled )
Log.Error( $"[{_name}] {message}" );
}
}
Game
library
#nullable enable annotations
namespace LichessNET.API;
/// <summary>
/// A sanitized failure returned by the Lichess API.
/// </summary>
public sealed class LichessApiException : HttpRequestException
{
public LichessApiException(HttpStatusCode? statusCode = null)
: this(CreateMessage(statusCode), statusCode)
{
}
private LichessApiException(string message, HttpStatusCode? statusCode)
: base(message, null, statusCode)
{
}
internal static LichessApiException InvalidResponse(HttpStatusCode statusCode)
{
return new LichessApiException(
"Lichess API returned a response that could not be processed.", statusCode);
}
private static string CreateMessage(HttpStatusCode? statusCode)
{
return statusCode.HasValue
? $"Lichess API request failed with HTTP status {(int)statusCode.Value} ({statusCode.Value})."
: "Lichess API request failed before an HTTP status was received.";
}
}
Game
library
#nullable enable annotations
using LichessNET.Entities.OAuth;
namespace LichessNET.API;
public partial class LichessApiClient
{
/// <summary>
/// Tests OAuth tokens without placing them in the request URI or logs.
/// </summary>
public Task<Dictionary<string, TokenInfo?>> TestTokensAsync(List<string> tokens)
{
return TestTokensAsync(tokens, CancellationToken.None);
}
public async Task<Dictionary<string, TokenInfo?>> TestTokensAsync(List<string> tokens,
CancellationToken cancellationToken = default)
{
if (tokens == null)
throw new ArgumentNullException(nameof(tokens));
if (tokens.Count == 0 || tokens.Count > 1000)
throw new ArgumentOutOfRangeException(nameof(tokens),
"Between 1 and 1000 OAuth tokens are required.");
if (tokens.Any(string.IsNullOrWhiteSpace))
throw new ArgumentException("OAuth tokens cannot be empty.", nameof(tokens));
cancellationToken.ThrowIfCancellationRequested();
var request = GetRequestScaffold("api/token/test");
var requestContent = new StringContent(
string.Join(',', tokens), Encoding.UTF8, "text/plain");
var response = await SendRequest(request, "POST", useToken: false,
cancellationToken: cancellationToken, requestContent: requestContent);
cancellationToken.ThrowIfCancellationRequested();
try
{
return await response.Content.ReadFromJsonAsync<Dictionary<string, TokenInfo?>>(
LichessJson.Options)
?? new Dictionary<string, TokenInfo?>();
}
catch (Exception exception) when (exception is not OperationCanceledException)
{
throw LichessApiException.InvalidResponse(response.StatusCode);
}
}
/// <summary>
/// Deletes a token.
/// </summary>
public async Task DeleteTokenAsync(string token)
{
var request = GetRequestScaffold("api/token");
request.Headers["Authorization"] = "Bearer " + token;
await SendRequest(request, "DELETE", useToken: false);
}
}
Game
library
#nullable enable annotations
using LichessNET.Entities.Teams;
using LichessNET.Entities.Tournament;
using LichessNET.Entities.Tournament.Arena;
namespace LichessNET.API;
public partial class LichessApiClient
{
/// <summary>
/// Get the team with the specified ID
/// </summary>
/// <param name="teamId">The ID of the team</param>
/// <returns>The team with the specified ID</returns>
public async Task<LichessTeam> GetTeamAsync(string teamId)
{
await _ratelimitController.Consume();
var request = GetRequestScaffold($"api/team/{teamId}");
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var team = LichessJson.Deserialize<LichessTeam>(content);
return team;
}
/// <summary>
/// Gets the most populat teams on Lichess in a paginated manner.
/// </summary>
/// <param name="page">Page number, defaults to 1</param>
/// <returns>The list of the most popular teams on the specified page.</returns>
public async Task<List<LichessTeam>> GetPopularTeamsAsync(int page = 1)
{
await _ratelimitController.Consume();
var request = GetRequestScaffold("api/team/all", new Tuple<string, string>("page", page.ToString()));
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var json = LichessJson.Parse(content);
return json.TryGetProperty("currentPageResults", out var results)
? results.Deserialize<List<LichessTeam>>(LichessJson.Options) ?? new List<LichessTeam>()
: new List<LichessTeam>();
}
/// <summary>
/// Get the list of teams that the specified user is in.
/// </summary>
/// <param name="username">The username of the user</param>
/// <returns>A list of teams that the specified user is in</returns>
public async Task<List<LichessTeam>> GetTeamOfUserAsync(string username)
{
await _ratelimitController.Consume();
var request = GetRequestScaffold($"api/team/of/{username}");
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var teams = LichessJson.Deserialize<List<LichessTeam>>(content);
return teams;
}
/// <summary>
/// This funciton gets the members of a team, in chronoclogical order of joining the team. (Latest first),
/// with up to 5000 members.
/// </summary>
/// <param name="teamId">ID of the Team</param>
/// <returns></returns>
public async Task<List<TeamMember>> GetTeamMembersAsync(string teamId)
{
await _ratelimitController.Consume();
var request = GetRequestScaffold($"api/team/{teamId}/users");
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var members = LichessJson.Deserialize<List<TeamMember>>(content);
return members;
}
/// <summary>
/// Retrieves all Swiss tournaments of a team.
/// </summary>
/// <param name="teamId">The ID of the team.</param>
/// <param name="max">The maximum number of tournaments to download. Default is 100.</param>
/// <returns>A task representing the asynchronous operation, containing the list of Swiss tournaments.</returns>
public async Task<List<SwissTournament>> GetTeamSwissTournamentsAsync(string teamId, int max = 100)
{
await _ratelimitController.Consume();
var endpoint = $"api/team/{teamId}/swiss?max={max}";
var request = GetRequestScaffold(endpoint);
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var tournaments = new List<SwissTournament>();
foreach (var line in content.Split('\n')) { if (string.IsNullOrWhiteSpace(line)) continue; var tournament = LichessJson.Deserialize<SwissTournament>(line.Trim()); if (tournament != null) tournaments.Add(tournament); }
return tournaments;
}
/// <summary>
/// Retrieves all Arena tournaments relevant to a team.
/// </summary>
/// <param name="teamId">The ID of the team.</param>
/// <param name="max">The maximum number of tournaments to download. Default is 100.</param>
/// <returns>A task representing the asynchronous operation, containing the list of Arena tournaments.</returns>
public async Task<List<ArenaTournament>> GetTeamArenaTournamentsAsync(string teamId, int max = 100)
{
await _ratelimitController.Consume();
var endpoint = $"api/team/{teamId}/arena?max={max}";
var request = GetRequestScaffold(endpoint);
var response = await SendRequest(request);
var content = await response.Content.ReadAsStringAsync();
var tournaments = new List<ArenaTournament>();
foreach (var line in content.Split('\n')) { if (string.IsNullOrWhiteSpace(line)) continue; var tournament = LichessJson.Deserialize<ArenaTournament>(line.Trim()); if (tournament != null) tournaments.Add(tournament); }
return tournaments;
}
/// <summary>
/// Joins a team.
/// </summary>
/// <param name="teamId">The ID of the team.</param>
/// <param name="message">The message for the team join request, required if the team manually reviews admission requests.</param>
/// <param name="password">The password for the team, if required.</param>
/// <returns>A task representing the asynchronous operation, containing the join team response.</returns>
public async Task<bool> JoinTeamAsync(string teamId, string message = null, string password = null)
{
await _ratelimitController.Consume();
var endpoint = $"team/{teamId}/join";
var request = GetRequestScaffold(endpoint);
var formData = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(message))
{
formData.Add("message", message);
}
if (!string.IsNullOrEmpty(password))
{
formData.Add("password", password);
}
var response = await SendRequest(request, "POST", formData: formData);
var content = await response.Content.ReadAsStringAsync();
return content.Contains("true");
}
/// <summary>
/// Leaves a team.
/// </summary>
/// <param name="teamId">The ID of the team.</param>
/// <returns>A task representing the asynchronous operation, containing the leave team response.</returns>
public async Task<bool> LeaveTeamAsync(string teamId)
{
await _ratelimitController.Consume();
var endpoint = $"team/{teamId}/quit";
var request = GetRequestScaffold(endpoint);
var response = await SendRequest(request, "POST");
var content = await response.Content.ReadAsStringAsync();
return content.Contains("true");
}
}
Game
library
#nullable enable annotations
using LichessNET.Entities.Enumerations;
namespace LichessNET.Entities.Game;
/// <summary>
/// Represents a player in a chess game, including their name, title, and rating.
/// This class will be used when parsing PGN Files
/// </summary>
public class GamePlayer
{
public string Name { get; set; }
//TODO: Add serialization for Title
public Title? Title { get; set; }
public int Rating { get; set; }
}
Game
library
#nullable enable annotations
using System.Globalization;
using LichessNET.Entities.Enumerations;
using LichessNET.Extensions;
namespace LichessNET.Entities.Board;
public enum BoardColorPreference
{
Random,
White,
Black
}
public enum BoardChatRoom
{
Player,
Spectator
}
public sealed class BoardSeekOptions
{
public bool Rated { get; set; }
public double TimeMinutes { get; set; } = 5;
public int IncrementSeconds { get; set; }
public int? DaysPerTurn { get; set; }
public BoardColorPreference Color { get; set; } = BoardColorPreference.Random;
public ChessVariant Variant { get; set; } = ChessVariant.Standard;
public string RatingRange { get; set; }
internal Dictionary<string, string> ToFormData()
{
Validate();
var form = new Dictionary<string, string>
{
["rated"] = Rated.ToString().ToLowerInvariant(),
["variant"] = Variant.ToApiName(),
["color"] = ToApiName(Color)
};
if (DaysPerTurn.HasValue)
{
form["days"] = DaysPerTurn.Value.ToString(CultureInfo.InvariantCulture);
}
else
{
form["time"] = TimeMinutes.ToString(CultureInfo.InvariantCulture);
form["increment"] = IncrementSeconds.ToString(CultureInfo.InvariantCulture);
}
if (!string.IsNullOrWhiteSpace(RatingRange))
form["ratingRange"] = RatingRange;
return form;
}
private void Validate()
{
if (DaysPerTurn.HasValue)
{
if (DaysPerTurn.Value < 1 || DaysPerTurn.Value > 14)
throw new ArgumentOutOfRangeException(nameof(DaysPerTurn), "Days per turn must be between 1 and 14.");
return;
}
if (TimeMinutes <= 0 || TimeMinutes > 180)
throw new ArgumentOutOfRangeException(nameof(TimeMinutes), "Clock time must be between 0 and 180 minutes.");
if (IncrementSeconds < 0 || IncrementSeconds > 180)
throw new ArgumentOutOfRangeException(nameof(IncrementSeconds), "Increment must be between 0 and 180 seconds.");
}
private static string ToApiName(BoardColorPreference color)
{
return color switch
{
BoardColorPreference.White => "white",
BoardColorPreference.Black => "black",
_ => "random"
};
}
}
public sealed class BoardGameInfo
{
[JsonPropertyName("gameId")]
public string GameId { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("fullId")]
public string FullId { get; set; }
[JsonPropertyName("color")]
public string Color { get; set; }
[JsonPropertyName("fen")]
public string Fen { get; set; }
[JsonPropertyName("hasMoved")]
public bool? HasMoved { get; set; }
[JsonPropertyName("isMyTurn")]
public bool? IsMyTurn { get; set; }
[JsonPropertyName("lastMove")]
public string LastMove { get; set; }
[JsonPropertyName("source")]
public string Source { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
[JsonPropertyName("variant")]
[JsonConverter(typeof(BoardObjectNameConverter))]
public string Variant { get; set; }
[JsonPropertyName("speed")]
public string Speed { get; set; }
[JsonPropertyName("perf")]
[JsonConverter(typeof(BoardObjectNameConverter))]
public string Perf { get; set; }
[JsonPropertyName("rated")]
public bool? Rated { get; set; }
[JsonPropertyName("opponent")]
public BoardPlayerInfo Opponent { get; set; }
[JsonPropertyName("white")]
public BoardPlayerInfo White { get; set; }
[JsonPropertyName("black")]
public BoardPlayerInfo Black { get; set; }
[JsonPropertyName("state")]
public BoardGameState State { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
public string BestGameId => !string.IsNullOrWhiteSpace(GameId) ? GameId : Id;
}
public sealed class BoardPlayerInfo
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("username")]
public string Username { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("rating")]
public int? Rating { get; set; }
[JsonPropertyName("provisional")]
public bool? Provisional { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class BoardClockState
{
public int? WhiteTimeMilliseconds { get; set; }
public int? BlackTimeMilliseconds { get; set; }
public int? WhiteIncrementMilliseconds { get; set; }
public int? BlackIncrementMilliseconds { get; set; }
public TimeSpan? WhiteTime => WhiteTimeMilliseconds.HasValue
? TimeSpan.FromMilliseconds(WhiteTimeMilliseconds.Value)
: null;
public TimeSpan? BlackTime => BlackTimeMilliseconds.HasValue
? TimeSpan.FromMilliseconds(BlackTimeMilliseconds.Value)
: null;
public static BoardClockState FromState(BoardGameState state)
{
return new BoardClockState
{
WhiteTimeMilliseconds = state?.WhiteTimeMilliseconds,
BlackTimeMilliseconds = state?.BlackTimeMilliseconds,
WhiteIncrementMilliseconds = state?.WhiteIncrementMilliseconds,
BlackIncrementMilliseconds = state?.BlackIncrementMilliseconds
};
}
}
public sealed class BoardGameState
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("moves")]
public string Moves { get; set; }
[JsonPropertyName("wtime")]
public int? WhiteTimeMilliseconds { get; set; }
[JsonPropertyName("btime")]
public int? BlackTimeMilliseconds { get; set; }
[JsonPropertyName("winc")]
public int? WhiteIncrementMilliseconds { get; set; }
[JsonPropertyName("binc")]
public int? BlackIncrementMilliseconds { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; }
[JsonPropertyName("winner")]
public string Winner { get; set; }
[JsonPropertyName("wdraw")]
public bool? WhiteOfferingDraw { get; set; }
[JsonPropertyName("bdraw")]
public bool? BlackOfferingDraw { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
public BoardClockState Clock => BoardClockState.FromState(this);
public bool IsFinished => !string.IsNullOrWhiteSpace(Status)
&& !Status.Equals("started", StringComparison.OrdinalIgnoreCase)
&& !Status.Equals("created", StringComparison.OrdinalIgnoreCase);
public IReadOnlyList<string> MoveList => BoardEventParser.SplitMoves(Moves);
}
public sealed class BoardGameStartEvent
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("game")]
public BoardGameInfo Game { get; set; }
}
public sealed class BoardGameFinishEvent
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("game")]
public BoardGameInfo Game { get; set; }
}
public sealed class BoardGameFullEvent
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("initialFen")]
public string InitialFen { get; set; }
[JsonPropertyName("rated")]
public bool? Rated { get; set; }
[JsonPropertyName("variant")]
[JsonConverter(typeof(BoardObjectNameConverter))]
public string Variant { get; set; }
[JsonPropertyName("speed")]
public string Speed { get; set; }
[JsonPropertyName("perf")]
[JsonConverter(typeof(BoardObjectNameConverter))]
public string Perf { get; set; }
[JsonPropertyName("createdAt")]
public long? CreatedAt { get; set; }
[JsonPropertyName("white")]
public BoardPlayerInfo White { get; set; }
[JsonPropertyName("black")]
public BoardPlayerInfo Black { get; set; }
[JsonPropertyName("state")]
public BoardGameState State { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> Extra { get; set; }
}
public sealed class BoardChatLineEvent
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("room")]
public string Room { get; set; }
[JsonPropertyName("username")]
public string Username { get; set; }
[JsonPropertyName("text")]
public string Text { get; set; }
}
public static class BoardEventParser
{
public static string GetEventType(JsonElement element)
{
return element.TryGetProperty("type", out var type)
? type.GetString()
: string.Empty;
}
public static BoardGameStartEvent ParseGameStart(JsonElement element)
{
return element.Deserialize<BoardGameStartEvent>(LichessJson.Options);
}
public static BoardGameFinishEvent ParseGameFinish(JsonElement element)
{
return element.Deserialize<BoardGameFinishEvent>(LichessJson.Options);
}
public static BoardGameFullEvent ParseGameFull(JsonElement element)
{
return element.Deserialize<BoardGameFullEvent>(LichessJson.Options);
}
public static BoardGameState ParseGameState(JsonElement element)
{
return element.Deserialize<BoardGameState>(LichessJson.Options);
}
public static BoardChatLineEvent ParseChatLine(JsonElement element)
{
return element.Deserialize<BoardChatLineEvent>(LichessJson.Options);
}
public static IReadOnlyList<string> SplitMoves(string moves)
{
if (string.IsNullOrWhiteSpace(moves))
return Array.Empty<string>();
return moves.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
}
}
/// <summary>
/// Preserves legacy string properties while accepting the object form used by
/// current Board API gameFull payloads.
/// </summary>
public sealed class BoardObjectNameConverter : JsonConverter<string>
{
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
return reader.GetString();
if (reader.TokenType == JsonTokenType.Null)
return null;
using var document = JsonDocument.ParseValue(ref reader);
var value = document.RootElement;
if (value.ValueKind != JsonValueKind.Object)
return value.ToString();
foreach (var propertyName in new[] { "key", "name", "short" })
{
if (value.TryGetProperty(propertyName, out var property) &&
property.ValueKind == JsonValueKind.String)
{
return property.GetString();
}
}
return value.GetRawText();
}
public override void Write(Utf8JsonWriter writer, string value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
}
Debug: View Raw JSON Response
{
"TotalCount": 242,
"Files": [
{
"Ident": "brycpt.lichessnetport",
"Path": "API/AccountAPI.cs",
"FileName": "AccountAPI.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.Entities;\r\nusing LichessNET.Entities.Account;\r\nusing LichessNET.Entities.Social;\r\nusing LichessNET.Entities.Social.Timeline;\r\n\r\nnamespace LichessNET.API;\r\n\r\npublic partial class LichessApiClient\r\n{\r\n /// <summary>\r\n /// Retrieves the email address of the authenticated user.\r\n /// </summary>\r\n /// <returns>\r\n /// A string representing the email address of the authenticated user.\r\n /// </returns>\r\n public async Task<string> GetAccountEmail()\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold(\"api/account/email\");\r\n\r\n var response = await SendRequest(request);\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, string>>();\r\n return content[\"email\"];\r\n }\r\n\r\n /// <summary>\r\n /// Retrieves the profile information of the authenticated user from the Lichess platform.\r\n /// </summary>\r\n /// <returns>\r\n /// An instance of <see cref=\"LichessUser\"/> representing the profile details of the user.\r\n /// </returns>\r\n public async Task<LichessUser> GetOwnProfile()\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold(\"api/account\");\r\n var response = await SendRequest(request);\r\n return await response.Content.ReadFromJsonAsync<LichessUser>();\r\n }\r\n\r\n /// <summary>\r\n /// Retrieves the account preferences of the authenticated user.\r\n /// </summary>\r\n /// <returns>\r\n /// An instance of <see cref=\"AccountPreferences\"/> representing the user's account preferences.\r\n /// </returns>\r\n public async Task<AccountPreferences> GetAccountPreferences()\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold(\"api/account/preferences\");\r\n\r\n var response = await SendRequest(request);\r\n\r\n\r\n var preferences = await response.Content.ReadFromJsonAsync<AccountPreferences>();\r\n return preferences;\r\n }\r\n\r\n /// <summary>\r\n /// Determines whether the authenticated user's account is in kid mode.\r\n /// </summary>\r\n /// <returns>\r\n /// A boolean value indicating if the user's account is set to kid mode.\r\n /// </returns>\r\n public async Task<bool> GetKidModeStatus()\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold(\"api/account/kid\");\r\n\r\n var response = await SendRequest(request);\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();\r\n return content[\"kid\"];\r\n }\r\n\r\n /// <summary>\r\n /// Sets the kid mode status for the authenticated account.\r\n /// </summary>\r\n /// <param name=\"enable\">\r\n /// A boolean value indicating whether to enable or disable kid mode.\r\n /// </param>\r\n /// <returns>\r\n /// A boolean indicating whether the operation was successful.\r\n /// </returns>\r\n public async Task<bool> SetKidModeStatus(bool enable)\r\n {\r\n var request = GetRequestScaffold(\"api/account/kid\", Tuple.Create(\"v\", enable.ToString()));\r\n var response = await SendRequest(request, \"POST\");\r\n\r\n var result = LichessJson.Deserialize<Dictionary<string, bool>>(await response.Content.ReadAsStringAsync());\r\n return result != null && result.TryGetValue(\"ok\", out var ok) && ok;\r\n }\r\n\r\n /// <summary>\r\n /// Follows a player with the specified username on Lichess.\r\n /// </summary>\r\n /// <param name=\"username\">\r\n /// The username of the player to follow.\r\n /// </param>\r\n /// <returns>\r\n /// A boolean value indicating whether the operation was successful.\r\n /// </returns>\r\n public async Task<bool> FollowPlayerAsync(string username)\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold($\"api/rel/follow/{username}\");\r\n var response = await SendRequest(request, \"POST\");\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();\r\n return content[\"ok\"];\r\n }\r\n\r\n /// <summary>\r\n /// Unfollows a specified player using their username.\r\n /// </summary>\r\n /// <param name=\"username\">\r\n /// The username of the player to unfollow.\r\n /// </param>\r\n /// <returns>\r\n /// A boolean value indicating whether the operation was successful.\r\n /// </returns>\r\n public async Task<bool> UnfollowPlayerAsync(string username)\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold($\"api/rel/unfollow/{username}\");\r\n var response = await SendRequest(request, \"POST\");\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();\r\n return content[\"ok\"];\r\n }\r\n\r\n /// <summary>\r\n /// Blocks a player on Lichess based on their username.\r\n /// </summary>\r\n /// <param name=\"username\">\r\n /// The username of the player to be blocked.\r\n /// </param>\r\n /// <returns>\r\n /// A boolean indicating whether the player was successfully blocked.\r\n /// </returns>\r\n public async Task<bool> BlockPlayerAsync(string username)\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold($\"api/rel/block/{username}\");\r\n var response = await SendRequest(request, \"POST\");\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();\r\n return content[\"ok\"];\r\n }\r\n\r\n /// <summary>\r\n /// Unblocks a player specified by username.\r\n /// </summary>\r\n /// <param name=\"username\">\r\n /// The username of the player to unblock.\r\n /// </param>\r\n /// <returns>\r\n /// A boolean value indicating whether the player was successfully unblocked.\r\n /// </returns>\r\n public async Task<bool> UnblockPlayerAsync(string username)\r\n {\r\n await _ratelimitController.Consume(\"api/account\", false);\r\n\r\n var request = GetRequestScaffold($\"api/rel/unblock/{username}\");\r\n var response = await SendRequest(request, \"POST\");\r\n var content = await response.Content.ReadFromJsonAsync<Dictionary<string, bool>>();\r\n return content[\"ok\"];\r\n }\r\n\r\n /// <summary>\r\n /// Gets the timeline of the authenticated user.\r\n /// </summary>\r\n /// <param name=\"since\">When the timeline should start</param>\r\n /// <param name=\"nb\">The number of timeline events that should be loaded. Defaults to 15, the maximum is 50.</param>\r\n /// <returns>A Timeline object</returns>\r\n public async Task<Timeline?> GetTimelineAsync(DateTime since, int nb = 15)\r\n {\r\n await _ratelimitController.Consume(\"api/timeline\", false);\r\n\r\n var unixTimestamp = new DateTimeOffset(since).ToUnixTimeMilliseconds();\r\n var request = GetRequestScaffold($\"api/timeline\",\r\n new Tuple<string, string>(\"since\", unixTimestamp.ToString()),\r\n new Tuple<string, string>(\"nb\", nb.ToString()));\r\n\r\n var response = await SendRequest(request);\r\n var content = await response.Content.ReadAsStringAsync();\r\n\r\n var root = LichessJson.Parse(content);\r\n var timeline = new Timeline\r\n {\r\n Entries = new List<TimelineEntry>(),\r\n Users = root.TryGetProperty(\"users\", out var users)\r\n ? users.Deserialize<Dictionary<string, TimelineUser>>(LichessJson.Options) ?? new Dictionary<string, TimelineUser>()\r\n : new Dictionary<string, TimelineUser>()\r\n };\r\n\r\n if (root.TryGetProperty(\"entries\", out var entries))\r\n {\r\n foreach (var entryElement in entries.EnumerateArray())\r\n {\r\n var type = entryElement.TryGetProperty(\"type\", out var typeElement) ? typeElement.GetString() : null;\r\n var date = entryElement.TryGetProperty(\"date\", out var dateElement) ? dateElement.GetInt64() : 0;\r\n var data = entryElement.TryGetProperty(\"data\", out var dataElement)\r\n ? TimelineEventDataConverter.DeserializeData(type, dataElement, LichessJson.Options)\r\n : new UnknownEventData();\r\n\r\n timeline.Entries.Add(new TimelineEntry\r\n {\r\n Type = type,\r\n Date = date,\r\n Data = data\r\n });\r\n }\r\n }\r\n\r\n return timeline;\r\n }\r\n}\r\n\r\n\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Code/Entities/Enumerations/Speed.cs",
"FileName": "Speed.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Enumerations;\r\n\r\npublic enum Speed\r\n{\r\n UltraBullet, \r\n Bullet,\r\n Blitz,\r\n Rapid,\r\n Classical,\r\n Correspondence,\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Code/Entities/Social/Timeline/TimelineEventData.cs",
"FileName": "TimelineEventData.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Social.Timeline\r\n{\r\n public abstract class TimelineEventData\r\n {\r\n }\r\n\r\n public class FollowEventData : TimelineEventData\r\n {\r\n public string U1 { get; set; }\r\n public string U2 { get; set; }\r\n }\r\n\r\n public class UnknownEventData : TimelineEventData\r\n {\r\n }\r\n\r\n public class TeamJoinEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string TeamId { get; set; }\r\n }\r\n\r\n public class TeamCreateEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string TeamId { get; set; }\r\n }\r\n\r\n public class ForumPostEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string TopicId { get; set; }\r\n public string TopicName { get; set; }\r\n public string PostId { get; set; }\r\n }\r\n\r\n public class UblogPostEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string Id { get; set; }\r\n public string Slug { get; set; }\r\n public string Title { get; set; }\r\n }\r\n\r\n public class TourJoinEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string TourId { get; set; }\r\n public string TourName { get; set; }\r\n }\r\n\r\n public class GameEndEventData : TimelineEventData\r\n {\r\n public string FullId { get; set; }\r\n public string Perf { get; set; }\r\n public string Opponent { get; set; }\r\n public bool Win { get; set; }\r\n }\r\n\r\n public class SimulCreateEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string SimulId { get; set; }\r\n public string SimulName { get; set; }\r\n }\r\n\r\n public class SimulJoinEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string SimulId { get; set; }\r\n public string SimulName { get; set; }\r\n }\r\n\r\n public class StudyLikeEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string StudyId { get; set; }\r\n public string StudyName { get; set; }\r\n }\r\n\r\n public class PlanStartEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n }\r\n\r\n public class PlanRenewEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public int Months { get; set; }\r\n }\r\n\r\n public class BlogPostEventData : TimelineEventData\r\n {\r\n public string Id { get; set; }\r\n public string Slug { get; set; }\r\n public string Title { get; set; }\r\n }\r\n\r\n public class UblogPostLikeEventData : TimelineEventData\r\n {\r\n public string UserId { get; set; }\r\n public string Id { get; set; }\r\n public string Title { get; set; }\r\n }\r\n\r\n public class StreamStartEventData : TimelineEventData\r\n {\r\n public string Id { get; set; }\r\n public string Title { get; set; }\r\n }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Code/Entities/Social/Timeline/TimelineEventDataConverter.cs",
"FileName": "TimelineEventDataConverter.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Social.Timeline;\r\n\r\ninternal class TimelineEventDataConverter : JsonConverter<TimelineEventData>\r\n{\r\n public override TimelineEventData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\r\n {\r\n using var document = JsonDocument.ParseValue(ref reader);\r\n return DeserializeData(null, document.RootElement, options);\r\n }\r\n\r\n public override void Write(Utf8JsonWriter writer, TimelineEventData value, JsonSerializerOptions options)\r\n {\r\n JsonSerializer.Serialize(writer, (object)value, options);\r\n }\r\n\r\n internal static TimelineEventData DeserializeData(string type, JsonElement data, JsonSerializerOptions options)\r\n {\r\n if (string.IsNullOrWhiteSpace(type) && data.TryGetProperty(\"type\", out var embeddedType))\r\n type = embeddedType.GetString();\r\n\r\n if (data.TryGetProperty(\"data\", out var nestedData))\r\n data = nestedData;\r\n\r\n TimelineEventData result = type switch\r\n {\r\n \"follow\" => data.Deserialize<FollowEventData>(options),\r\n \"team-join\" => data.Deserialize<TeamJoinEventData>(options),\r\n \"team-create\" => data.Deserialize<TeamCreateEventData>(options),\r\n \"forum-post\" => data.Deserialize<ForumPostEventData>(options),\r\n \"ublog-post\" => data.Deserialize<UblogPostEventData>(options),\r\n \"tour-join\" => data.Deserialize<TourJoinEventData>(options),\r\n \"game-end\" => data.Deserialize<GameEndEventData>(options),\r\n \"simul-create\" => data.Deserialize<SimulCreateEventData>(options),\r\n \"simul-join\" => data.Deserialize<SimulJoinEventData>(options),\r\n \"study-like\" => data.Deserialize<StudyLikeEventData>(options),\r\n \"plan-start\" => data.Deserialize<PlanStartEventData>(options),\r\n \"plan-renew\" => data.Deserialize<PlanRenewEventData>(options),\r\n \"blog-post\" => data.Deserialize<BlogPostEventData>(options),\r\n \"ublog-post-like\" => data.Deserialize<UblogPostLikeEventData>(options),\r\n \"stream-start\" => data.Deserialize<StreamStartEventData>(options),\r\n _ => null\r\n };\r\n\r\n return result ?? new UnknownEventData();\r\n }\r\n}\r\n\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Converters/PermissionJsonConverter.cs",
"FileName": "PermissionJsonConverter.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing System.Text.Json;\r\nusing System.Text.Json.Serialization;\r\nusing LichessNET.Entities.Enumerations;\r\n\r\nnamespace LichessNET.Converters;\r\n\r\npublic class PermissionJsonConverter : JsonConverter<List<TokenPermission>>\r\n{\r\n public override List<TokenPermission>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\r\n {\r\n\r\n var permissions = reader.GetString();\r\n string[] permissionsList = permissions.Split(',');\r\n List<TokenPermission> tokenPermissions = new List<TokenPermission>();\r\n\r\n foreach (string permission in permissionsList)\r\n {\r\n switch (permission)\r\n {\r\n case \"email:read\":\r\n tokenPermissions.Add(TokenPermission.ReadEmail);\r\n break;\r\n case \"preference:read\":\r\n tokenPermissions.Add(TokenPermission.ReadPreferences);\r\n break;\r\n case \"preference:write\":\r\n tokenPermissions.Add(TokenPermission.WritePreferences);\r\n break;\r\n case \"follow:read\":\r\n tokenPermissions.Add(TokenPermission.ReadFollows);\r\n break;\r\n case \"follow:write\":\r\n tokenPermissions.Add(TokenPermission.WriteFollows);\r\n break;\r\n case \"msg:write\":\r\n tokenPermissions.Add(TokenPermission.WriteMessages);\r\n break;\r\n case \"challenge:read\":\r\n tokenPermissions.Add(TokenPermission.ReadChallenges);\r\n break;\r\n case \"challenge:write\":\r\n tokenPermissions.Add(TokenPermission.WriteChallenges);\r\n break;\r\n case \"challenge:bulk\":\r\n tokenPermissions.Add(TokenPermission.BulkChallenges);\r\n break;\r\n case \"tournament:write\":\r\n tokenPermissions.Add(TokenPermission.WriteTournaments);\r\n break;\r\n case \"team:read\":\r\n tokenPermissions.Add(TokenPermission.ReadTeams);\r\n break;\r\n case \"team:write\":\r\n tokenPermissions.Add(TokenPermission.WriteTeams);\r\n break;\r\n case \"team:lead\":\r\n tokenPermissions.Add(TokenPermission.ManageTeams);\r\n break;\r\n case \"puzzle:read\":\r\n tokenPermissions.Add(TokenPermission.ReadPuzzleActivity);\r\n break;\r\n case \"racer:write\":\r\n tokenPermissions.Add(TokenPermission.WriteRaces);\r\n break;\r\n case \"study:read\":\r\n tokenPermissions.Add(TokenPermission.ReadStudies);\r\n break;\r\n case \"study:write\":\r\n tokenPermissions.Add(TokenPermission.WriteStudies);\r\n break;\r\n case \"board:play\":\r\n tokenPermissions.Add(TokenPermission.PlayGames);\r\n break;\r\n case \"engine:read\":\r\n tokenPermissions.Add(TokenPermission.ReadEngines);\r\n break;\r\n case \"engine:write\":\r\n tokenPermissions.Add(TokenPermission.ManageEngines);\r\n break;\r\n }\r\n }\r\n\r\n return tokenPermissions;\r\n }\r\n\r\n public override void Write(Utf8JsonWriter writer, List<TokenPermission> value, JsonSerializerOptions options)\r\n {\r\n throw new NotImplementedException();\r\n }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Game/GameStream.cs",
"FileName": "GameStream.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.API;\r\n\r\nnamespace LichessNET.Entities.Game;\r\n\r\n/// <summary>\r\n/// Represents a stream for a Lichess game, handling real-time updates of moves and game information.\r\n/// </summary>\r\npublic class GameStream\r\n{\r\n public delegate void GameInfoFetchedHandler(object sender, OngoingGame game);\r\n public delegate void MoveUpdateHandler(object sender, Move move);\r\n\r\n private readonly Dictionary<string, OngoingGame> _games = new Dictionary<string, OngoingGame>();\r\n private readonly LichessStream _stream;\r\n\r\n public GameStream(string requestUri, string method = \"GET\")\r\n : this(requestUri, method, CancellationToken.None)\r\n {\r\n }\r\n\r\n public GameStream(string requestUri, string method, CancellationToken cancellationToken)\r\n {\r\n _stream = new LichessStream(requestUri, method);\r\n _stream.GameUpdateReceived += ProcessData;\r\n _ = _stream.StreamGameAsync(cancellationToken);\r\n }\r\n\r\n public event MoveUpdateHandler? OnMoveMade;\r\n public event GameInfoFetchedHandler? OnGameInfoFetched;\r\n\r\n private void ProcessData(object sender, JsonElement data)\r\n {\r\n FetchDataForNewGame(data);\r\n FetchDataForMove(data);\r\n }\r\n\r\n private void FetchDataForNewGame(JsonElement data)\r\n {\r\n if (!data.TryGetProperty(\"id\", out var idProperty))\r\n return;\r\n\r\n var gameId = idProperty.GetString();\r\n if (string.IsNullOrWhiteSpace(gameId))\r\n return;\r\n\r\n var game = new OngoingGame\r\n {\r\n GameId = gameId,\r\n PlysAtInitFen = data.TryGetProperty(\"turns\", out var turns) ? turns.GetInt32() : 0\r\n };\r\n\r\n _games[gameId] = game;\r\n OnGameInfoFetched?.Invoke(this, game);\r\n }\r\n\r\n private void FetchDataForMove(JsonElement data)\r\n {\r\n if (!data.TryGetProperty(\"lm\", out var lastMoveProperty))\r\n return;\r\n\r\n var gameId = _games.Count == 1 ? _games.First().Key : string.Empty;\r\n if (string.IsNullOrWhiteSpace(gameId) || !_games.TryGetValue(gameId, out var game))\r\n return;\r\n\r\n game.Moves ??= new List<Move>();\r\n var fen = data.TryGetProperty(\"fen\", out var fenProperty) ? fenProperty.GetString() : string.Empty;\r\n var move = new Move\r\n {\r\n Notation = lastMoveProperty.GetString(),\r\n IsWhite = fen?.Contains(\" w \") ?? false,\r\n GameID = gameId,\r\n MoveNumber = (game.Moves.Count / 2) + 1\r\n };\r\n\r\n game.Moves.Add(move);\r\n OnMoveMade?.Invoke(this, move);\r\n }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Game/TimeControl.cs",
"FileName": "TimeControl.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Game;\r\n\r\npublic class TimeControl\r\n{\r\n public string Type { get; set; }\r\n public int Limit { get; set; }\r\n public int Increment { get; set; }\r\n public string Show { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Puzzle/Dashboard/PuzzleResults.cs",
"FileName": "PuzzleResults.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Puzzle.Dashboard;\r\n\r\npublic class PuzzleResults\r\n{\r\n public int FirstWins { get; set; }\r\n public int Nb { get; set; }\r\n public int Performance { get; set; }\r\n public int PuzzleRatingAvg { get; set; }\r\n public int ReplayWins { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Social/Stream/Streamer.cs",
"FileName": "Streamer.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Social.Stream;\r\n\r\npublic class Streamer\r\n{\r\n public string Name { get; set; }\r\n public string Headline { get; set; }\r\n public string Description { get; set; }\r\n public string Twitch { get; set; }\r\n public string YouTube { get; set; }\r\n public string Image { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Stats/Performance/Glicko.cs",
"FileName": "Glicko.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Account.Performance;\r\n\r\npublic class Glicko\r\n{\r\n public double Rating { get; set; }\r\n public double Deviation { get; set; }\r\n public bool Provisional { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Stats/Performance/PerformanceStats.cs",
"FileName": "PerformanceStats.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing System.Text.Json.Serialization;\r\n\r\nnamespace LichessNET.Entities.Account.Performance;\r\n\r\npublic class PerformanceStats\r\n{\r\n public string User { get; set; }\r\n public Perf Perf { get; set; }\r\n public int Rank { get; set; }\r\n public double Percentile { get; set; }\r\n public Stat Stat { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Stats/Performance/StatEntities.cs",
"FileName": "StatEntities.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Account.Performance;\r\n\r\npublic class PerfType\r\n{\r\n public string Key { get; set; }\r\n public string Name { get; set; }\r\n}\r\n\r\npublic class Highest\r\n{\r\n public int Int { get; set; }\r\n public DateTime At { get; set; }\r\n public string GameId { get; set; }\r\n}\r\n\r\npublic class BestWins\r\n{\r\n public List<Result> Results { get; set; }\r\n}\r\n\r\npublic class WorstLosses\r\n{\r\n public List<Result> Results { get; set; }\r\n}\r\n\r\npublic class Result\r\n{\r\n public int OpRating { get; set; }\r\n public OpId OpId { get; set; }\r\n public DateTime At { get; set; }\r\n public string GameId { get; set; }\r\n}\r\n\r\npublic class OpId\r\n{\r\n public string Id { get; set; }\r\n public string Name { get; set; }\r\n}\r\n\r\npublic class Count\r\n{\r\n public int All { get; set; }\r\n public int Rated { get; set; }\r\n public int Win { get; set; }\r\n public int Loss { get; set; }\r\n public int Draw { get; set; }\r\n public int Tour { get; set; }\r\n public int Berserk { get; set; }\r\n public double OpAvg { get; set; }\r\n public int Seconds { get; set; }\r\n public int Disconnects { get; set; }\r\n}\r\n\r\npublic class ResultStreak\r\n{\r\n public Streak Win { get; set; }\r\n public Streak Loss { get; set; }\r\n}\r\n\r\npublic class Streak\r\n{\r\n public Cur Cur { get; set; }\r\n public Max Max { get; set; }\r\n}\r\n\r\npublic class Cur\r\n{\r\n public int V { get; set; }\r\n}\r\n\r\npublic class Max\r\n{\r\n public int V { get; set; }\r\n public From From { get; set; }\r\n public To To { get; set; }\r\n}\r\n\r\npublic class From\r\n{\r\n public DateTime At { get; set; }\r\n public string GameId { get; set; }\r\n}\r\n\r\npublic class To\r\n{\r\n public DateTime At { get; set; }\r\n public string GameId { get; set; }\r\n}\r\n\r\npublic class UserId\r\n{\r\n public string Id { get; set; }\r\n public string Name { get; set; }\r\n public string Title { get; set; }\r\n}\r\n\r\npublic class PlayStreak\r\n{\r\n public Nb Nb { get; set; }\r\n public Time Time { get; set; }\r\n public DateTime LastDate { get; set; }\r\n}\r\n\r\npublic class Nb\r\n{\r\n public Cur Cur { get; set; }\r\n public Max Max { get; set; }\r\n}\r\n\r\npublic class Time\r\n{\r\n public Cur Cur { get; set; }\r\n public Max Max { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Teams/LichessTeam.cs",
"FileName": "LichessTeam.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.Entities.Social;\r\n\r\nnamespace LichessNET.Entities.Teams;\r\n\r\n/// <summary>\r\n/// Represents a team on Lichess, containing information such as its ID, name, description, and members.\r\n/// </summary>\r\npublic class LichessTeam\r\n{\r\n public string Id { get; set; }\r\n public string Name { get; set; }\r\n public string Description { get; set; }\r\n public string Flair { get; set; }\r\n public TeamLeader Leader { get; set; }\r\n public List<TeamLeader> Leaders { get; set; }\r\n public int NbMembers { get; set; }\r\n public bool Open { get; set; }\r\n public bool Joined { get; set; }\r\n public bool Requested { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Teams/TeamLeader.cs",
"FileName": "TeamLeader.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Entities.Social;\r\n\r\n/// <summary>\r\n/// Represents a leader of a team on Lichess, providing properties for flair, identification, name, and patron status.\r\n/// </summary>\r\npublic class TeamLeader\r\n{\r\n public string Flair { get; set; }\r\n public string Id { get; set; }\r\n public string Name { get; set; }\r\n public bool Patron { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Internal/LichessLog.cs",
"FileName": "LichessLog.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nnamespace LichessNET.Internal;\r\n\r\ninternal sealed class LichessLog\r\n{\r\n\tprivate readonly string _name;\r\n\tprivate readonly bool _enabled;\r\n\r\n\tpublic LichessLog( string name, bool enabled = true )\r\n\t{\r\n\t\t_name = name;\r\n\t\t_enabled = enabled;\r\n\t}\r\n\r\n\tpublic void Debug( string message )\r\n\t{\r\n\t\tif ( _enabled )\r\n\t\t\tLog.Info( $\"[{_name}] {message}\" );\r\n\t}\r\n\r\n\tpublic void Information( string message )\r\n\t{\r\n\t\tif ( _enabled )\r\n\t\t\tLog.Info( $\"[{_name}] {message}\" );\r\n\t}\r\n\r\n\tpublic void Warning( string message )\r\n\t{\r\n\t\tif ( _enabled )\r\n\t\t\tLog.Warning( $\"[{_name}] {message}\" );\r\n\t}\r\n\r\n\tpublic void Error( string message )\r\n\t{\r\n\t\tif ( _enabled )\r\n\t\t\tLog.Error( $\"[{_name}] {message}\" );\r\n\t}\r\n}\r\n\r\n\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "API/LichessApiException.cs",
"FileName": "LichessApiException.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\n\nnamespace LichessNET.API;\n\n/// <summary>\n/// A sanitized failure returned by the Lichess API.\n/// </summary>\npublic sealed class LichessApiException : HttpRequestException\n{\n public LichessApiException(HttpStatusCode? statusCode = null)\n : this(CreateMessage(statusCode), statusCode)\n {\n }\n\n private LichessApiException(string message, HttpStatusCode? statusCode)\n : base(message, null, statusCode)\n {\n }\n\n internal static LichessApiException InvalidResponse(HttpStatusCode statusCode)\n {\n return new LichessApiException(\n \"Lichess API returned a response that could not be processed.\", statusCode);\n }\n\n private static string CreateMessage(HttpStatusCode? statusCode)\n {\n return statusCode.HasValue\n ? $\"Lichess API request failed with HTTP status {(int)statusCode.Value} ({statusCode.Value}).\"\n : \"Lichess API request failed before an HTTP status was received.\";\n }\n}\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "API/OAuthAPI.cs",
"FileName": "OAuthAPI.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.Entities.OAuth;\r\n\r\nnamespace LichessNET.API;\r\n\r\npublic partial class LichessApiClient\r\n{\r\n /// <summary>\r\n /// Tests OAuth tokens without placing them in the request URI or logs.\r\n /// </summary>\r\n public Task<Dictionary<string, TokenInfo?>> TestTokensAsync(List<string> tokens)\r\n {\r\n return TestTokensAsync(tokens, CancellationToken.None);\r\n }\r\n\r\n public async Task<Dictionary<string, TokenInfo?>> TestTokensAsync(List<string> tokens,\r\n CancellationToken cancellationToken = default)\r\n {\r\n if (tokens == null)\r\n throw new ArgumentNullException(nameof(tokens));\r\n\r\n if (tokens.Count == 0 || tokens.Count > 1000)\r\n throw new ArgumentOutOfRangeException(nameof(tokens),\r\n \"Between 1 and 1000 OAuth tokens are required.\");\r\n\r\n if (tokens.Any(string.IsNullOrWhiteSpace))\r\n throw new ArgumentException(\"OAuth tokens cannot be empty.\", nameof(tokens));\r\n\r\n cancellationToken.ThrowIfCancellationRequested();\r\n\r\n var request = GetRequestScaffold(\"api/token/test\");\r\n var requestContent = new StringContent(\r\n string.Join(',', tokens), Encoding.UTF8, \"text/plain\");\r\n var response = await SendRequest(request, \"POST\", useToken: false,\r\n cancellationToken: cancellationToken, requestContent: requestContent);\r\n\r\n cancellationToken.ThrowIfCancellationRequested();\r\n try\r\n {\r\n return await response.Content.ReadFromJsonAsync<Dictionary<string, TokenInfo?>>(\r\n LichessJson.Options)\r\n ?? new Dictionary<string, TokenInfo?>();\r\n }\r\n catch (Exception exception) when (exception is not OperationCanceledException)\r\n {\r\n throw LichessApiException.InvalidResponse(response.StatusCode);\r\n }\r\n }\r\n\r\n /// <summary>\r\n /// Deletes a token.\r\n /// </summary>\r\n public async Task DeleteTokenAsync(string token)\r\n {\r\n var request = GetRequestScaffold(\"api/token\");\r\n request.Headers[\"Authorization\"] = \"Bearer \" + token;\r\n await SendRequest(request, \"DELETE\", useToken: false);\r\n }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Code/API/TeamAPI.cs",
"FileName": "TeamAPI.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.Entities.Teams;\r\nusing LichessNET.Entities.Tournament;\r\nusing LichessNET.Entities.Tournament.Arena;\r\n\r\nnamespace LichessNET.API;\r\n\r\npublic partial class LichessApiClient\r\n{\r\n /// <summary>\r\n /// Get the team with the specified ID\r\n /// </summary>\r\n /// <param name=\"teamId\">The ID of the team</param>\r\n /// <returns>The team with the specified ID</returns>\r\n public async Task<LichessTeam> GetTeamAsync(string teamId)\r\n {\r\n await _ratelimitController.Consume();\r\n var request = GetRequestScaffold($\"api/team/{teamId}\");\r\n var response = await SendRequest(request);\r\n\r\n var content = await response.Content.ReadAsStringAsync();\r\n var team = LichessJson.Deserialize<LichessTeam>(content);\r\n\r\n return team;\r\n }\r\n\r\n /// <summary>\r\n /// Gets the most populat teams on Lichess in a paginated manner.\r\n /// </summary>\r\n /// <param name=\"page\">Page number, defaults to 1</param>\r\n /// <returns>The list of the most popular teams on the specified page.</returns>\r\n public async Task<List<LichessTeam>> GetPopularTeamsAsync(int page = 1)\r\n {\r\n await _ratelimitController.Consume();\r\n var request = GetRequestScaffold(\"api/team/all\", new Tuple<string, string>(\"page\", page.ToString()));\r\n var response = await SendRequest(request);\r\n\r\n var content = await response.Content.ReadAsStringAsync();\r\n var json = LichessJson.Parse(content);\r\n return json.TryGetProperty(\"currentPageResults\", out var results)\r\n ? results.Deserialize<List<LichessTeam>>(LichessJson.Options) ?? new List<LichessTeam>()\r\n : new List<LichessTeam>();\r\n }\r\n\r\n /// <summary>\r\n /// Get the list of teams that the specified user is in.\r\n /// </summary>\r\n /// <param name=\"username\">The username of the user</param>\r\n /// <returns>A list of teams that the specified user is in</returns>\r\n public async Task<List<LichessTeam>> GetTeamOfUserAsync(string username)\r\n {\r\n await _ratelimitController.Consume();\r\n\r\n var request = GetRequestScaffold($\"api/team/of/{username}\");\r\n var response = await SendRequest(request);\r\n\r\n var content = await response.Content.ReadAsStringAsync();\r\n var teams = LichessJson.Deserialize<List<LichessTeam>>(content);\r\n\r\n return teams;\r\n }\r\n\r\n /// <summary>\r\n /// This funciton gets the members of a team, in chronoclogical order of joining the team. (Latest first),\r\n /// with up to 5000 members.\r\n /// </summary>\r\n /// <param name=\"teamId\">ID of the Team</param>\r\n /// <returns></returns>\r\n public async Task<List<TeamMember>> GetTeamMembersAsync(string teamId)\r\n {\r\n await _ratelimitController.Consume();\r\n var request = GetRequestScaffold($\"api/team/{teamId}/users\");\r\n\r\n var response = await SendRequest(request);\r\n\r\n var content = await response.Content.ReadAsStringAsync();\r\n var members = LichessJson.Deserialize<List<TeamMember>>(content);\r\n\r\n return members;\r\n }\r\n\r\n /// <summary>\r\n /// Retrieves all Swiss tournaments of a team.\r\n /// </summary>\r\n /// <param name=\"teamId\">The ID of the team.</param>\r\n /// <param name=\"max\">The maximum number of tournaments to download. Default is 100.</param>\r\n /// <returns>A task representing the asynchronous operation, containing the list of Swiss tournaments.</returns>\r\n public async Task<List<SwissTournament>> GetTeamSwissTournamentsAsync(string teamId, int max = 100)\r\n {\r\n await _ratelimitController.Consume();\r\n\r\n var endpoint = $\"api/team/{teamId}/swiss?max={max}\";\r\n var request = GetRequestScaffold(endpoint);\r\n\r\n var response = await SendRequest(request);\r\n var content = await response.Content.ReadAsStringAsync();\r\n\r\n var tournaments = new List<SwissTournament>();\r\n foreach (var line in content.Split('\\n')) { if (string.IsNullOrWhiteSpace(line)) continue; var tournament = LichessJson.Deserialize<SwissTournament>(line.Trim()); if (tournament != null) tournaments.Add(tournament); }\r\n\r\n return tournaments;\r\n }\r\n\r\n /// <summary>\r\n /// Retrieves all Arena tournaments relevant to a team.\r\n /// </summary>\r\n /// <param name=\"teamId\">The ID of the team.</param>\r\n /// <param name=\"max\">The maximum number of tournaments to download. Default is 100.</param>\r\n /// <returns>A task representing the asynchronous operation, containing the list of Arena tournaments.</returns>\r\n public async Task<List<ArenaTournament>> GetTeamArenaTournamentsAsync(string teamId, int max = 100)\r\n {\r\n await _ratelimitController.Consume();\r\n\r\n var endpoint = $\"api/team/{teamId}/arena?max={max}\";\r\n var request = GetRequestScaffold(endpoint);\r\n\r\n var response = await SendRequest(request);\r\n var content = await response.Content.ReadAsStringAsync();\r\n\r\n var tournaments = new List<ArenaTournament>();\r\n foreach (var line in content.Split('\\n')) { if (string.IsNullOrWhiteSpace(line)) continue; var tournament = LichessJson.Deserialize<ArenaTournament>(line.Trim()); if (tournament != null) tournaments.Add(tournament); }\r\n\r\n return tournaments;\r\n }\r\n\r\n /// <summary>\r\n /// Joins a team.\r\n /// </summary>\r\n /// <param name=\"teamId\">The ID of the team.</param>\r\n /// <param name=\"message\">The message for the team join request, required if the team manually reviews admission requests.</param>\r\n /// <param name=\"password\">The password for the team, if required.</param>\r\n /// <returns>A task representing the asynchronous operation, containing the join team response.</returns>\r\n public async Task<bool> JoinTeamAsync(string teamId, string message = null, string password = null)\r\n {\r\n await _ratelimitController.Consume();\r\n\r\n var endpoint = $\"team/{teamId}/join\";\r\n var request = GetRequestScaffold(endpoint);\r\n\r\n var formData = new Dictionary<string, string>();\r\n if (!string.IsNullOrEmpty(message))\r\n {\r\n formData.Add(\"message\", message);\r\n }\r\n\r\n if (!string.IsNullOrEmpty(password))\r\n {\r\n formData.Add(\"password\", password);\r\n }\r\n\r\n var response = await SendRequest(request, \"POST\", formData: formData);\r\n var content = await response.Content.ReadAsStringAsync();\r\n\r\n return content.Contains(\"true\");\r\n }\r\n\r\n /// <summary>\r\n /// Leaves a team.\r\n /// </summary>\r\n /// <param name=\"teamId\">The ID of the team.</param>\r\n /// <returns>A task representing the asynchronous operation, containing the leave team response.</returns>\r\n public async Task<bool> LeaveTeamAsync(string teamId)\r\n {\r\n await _ratelimitController.Consume();\r\n\r\n var endpoint = $\"team/{teamId}/quit\";\r\n var request = GetRequestScaffold(endpoint);\r\n\r\n var response = await SendRequest(request, \"POST\");\r\n var content = await response.Content.ReadAsStringAsync();\r\n\r\n return content.Contains(\"true\");\r\n }\r\n}\r\n\r\n\r\n\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Code/Entities/Game/GamePlayer.cs",
"FileName": "GamePlayer.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing LichessNET.Entities.Enumerations;\r\n\r\nnamespace LichessNET.Entities.Game;\r\n\r\n/// <summary>\r\n/// Represents a player in a chess game, including their name, title, and rating.\r\n/// This class will be used when parsing PGN Files\r\n/// </summary>\r\npublic class GamePlayer\r\n{\r\n public string Name { get; set; }\r\n\r\n //TODO: Add serialization for Title\r\n public Title? Title { get; set; }\r\n public int Rating { get; set; }\r\n}\r\n\r\n"
},
{
"Ident": "brycpt.lichessnetport",
"Path": "Entities/Board/BoardModels.cs",
"FileName": "BoardModels.cs",
"PackageType": "library",
"CodeKind": "Game",
"AssetVersionId": 308901,
"IsPrivate": false,
"Code": "#nullable enable annotations\r\n\r\nusing System.Globalization;\r\nusing LichessNET.Entities.Enumerations;\r\nusing LichessNET.Extensions;\r\n\r\nnamespace LichessNET.Entities.Board;\r\n\r\npublic enum BoardColorPreference\r\n{\r\n Random,\r\n White,\r\n Black\r\n}\r\n\r\npublic enum BoardChatRoom\r\n{\r\n Player,\r\n Spectator\r\n}\r\n\r\npublic sealed class BoardSeekOptions\r\n{\r\n public bool Rated { get; set; }\r\n public double TimeMinutes { get; set; } = 5;\r\n public int IncrementSeconds { get; set; }\r\n public int? DaysPerTurn { get; set; }\r\n public BoardColorPreference Color { get; set; } = BoardColorPreference.Random;\r\n public ChessVariant Variant { get; set; } = ChessVariant.Standard;\r\n public string RatingRange { get; set; }\r\n\r\n internal Dictionary<string, string> ToFormData()\r\n {\r\n Validate();\r\n\r\n var form = new Dictionary<string, string>\r\n {\r\n [\"rated\"] = Rated.ToString().ToLowerInvariant(),\r\n [\"variant\"] = Variant.ToApiName(),\r\n [\"color\"] = ToApiName(Color)\r\n };\r\n\r\n if (DaysPerTurn.HasValue)\r\n {\r\n form[\"days\"] = DaysPerTurn.Value.ToString(CultureInfo.InvariantCulture);\r\n }\r\n else\r\n {\r\n form[\"time\"] = TimeMinutes.ToString(CultureInfo.InvariantCulture);\r\n form[\"increment\"] = IncrementSeconds.ToString(CultureInfo.InvariantCulture);\r\n }\r\n\r\n if (!string.IsNullOrWhiteSpace(RatingRange))\r\n form[\"ratingRange\"] = RatingRange;\r\n\r\n return form;\r\n }\r\n\r\n private void Validate()\r\n {\r\n if (DaysPerTurn.HasValue)\r\n {\r\n if (DaysPerTurn.Value < 1 || DaysPerTurn.Value > 14)\r\n throw new ArgumentOutOfRangeException(nameof(DaysPerTurn), \"Days per turn must be between 1 and 14.\");\r\n\r\n return;\r\n }\r\n\r\n if (TimeMinutes <= 0 || TimeMinutes > 180)\r\n throw new ArgumentOutOfRangeException(nameof(TimeMinutes), \"Clock time must be between 0 and 180 minutes.\");\r\n\r\n if (IncrementSeconds < 0 || IncrementSeconds > 180)\r\n throw new ArgumentOutOfRangeException(nameof(IncrementSeconds), \"Increment must be between 0 and 180 seconds.\");\r\n }\r\n\r\n private static string ToApiName(BoardColorPreference color)\r\n {\r\n return color switch\r\n {\r\n BoardColorPreference.White => \"white\",\r\n BoardColorPreference.Black => \"black\",\r\n _ => \"random\"\r\n };\r\n }\r\n}\r\n\r\npublic sealed class BoardGameInfo\r\n{\r\n [JsonPropertyName(\"gameId\")]\r\n public string GameId { get; set; }\r\n\r\n [JsonPropertyName(\"id\")]\r\n public string Id { get; set; }\r\n\r\n [JsonPropertyName(\"fullId\")]\r\n public string FullId { get; set; }\r\n\r\n [JsonPropertyName(\"color\")]\r\n public string Color { get; set; }\r\n\r\n [JsonPropertyName(\"fen\")]\r\n public string Fen { get; set; }\r\n\r\n [JsonPropertyName(\"hasMoved\")]\r\n public bool? HasMoved { get; set; }\r\n\r\n [JsonPropertyName(\"isMyTurn\")]\r\n public bool? IsMyTurn { get; set; }\r\n\r\n [JsonPropertyName(\"lastMove\")]\r\n public string LastMove { get; set; }\r\n\r\n [JsonPropertyName(\"source\")]\r\n public string Source { get; set; }\r\n\r\n [JsonPropertyName(\"status\")]\r\n public string Status { get; set; }\r\n\r\n [JsonPropertyName(\"variant\")]\r\n [JsonConverter(typeof(BoardObjectNameConverter))]\r\n public string Variant { get; set; }\r\n\r\n [JsonPropertyName(\"speed\")]\r\n public string Speed { get; set; }\r\n\r\n [JsonPropertyName(\"perf\")]\r\n [JsonConverter(typeof(BoardObjectNameConverter))]\r\n public string Perf { get; set; }\r\n\r\n [JsonPropertyName(\"rated\")]\r\n public bool? Rated { get; set; }\r\n\r\n [JsonPropertyName(\"opponent\")]\r\n public BoardPlayerInfo Opponent { get; set; }\r\n\r\n [JsonPropertyName(\"white\")]\r\n public BoardPlayerInfo White { get; set; }\r\n\r\n [JsonPropertyName(\"black\")]\r\n public BoardPlayerInfo Black { get; set; }\r\n\r\n [JsonPropertyName(\"state\")]\r\n public BoardGameState State { get; set; }\r\n\r\n [JsonExtensionData]\r\n public Dictionary<string, JsonElement> Extra { get; set; }\r\n\r\n public string BestGameId => !string.IsNullOrWhiteSpace(GameId) ? GameId : Id;\r\n}\r\n\r\npublic sealed class BoardPlayerInfo\r\n{\r\n [JsonPropertyName(\"id\")]\r\n public string Id { get; set; }\r\n\r\n [JsonPropertyName(\"name\")]\r\n public string Name { get; set; }\r\n\r\n [JsonPropertyName(\"username\")]\r\n public string Username { get; set; }\r\n\r\n [JsonPropertyName(\"title\")]\r\n public string Title { get; set; }\r\n\r\n [JsonPropertyName(\"rating\")]\r\n public int? Rating { get; set; }\r\n\r\n [JsonPropertyName(\"provisional\")]\r\n public bool? Provisional { get; set; }\r\n\r\n [JsonExtensionData]\r\n public Dictionary<string, JsonElement> Extra { get; set; }\r\n}\r\n\r\npublic sealed class BoardClockState\r\n{\r\n public int? WhiteTimeMilliseconds { get; set; }\r\n public int? BlackTimeMilliseconds { get; set; }\r\n public int? WhiteIncrementMilliseconds { get; set; }\r\n public int? BlackIncrementMilliseconds { get; set; }\r\n\r\n public TimeSpan? WhiteTime => WhiteTimeMilliseconds.HasValue\r\n ? TimeSpan.FromMilliseconds(WhiteTimeMilliseconds.Value)\r\n : null;\r\n\r\n public TimeSpan? BlackTime => BlackTimeMilliseconds.HasValue\r\n ? TimeSpan.FromMilliseconds(BlackTimeMilliseconds.Value)\r\n : null;\r\n\r\n public static BoardClockState FromState(BoardGameState state)\r\n {\r\n return new BoardClockState\r\n {\r\n WhiteTimeMilliseconds = state?.WhiteTimeMilliseconds,\r\n BlackTimeMilliseconds = state?.BlackTimeMilliseconds,\r\n WhiteIncrementMilliseconds = state?.WhiteIncrementMilliseconds,\r\n BlackIncrementMilliseconds = state?.BlackIncrementMilliseconds\r\n };\r\n }\r\n}\r\n\r\npublic sealed class BoardGameState\r\n{\r\n [JsonPropertyName(\"type\")]\r\n public string Type { get; set; }\r\n\r\n [JsonPropertyName(\"moves\")]\r\n public string Moves { get; set; }\r\n\r\n [JsonPropertyName(\"wtime\")]\r\n public int? WhiteTimeMilliseconds { get; set; }\r\n\r\n [JsonPropertyName(\"btime\")]\r\n public int? BlackTimeMilliseconds { get; set; }\r\n\r\n [JsonPropertyName(\"winc\")]\r\n public int? WhiteIncrementMilliseconds { get; set; }\r\n\r\n [JsonPropertyName(\"binc\")]\r\n public int? BlackIncrementMilliseconds { get; set; }\r\n\r\n [JsonPropertyName(\"status\")]\r\n public string Status { get; set; }\r\n\r\n [JsonPropertyName(\"winner\")]\r\n public string Winner { get; set; }\r\n\r\n [JsonPropertyName(\"wdraw\")]\r\n public bool? WhiteOfferingDraw { get; set; }\r\n\r\n [JsonPropertyName(\"bdraw\")]\r\n public bool? BlackOfferingDraw { get; set; }\r\n\r\n [JsonExtensionData]\r\n public Dictionary<string, JsonElement> Extra { get; set; }\r\n\r\n public BoardClockState Clock => BoardClockState.FromState(this);\r\n\r\n public bool IsFinished => !string.IsNullOrWhiteSpace(Status)\r\n && !Status.Equals(\"started\", StringComparison.OrdinalIgnoreCase)\r\n && !Status.Equals(\"created\", StringComparison.OrdinalIgnoreCase);\r\n\r\n public IReadOnlyList<string> MoveList => BoardEventParser.SplitMoves(Moves);\r\n}\r\n\r\npublic sealed class BoardGameStartEvent\r\n{\r\n [JsonPropertyName(\"type\")]\r\n public string Type { get; set; }\r\n\r\n [JsonPropertyName(\"game\")]\r\n public BoardGameInfo Game { get; set; }\r\n}\r\n\r\npublic sealed class BoardGameFinishEvent\r\n{\r\n [JsonPropertyName(\"type\")]\r\n public string Type { get; set; }\r\n\r\n [JsonPropertyName(\"game\")]\r\n public BoardGameInfo Game { get; set; }\r\n}\r\n\r\npublic sealed class BoardGameFullEvent\r\n{\r\n [JsonPropertyName(\"type\")]\r\n public string Type { get; set; }\r\n\r\n [JsonPropertyName(\"id\")]\r\n public string Id { get; set; }\r\n\r\n [JsonPropertyName(\"initialFen\")]\r\n public string InitialFen { get; set; }\r\n\r\n [JsonPropertyName(\"rated\")]\r\n public bool? Rated { get; set; }\r\n\r\n [JsonPropertyName(\"variant\")]\r\n [JsonConverter(typeof(BoardObjectNameConverter))]\r\n public string Variant { get; set; }\r\n\r\n [JsonPropertyName(\"speed\")]\r\n public string Speed { get; set; }\r\n\r\n [JsonPropertyName(\"perf\")]\r\n [JsonConverter(typeof(BoardObjectNameConverter))]\r\n public string Perf { get; set; }\r\n\r\n [JsonPropertyName(\"createdAt\")]\r\n public long? CreatedAt { get; set; }\r\n\r\n [JsonPropertyName(\"white\")]\r\n public BoardPlayerInfo White { get; set; }\r\n\r\n [JsonPropertyName(\"black\")]\r\n public BoardPlayerInfo Black { get; set; }\r\n\r\n [JsonPropertyName(\"state\")]\r\n public BoardGameState State { get; set; }\r\n\r\n [JsonExtensionData]\r\n public Dictionary<string, JsonElement> Extra { get; set; }\r\n}\r\n\r\npublic sealed class BoardChatLineEvent\r\n{\r\n [JsonPropertyName(\"type\")]\r\n public string Type { get; set; }\r\n\r\n [JsonPropertyName(\"room\")]\r\n public string Room { get; set; }\r\n\r\n [JsonPropertyName(\"username\")]\r\n public string Username { get; set; }\r\n\r\n [JsonPropertyName(\"text\")]\r\n public string Text { get; set; }\r\n}\r\n\r\npublic static class BoardEventParser\r\n{\r\n public static string GetEventType(JsonElement element)\r\n {\r\n return element.TryGetProperty(\"type\", out var type)\r\n ? type.GetString()\r\n : string.Empty;\r\n }\r\n\r\n public static BoardGameStartEvent ParseGameStart(JsonElement element)\r\n {\r\n return element.Deserialize<BoardGameStartEvent>(LichessJson.Options);\r\n }\r\n\r\n public static BoardGameFinishEvent ParseGameFinish(JsonElement element)\r\n {\r\n return element.Deserialize<BoardGameFinishEvent>(LichessJson.Options);\r\n }\r\n\r\n public static BoardGameFullEvent ParseGameFull(JsonElement element)\r\n {\r\n return element.Deserialize<BoardGameFullEvent>(LichessJson.Options);\r\n }\r\n\r\n public static BoardGameState ParseGameState(JsonElement element)\r\n {\r\n return element.Deserialize<BoardGameState>(LichessJson.Options);\r\n }\r\n\r\n public static BoardChatLineEvent ParseChatLine(JsonElement element)\r\n {\r\n return element.Deserialize<BoardChatLineEvent>(LichessJson.Options);\r\n }\r\n\r\n public static IReadOnlyList<string> SplitMoves(string moves)\r\n {\r\n if (string.IsNullOrWhiteSpace(moves))\r\n return Array.Empty<string>();\r\n\r\n return moves.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);\r\n }\r\n}\r\n\n\n/// <summary>\n/// Preserves legacy string properties while accepting the object form used by\n/// current Board API gameFull payloads.\n/// </summary>\npublic sealed class BoardObjectNameConverter : JsonConverter<string>\n{\n public override string? Read(ref Utf8JsonReader reader, Type typeToConvert,\n JsonSerializerOptions options)\n {\n if (reader.TokenType == JsonTokenType.String)\n return reader.GetString();\n\n if (reader.TokenType == JsonTokenType.Null)\n return null;\n\n using var document = JsonDocument.ParseValue(ref reader);\n var value = document.RootElement;\n if (value.ValueKind != JsonValueKind.Object)\n return value.ToString();\n\n foreach (var propertyName in new[] { \"key\", \"name\", \"short\" })\n {\n if (value.TryGetProperty(propertyName, out var property) &&\n property.ValueKind == JsonValueKind.String)\n {\n return property.GetString();\n }\n }\n\n return value.GetRawText();\n }\n\n public override void Write(Utf8JsonWriter writer, string value,\n JsonSerializerOptions options)\n {\n writer.WriteStringValue(value);\n }\n}\n"
}
]
}