Skip to content

Commit fd37ff2

Browse files
committed
games: Upload Chess games to Lichess
1 parent d5c61ad commit fd37ff2

6 files changed

Lines changed: 45 additions & 17 deletions

File tree

src/ps/games/chess/index.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export class Chess extends Game<State> {
2828
log: Log[] = [];
2929
winCtx?: WinCtx | { type: EndType };
3030
cache: Record<string, Record<Turn, number>> = {};
31+
lichessURL: string | null = null;
3132

3233
theme: ThemeColours = {
3334
W: '#fff',
@@ -45,6 +46,17 @@ export class Chess extends Game<State> {
4546
if (ctx.backup) {
4647
this.lib.loadPgn(this.state.pgn);
4748
}
49+
50+
this.lib.setHeader('Event', `Room Match ${this.id}`);
51+
this.lib.setHeader('Site', 'https://play.pokemonshowdown.com/boardgames');
52+
}
53+
54+
onStart(): ActionResponse {
55+
this.lib.setHeader('Date', new Date().toDateString());
56+
this.lib.setHeader('White', this.players.W.name);
57+
this.lib.setHeader('Black', this.players.B.name);
58+
59+
return { success: true, data: null };
4860
}
4961

5062
action(user: User, ctx: string): void {
@@ -115,8 +127,9 @@ export class Chess extends Game<State> {
115127
this.drawOffered = null;
116128
}
117129

118-
onReplacePlayer(): ActionResponse<null> {
130+
onReplacePlayer(turn: Turn, withPlayer: User): ActionResponse<null> {
119131
this.cleanup();
132+
this.lib.setHeader(turn === 'W' ? 'White' : 'Black', withPlayer.name);
120133
return { success: true, data: null };
121134
}
122135

@@ -141,6 +154,20 @@ export class Chess extends Game<State> {
141154
this.throw();
142155
}
143156

157+
async getURL(): Promise<string | null> {
158+
if (this.lichessURL) return this.lichessURL;
159+
160+
this.lichessURL = await fetch('https://lichess.org/api/import', {
161+
method: 'POST',
162+
body: JSON.stringify({ pgn: this.state.pgn }),
163+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
164+
})
165+
.then(res => res.json())
166+
.then(res => (res as { url: string }).url);
167+
168+
return this.lichessURL;
169+
}
170+
144171
// renderEmbed(): EmbedBuilder {
145172
// // TODO
146173
// }

src/ps/games/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export interface Player {
4242

4343
export type BaseState = { board: unknown; turn: string };
4444

45-
export type ActionResponse<T = undefined> = { success: true; data: T } | { success: false; error: TranslatedText };
45+
export type ActionResponse<T = null> = { success: true; data: T } | { success: false; error: TranslatedText };
4646

4747
export type EndType = 'regular' | 'force' | 'dq' | 'loss';
4848

src/ps/games/connectfour/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class ConnectFour extends Game<State> {
103103
throw new Error(`winCtx not defined for C4 - ${JSON.stringify(this.winCtx)}`);
104104
}
105105

106-
renderEmbed(): EmbedBuilder {
106+
async renderEmbed(): Promise<EmbedBuilder> {
107107
const winner = this.winCtx && this.winCtx.type === 'win' ? this.winCtx.winner.id : null;
108108
const title = Object.values(this.players)
109109
.map(player => `${player.name} (${player.turn})${player.id === winner ? ` ${WINNER_ICON}` : ''}`)

src/ps/games/game.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export class Game<State extends BaseState> {
6868
render() {
6969
return null as unknown as ReactElement;
7070
}
71-
renderEmbed?(): EmbedBuilder | null;
71+
renderEmbed?(): Promise<EmbedBuilder | null>;
7272

7373
action(user: User, ctx: string, reaction: boolean): void;
7474
action() {}
@@ -352,7 +352,7 @@ export class Game<State extends BaseState> {
352352
this.startedAt = new Date();
353353
this.setTimer('Game started');
354354
this.backup();
355-
return { success: true, data: undefined };
355+
return { success: true, data: null };
356356
}
357357

358358
// Only gets next turn. No side effects.
@@ -403,7 +403,7 @@ export class Game<State extends BaseState> {
403403
if (this.spectators.length > 0) this.room.pageHTML(this.spectators, this.render(null), { name: this.id });
404404
}
405405

406-
getURL(): string | null {
406+
getURL(): Promise<string | null> | string | null {
407407
if (this.meta.players === 'single') return null;
408408
return `${process.env.WEB_URL}/${this.meta.id}/${this.id.replace(/^#/, '')}`;
409409
}
@@ -419,11 +419,12 @@ export class Game<State extends BaseState> {
419419
this.room.send(message);
420420
if (this.started && typeof this.renderEmbed === 'function' && this.roomid === 'boardgames') {
421421
// Send only for games from BG
422-
const embed = this.renderEmbed();
423-
if (embed) {
424-
const channel = getChannel(BOT_LOG_CHANNEL);
425-
channel?.send({ embeds: [embed] });
426-
}
422+
this.renderEmbed().then(embed => {
423+
if (embed) {
424+
const channel = getChannel(BOT_LOG_CHANNEL);
425+
channel?.send({ embeds: [embed] });
426+
}
427+
});
427428
}
428429
// Upload to DB
429430
if (IS_ENABLED.DB && this.meta.players !== 'single') {
@@ -439,8 +440,8 @@ export class Game<State extends BaseState> {
439440
winCtx: 'winCtx' in this ? this.winCtx : null,
440441
};
441442
uploadGame(model)
442-
.then(() => {
443-
const replay = this.getURL();
443+
.then(async () => {
444+
const replay = await this.getURL();
444445
if (replay) this.room.send(replay as NoTranslate);
445446
})
446447
.catch(err => {

src/ps/games/othello/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class Othello extends Game<State> {
141141
});
142142
}
143143

144-
renderEmbed(): EmbedBuilder {
144+
async renderEmbed(): Promise<EmbedBuilder> {
145145
const winner = this.winCtx && this.winCtx.type === 'win' ? this.winCtx.winner.id : null;
146146
const title = Object.values(this.players)
147147
.map(player => `${player.name} (${player.turn})${player.id === winner ? ` ${WINNER_ICON}` : ''}`)
@@ -151,7 +151,7 @@ export class Othello extends Game<State> {
151151
.setColor('#008000')
152152
.setAuthor({ name: 'Othello - Room Match' })
153153
.setTitle(title)
154-
.setURL(this.getURL())
154+
.setURL(await this.getURL())
155155
.addFields([
156156
{
157157
name: [count.B, count.W].join(' - '),

src/ps/games/scrabble/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export class Scrabble extends Game<State> {
6969
this.state.score[player] = 0;
7070
this.state.racks[player] = this.state.bag.splice(0, RACK_SIZE);
7171
});
72-
return { success: true, data: undefined };
72+
return { success: true, data: null };
7373
}
7474

7575
action(user: User, ctx: string): void {
@@ -380,7 +380,7 @@ export class Scrabble extends Game<State> {
380380
return render.bind(this.renderCtx)(ctx);
381381
}
382382

383-
renderEmbed(): EmbedBuilder | null {
383+
async renderEmbed(): Promise<EmbedBuilder | null> {
384384
const winners = this.winCtx && this.winCtx.type === 'win' ? this.winCtx.winnerIds : null;
385385
if (!winners) return null;
386386
const winnerPlayers = winners.map(winner => ({ ...this.players[winner], best: this.state.best[winner] }));

0 commit comments

Comments
 (0)