From 48d76a0dbb520cd51b866d9815c7e495f08a59c0 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 14:31:52 +0300 Subject: [PATCH 01/33] Handler moved --- src/handlers/index.js | 6 ++++-- src/handlers/inlineJoin/index.js | 35 ++++++++++++++++++++++++++++++++ src/index.js | 32 ++--------------------------- 3 files changed, 41 insertions(+), 32 deletions(-) create mode 100644 src/handlers/inlineJoin/index.js diff --git a/src/handlers/index.js b/src/handlers/index.js index 9d90316..c769daf 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -4,10 +4,11 @@ const loadHandler = require('./load') const leaveHandler = require('./leave') const movesHandler = require('./moves') const startHandler = require('./start') +const inlineHandler = require('./inline') const actionsHandler = require('./actions') const messageHandler = require('./message') const optionsHandler = require('./options') -const inlineHandler = require('./inline') +const inlineJoinHandler = require('./inlineJoin') module.exports = { newHandler, @@ -16,8 +17,9 @@ module.exports = { leaveHandler, movesHandler, startHandler, + inlineHandler, actionsHandler, messageHandler, optionsHandler, - inlineHandler, + inlineJoinHandler, } diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js new file mode 100644 index 0000000..1cc2264 --- /dev/null +++ b/src/handlers/inlineJoin/index.js @@ -0,0 +1,35 @@ +// const chess = require('chess') + +const { board } = require('@/keyboards') +const { debug, unescapeUser } = require('@/helpers') + +module.exports = () => [ + /^join::(\w)::(\d+)/, async (ctx) => { + debug(ctx.update) + const userId = Number(ctx.match[2]) + const iAmWhite = () => ctx.match[1] !== 'w' + + if (ctx.from.id === userId) { + return ctx.answerCbQuery('You can\'t join yourself!') + } + + const enemy = await ctx.db('users').where('id', userId).first() + + await ctx.db('games').returning('id').insert({ + user_w: !iAmWhite() ? enemy.id : ctx.from.id, + user_b: !iAmWhite() ? ctx.from.id : enemy.id, + inline_id: ctx.update.callback_query.inline_message_id, + }).catch(debug) + + await ctx.editMessageText( + !iAmWhite() + ? `Black (top): ${ctx.from.first_name} + White (bottom): ${unescapeUser(enemy).first_name}` + : `Black (top): ${unescapeUser(enemy).first_name} + White (bottom): ${ctx.from.first_name}`, + board() + ) + + return ctx.answerCbQuery() + }, +] diff --git a/src/index.js b/src/index.js index 5af708d..4719981 100644 --- a/src/index.js +++ b/src/index.js @@ -5,10 +5,9 @@ const knex = require('knex') const Telegraf = require('telegraf') const Stage = require('telegraf/stage') -const { debug, unescapeUser } = require('@/helpers') const { gameScene } = require('@/scenes') const knexConfig = require('@/../knexfile') -const { inlineHandler, loadHandler, joinHandler, newHandler } = require('@/handlers') +const { inlineHandler, loadHandler, joinHandler, newHandler, inlineJoinHandler } = require('@/handlers') const { session } = Telegraf const { @@ -28,33 +27,6 @@ bot.on(...inlineHandler()) bot.start(...loadHandler()) bot.action(...newHandler()) bot.action(...joinHandler()) - -bot.action(/^join::(\w)::(\d+)/, async (ctx) => { - debug(ctx.update) - const userId = Number(ctx.match[2]) - const iAmWhite = () => ctx.match[1] !== 'w' - - if (ctx.from.id === userId) { - return ctx.answerCbQuery('You can\'t join yourself!') - } - - const enemy = await ctx.db('users').where('id', userId).first() - - await ctx.db('games').returning('id').insert({ - user_w: !iAmWhite() ? enemy.id : ctx.from.id, - user_b: !iAmWhite() ? ctx.from.id : enemy.id, - inline_id: ctx.update.callback_query.inline_message_id, - }).catch(debug) - - await ctx.editMessageText( - !iAmWhite() - ? `Black (top): ${ctx.from.first_name} -White (bottom): ${unescapeUser(enemy).first_name}` - : `Black (top): ${unescapeUser(enemy).first_name} -White (bottom): ${ctx.from.first_name}` - ) - - return ctx.answerCbQuery() -}) +bot.action(...inlineJoinHandler()) bot.startPolling() From b15cd6fcb901089640043436dcb3a626e6256324 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 14:49:37 +0300 Subject: [PATCH 02/33] Try sticker --- src/handlers/inline/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 70fe43c..2968be9 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -34,10 +34,10 @@ module.exports = () => [ await ctx.answerInlineQuery([ { id: 1, - type: 'photo', - thumb_url: 'https://crossword.live/img/w.png', - photo_url: 'https://crossword.live/img/w.png', - title: 'Play with white pieces', + type: 'sticker', + // thumb_url: 'https://crossword.live/img/w.png', + sticker_file_id: 'CAADAgAD3AADWQMDAAH0zFgaGiqNBgI', + // title: 'Play with white pieces', input_message_content: { message_text: `Black (top): ? White (bottom): ${user.first_name}`, @@ -49,10 +49,10 @@ White (bottom): ${user.first_name}`, }, { id: 2, - type: 'photo', - thumb_url: 'https://crossword.live/img/b.png', - photo_url: 'https://crossword.live/img/b.png', - title: 'Play with black pieces', + type: 'sticker', + // thumb_url: 'https://crossword.live/img/b.png', + sticker_file_id: 'CAADAgADGQEAAtLdaQVS6DvPqWaenwI', + // title: 'Play with black pieces', input_message_content: { message_text: `White (top): ? Black (bottom): ${user.first_name}`, From 0a386ae6b20cabf1b255130cc2b6993382450007 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 15:22:41 +0300 Subject: [PATCH 03/33] Updated stickers --- src/handlers/inline/index.js | 8 ++------ src/handlers/inlineJoin/index.js | 9 ++++++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 2968be9..5fe0937 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -35,9 +35,7 @@ module.exports = () => [ { id: 1, type: 'sticker', - // thumb_url: 'https://crossword.live/img/w.png', - sticker_file_id: 'CAADAgAD3AADWQMDAAH0zFgaGiqNBgI', - // title: 'Play with white pieces', + sticker_file_id: 'CAADAgAD-wADe0iRBVUqQY7O3p9SAg', input_message_content: { message_text: `Black (top): ? White (bottom): ${user.first_name}`, @@ -50,9 +48,7 @@ White (bottom): ${user.first_name}`, { id: 2, type: 'sticker', - // thumb_url: 'https://crossword.live/img/b.png', - sticker_file_id: 'CAADAgADGQEAAtLdaQVS6DvPqWaenwI', - // title: 'Play with black pieces', + sticker_file_id: 'CAADAgAD_AADe0iRBbZltOuZOHrNAg', input_message_content: { message_text: `White (top): ? Black (bottom): ${user.first_name}`, diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index 1cc2264..0c80bd4 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -1,4 +1,4 @@ -// const chess = require('chess') +const chess = require('chess') const { board } = require('@/keyboards') const { debug, unescapeUser } = require('@/helpers') @@ -21,15 +21,18 @@ module.exports = () => [ inline_id: ctx.update.callback_query.inline_message_id, }).catch(debug) + const gameClient = chess.create({ PGN: true }) + const status = gameClient.getStatus() + await ctx.editMessageText( !iAmWhite() ? `Black (top): ${ctx.from.first_name} White (bottom): ${unescapeUser(enemy).first_name}` : `Black (top): ${unescapeUser(enemy).first_name} White (bottom): ${ctx.from.first_name}`, - board() + board(status.board.squares, true) ) - return ctx.answerCbQuery() + return ctx.answerCbQuery('Now play!') }, ] From 0a58df8b890341c691e6f76c58644cccf64a936c Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 15:29:31 +0300 Subject: [PATCH 04/33] Fixed keyboard --- src/keyboards/board/index.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/keyboards/board/index.js b/src/keyboards/board/index.js index def6d39..b0746d1 100644 --- a/src/keyboards/board/index.js +++ b/src/keyboards/board/index.js @@ -29,10 +29,13 @@ module.exports = (board, isWhite, actions) => { : { text: unescape('%u0020'), callback_data: `${col}${row}` } })) - return Markup.inlineKeyboard([ - ...(isWhite - ? boardMarkup - : boardMarkup.map((row) => row.reverse()).reverse()), - actions, - ]).extra() + const keyboard = isWhite + ? boardMarkup + : boardMarkup.map((row) => row.reverse()).reverse() + + if (actions) { + keyboard.push(actions) + } + + return Markup.inlineKeyboard(keyboard).extra() } From aa33ceda0241595f9e6893144ea5ec8af8c4b782 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 15:29:31 +0300 Subject: [PATCH 05/33] Fixed keyboard --- src/handlers/inlineJoin/index.js | 4 ++-- src/keyboards/board/index.js | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index 0c80bd4..b3560f2 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -27,9 +27,9 @@ module.exports = () => [ await ctx.editMessageText( !iAmWhite() ? `Black (top): ${ctx.from.first_name} - White (bottom): ${unescapeUser(enemy).first_name}` +White (bottom): ${unescapeUser(enemy).first_name}` : `Black (top): ${unescapeUser(enemy).first_name} - White (bottom): ${ctx.from.first_name}`, +White (bottom): ${ctx.from.first_name}`, board(status.board.squares, true) ) diff --git a/src/keyboards/board/index.js b/src/keyboards/board/index.js index def6d39..b0746d1 100644 --- a/src/keyboards/board/index.js +++ b/src/keyboards/board/index.js @@ -29,10 +29,13 @@ module.exports = (board, isWhite, actions) => { : { text: unescape('%u0020'), callback_data: `${col}${row}` } })) - return Markup.inlineKeyboard([ - ...(isWhite - ? boardMarkup - : boardMarkup.map((row) => row.reverse()).reverse()), - actions, - ]).extra() + const keyboard = isWhite + ? boardMarkup + : boardMarkup.map((row) => row.reverse()).reverse() + + if (actions) { + keyboard.push(actions) + } + + return Markup.inlineKeyboard(keyboard).extra() } From d03eab20af40dd9e20d5be9cb87e1010b1eb24d0 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 24 Apr 2019 15:07:12 +0000 Subject: [PATCH 06/33] changed sticker --- src/handlers/inline/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 5fe0937..289f896 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -35,7 +35,7 @@ module.exports = () => [ { id: 1, type: 'sticker', - sticker_file_id: 'CAADAgAD-wADe0iRBVUqQY7O3p9SAg', + sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', input_message_content: { message_text: `Black (top): ? White (bottom): ${user.first_name}`, @@ -48,7 +48,7 @@ White (bottom): ${user.first_name}`, { id: 2, type: 'sticker', - sticker_file_id: 'CAADAgAD_AADe0iRBbZltOuZOHrNAg', + sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', input_message_content: { message_text: `White (top): ? Black (bottom): ${user.first_name}`, From e21e42b47b82b8c544bf8278cdcb2a0ea1426c02 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 20:52:21 +0300 Subject: [PATCH 07/33] Added game message --- src/handlers/inline/index.js | 4 +- src/handlers/inlineJoin/index.js | 6 +- src/handlers/inlineMove/index.js | 127 +++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/handlers/inlineMove/index.js diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 5fe0937..289f896 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -35,7 +35,7 @@ module.exports = () => [ { id: 1, type: 'sticker', - sticker_file_id: 'CAADAgAD-wADe0iRBVUqQY7O3p9SAg', + sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', input_message_content: { message_text: `Black (top): ? White (bottom): ${user.first_name}`, @@ -48,7 +48,7 @@ White (bottom): ${user.first_name}`, { id: 2, type: 'sticker', - sticker_file_id: 'CAADAgAD_AADe0iRBbZltOuZOHrNAg', + sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', input_message_content: { message_text: `White (top): ? Black (bottom): ${user.first_name}`, diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index b3560f2..6e95aab 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -27,9 +27,11 @@ module.exports = () => [ await ctx.editMessageText( !iAmWhite() ? `Black (top): ${ctx.from.first_name} -White (bottom): ${unescapeUser(enemy).first_name}` +White (bottom): ${unescapeUser(enemy).first_name} +White's turn` : `Black (top): ${unescapeUser(enemy).first_name} -White (bottom): ${ctx.from.first_name}`, +White (bottom): ${ctx.from.first_name} +White's turn`, board(status.board.squares, true) ) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js new file mode 100644 index 0000000..2abb4aa --- /dev/null +++ b/src/handlers/inlineMove/index.js @@ -0,0 +1,127 @@ +const chess = require('chess') + +const { board } = require('@/keyboards') +const { debug, unescapeUser } = require('@/helpers') + +const isWhiteTurn = (moves) => !(moves.length % 2) +const isWhiteUser = (game, ctx) => game.user_w === ctx.update.callback_query.from.id +const isBlackUser = (game, ctx) => game.user_b === ctx.update.callback_query.from.id + +const statusMessage = ({ isCheck, isCheckmate, isRepetition }) => ` +${isCheck ? '|CHECK|' : ''} +${isCheckmate ? '|CHECKMATE|' : ''} +${isRepetition ? '|REPETITION|' : ''}` + +const topMessage = (moves, player, enemy) => isWhiteTurn(moves) + ? `White (top): ${player.first_name} +Black (bottom): ${enemy.first_name} +Black's turn` + : `Black (top): ${player.first_name} +White (bottom): ${enemy.first_name} +White's turn` + +module.exports = () => [ + /^([a-h])([1-8])$/, + async (ctx) => { + const gameState = await ctx.db('games') + .where('inline_id', ctx.update.callback_query.inline_message_id) + .first() + + if (![gameState.user_w, gameState.user_b].includes(ctx.update.callback_query.from.id)) { + return ctx.answerCbQuery('This board is full, please start a new one.') + } + + const movesState = await ctx.db('moves') + .where('game_id', gameState.id) + .orderBy('created_at', 'asc') + .select() + + if ((isWhiteTurn(movesState) && isBlackUser(gameState, ctx)) || + (!isWhiteTurn(movesState) && isWhiteUser(gameState, ctx))) { + return ctx.answerCbQuery('Wait, please. Now is not your turn.') + } + + const gameClient = chess.create({ PGN: true }) + + movesState.forEach(({ move }) => { + try { + gameClient.move(move) + } catch (error) { + debug(error) + } + }) + + let moves = [] + let status = gameClient.getStatus() + const square = status.board.squares + .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) + + if (ctx.session.moving) { + if ( + !square || + !square.piece || + (square.piece.side.name === 'black' && isWhiteTurn(movesState)) || + (square.piece.side.name === 'white' && !isWhiteTurn(movesState)) + ) { + return ctx.answerCbQuery('Please, move your pieces!') + } + + moves = Object.keys(status.notatedMoves) + .filter((key) => status.notatedMoves[key].src === square) + .map((key) => ({ ...status.notatedMoves[key], key })) + + await ctx.editMessageReplyMarkup(board( + status.board.squares.map((sqr) => { + const move = moves + .find((({ file, rank }) => ({ dest }) => dest.file === file && + dest.rank === rank)(sqr)) + + return move ? { ...sqr, destination: move } : sqr + }), + isWhiteTurn(movesState) + ).reply_markup) + .catch(debug) + + ctx.session.moving = true + ctx.session.moves = moves + ctx.session.selected = square + } else { + const moving = ctx.session.moves + .find(({ dest: { file, rank } }) => file === square.file && rank === square.rank) + + if (moving && !movesState.find(({ move }) => move === moving.key)) { + try { + gameClient.move(moving.key) + } catch (error) { + debug(error) + } + + status = gameClient.getStatus() + + await ctx.db('moves').insert({ game_id: gameState.id, move: moving.key }) + .catch(debug) + + ctx.session.moves = null + ctx.session.moving = false + ctx.session.selected = null + } + + const enemy = await ctx.db('users') + .where('id', isWhiteTurn(movesState) ? gameState.user_b : gameState.user_w) + .first() + .catch(debug) + + await ctx.editMessageText( + topMessage( + movesState, + ctx.update.callback_query.from, + unescapeUser(enemy) + ) + statusMessage(status), + board(status.board.squares, !isWhiteTurn(movesState)) + ) + .catch(debug) + } + + return ctx.answerCbQuery() + }, +] From cc55b659bdad559b4f962c750896f9770c48027d Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 21:07:52 +0300 Subject: [PATCH 08/33] Added inline move action --- src/handlers/index.js | 2 ++ src/handlers/inline/index.js | 6 ++++-- src/handlers/inlineJoin/index.js | 3 ++- src/index.js | 10 +++++++++- src/keyboards/board/index.js | 4 ++-- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/handlers/index.js b/src/handlers/index.js index c769daf..e74f15f 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -9,6 +9,7 @@ const actionsHandler = require('./actions') const messageHandler = require('./message') const optionsHandler = require('./options') const inlineJoinHandler = require('./inlineJoin') +const inlineMoveHandler = require('./inlineMove') module.exports = { newHandler, @@ -22,4 +23,5 @@ module.exports = { messageHandler, optionsHandler, inlineJoinHandler, + inlineMoveHandler, } diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 289f896..c1c4268 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -38,7 +38,8 @@ module.exports = () => [ sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', input_message_content: { message_text: `Black (top): ? -White (bottom): ${user.first_name}`, +White (bottom): ${user.first_name} +Waiting for a black side`, }, ...board(status.board.squares, true, [{ text: 'Join the game', @@ -51,7 +52,8 @@ White (bottom): ${user.first_name}`, sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', input_message_content: { message_text: `White (top): ? -Black (bottom): ${user.first_name}`, +Black (bottom): ${user.first_name} +Waiting for a white side`, }, ...board(status.board.squares, false, [{ text: 'Join the game', diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index 6e95aab..e7256c7 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -4,7 +4,8 @@ const { board } = require('@/keyboards') const { debug, unescapeUser } = require('@/helpers') module.exports = () => [ - /^join::(\w)::(\d+)/, async (ctx) => { + /^join::([wb])::(\d+)/, + async (ctx) => { debug(ctx.update) const userId = Number(ctx.match[2]) const iAmWhite = () => ctx.match[1] !== 'w' diff --git a/src/index.js b/src/index.js index 4719981..a8ef0af 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,14 @@ const Stage = require('telegraf/stage') const { gameScene } = require('@/scenes') const knexConfig = require('@/../knexfile') -const { inlineHandler, loadHandler, joinHandler, newHandler, inlineJoinHandler } = require('@/handlers') +const { + newHandler, + joinHandler, + loadHandler, + inlineHandler, + inlineJoinHandler, + inlineMoveHandler, +} = require('@/handlers') const { session } = Telegraf const { @@ -28,5 +35,6 @@ bot.start(...loadHandler()) bot.action(...newHandler()) bot.action(...joinHandler()) bot.action(...inlineJoinHandler()) +bot.action(...inlineMoveHandler()) bot.startPolling() diff --git a/src/keyboards/board/index.js b/src/keyboards/board/index.js index b0746d1..b01cefd 100644 --- a/src/keyboards/board/index.js +++ b/src/keyboards/board/index.js @@ -25,8 +25,8 @@ module.exports = (board, isWhite, actions) => { } return square.destination - ? { text: 'O', callback_data: `${col}${row}` } - : { text: unescape('%u0020'), callback_data: `${col}${row}` } + ? { text: 'O', callback_data: `${prefix}${col}${row}` } + : { text: unescape('%u0020'), callback_data: `${prefix}${col}${row}` } })) const keyboard = isWhite From 6f0eb6d8ed999f8238d3e64a477e660bfea8fb0a Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 21:07:52 +0300 Subject: [PATCH 09/33] Added inline move action --- src/handlers/index.js | 2 ++ src/handlers/inline/index.js | 6 ++++-- src/handlers/inlineJoin/index.js | 3 ++- src/index.js | 10 +++++++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/handlers/index.js b/src/handlers/index.js index c769daf..e74f15f 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -9,6 +9,7 @@ const actionsHandler = require('./actions') const messageHandler = require('./message') const optionsHandler = require('./options') const inlineJoinHandler = require('./inlineJoin') +const inlineMoveHandler = require('./inlineMove') module.exports = { newHandler, @@ -22,4 +23,5 @@ module.exports = { messageHandler, optionsHandler, inlineJoinHandler, + inlineMoveHandler, } diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js index 289f896..c1c4268 100644 --- a/src/handlers/inline/index.js +++ b/src/handlers/inline/index.js @@ -38,7 +38,8 @@ module.exports = () => [ sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', input_message_content: { message_text: `Black (top): ? -White (bottom): ${user.first_name}`, +White (bottom): ${user.first_name} +Waiting for a black side`, }, ...board(status.board.squares, true, [{ text: 'Join the game', @@ -51,7 +52,8 @@ White (bottom): ${user.first_name}`, sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', input_message_content: { message_text: `White (top): ? -Black (bottom): ${user.first_name}`, +Black (bottom): ${user.first_name} +Waiting for a white side`, }, ...board(status.board.squares, false, [{ text: 'Join the game', diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index 6e95aab..e7256c7 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -4,7 +4,8 @@ const { board } = require('@/keyboards') const { debug, unescapeUser } = require('@/helpers') module.exports = () => [ - /^join::(\w)::(\d+)/, async (ctx) => { + /^join::([wb])::(\d+)/, + async (ctx) => { debug(ctx.update) const userId = Number(ctx.match[2]) const iAmWhite = () => ctx.match[1] !== 'w' diff --git a/src/index.js b/src/index.js index 4719981..a8ef0af 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,14 @@ const Stage = require('telegraf/stage') const { gameScene } = require('@/scenes') const knexConfig = require('@/../knexfile') -const { inlineHandler, loadHandler, joinHandler, newHandler, inlineJoinHandler } = require('@/handlers') +const { + newHandler, + joinHandler, + loadHandler, + inlineHandler, + inlineJoinHandler, + inlineMoveHandler, +} = require('@/handlers') const { session } = Telegraf const { @@ -28,5 +35,6 @@ bot.start(...loadHandler()) bot.action(...newHandler()) bot.action(...joinHandler()) bot.action(...inlineJoinHandler()) +bot.action(...inlineMoveHandler()) bot.startPolling() From 34dd058a80c8599dbf5f54995f022cd1b76e77f3 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 21:18:59 +0300 Subject: [PATCH 10/33] Added debug --- src/handlers/inlineMove/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 2abb4aa..7f17659 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,6 +27,8 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() + debug(gameState) + if (![gameState.user_w, gameState.user_b].includes(ctx.update.callback_query.from.id)) { return ctx.answerCbQuery('This board is full, please start a new one.') } From eee5e69ec436ad68a55dacb585b6b274f2404562 Mon Sep 17 00:00:00 2001 From: Piterden Date: Wed, 24 Apr 2019 21:23:02 +0300 Subject: [PATCH 11/33] Fixed users ID to number --- src/handlers/inlineMove/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 7f17659..8071fab 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -29,7 +29,10 @@ module.exports = () => [ debug(gameState) - if (![gameState.user_w, gameState.user_b].includes(ctx.update.callback_query.from.id)) { + if (![ + Number(gameState.user_w), + Number(gameState.user_b), + ].includes(ctx.update.callback_query.from.id)) { return ctx.answerCbQuery('This board is full, please start a new one.') } From bde21a605c5975a861021d750ac84f8eba051a55 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 01:18:01 +0300 Subject: [PATCH 12/33] Debug session --- src/handlers/inlineMove/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 8071fab..5efbf5c 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,7 +27,7 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(gameState) + debug(ctx.session) if (![ Number(gameState.user_w), From e31bf89076190a53d1084023140207754a3ab4e7 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 02:51:05 +0300 Subject: [PATCH 13/33] Debug from and chat --- src/handlers/inlineMove/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 5efbf5c..0d1c373 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,7 +27,8 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(ctx.session) + debug(ctx.from) + debug(ctx.chat) if (![ Number(gameState.user_w), From af9e3727d1dee26afbffbf471c7e79a2051497f0 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 02:52:59 +0300 Subject: [PATCH 14/33] Removed prefix --- src/keyboards/board/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keyboards/board/index.js b/src/keyboards/board/index.js index b01cefd..b0746d1 100644 --- a/src/keyboards/board/index.js +++ b/src/keyboards/board/index.js @@ -25,8 +25,8 @@ module.exports = (board, isWhite, actions) => { } return square.destination - ? { text: 'O', callback_data: `${prefix}${col}${row}` } - : { text: unescape('%u0020'), callback_data: `${prefix}${col}${row}` } + ? { text: 'O', callback_data: `${col}${row}` } + : { text: unescape('%u0020'), callback_data: `${col}${row}` } })) const keyboard = isWhite From 0fb2f7f8c506a22c4746bbdb27aa655b5aabca0e Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 03:01:55 +0300 Subject: [PATCH 15/33] NPM updated --- package.json | 12 ++++++------ src/handlers/inlineMove/index.js | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 13663de..bdcde08 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,12 @@ }, "license": "MIT", "dependencies": { - "chess": "^0.2.8", - "dotenv": "^5.0.1", - "knex": "^0.14.6", + "chess": "^0.4.1", + "dotenv": "^7.0.0", + "knex": "^0.16.5", "module-alias": "^2.2.0", - "pg": "^7.8.1", - "telegraf": "^3.27.1" + "pg": "^7.10.0", + "telegraf": "^3.29.0" }, "devDependencies": { "@atomix/eslint-config": "^7.0.0-next.1", @@ -29,7 +29,7 @@ "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.1.1", "eslint-plugin-standard": "^4.0.0", - "nodemon": "^1.18.10", + "nodemon": "^1.18.11", "prettier": "^1.17.0", "sqlite3": "^4.0.6" }, diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 0d1c373..f289c2b 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,8 +27,7 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(ctx.from) - debug(ctx.chat) + debug(ctx) if (![ Number(gameState.user_w), From fd3b9a75d1be01712eade400ea9c8d9e60127a54 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 03:09:43 +0300 Subject: [PATCH 16/33] Updated dotenv command --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index a8ef0af..947f41e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -require('dotenv').load() +require('dotenv').config() require('module-alias/register') const knex = require('knex') From ebc65eb904f35f1947865c1162cf706d36938c10 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 03:11:12 +0300 Subject: [PATCH 17/33] Fixed dotenv command --- knexfile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/knexfile.js b/knexfile.js index f9c4aa6..096f5bd 100644 --- a/knexfile.js +++ b/knexfile.js @@ -1,4 +1,4 @@ -require('dotenv').load() +require('dotenv').config() const { DB_CLIENT, From b0e3667fd1eb0cb81d442be2cddb22e56c8ea3bd Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 03:16:39 +0300 Subject: [PATCH 18/33] Debug updated --- src/handlers/inlineMove/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index f289c2b..037087a 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,7 +27,8 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(ctx) + debug(ctx.update) + debug(Object.keys(ctx)) if (![ Number(gameState.user_w), From f452abb0215d6b832e8b54e09d3fe6efaa34706e Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 03:21:59 +0300 Subject: [PATCH 19/33] Fixed session init --- src/index.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 947f41e..cc6cbbe 100644 --- a/src/index.js +++ b/src/index.js @@ -27,7 +27,17 @@ const bot = new Telegraf(BOT_TOKEN, { username: BOT_NAME }) bot.context.db = knex(knexConfig) -bot.use(session()) +bot.use(session({ + getSessionKey: (ctx) => { + const key = ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}` + + if (!key) { + return ctx.from && ctx.callback_query && + `${ctx.from.id}:${ctx.callback_query.chat_instance}` + } + return key + }, +})) bot.use(stage.middleware()) bot.on(...inlineHandler()) From 7c1fd92b132bd980da5478885e2534013a2149c4 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:02:57 +0300 Subject: [PATCH 20/33] Added state instead of session --- src/handlers/index.js | 4 +- src/handlers/inline/index.js | 68 ------------------------------- src/handlers/inlineMove/index.js | 19 +++++---- src/handlers/inlineQuery/index.js | 65 +++++++++++++++++++++++++++++ src/index.js | 17 ++------ 5 files changed, 81 insertions(+), 92 deletions(-) delete mode 100644 src/handlers/inline/index.js create mode 100644 src/handlers/inlineQuery/index.js diff --git a/src/handlers/index.js b/src/handlers/index.js index e74f15f..fb09f67 100644 --- a/src/handlers/index.js +++ b/src/handlers/index.js @@ -4,12 +4,12 @@ const loadHandler = require('./load') const leaveHandler = require('./leave') const movesHandler = require('./moves') const startHandler = require('./start') -const inlineHandler = require('./inline') const actionsHandler = require('./actions') const messageHandler = require('./message') const optionsHandler = require('./options') const inlineJoinHandler = require('./inlineJoin') const inlineMoveHandler = require('./inlineMove') +const inlineQueryHandler = require('./inlineQuery') module.exports = { newHandler, @@ -18,10 +18,10 @@ module.exports = { leaveHandler, movesHandler, startHandler, - inlineHandler, actionsHandler, messageHandler, optionsHandler, inlineJoinHandler, inlineMoveHandler, + inlineQueryHandler, } diff --git a/src/handlers/inline/index.js b/src/handlers/inline/index.js deleted file mode 100644 index c1c4268..0000000 --- a/src/handlers/inline/index.js +++ /dev/null @@ -1,68 +0,0 @@ -const chess = require('chess') - -const { board } = require('@/keyboards') -const { debug, unescapeUser, escapeUser } = require('@/helpers') - -const gameClient = chess.create({ PGN: true }) -const status = gameClient.getStatus() - -module.exports = () => [ - 'inline_query', - async (ctx) => { - debug(ctx.update) - - let user = await ctx.db('users') - .where('id', ctx.update.inline_query.from.id) - .first() - - if (user) { - user = unescapeUser(user) - - if (JSON.stringify(user) !== JSON.stringify(ctx.update.inline_query.from)) { - await ctx.db('users') - .where('id', user.id) - .update(escapeUser(ctx.update.inline_query.from)) - } - } else { - const users = await ctx.db('users') - .insert(escapeUser(ctx.update.inline_query.from)) - .returning(Object.keys(ctx.update.inline_query.from)) - - user = unescapeUser(users[0]) - } - - await ctx.answerInlineQuery([ - { - id: 1, - type: 'sticker', - sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', - input_message_content: { - message_text: `Black (top): ? -White (bottom): ${user.first_name} -Waiting for a black side`, - }, - ...board(status.board.squares, true, [{ - text: 'Join the game', - callback_data: `join::w::${user.id}`, - }]), - }, - { - id: 2, - type: 'sticker', - sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', - input_message_content: { - message_text: `White (top): ? -Black (bottom): ${user.first_name} -Waiting for a white side`, - }, - ...board(status.board.squares, false, [{ - text: 'Join the game', - callback_data: `join::b::${user.id}`, - }]), - }, - ], { - is_personal: true, - cache_time: 0, - }) - }, -] diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 037087a..2b89006 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -23,12 +23,13 @@ White's turn` module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { + ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || {} + const gameState = await ctx.db('games') .where('inline_id', ctx.update.callback_query.inline_message_id) .first() debug(ctx.update) - debug(Object.keys(ctx)) if (![ Number(gameState.user_w), @@ -62,7 +63,7 @@ module.exports = () => [ const square = status.board.squares .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) - if (ctx.session.moving) { + if (ctx.state[ctx.update.callback_query.inline_message_id].moving) { if ( !square || !square.piece || @@ -88,11 +89,11 @@ module.exports = () => [ ).reply_markup) .catch(debug) - ctx.session.moving = true - ctx.session.moves = moves - ctx.session.selected = square + ctx.state[ctx.update.callback_query.inline_message_id].moving = true + ctx.state[ctx.update.callback_query.inline_message_id].moves = moves + ctx.state[ctx.update.callback_query.inline_message_id].selected = square } else { - const moving = ctx.session.moves + const moving = ctx.state[ctx.update.callback_query.inline_message_id].moves .find(({ dest: { file, rank } }) => file === square.file && rank === square.rank) if (moving && !movesState.find(({ move }) => move === moving.key)) { @@ -107,9 +108,9 @@ module.exports = () => [ await ctx.db('moves').insert({ game_id: gameState.id, move: moving.key }) .catch(debug) - ctx.session.moves = null - ctx.session.moving = false - ctx.session.selected = null + ctx.state[ctx.update.callback_query.inline_message_id].moves = null + ctx.state[ctx.update.callback_query.inline_message_id].moving = false + ctx.state[ctx.update.callback_query.inline_message_id].selected = null } const enemy = await ctx.db('users') diff --git a/src/handlers/inlineQuery/index.js b/src/handlers/inlineQuery/index.js new file mode 100644 index 0000000..6050bc2 --- /dev/null +++ b/src/handlers/inlineQuery/index.js @@ -0,0 +1,65 @@ +const chess = require('chess') + +const { board } = require('@/keyboards') +const { debug, unescapeUser, escapeUser } = require('@/helpers') + +const gameClient = chess.create({ PGN: true }) +const status = gameClient.getStatus() + +module.exports = () => async (ctx) => { + debug(ctx.update) + + let user = await ctx.db('users') + .where('id', ctx.update.inline_query.from.id) + .first() + + if (user) { + user = unescapeUser(user) + + if (JSON.stringify(user) !== JSON.stringify(ctx.update.inline_query.from)) { + await ctx.db('users') + .where('id', user.id) + .update(escapeUser(ctx.update.inline_query.from)) + } + } else { + const users = await ctx.db('users') + .insert(escapeUser(ctx.update.inline_query.from)) + .returning(Object.keys(ctx.update.inline_query.from)) + + user = unescapeUser(users[0]) + } + + await ctx.answerInlineQuery([ + { + id: 1, + type: 'sticker', + sticker_file_id: 'CAADAgADNAADX5T2DgeepFdKYLnKAg', + input_message_content: { + message_text: `Black (top): ? +White (bottom): ${user.first_name} +Waiting for a black side`, + }, + ...board(status.board.squares, true, [{ + text: 'Join the game', + callback_data: `join::w::${user.id}`, + }]), + }, + { + id: 2, + type: 'sticker', + sticker_file_id: 'CAADAgADMwADX5T2DqhR9w5HSpCZAg', + input_message_content: { + message_text: `White (top): ? +Black (bottom): ${user.first_name} +Waiting for a white side`, + }, + ...board(status.board.squares, false, [{ + text: 'Join the game', + callback_data: `join::b::${user.id}`, + }]), + }, + ], { + is_personal: true, + cache_time: 0, + }) +} diff --git a/src/index.js b/src/index.js index cc6cbbe..e453a0f 100644 --- a/src/index.js +++ b/src/index.js @@ -11,9 +11,9 @@ const { newHandler, joinHandler, loadHandler, - inlineHandler, inlineJoinHandler, inlineMoveHandler, + inlineQueryHandler, } = require('@/handlers') const { session } = Telegraf @@ -27,24 +27,15 @@ const bot = new Telegraf(BOT_TOKEN, { username: BOT_NAME }) bot.context.db = knex(knexConfig) -bot.use(session({ - getSessionKey: (ctx) => { - const key = ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}` - - if (!key) { - return ctx.from && ctx.callback_query && - `${ctx.from.id}:${ctx.callback_query.chat_instance}` - } - return key - }, -})) +bot.use(session()) bot.use(stage.middleware()) -bot.on(...inlineHandler()) bot.start(...loadHandler()) bot.action(...newHandler()) bot.action(...joinHandler()) + bot.action(...inlineJoinHandler()) bot.action(...inlineMoveHandler()) +bot.on('inline_query', inlineQueryHandler()) bot.startPolling() From eb3bd4b36a8689051f9bc72954622d0d4471f32b Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:08:57 +0300 Subject: [PATCH 21/33] Fixed condition error --- src/handlers/inlineMove/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 2b89006..868d857 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -23,7 +23,8 @@ White's turn` module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { - ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || {} + ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || + { moves: [], moving: false, selected: null } const gameState = await ctx.db('games') .where('inline_id', ctx.update.callback_query.inline_message_id) @@ -63,7 +64,7 @@ module.exports = () => [ const square = status.board.squares .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) - if (ctx.state[ctx.update.callback_query.inline_message_id].moving) { + if (!ctx.state[ctx.update.callback_query.inline_message_id].moving) { if ( !square || !square.piece || From ea5e740c1f67104586dcb31e32d878f92469f016 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:08:57 +0300 Subject: [PATCH 22/33] Fixed condition error --- src/handlers/inlineMove/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 2b89006..e977bde 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -23,13 +23,14 @@ White's turn` module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { - ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || {} + ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || + { moves: [], moving: false, selected: null } const gameState = await ctx.db('games') .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(ctx.update) + debug(ctx.state) if (![ Number(gameState.user_w), @@ -63,7 +64,7 @@ module.exports = () => [ const square = status.board.squares .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) - if (ctx.state[ctx.update.callback_query.inline_message_id].moving) { + if (!ctx.state[ctx.update.callback_query.inline_message_id].moving) { if ( !square || !square.piece || From c2dd9d7ab5419eeea535d7a19c699b1641477819 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:18:10 +0300 Subject: [PATCH 23/33] Added ready condition --- src/handlers/inlineMove/index.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index e977bde..e0c8641 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -20,9 +20,13 @@ Black's turn` White (bottom): ${enemy.first_name} White's turn` +const isReady = (game) => !!(game.user_w && game.user_b) + module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { + debug(ctx.state) + ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || { moves: [], moving: false, selected: null } @@ -30,12 +34,12 @@ module.exports = () => [ .where('inline_id', ctx.update.callback_query.inline_message_id) .first() - debug(ctx.state) + if (!isReady(gameState)) { + return ctx.answerCbQuery('Join the game to move pieces!') + } - if (![ - Number(gameState.user_w), - Number(gameState.user_b), - ].includes(ctx.update.callback_query.from.id)) { + if (![Number(gameState.user_w), Number(gameState.user_b)] + .includes(ctx.update.callback_query.from.id)) { return ctx.answerCbQuery('This board is full, please start a new one.') } From 1ecf230d6bfe0a1e37299a4b45ac0b4bce3e0313 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:41:42 +0300 Subject: [PATCH 24/33] WIP debug --- src/handlers/inlineMove/index.js | 19 +++++++++---------- src/index.js | 5 ++++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index e0c8641..36ace55 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -25,10 +25,9 @@ const isReady = (game) => !!(game.user_w && game.user_b) module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { - debug(ctx.state) + debug(ctx.session) - ctx.state[ctx.update.callback_query.inline_message_id] = ctx.state[ctx.update.callback_query.inline_message_id] || - { moves: [], moving: false, selected: null } + // ctx.state = ctx.state || { moves: null, moving: false, selected: null } const gameState = await ctx.db('games') .where('inline_id', ctx.update.callback_query.inline_message_id) @@ -94,11 +93,11 @@ module.exports = () => [ ).reply_markup) .catch(debug) - ctx.state[ctx.update.callback_query.inline_message_id].moving = true - ctx.state[ctx.update.callback_query.inline_message_id].moves = moves - ctx.state[ctx.update.callback_query.inline_message_id].selected = square + // ctx.state[ctx.update.callback_query.inline_message_id].moving = true + // ctx.state[ctx.update.callback_query.inline_message_id].moves = moves + // ctx.state[ctx.update.callback_query.inline_message_id].selected = square } else { - const moving = ctx.state[ctx.update.callback_query.inline_message_id].moves + const moving = ctx.session.moves .find(({ dest: { file, rank } }) => file === square.file && rank === square.rank) if (moving && !movesState.find(({ move }) => move === moving.key)) { @@ -113,9 +112,9 @@ module.exports = () => [ await ctx.db('moves').insert({ game_id: gameState.id, move: moving.key }) .catch(debug) - ctx.state[ctx.update.callback_query.inline_message_id].moves = null - ctx.state[ctx.update.callback_query.inline_message_id].moving = false - ctx.state[ctx.update.callback_query.inline_message_id].selected = null + // ctx.state[ctx.update.callback_query.inline_message_id].moves = null + // ctx.state[ctx.update.callback_query.inline_message_id].moving = false + // ctx.state[ctx.update.callback_query.inline_message_id].selected = null } const enemy = await ctx.db('users') diff --git a/src/index.js b/src/index.js index e453a0f..db9e954 100644 --- a/src/index.js +++ b/src/index.js @@ -27,7 +27,10 @@ const bot = new Telegraf(BOT_TOKEN, { username: BOT_NAME }) bot.context.db = knex(knexConfig) -bot.use(session()) +bot.use(session({ + getSessionKey: (ctx) => (ctx.update.callback_query && ctx.update.callback_query.inline_message_id) || + (ctx.from && ctx.chat && `${ctx.from.id}:${ctx.chat.id}`), +})) bot.use(stage.middleware()) bot.start(...loadHandler()) From 984ddc5bd2549ff217bf9daef1c78a494768c318 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 04:46:24 +0300 Subject: [PATCH 25/33] Restored session --- src/handlers/inlineMove/index.js | 46 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 36ace55..e74ba3d 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -27,8 +27,6 @@ module.exports = () => [ async (ctx) => { debug(ctx.session) - // ctx.state = ctx.state || { moves: null, moving: false, selected: null } - const gameState = await ctx.db('games') .where('inline_id', ctx.update.callback_query.inline_message_id) .first() @@ -67,7 +65,7 @@ module.exports = () => [ const square = status.board.squares .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) - if (!ctx.state[ctx.update.callback_query.inline_message_id].moving) { + if (!ctx.session.moving) { if ( !square || !square.piece || @@ -93,9 +91,9 @@ module.exports = () => [ ).reply_markup) .catch(debug) - // ctx.state[ctx.update.callback_query.inline_message_id].moving = true - // ctx.state[ctx.update.callback_query.inline_message_id].moves = moves - // ctx.state[ctx.update.callback_query.inline_message_id].selected = square + ctx.session.moves = moves + ctx.session.moving = true + ctx.session.selected = square } else { const moving = ctx.session.moves .find(({ dest: { file, rank } }) => file === square.file && rank === square.rank) @@ -112,25 +110,25 @@ module.exports = () => [ await ctx.db('moves').insert({ game_id: gameState.id, move: moving.key }) .catch(debug) - // ctx.state[ctx.update.callback_query.inline_message_id].moves = null - // ctx.state[ctx.update.callback_query.inline_message_id].moving = false - // ctx.state[ctx.update.callback_query.inline_message_id].selected = null + ctx.session.moves = null + ctx.session.moving = false + ctx.session.selected = null + + const enemy = await ctx.db('users') + .where('id', isWhiteTurn(movesState) ? gameState.user_b : gameState.user_w) + .first() + .catch(debug) + + await ctx.editMessageText( + topMessage( + movesState, + ctx.update.callback_query.from, + unescapeUser(enemy) + ) + statusMessage(status), + board(status.board.squares, !isWhiteTurn(movesState)) + ) + .catch(debug) } - - const enemy = await ctx.db('users') - .where('id', isWhiteTurn(movesState) ? gameState.user_b : gameState.user_w) - .first() - .catch(debug) - - await ctx.editMessageText( - topMessage( - movesState, - ctx.update.callback_query.from, - unescapeUser(enemy) - ) + statusMessage(status), - board(status.board.squares, !isWhiteTurn(movesState)) - ) - .catch(debug) } return ctx.answerCbQuery() From 44c0cf59a07f605a817aa378e0f9d2382913eb17 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 05:09:50 +0300 Subject: [PATCH 26/33] Updated internal data collecting --- src/handlers/inlineJoin/index.js | 25 +++++++++++++++++-------- src/handlers/inlineQuery/index.js | 14 ++++++++------ src/helpers/index.js | 2 ++ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index e7256c7..4231938 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -14,24 +14,33 @@ module.exports = () => [ return ctx.answerCbQuery('You can\'t join yourself!') } - const enemy = await ctx.db('users').where('id', userId).first() + let enemy = await ctx.db('users').where('id', userId).first() - await ctx.db('games').returning('id').insert({ + if (!enemy) { + enemy = ctx.tg.getChatMember( + ctx.callbackQuery.chat_instance, + userId + ) + } + + const [gameId] = await ctx.db('games').returning('id').insert({ user_w: !iAmWhite() ? enemy.id : ctx.from.id, user_b: !iAmWhite() ? ctx.from.id : enemy.id, inline_id: ctx.update.callback_query.inline_message_id, - }).catch(debug) + }).catch(debug) || [null] const gameClient = chess.create({ PGN: true }) const status = gameClient.getStatus() + ctx.session.gameId = gameId + await ctx.editMessageText( - !iAmWhite() - ? `Black (top): ${ctx.from.first_name} -White (bottom): ${unescapeUser(enemy).first_name} -White's turn` - : `Black (top): ${unescapeUser(enemy).first_name} + iAmWhite() + ? `Black (top): ${unescapeUser(enemy).first_name} White (bottom): ${ctx.from.first_name} +White's turn` + : `Black (top): ${ctx.from.first_name} +White (bottom): ${unescapeUser(enemy).first_name} White's turn`, board(status.board.squares, true) ) diff --git a/src/handlers/inlineQuery/index.js b/src/handlers/inlineQuery/index.js index 6050bc2..e1b0918 100644 --- a/src/handlers/inlineQuery/index.js +++ b/src/handlers/inlineQuery/index.js @@ -10,21 +10,23 @@ module.exports = () => async (ctx) => { debug(ctx.update) let user = await ctx.db('users') - .where('id', ctx.update.inline_query.from.id) + .where('id', Number(ctx.from.id)) .first() if (user) { user = unescapeUser(user) - if (JSON.stringify(user) !== JSON.stringify(ctx.update.inline_query.from)) { + if (JSON.stringify(user) !== JSON.stringify(ctx.from)) { await ctx.db('users') - .where('id', user.id) - .update(escapeUser(ctx.update.inline_query.from)) + .where('id', Number(user.id)) + .update(escapeUser(ctx.from)) + .catch(debug) } } else { const users = await ctx.db('users') - .insert(escapeUser(ctx.update.inline_query.from)) - .returning(Object.keys(ctx.update.inline_query.from)) + .insert(escapeUser(ctx.from)) + .returning(Object.keys(ctx.from)) + .catch(debug) user = unescapeUser(users[0]) } diff --git a/src/helpers/index.js b/src/helpers/index.js index 37948d1..e75112c 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -4,10 +4,12 @@ const emodji = require('./emodji') module.exports = { debug, emodji, + unescapeUser: (user) => Object.keys(user).reduce((acc, key) => { acc[key] = typeof user[key] === 'string' ? unescape(user[key]) : user[key] return acc }, {}), + escapeUser: (user) => Object.keys(user).reduce((acc, key) => { acc[key] = typeof user[key] === 'string' ? escape(user[key]) : user[key] return acc From f67e6d45c7d146526d7d56b41cbf311302fa88f2 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 05:15:52 +0300 Subject: [PATCH 27/33] Debugging in progress --- src/handlers/inlineMove/index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index e74ba3d..8357c38 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -25,12 +25,12 @@ const isReady = (game) => !!(game.user_w && game.user_b) module.exports = () => [ /^([a-h])([1-8])$/, async (ctx) => { - debug(ctx.session) - const gameState = await ctx.db('games') - .where('inline_id', ctx.update.callback_query.inline_message_id) + .where('id', ctx.session.gameId) .first() + debug(gameState) + if (!isReady(gameState)) { return ctx.answerCbQuery('Join the game to move pieces!') } @@ -115,10 +115,14 @@ module.exports = () => [ ctx.session.selected = null const enemy = await ctx.db('users') - .where('id', isWhiteTurn(movesState) ? gameState.user_b : gameState.user_w) + .where('id', isWhiteTurn(movesState) + ? Number(gameState.user_b) + : Number(gameState.user_w)) .first() .catch(debug) + debug(enemy) + await ctx.editMessageText( topMessage( movesState, From f9ba46ab2ed577df72de9b695b91cf120624e94c Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 05:27:20 +0300 Subject: [PATCH 28/33] Fixed enemy side --- src/handlers/inlineJoin/index.js | 19 +++++++++++-------- src/handlers/inlineMove/index.js | 4 ++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/handlers/inlineJoin/index.js b/src/handlers/inlineJoin/index.js index 4231938..a478398 100644 --- a/src/handlers/inlineJoin/index.js +++ b/src/handlers/inlineJoin/index.js @@ -1,7 +1,7 @@ const chess = require('chess') const { board } = require('@/keyboards') -const { debug, unescapeUser } = require('@/helpers') +const { debug, escapeUser, unescapeUser } = require('@/helpers') module.exports = () => [ /^join::([wb])::(\d+)/, @@ -14,33 +14,36 @@ module.exports = () => [ return ctx.answerCbQuery('You can\'t join yourself!') } - let enemy = await ctx.db('users').where('id', userId).first() + let enemy = await ctx.db('users').where('id', userId).first().catch(debug) - if (!enemy) { + if (enemy) { + enemy = unescapeUser(enemy) + } else { enemy = ctx.tg.getChatMember( ctx.callbackQuery.chat_instance, userId ) + await ctx.db('users').insert(escapeUser(enemy)).catch(debug) } const [gameId] = await ctx.db('games').returning('id').insert({ user_w: !iAmWhite() ? enemy.id : ctx.from.id, user_b: !iAmWhite() ? ctx.from.id : enemy.id, inline_id: ctx.update.callback_query.inline_message_id, - }).catch(debug) || [null] + }).catch(debug) + + ctx.session.gameId = gameId const gameClient = chess.create({ PGN: true }) const status = gameClient.getStatus() - ctx.session.gameId = gameId - await ctx.editMessageText( iAmWhite() - ? `Black (top): ${unescapeUser(enemy).first_name} + ? `Black (top): ${enemy.first_name} White (bottom): ${ctx.from.first_name} White's turn` : `Black (top): ${ctx.from.first_name} -White (bottom): ${unescapeUser(enemy).first_name} +White (bottom): ${enemy.first_name} White's turn`, board(status.board.squares, true) ) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 8357c38..fb5beed 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -116,8 +116,8 @@ module.exports = () => [ const enemy = await ctx.db('users') .where('id', isWhiteTurn(movesState) - ? Number(gameState.user_b) - : Number(gameState.user_w)) + ? Number(gameState.user_w) + : Number(gameState.user_b)) .first() .catch(debug) From 9f59482066472bb6c613a874dba49f76d3c41053 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 05:45:15 +0300 Subject: [PATCH 29/33] Fixed other side enemy --- src/handlers/inlineMove/index.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index fb5beed..721ef5b 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -1,7 +1,7 @@ const chess = require('chess') const { board } = require('@/keyboards') -const { debug, unescapeUser } = require('@/helpers') +const { debug, escapeUser, unescapeUser } = require('@/helpers') const isWhiteTurn = (moves) => !(moves.length % 2) const isWhiteUser = (game, ctx) => game.user_w === ctx.update.callback_query.from.id @@ -114,20 +114,32 @@ module.exports = () => [ ctx.session.moving = false ctx.session.selected = null - const enemy = await ctx.db('users') + let enemy = await ctx.db('users') .where('id', isWhiteTurn(movesState) ? Number(gameState.user_w) : Number(gameState.user_b)) .first() .catch(debug) + if (enemy) { + enemy = unescapeUser(enemy) + } else { + enemy = ctx.tg.getChatMember( + ctx.callbackQuery.chat_instance, + isWhiteTurn(movesState) + ? Number(gameState.user_w) + : Number(gameState.user_b) + ) + await ctx.db('users').insert(escapeUser(enemy)).catch(debug) + } + debug(enemy) await ctx.editMessageText( topMessage( movesState, ctx.update.callback_query.from, - unescapeUser(enemy) + enemy ) + statusMessage(status), board(status.board.squares, !isWhiteTurn(movesState)) ) From f38194fbdd89b6a080ff535cc0db32a90299c2dc Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 06:15:34 +0300 Subject: [PATCH 30/33] Disabled old handlers --- src/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.js b/src/index.js index db9e954..de11cbe 100644 --- a/src/index.js +++ b/src/index.js @@ -8,9 +8,9 @@ const Stage = require('telegraf/stage') const { gameScene } = require('@/scenes') const knexConfig = require('@/../knexfile') const { - newHandler, - joinHandler, - loadHandler, + // newHandler, + // joinHandler, + // loadHandler, inlineJoinHandler, inlineMoveHandler, inlineQueryHandler, @@ -33,9 +33,9 @@ bot.use(session({ })) bot.use(stage.middleware()) -bot.start(...loadHandler()) -bot.action(...newHandler()) -bot.action(...joinHandler()) +// bot.start(...loadHandler()) +// bot.action(...newHandler()) +// bot.action(...joinHandler()) bot.action(...inlineJoinHandler()) bot.action(...inlineMoveHandler()) From 236d27b0aae5070609844896acaaa7692d9023a9 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 06:56:43 +0300 Subject: [PATCH 31/33] NPM cleaned --- .eslintrc.yaml | 15 ++------------- package.json | 2 -- src/keyboards/board/index.js | 3 ++- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index a1d5ca8..c2d21ea 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -1,34 +1,23 @@ root: true parser: babel-eslint extends: - - '@atomix' + # - '@atomix' - standard env: - es6: true node: true - browser: true parserOptions: sourceType: module ecmaVersion: 2018 ecmaFeatures: globalReturn: false - modules: true + modules: false experimentalObjectRestSpread: true -globals: - BigInt: true rules: max-len: off - import/no-cycle: off - no-param-reassign: off - prettier/prettier: off prefer-rest-params: off prefer-destructuring: off - no-underscore-dangle: off - unicorn/filename-case: off unicorn/prefer-spread: off - class-methods-use-this: off function-paren-newline: off - import/no-default-export: off curly: - error comma-dangle: diff --git a/package.json b/package.json index bdcde08..33b9b77 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,12 @@ "telegraf": "^3.29.0" }, "devDependencies": { - "@atomix/eslint-config": "^7.0.0-next.1", "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.1.1", "eslint-plugin-standard": "^4.0.0", "nodemon": "^1.18.11", - "prettier": "^1.17.0", "sqlite3": "^4.0.6" }, "_moduleAliases": { diff --git a/src/keyboards/board/index.js b/src/keyboards/board/index.js index b0746d1..323956a 100644 --- a/src/keyboards/board/index.js +++ b/src/keyboards/board/index.js @@ -6,7 +6,8 @@ const { Markup } = Telegraf module.exports = (board, isWhite, actions) => { const horizontal = 'abcdefgh'.split('') - const vertical = Array.from({ length: 8 }).map((item, idx) => idx + 1).reverse() + const vertical = Array.from({ length: 8 }) + .map((item, idx) => idx + 1).reverse() const boardMarkup = vertical.map((row) => horizontal.map((col) => { const square = board From df1b96bc5fc6f94f69db34afa06768cbe5e4ad79 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 10:51:12 +0300 Subject: [PATCH 32/33] Try to limit sides --- src/handlers/inlineMove/index.js | 85 ++++++++++++++++---------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/handlers/inlineMove/index.js b/src/handlers/inlineMove/index.js index 721ef5b..6c5ea9d 100644 --- a/src/handlers/inlineMove/index.js +++ b/src/handlers/inlineMove/index.js @@ -4,8 +4,8 @@ const { board } = require('@/keyboards') const { debug, escapeUser, unescapeUser } = require('@/helpers') const isWhiteTurn = (moves) => !(moves.length % 2) -const isWhiteUser = (game, ctx) => game.user_w === ctx.update.callback_query.from.id -const isBlackUser = (game, ctx) => game.user_b === ctx.update.callback_query.from.id +const isWhiteUser = (game, ctx) => Number(game.user_w) === ctx.from.id +const isBlackUser = (game, ctx) => Number(game.user_b) === ctx.from.id const statusMessage = ({ isCheck, isCheckmate, isRepetition }) => ` ${isCheck ? '|CHECK|' : ''} @@ -36,7 +36,7 @@ module.exports = () => [ } if (![Number(gameState.user_w), Number(gameState.user_b)] - .includes(ctx.update.callback_query.from.id)) { + .includes(ctx.from.id)) { return ctx.answerCbQuery('This board is full, please start a new one.') } @@ -65,14 +65,14 @@ module.exports = () => [ const square = status.board.squares .find(({ file, rank }) => file === ctx.match[1] && rank === Number(ctx.match[2])) - if (!ctx.session.moving) { + if (!ctx.session.moves) { if ( !square || !square.piece || (square.piece.side.name === 'black' && isWhiteTurn(movesState)) || (square.piece.side.name === 'white' && !isWhiteTurn(movesState)) ) { - return ctx.answerCbQuery('Please, move your pieces!') + return ctx.answerCbQuery() } moves = Object.keys(status.notatedMoves) @@ -92,7 +92,6 @@ module.exports = () => [ .catch(debug) ctx.session.moves = moves - ctx.session.moving = true ctx.session.selected = square } else { const moving = ctx.session.moves @@ -107,44 +106,46 @@ module.exports = () => [ status = gameClient.getStatus() - await ctx.db('moves').insert({ game_id: gameState.id, move: moving.key }) - .catch(debug) - - ctx.session.moves = null - ctx.session.moving = false - ctx.session.selected = null - - let enemy = await ctx.db('users') - .where('id', isWhiteTurn(movesState) - ? Number(gameState.user_w) - : Number(gameState.user_b)) - .first() - .catch(debug) - - if (enemy) { - enemy = unescapeUser(enemy) - } else { - enemy = ctx.tg.getChatMember( - ctx.callbackQuery.chat_instance, - isWhiteTurn(movesState) - ? Number(gameState.user_w) - : Number(gameState.user_b) - ) - await ctx.db('users').insert(escapeUser(enemy)).catch(debug) - } - - debug(enemy) - - await ctx.editMessageText( - topMessage( - movesState, - ctx.update.callback_query.from, - enemy - ) + statusMessage(status), - board(status.board.squares, !isWhiteTurn(movesState)) - ) + await ctx.db('moves').insert({ + game_id: ctx.session.gameId, + move: moving.key, + }) .catch(debug) } + + ctx.session.moves = null + ctx.session.selected = null + + let enemy = await ctx.db('users') + .where('id', isWhiteTurn(movesState) + ? Number(gameState.user_w) + : Number(gameState.user_b)) + .first() + .catch(debug) + + if (enemy) { + enemy = unescapeUser(enemy) + } else { + enemy = ctx.tg.getChatMember( + ctx.callbackQuery.chat_instance, + isWhiteTurn(movesState) + ? Number(gameState.user_w) + : Number(gameState.user_b) + ) + await ctx.db('users').insert(escapeUser(enemy)).catch(debug) + } + + debug(enemy) + + await ctx.editMessageText( + topMessage( + movesState, + ctx.update.callback_query.from, + enemy + ) + statusMessage(status), + board(status.board.squares, !isWhiteTurn(movesState)) + ) + .catch(debug) } return ctx.answerCbQuery() From 7c4a4a024c1283579a1fac62d4e7820df0de5337 Mon Sep 17 00:00:00 2001 From: Piterden Date: Thu, 25 Apr 2019 10:56:20 +0300 Subject: [PATCH 33/33] Updated NPM --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 33b9b77..d6e7885 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "telegraf": "^3.29.0" }, "devDependencies": { + "babel-eslint": "^10.0.1", "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", + "eslint-plugin-import": "^2.17.2", "eslint-plugin-node": "^8.0.1", "eslint-plugin-promise": "^4.1.1", "eslint-plugin-standard": "^4.0.0",