import path from 'path' import Game from './game.js' import emoji from 'node-emoji' import logTimestamp from 'log-timestamp' import commander from 'bot-commander' import showdown from 'showdown' import uniqueRandomArray from 'unique-random-array' import words from './assets/words.js' import allWords from './assets/all_words.js' import { fileURLToPath } from 'url' import { MatrixClient, MatrixAuth, SimpleFsStorageProvider, RustSdkCryptoStorageProvider, AutojoinRoomsMixin } from 'matrix-bot-sdk' const converter = new showdown.Converter() const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const datasPath = process.env.MATRIX_DATA ?? (__dirname + '/datas') const storage = new SimpleFsStorageProvider(datasPath + '/bot.json') const cryptoProvider = new RustSdkCryptoStorageProvider(datasPath + '/crypto') const url = process.env.MATRIX_URL const username = process.env.MATRIX_USERNAME const password = process.env.MATRIX_PASSWORD const auth = new MatrixAuth(url) const authClient = await auth.passwordLogin(username, password) const token = authClient.accessToken const client = new MatrixClient(url, token, storage, cryptoProvider) const games = [] const randomSeeds = uniqueRandomArray(words.seeds); const sendMessage = async (room, text) => { client.sendMessage(room, { 'msgtype': 'm.text', 'format': 'org.matrix.custom.html', 'body': text, 'formatted_body': converter.makeHtml(text), }); } const renderMatrix = (game, options) => { const chars = { 0: ':large_blue_square:', 1: ':large_blue_square:', 2: ':large_yellow_circle:', 3: ':large_red_square:', } options = options ?? {} options.onlySigns = options.onlySigns ?? false options.hideNotPlayedLines = options.hideNotPlayedLines ?? false let render = '' for (let row of game.matrix()) { const letters = (row.word ?? '.'.repeat(game.expectedWord.length)).split('') if (options.hideNotPlayedLines && !row.word) { continue } let signs = [] for (let index in row.result) { signs.push(chars[row.result[index]]) } const elements = options.onlySigns ? {signs} : {letters, signs} for (let key in elements) { render += '' for (let item of elements[key]) { render += `` } render += '' } } render + '
${item}
' const markdown = emoji.emojify(render) return markdown } const startGame = async (meta, seed) => { let data if (seed) { const index = words.seeds.indexOf(seed) if (index === -1) { return await sendMessage(meta.room, 'Désolé mais cette seed n\'existe pas.') } data = words.datas[index] } else { seed = randomSeeds() data = words.datas[words.seeds.indexOf(seed)] } games[meta.room] = new Game(data, allWords) const firstLetter = data.substr(0, 1) const size = data.length await sendMessage(meta.room, `Le mot commence par la lettre \`${firstLetter}\` et contient ${size} lettres. Vous pouvez partager cette grille avec sa seed : \`${seed}\`.`) await sendMessage(meta.room, renderMatrix(games[meta.room])) console.log(`${meta.sender} on room ${meta.room} starts the game (${data})`) } commander .command('help') .description('`help` : affiche l\'aide') .action(async (meta) => { let message = [] for (let command of commander.commands) { message.push('* ' + command._description) } const text = message.join("\n") sendMessage(meta.room, text); }) commander .command('start [seed]') .description('`start` ou `start seed` : démarrer une partie avec éventuellement la seed d\'une grille') .action(async (meta, seed) => { if (games.hasOwnProperty(meta.room)) { const currentGame = games[meta.room] if (!currentGame.isFinish()) { for (let item of [ 'Il y a une partie en cours !', 'Le mot commence par la lettre `' + currentGame.expectedWord.substr(0, 1) + '`', renderMatrix(currentGame), ]) { await sendMessage(meta.room, item) } return } } startGame(meta, seed) }) commander .command('restart [seed]') .description('`restart` ou `restart seed` : démarrer une nouvelle partie avec éventuellement la seed d\'une grille') .action(async (meta, seed) => { startGame(meta, seed) }) commander .command('test [word]') .description('`test mot` : tester le mot `mot`') .action(async (meta, word) => { if (!games.hasOwnProperty(meta.room)) { return await sendMessage( meta.room, 'Il n\'y a pas de partie en cours. Taper `start` pour en lancer une.' ) } const currentGame = games[meta.room] if (currentGame.isFinish()) { return await sendMessage( meta.room, 'La partie est terminée. Taper `restart` pour en lancer une nouvelle.' ) } if (!currentGame.tryWord(word)) { return await sendMessage( meta.room, 'Le mot de ne fait pas la bonne longueur ou n\'apparait pas dans le dictionnaire.' ) } if (currentGame.isWon()) { await sendMessage(meta.room, 'Bravo ! Tu as découvert le mot !') return await sendMessage(meta.room, renderMatrix(currentGame, { onlySigns: true, hideNotPlayedLines: true, })) } if (currentGame.isFinish()) { const expectedWord = currentGame.expectedWord await sendMessage( meta.room, `Tes propositions sont épuisées… Le mot a deviner était \`${expectedWord}\`` ) return await sendMessage(meta.room, renderMatrix(currentGame, { onlySigns: true, hideNotPlayedLines: true, })) } await sendMessage(meta.room, renderMatrix(currentGame)) }) AutojoinRoomsMixin.setupOnClient(client) client .start() .then(() => console.log('Client started')) .catch(() => { console.error('An error occurred') }) client.on('room.message', (roomId, event) => { if (!event['content']) { return } const meta = { sender: event['sender'], body: event['content']['body'], room: roomId, } commander.parse(meta.body.toLowerCase(), meta) })