From 7930b43c0ead6da8992c8dc6f7d2a9bdc44b9dcc Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 16 Oct 2025 13:36:14 -0400 Subject: [PATCH 1/2] add armageddon support --- README.md | 6 +++++- package.json | 2 ++ pnpm-lock.yaml | 23 +++++++++++++++++++++++ src/page/bulkNew.ts | 40 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bef98c7..e512ca6 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,12 @@ Open the browser console and run: ```js localStorage.setItem('lichessHost', 'http://localhost:8080'); + +localStorage.setItem('lichessHost', 'https://lichess.dev'); ``` -Modify the CSP meta tag in `index.html` to add that domain. For example, change `lichess.org` to `localhost:8080`. +Modify the CSP meta tag in `index.html` to include that domain. Refresh and verify the configuration value in the footer. + +To reset back to prod default, log out and it will clear localStorage. diff --git a/package.json b/package.json index 8813577..4122eb1 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,10 @@ "license": "GPL-3.0-or-later", "dependencies": { "@bity/oauth2-auth-code-pkce": "~2.13.0", + "@lichess-org/types": "^2.0.86", "bootstrap": "~5.3.8", "cheerio": "^1.1.2", + "openapi-fetch": "^0.15.0", "page": "~1.11.6", "snabbdom": "~3.6.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77eb683..0580196 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,18 @@ importers: '@bity/oauth2-auth-code-pkce': specifier: ~2.13.0 version: 2.13.0 + '@lichess-org/types': + specifier: ^2.0.86 + version: 2.0.86 bootstrap: specifier: ~5.3.8 version: 5.3.8(@popperjs/core@2.11.8) cheerio: specifier: ^1.1.2 version: 1.1.2 + openapi-fetch: + specifier: ^0.15.0 + version: 0.15.0 page: specifier: ~1.11.6 version: 1.11.6 @@ -269,6 +275,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@lichess-org/types@2.0.86': + resolution: {integrity: sha512-Mx1cMCUkao+QgAz0liuhNwoe89GHu35lKrtIF+c0htpRyu39am80E9uZGVZrJSWe1/Nn4M9Lws4qCU6lpKn/QQ==} + '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -745,6 +754,12 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + openapi-fetch@0.15.0: + resolution: {integrity: sha512-OjQUdi61WO4HYhr9+byCPMj0+bgste/LtSBEcV6FzDdONTs7x0fWn8/ndoYwzqCsKWIxEZwo4FN/TG1c1rI8IQ==} + + openapi-typescript-helpers@0.0.15: + resolution: {integrity: sha512-opyTPaunsklCBpTK8JGef6mfPhLSnyy5a0IN9vKtx3+4aExf+KxEqYwIy3hqkedXIB97u357uLMJsOnm3GVjsw==} + page@1.11.6: resolution: {integrity: sha512-P6e2JfzkBrPeFCIPplLP7vDDiU84RUUZMrWdsH4ZBGJ8OosnwFkcUkBHp1DTIjuipLliw9yQn/ZJsXZvarsO+g==} @@ -1174,6 +1189,8 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 optional: true + '@lichess-org/types@2.0.86': {} + '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -1645,6 +1662,12 @@ snapshots: dependencies: boolbase: 1.0.0 + openapi-fetch@0.15.0: + dependencies: + openapi-typescript-helpers: 0.0.15 + + openapi-typescript-helpers@0.0.15: {} + page@1.11.6: dependencies: path-to-regexp: 1.2.1 diff --git a/src/page/bulkNew.ts b/src/page/bulkNew.ts index 3da51df..431f023 100644 --- a/src/page/bulkNew.ts +++ b/src/page/bulkNew.ts @@ -3,12 +3,14 @@ import page from 'page'; import { App } from '../app'; import type { Me } from '../auth'; import { type Feedback, formData, isSuccess, responseToFeedback } from '../form'; -import { gameRuleKeys, gameRules } from '../util'; +import { gameRuleKeys, gameRules, sleep } from '../util'; import * as form from '../view/form'; import layout from '../view/layout'; import { type Pairing, filterRound, getPairings, getPlayers, saveUrls } from '../scraper/scraper'; import { bulkPairing } from '../endpoints'; import { href } from '../view/util'; +import createClient from 'openapi-fetch'; +import type { paths } from '@lichess-org/types'; interface Tokens { [username: string]: string; @@ -111,6 +113,38 @@ export class BulkNew { this.feedback = await responseToFeedback(req); if (isSuccess(this.feedback)) { + if (!!get('armageddon')) { + await sleep(3000); + const addTimeResponses = new Map(); + for (const game of this.feedback.result.games) { + const client = createClient({ + baseUrl: this.app.config.lichessHost, + headers: { + Authorization: `Bearer ${tokens[game.white]}`, + }, + }); + const resp = await client.POST('/api/round/{gameId}/add-time/{seconds}', { + params: { + path: { + gameId: game.id, + seconds: 60, + }, + }, + }); + addTimeResponses.set(game.id, resp.response.status); + } + + const alerts: string[] = []; + addTimeResponses.forEach((status, gameId) => { + if (status !== 200) { + alerts.push(`Failed to add armageddon time to game ${gameId}, status ${status}`); + } + }); + if (alerts.length) { + alert(alerts.join('\n')); + } + } + saveUrls(this.feedback.result.id, get('cr-pairings-url'), get('cr-players-url')); page(`/endpoint/schedule-games/${this.feedback.result.id}`); } @@ -197,6 +231,10 @@ export class BulkNew { ]), ]), form.clock(), + h( + 'div.form-check.form-switch.mb-3', + form.checkboxWithLabel('armageddon', 'Armageddon? (+60 seconds for black)'), + ), h('div.form-check.form-switch.mb-3', form.checkboxWithLabel('rated', 'Rated games', true)), form.variant(), form.fen(), From f80c420a224b28afee9c6ab0cd07654b9bd7e262 Mon Sep 17 00:00:00 2001 From: Trevor Fitzgerald Date: Thu, 16 Oct 2025 15:28:09 -0400 Subject: [PATCH 2/2] remove sleep --- src/page/bulkNew.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/page/bulkNew.ts b/src/page/bulkNew.ts index 431f023..969887f 100644 --- a/src/page/bulkNew.ts +++ b/src/page/bulkNew.ts @@ -3,7 +3,7 @@ import page from 'page'; import { App } from '../app'; import type { Me } from '../auth'; import { type Feedback, formData, isSuccess, responseToFeedback } from '../form'; -import { gameRuleKeys, gameRules, sleep } from '../util'; +import { gameRuleKeys, gameRules } from '../util'; import * as form from '../view/form'; import layout from '../view/layout'; import { type Pairing, filterRound, getPairings, getPlayers, saveUrls } from '../scraper/scraper'; @@ -114,7 +114,6 @@ export class BulkNew { if (isSuccess(this.feedback)) { if (!!get('armageddon')) { - await sleep(3000); const addTimeResponses = new Map(); for (const game of this.feedback.result.games) { const client = createClient({