-
Notifications
You must be signed in to change notification settings - Fork 0
Challenges
Send and receive challenges to play games.
Namespace: LichessSharp.Api.Contracts
Access: client.Challenges
Required Scope: challenge:read, challenge:write, bot:play, or board:play
using LichessSharp;
using LichessSharp.Api.Contracts;
using var client = new LichessClient(token);
// Challenge someone to a 5+3 blitz game
var challenge = await client.Challenges.CreateAsync("opponent_username", new ChallengeCreateOptions
{
Rated = true,
ClockLimit = 300, // 5 minutes in seconds
ClockIncrement = 3, // 3 second increment
Color = ChallengeColor.Random
});
Console.WriteLine($"Challenge sent: {challenge.Url}");
Console.WriteLine($"Status: {challenge.Status}");Get all pending challenges (incoming and outgoing).
Required Scope: challenge:read
Task<ChallengeList> GetPendingAsync(CancellationToken cancellationToken = default)Example:
using var client = new LichessClient(token);
var pending = await client.Challenges.GetPendingAsync();
Console.WriteLine("Incoming challenges:");
foreach (var challenge in pending.In ?? [])
{
Console.WriteLine($" From {challenge.Challenger?.Name}: {challenge.TimeControl?.Show} {challenge.Variant?.Name}");
}
Console.WriteLine("\nOutgoing challenges:");
foreach (var challenge in pending.Out ?? [])
{
Console.WriteLine($" To {challenge.DestUser?.Name}: {challenge.Status}");
}Get details of a specific challenge.
Required Scope: challenge:read
Task<ChallengeJson> ShowAsync(string challengeId, CancellationToken cancellationToken = default)Example:
using var client = new LichessClient(token);
var challenge = await client.Challenges.ShowAsync("challengeId");
Console.WriteLine($"Challenge: {challenge.Id}");
Console.WriteLine($"Status: {challenge.Status}");
Console.WriteLine($"Challenger: {challenge.Challenger?.Name} ({challenge.Challenger?.Rating})");
Console.WriteLine($"Opponent: {challenge.DestUser?.Name} ({challenge.DestUser?.Rating})");
Console.WriteLine($"Time Control: {challenge.TimeControl?.Show}");
Console.WriteLine($"Rated: {challenge.Rated}");
Console.WriteLine($"Variant: {challenge.Variant?.Name}");Accept an incoming challenge.
Task<bool> AcceptAsync(string challengeId, CancellationToken cancellationToken = default)Example:
using var client = new LichessClient(token);
// Accept a specific challenge
await client.Challenges.AcceptAsync("challengeId");
// Accept all incoming challenges
var pending = await client.Challenges.GetPendingAsync();
foreach (var challenge in pending.In ?? [])
{
await client.Challenges.AcceptAsync(challenge.Id);
Console.WriteLine($"Accepted challenge from {challenge.Challenger?.Name}");
}Decline an incoming challenge with an optional reason.
Task<bool> DeclineAsync(
string challengeId,
ChallengeDeclineReason? reason = null,
CancellationToken cancellationToken = default)ChallengeDeclineReason Values:
| Value | Description |
|---|---|
Generic |
Generic decline |
Later |
Will play later |
TooFast |
Time control too fast |
TooSlow |
Time control too slow |
TimeControl |
Time control not acceptable |
Rated |
Only accepting rated games |
Casual |
Only accepting casual games |
Standard |
Only accepting standard chess |
Variant |
Variant not acceptable |
NoBot |
Not accepting challenges from bots |
OnlyBot |
Only accepting challenges from bots |
Example:
using var client = new LichessClient(token);
// Decline with a reason
await client.Challenges.DeclineAsync("challengeId", ChallengeDeclineReason.TooFast);
// Decline bullet challenges automatically
var pending = await client.Challenges.GetPendingAsync();
foreach (var challenge in pending.In ?? [])
{
if (challenge.Speed == "bullet")
{
await client.Challenges.DeclineAsync(challenge.Id, ChallengeDeclineReason.TooFast);
}
}Cancel a challenge you created.
Task<bool> CancelAsync(
string challengeId,
string? opponentToken = null,
CancellationToken cancellationToken = default)Example:
using var client = new LichessClient(token);
await client.Challenges.CancelAsync("challengeId");
Console.WriteLine("Challenge canceled");Challenge another player to a game.
Task<ChallengeJson> CreateAsync(
string username,
ChallengeCreateOptions? options = null,
CancellationToken cancellationToken = default)ChallengeCreateOptions:
| Property | Type | Description |
|---|---|---|
| Rated | bool? | Whether the game is rated |
| ClockLimit | int? | Initial time in seconds (0-10800) |
| ClockIncrement | int? | Increment in seconds (0-180) |
| Days | int? | Days per turn for correspondence (1, 2, 3, 5, 7, 10, 14) |
| Color | ChallengeColor? | Color preference (Random, White, Black) |
| Variant | string? | Variant (standard, chess960, crazyhouse, etc.) |
| Fen | string? | Starting FEN for custom positions |
| Rules | ChallengeRules? | Extra game rules |
| Message | string? | Message to send with challenge |
Example - Standard blitz challenge:
using var client = new LichessClient(token);
var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
Rated = true,
ClockLimit = 180, // 3 minutes
ClockIncrement = 0,
Color = ChallengeColor.Random
});Example - Chess960 rapid:
var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
Rated = true,
ClockLimit = 900, // 15 minutes
ClockIncrement = 10,
Variant = "chess960"
});Example - Correspondence:
var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
Rated = true,
Days = 3, // 3 days per move
Color = ChallengeColor.White
});Example - Custom position:
var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
Rated = false,
ClockLimit = 300,
ClockIncrement = 3,
Variant = "fromPosition",
Fen = "r1bqkb1r/pppp1ppp/2n2n2/4p3/2B1P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 4 4" // Italian Game
});Example - With special rules:
var challenge = await client.Challenges.CreateAsync("opponent", new ChallengeCreateOptions
{
Rated = true,
ClockLimit = 600,
ClockIncrement = 0,
Rules = ChallengeRules.NoAbort | ChallengeRules.NoRematch | ChallengeRules.NoEarlyDraw
});Create an open challenge that any two players can join.
Task<ChallengeOpenJson> CreateOpenAsync(
ChallengeOpenOptions? options = null,
CancellationToken cancellationToken = default)ChallengeOpenOptions (extends ChallengeCreateOptions):
| Property | Type | Description |
|---|---|---|
| Name | string? | Name for the challenge |
| Users | IReadOnlyList? | Users to notify |
| ExpiresAt | int? | Expiration in minutes |
Example:
using var client = new LichessClient(token);
var open = await client.Challenges.CreateOpenAsync(new ChallengeOpenOptions
{
Name = "Club Tournament Match",
Rated = true,
ClockLimit = 300,
ClockIncrement = 3,
Users = new[] { "player1", "player2" }
});
Console.WriteLine($"Challenge URL: {open.Url}");
Console.WriteLine($"White URL: {open.UrlWhite}");
Console.WriteLine($"Black URL: {open.UrlBlack}");
// Send UrlWhite to one player and UrlBlack to the otherChallenge the Lichess AI (Stockfish).
Task<ChallengeAiResponse> ChallengeAiAsync(
ChallengeAiOptions options,
CancellationToken cancellationToken = default)ChallengeAiOptions:
| Property | Type | Description |
|---|---|---|
| Level | int | AI level 1-8 (default 8) |
| ClockLimit | int? | Initial time in seconds |
| ClockIncrement | int? | Increment in seconds |
| Days | int? | Days per turn for correspondence |
| Color | ChallengeColor? | Your color preference |
| Variant | string? | Variant to play |
| Fen | string? | Starting FEN |
Example:
using var client = new LichessClient(token);
// Play against max strength AI
var game = await client.Challenges.ChallengeAiAsync(new ChallengeAiOptions
{
Level = 8,
ClockLimit = 300,
ClockIncrement = 3,
Color = ChallengeColor.White
});
Console.WriteLine($"Game started: {game.Id}");
// Play against lower level AI
var easyGame = await client.Challenges.ChallengeAiAsync(new ChallengeAiOptions
{
Level = 1,
ClockLimit = 600,
ClockIncrement = 0
});Add time to your opponent's clock (sportsmanship gesture).
Task<bool> AddTimeAsync(
string gameId,
int seconds,
CancellationToken cancellationToken = default)Parameters:
| Name | Type | Description |
|---|---|---|
| gameId | string | The game ID |
| seconds | int | Seconds to add (1-60) |
Example:
using var client = new LichessClient(token);
// Give opponent 15 extra seconds
await client.Challenges.AddTimeAsync(gameId, 15);Start the clocks immediately, even if a player hasn't moved.
Task<bool> StartClocksAsync(
string gameId,
string? token1 = null,
string? token2 = null,
CancellationToken cancellationToken = default)Example:
using var client = new LichessClient(token);
// Both players agree to start clocks
await client.Challenges.StartClocksAsync(gameId, player1Token, player2Token);Challenges are commonly handled in the Bot or Board event loops:
using var client = new LichessClient(token);
await foreach (var evt in client.Bot.StreamEventsAsync())
{
switch (evt.Type)
{
case "challenge":
var challenge = evt.Challenge!;
// Auto-accept rated blitz from non-bots
if (challenge.Rated &&
challenge.Speed == "blitz" &&
challenge.Challenger?.Title != "BOT")
{
await client.Challenges.AcceptAsync(challenge.Id);
}
else
{
await client.Challenges.DeclineAsync(challenge.Id, ChallengeDeclineReason.Generic);
}
break;
case "gameStart":
// Handle the game
break;
}
}- Bot API - Handle challenges as a bot
- Board API - Handle challenges as a human player
- Bulk Pairings API - Create many games at once