Merge branch 'dev' of https://github.com/Piterden/chessbot into inline
This commit is contained in:
+2
-13
@@ -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:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
require('dotenv').load()
|
||||
require('dotenv').config()
|
||||
|
||||
const {
|
||||
DB_CLIENT,
|
||||
|
||||
+8
-8
@@ -15,22 +15,22 @@
|
||||
},
|
||||
"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",
|
||||
"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",
|
||||
"nodemon": "^1.18.10",
|
||||
"prettier": "^1.17.0",
|
||||
"nodemon": "^1.18.11",
|
||||
"sqlite3": "^4.0.6"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
|
||||
@@ -7,7 +7,9 @@ const startHandler = require('./start')
|
||||
const actionsHandler = require('./actions')
|
||||
const messageHandler = require('./message')
|
||||
const optionsHandler = require('./options')
|
||||
const inlineHandler = require('./inline')
|
||||
const inlineJoinHandler = require('./inlineJoin')
|
||||
const inlineMoveHandler = require('./inlineMove')
|
||||
const inlineQueryHandler = require('./inlineQuery')
|
||||
|
||||
module.exports = {
|
||||
newHandler,
|
||||
@@ -19,5 +21,7 @@ module.exports = {
|
||||
actionsHandler,
|
||||
messageHandler,
|
||||
optionsHandler,
|
||||
inlineHandler,
|
||||
inlineJoinHandler,
|
||||
inlineMoveHandler,
|
||||
inlineQueryHandler,
|
||||
}
|
||||
|
||||
@@ -1,70 +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: 'photo',
|
||||
thumb_url: 'https://crossword.live/img/w.png',
|
||||
photo_url: 'https://crossword.live/img/w.png',
|
||||
title: 'Play with white pieces',
|
||||
input_message_content: {
|
||||
message_text: `Black (top): ?
|
||||
White (bottom): ${user.first_name}`,
|
||||
},
|
||||
...board(status.board.squares, true, [{
|
||||
text: 'Join the game',
|
||||
callback_data: `join::w::${user.id}`,
|
||||
}]),
|
||||
},
|
||||
{
|
||||
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',
|
||||
input_message_content: {
|
||||
message_text: `White (top): ?
|
||||
Black (bottom): ${user.first_name}`,
|
||||
},
|
||||
...board(status.board.squares, false, [{
|
||||
text: 'Join the game',
|
||||
callback_data: `join::b::${user.id}`,
|
||||
}]),
|
||||
},
|
||||
], {
|
||||
is_personal: true,
|
||||
cache_time: 0,
|
||||
})
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
const chess = require('chess')
|
||||
|
||||
const { board } = require('@/keyboards')
|
||||
const { debug, escapeUser, unescapeUser } = require('@/helpers')
|
||||
|
||||
module.exports = () => [
|
||||
/^join::([wb])::(\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!')
|
||||
}
|
||||
|
||||
let enemy = await ctx.db('users').where('id', userId).first().catch(debug)
|
||||
|
||||
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)
|
||||
|
||||
ctx.session.gameId = gameId
|
||||
|
||||
const gameClient = chess.create({ PGN: true })
|
||||
const status = gameClient.getStatus()
|
||||
|
||||
await ctx.editMessageText(
|
||||
iAmWhite()
|
||||
? `Black (top): ${enemy.first_name}
|
||||
White (bottom): ${ctx.from.first_name}
|
||||
White's turn`
|
||||
: `Black (top): ${ctx.from.first_name}
|
||||
White (bottom): ${enemy.first_name}
|
||||
White's turn`,
|
||||
board(status.board.squares, true)
|
||||
)
|
||||
|
||||
return ctx.answerCbQuery('Now play!')
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,153 @@
|
||||
const chess = require('chess')
|
||||
|
||||
const { board } = require('@/keyboards')
|
||||
const { debug, escapeUser, unescapeUser } = require('@/helpers')
|
||||
|
||||
const isWhiteTurn = (moves) => !(moves.length % 2)
|
||||
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|' : ''}
|
||||
${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`
|
||||
|
||||
const isReady = (game) => !!(game.user_w && game.user_b)
|
||||
|
||||
module.exports = () => [
|
||||
/^([a-h])([1-8])$/,
|
||||
async (ctx) => {
|
||||
const gameState = await ctx.db('games')
|
||||
.where('id', ctx.session.gameId)
|
||||
.first()
|
||||
|
||||
debug(gameState)
|
||||
|
||||
if (!isReady(gameState)) {
|
||||
return ctx.answerCbQuery('Join the game to move pieces!')
|
||||
}
|
||||
|
||||
if (![Number(gameState.user_w), Number(gameState.user_b)]
|
||||
.includes(ctx.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.moves) {
|
||||
if (
|
||||
!square ||
|
||||
!square.piece ||
|
||||
(square.piece.side.name === 'black' && isWhiteTurn(movesState)) ||
|
||||
(square.piece.side.name === 'white' && !isWhiteTurn(movesState))
|
||||
) {
|
||||
return ctx.answerCbQuery()
|
||||
}
|
||||
|
||||
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.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: 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()
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,67 @@
|
||||
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', Number(ctx.from.id))
|
||||
.first()
|
||||
|
||||
if (user) {
|
||||
user = unescapeUser(user)
|
||||
|
||||
if (JSON.stringify(user) !== JSON.stringify(ctx.from)) {
|
||||
await ctx.db('users')
|
||||
.where('id', Number(user.id))
|
||||
.update(escapeUser(ctx.from))
|
||||
.catch(debug)
|
||||
}
|
||||
} else {
|
||||
const users = await ctx.db('users')
|
||||
.insert(escapeUser(ctx.from))
|
||||
.returning(Object.keys(ctx.from))
|
||||
.catch(debug)
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+19
-35
@@ -1,14 +1,20 @@
|
||||
require('dotenv').load()
|
||||
require('dotenv').config()
|
||||
require('module-alias/register')
|
||||
|
||||
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 {
|
||||
// newHandler,
|
||||
// joinHandler,
|
||||
// loadHandler,
|
||||
inlineJoinHandler,
|
||||
inlineMoveHandler,
|
||||
inlineQueryHandler,
|
||||
} = require('@/handlers')
|
||||
|
||||
const { session } = Telegraf
|
||||
const {
|
||||
@@ -21,40 +27,18 @@ 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.on(...inlineHandler())
|
||||
bot.start(...loadHandler())
|
||||
bot.action(...newHandler())
|
||||
bot.action(...joinHandler())
|
||||
// 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.action(...inlineMoveHandler())
|
||||
bot.on('inline_query', inlineQueryHandler())
|
||||
|
||||
bot.startPolling()
|
||||
|
||||
@@ -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
|
||||
@@ -29,10 +30,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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user