src/IngestionFunctions/Services/LiChessService.cs
/// <summary>
/// Find completed chess games from lichess played by the specified player (beginning with the newest games).
/// </summary>
/// <param name="playerName">The player to get games for.</param>
/// <param name="since">Unix millisecond timestamp for the oldest game to retrieve. If not specified, defaults to player's account creation date.</param>
/// <param name="max">Maximum games to retrieve (default 100).</param>
/// <returns>An async enumerable of the newest games for the specified player.</returns>
Find completed chess games from lichess played by the specified player (beginning with the newest games).
Find completed chess games from lichess played by the specified player ( beginning with the newest games ) .
public async IAsyncEnumerable<ChessGame> GetPlayerGamesAsync(string playerName, DateTimeOffset? since, int max = 100)
        {
            if (string.IsNullOrWhiteSpace(playerName))
            {
                throw new ArgumentException("Player name must not be null or empty", nameof(playerName));
            }
            var pgnLines = new List<string>();
            var sinceClause = since.HasValue ? $"&since={since.Value.ToUnixTimeMilliseconds()}" : string.Empty;
            var uri = new Uri(string.Format(CultureInfo.InvariantCulture, UrlFormatString, playerName, max, sinceClause));
            var response = await HttpClient.GetAsync(uri);
            if (!response.IsSuccessStatusCode)
            {
                Logger.LogError("HTTP request to {URI} failed: {StatusCode}: {ErrorMessage}", uri, response.StatusCode, response.ReasonPhrase);
            }
            else
            {
                using var responseBody = await response.Content.ReadAsStreamAsync();
                if (response == null)
                {
                    Logger.LogError("HTTP request to {URI} failed", uri);
                }
                else
                {
                    using var responseReader = new StreamReader(responseBody);
                    var line = await responseReader.ReadLineAsync();
                    const string UtcDatePrefix = "[UTCDate \"";
                    const string UtcTimePrefix = "[UTCTime \"";
                    string? utcDate = null;
                    string? utcTime = null;
                    while (line != null)
                    {
                        pgnLines.Add(line);
                        if (line.StartsWith(UtcDatePrefix))
                        {
                            utcDate = line.Substring(UtcDatePrefix.Length, 10);
                        }
                        else if (line.StartsWith(UtcTimePrefix))
                        {
                            utcTime = line.Substring(UtcTimePrefix.Length, 8);
                        }
                        else if (line.StartsWith("1."))
                        {
                            Engine.LoadPGN(string.Join('\n', pgnLines));
                            if (utcDate != null && utcTime != null)
                            {
                                Engine.Game.StartDate = DateTimeOffset.ParseExact($"{utcDate}|{utcTime}", "yyyy.MM.dd|HH:mm:ss", CultureInfo.InvariantCulture);
                            }
                            pgnLines = new List<string>();
                            utcDate = null;
                            utcTime = null;
                            Logger.LogInformation("Read game {GamePath}", Engine.Game.Site);
                            yield return Engine.Game;
                        }
                        line = await responseReader.ReadLineAsync();
                    }
                }
            }
        }
public async IAsyncEnumerable < ChessGame > GetPlayerGamesAsync ( string playerName , DateTimeOffset ? since , int max = 100 ) { if ( string . IsNullOrWhiteSpace ( playerName ) ) { throw new ArgumentException ( " " , nameof ( playerName ) ) ; } var pgnLines = new List < string > ( ) ; var sinceClause = since . HasValue ? $" &since= { since . Value . ToUnixTimeMilliseconds ( ) } " : string . Empty ; var uri = new Uri ( string . Format ( CultureInfo . InvariantCulture , UrlFormatString , playerName , max , sinceClause ) ) ; var response = await HttpClient . GetAsync ( uri ) ; if ( ! response . IsSuccessStatusCode ) { Logger . LogError ( " " , uri , response . StatusCode , response . ReasonPhrase ) ; } else { using var responseBody = await response . Content . ReadAsStreamAsync ( ) ; if ( response == null ) { Logger . LogError ( " " , uri ) ; } else { using var responseReader = new StreamReader ( responseBody ) ; var line = await responseReader . ReadLineAsync ( ) ; const string UtcDatePrefix = " \" " ; const string UtcTimePrefix = " \" " ; string ? utcDate = null ; string ? utcTime = null ; while ( line != null ) { pgnLines . Add ( line ) ; if ( line . StartsWith ( UtcDatePrefix ) ) { utcDate = line . Substring ( UtcDatePrefix . Length , 10 ) ; } else if ( line . StartsWith ( UtcTimePrefix ) ) { utcTime = line . Substring ( UtcTimePrefix . Length , 8 ) ; } else if ( line . StartsWith ( " " ) ) { Engine . LoadPGN ( string . Join ( ' \n ' , pgnLines ) ) ; if ( utcDate != null && utcTime != null ) { Engine . Game . StartDate = DateTimeOffset . ParseExact ( $" { utcDate } | { utcTime } " , " " , CultureInfo . InvariantCulture ) ; } pgnLines = new List < string > ( ) ; utcDate = null ; utcTime = null ; Logger . LogInformation ( " " , Engine . Game . Site ) ; yield return Engine . Game ; } line = await responseReader . ReadLineAsync ( ) ; } } } }
Find completed chess games from lichess played by the specified player (beginning with the newest games).
Find completed chess games from lichess played by the specified player ( beginning with the newest games ) .
// Query LiChess's /games/user endpoint // Read the response one line at a time // If the line contains game moves, then return the game and reset the PGN line collection // Set the game time more precisely // Reset variables // Read the next line
An async enumerable of the newest games for the specified player.
An async enumerable of the newest games for the specified player .
playerName
string
The player to get games for.
The player to get games for .
since
DateTimeOffset?
Unix millisecond timestamp for the oldest game to retrieve. If not specified, defaults to player's account creation date.
Unix millisecond timestamp for the oldest game to retrieve . If not specified defaults to player ' s account creation date .
player's account creation date
max
int
Maximum games to retrieve (default 100).
Maximum games to retrieve ( default 100 ) .