Merge pull request #24 from cassoule/poker

Poker
This commit is contained in:
Milo Gourvest
2025-06-07 18:05:39 +02:00
committed by GitHub
8 changed files with 330 additions and 78 deletions

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="EditorConfigDeprecatedDescriptor" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

8
.idea/laravel-idea.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="InertiaPackage">
<option name="directoryPaths">
<list />
</option>
</component>
</project>

15
.idea/php.xml generated
View File

@@ -10,9 +10,24 @@
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="ceb1e1c5-a5b2-49ae-acba-ec4afdb2ab09" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpStan">
<PhpStan_settings>
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="ceb1e1c5-a5b2-49ae-acba-ec4afdb2ab09" timeout="60000" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="Psalm">
<Psalm_settings>
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="ceb1e1c5-a5b2-49ae-acba-ec4afdb2ab09" timeout="60000" />
</Psalm_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>

View File

@@ -1,22 +1,7 @@
import 'dotenv/config';
import { getRPSChoices, getTimesChoices } from './game.js';
import { getTimesChoices } from './game.js';
import { capitalize, InstallGlobalCommands } from './utils.js';
// Get the game choices from game.js
function createCommandChoices() {
const choices = getRPSChoices();
const commandChoices = [];
for (let choice of choices) {
commandChoices.push({
name: capitalize(choice),
value: choice.toLowerCase(),
});
}
return commandChoices;
}
function createTimesChoices() {
const choices = getTimesChoices();
const commandChoices = [];
@@ -40,24 +25,6 @@ const TEST_COMMAND = {
contexts: [0, 1, 2],
};
// Command containing options
const CHALLENGE_COMMAND = {
name: 'challenge',
description: 'Challenge to a match of rock paper scissors',
options: [
{
type: 3,
name: 'object',
description: 'Pick your object',
required: true,
choices: createCommandChoices(),
},
],
type: 1,
integration_types: [0, 1],
contexts: [0, 2],
};
// Timeout vote command
const TIMEOUT_COMMAND = {
name: 'timeout',

20
game.js
View File

@@ -1,4 +1,6 @@
import { capitalize } from './utils.js';
import pkg from 'pokersolver';
const { Hand } = pkg;
import {updateUserCoins, getUser, insertLog, insertGame, getUserElo, insertElos, updateElo} from './init_database.js'
@@ -217,4 +219,22 @@ export async function eloHandler(p1, p2, p1score, p2score, type) {
type: type,
timestamp: Date.now(),
})
}
export function pokerTest() {
console.log('pokerTest')
let hand1 = Hand.solve(['Ad', 'As', 'Jc', 'Th', '2d', '3c', 'Kd'], 'standard', false);
//let hand2 = Hand.solve(['Ad', 'As', 'Jc', 'Th', '2d', '3c', 'Kd'], 'standard', false);
let hand2 = Hand.solve(['Ad', 'As', 'Jc', 'Th', '2d', 'Qs', 'Qd'], 'standard', false);
/*console.log(hand1.name)
console.log(hand2.name)
console.log(hand1.descr)
console.log(hand2.descr)*/
console.log(hand1.toString())
console.log(hand2.toString())
let winner = Hand.winners([hand1, hand2]); // hand2
console.log(winner)
console.log(winner.includes(hand1));
console.log(winner.includes(hand2));
}

275
index.js
View File

@@ -18,7 +18,7 @@ import {
getAPOUsers,
postAPOBuy
} from './utils.js';
import {channelPointsHandler, eloHandler, slowmodesHandler} from './game.js';
import {channelPointsHandler, eloHandler, pokerTest, slowmodesHandler} from './game.js';
import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
import cron from 'node-cron';
import Database from "better-sqlite3";
@@ -46,6 +46,8 @@ import {
} from './init_database.js';
import { getValorantSkins, getSkinTiers } from './valo.js';
import {sleep} from "openai/core";
import { v4 as uuidv4 } from 'uuid';
import { uniqueNamesGenerator, adjectives, languages, animals } from 'unique-names-generator';
// Create an express app
const app = express();
@@ -333,8 +335,6 @@ client.on('messageCreate', async (message) => {
if (message.content.toLowerCase().startsWith(`<@${process.env.APP_ID}>`) || message.mentions.repliedUser?.id === process.env.APP_ID) {
let startTime = Date.now()
console.log('-------------------------------')
console.log('Request received : ' + startTime)
let akhyAuthor = await getUser.get(message.author.id)
const now = Date.now();
@@ -404,12 +404,11 @@ client.on('messageCreate', async (message) => {
// Fetch last messages from the channel
const fetched = await message.channel.messages.fetch({ limit: 100 });
const messagesArray = Array.from(fetched.values()).reverse(); // oldest to newest
console.log('after Discord fetch : ' + startTime + ', ' + (Date.now() - startTime))
const requestMessage = message.content.replace(`<@${process.env.APP_ID}>`, '')
// Map to OpenAI/Gemini format
console.log(process.env.MODEL)
console.log('AI fetch', process.env.MODEL)
const allAkhys = await getAllUsers.all()
let allAkhysText = ''
allAkhys.forEach(akhy => {
@@ -494,8 +493,6 @@ client.on('messageCreate', async (message) => {
// await gork(formatted); IA en marche
const reply = await gork(formatted);
console.log('after AI fetch : ' + startTime + ', ' + (Date.now() - startTime))
// Send response to the channel
await message.reply(reply);
@@ -528,7 +525,8 @@ client.on('messageCreate', async (message) => {
console.log(activePolls)
}
else if (message.author.id === process.env.DEV_ID) {
if (message.content === 'flopo:add-coins-to-users') {
const prefix = process.env.DEV_SITE === 'true' ? 'test' : 'flopo'
if (message.content === prefix + ':add-coins-to-users') {
console.log(message.author.id)
try {
const stmtUpdateUsers = flopoDB.prepare(`
@@ -540,15 +538,15 @@ client.on('messageCreate', async (message) => {
console.log(e)
}
}
else if (message.content === 'flopo:users') {
else if (message.content === prefix + ':users') {
const allAkhys = getAllUsers.all()
console.log(allAkhys)
}
else if (message.content === 'flopo:cancel') {
else if (message.content === prefix + ':cancel') {
await message.delete()
}
else if (message.content.startsWith('flopo:reset-user-coins')) {
const userId = message.content.replace('flopo:reset-user-coins ', '')
else if (message.content.startsWith(prefix + ':reset-user-coins')) {
const userId = message.content.replace(prefix + ':reset-user-coins ', '')
const authorDB = getUser.get(userId)
if (authorDB) {
updateUserCoins.run({
@@ -560,8 +558,8 @@ client.on('messageCreate', async (message) => {
console.log('invalid user')
}
}
else if (message.content.startsWith('flopo:send-message')) {
const msg = message.content.replace('flopo:send-message ', '')
else if (message.content.startsWith(prefix + ':send-message')) {
const msg = message.content.replace(prefix + ':send-message ', '')
await fetch(process.env.BASE_URL + '/send-message', {
method: 'POST',
headers: {
@@ -573,8 +571,9 @@ client.on('messageCreate', async (message) => {
})
});
}
else if (message.content.startsWith('flopo:sql')) {
let sqlCommand = message.content.replace('flopo:sql ', '')
else if (message.content.startsWith(prefix + ':sql')) {
let sqlCommand = message.content.replace(prefix + ':sql ', '')
console.log(sqlCommand)
try {
if (sqlCommand.startsWith('SELECT')) {
const stmt = flopoDB.prepare(`${sqlCommand}`).all();
@@ -587,6 +586,9 @@ client.on('messageCreate', async (message) => {
console.log(e)
}
}
else if (message.content.startsWith(prefix + ':poker')) {
io.emit('message', message.content);
}
}
}
});
@@ -617,7 +619,7 @@ client.once('ready', async () => {
for (const id in activeSearchs) {
const search = activeSearchs[id];
if (Date.now() >= search.timestamp + FIVE_MINUTES) {
console.log(`Removing expired searchs : ${id}`);
console.log(`Removing expired search : ${id}`);
delete activeSearchs[id];
}
}
@@ -727,8 +729,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
*/
if (type === InteractionType.APPLICATION_COMMAND) {
const { name } = data;
console.log(name)
// 'timeout' command
if (name === 'timeout') {
@@ -1010,8 +1010,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
timestamp: Date.now(),
};
console.log(activeInventories[id].reqBodyId);
if (invSkins.length === 0) {
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
@@ -1153,9 +1151,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
const selectedLevel = randomSkin.levels[randomLevel - 1]
const selectedChroma = randomSkin.chromas[randomChroma - 1]
// console.log(randomSkin.chromas)
// console.log(randomIndex)
// Set timeout for the reveal
setTimeout(async () => {
// Prepare the final embed
@@ -1191,7 +1186,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
res = randomSkin.displayIcon
}
if (res) return res;
console.log('default')
return randomSkin.displayIcon
};
const chromaName = () => {
@@ -1571,7 +1565,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
// Record the vote
poll.voters.push(voterId);
console.log(poll)
if (isVotingFor) {
poll.for++;
} else {
@@ -2002,7 +1995,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
}
const upgradePrice = process.env.VALO_UPGRADE_PRICE ?? invSkins[activeInventories[invId].page].maxPrice/10
console.log(`upgrade price : ${upgradePrice}`)
const buyResponse = await postAPOBuy(req.body.member.user.id, upgradePrice)
if (buyResponse.status === 500 || buyResponse.ok === false) {
@@ -2144,7 +2136,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
res = trueSkin.displayIcon
}
if (res) return res;
console.log('default')
return trueSkin.displayIcon
};
const chromaName = () => {
@@ -2894,7 +2885,6 @@ app.post('/timeout/vote', async (req, res) => {
const guild = await client.guilds.fetch(process.env.GUILD_ID)
const commandMember = await guild.members.fetch(commandUserId);
console.log(commandMember.roles.cache.map(role => role.id))
// Check if the voter has the required voting role
const voterRoles = commandMember.roles.cache.map(role => role.id) || [];
if (!voterRoles.includes(process.env.VOTING_ROLE_ID)) {
@@ -2908,7 +2898,6 @@ app.post('/timeout/vote', async (req, res) => {
// Record the vote
poll.voters.push(voterId);
console.log(poll)
if (isVotingFor) {
poll.for++;
} else {
@@ -3419,6 +3408,75 @@ app.post('/buy-coins', (req, res) => {
res.status(200).json({ message : `+${coins}` });
});
const pokerRooms = {}
app.post('/create-poker-room', async (req, res) => {
const { creatorId } = req.body
const id = uuidv4()
const t12names = [
'cassoule',
'passoule',
'kiwiko',
'piwiko',
'wata',
'pata',
'apologize',
'apologay',
'daspoon',
'esteban',
'edorima',
'momozhok',
'popozhok',
'dodozhok',
'flopozhok',
'thomas',
'poma'
]
const name = uniqueNamesGenerator({ dictionaries: [adjectives, t12names], separator: ' ', style: 'capital' });
const creator = await client.users.fetch(creatorId)
if (!creator) {
return res.status(404).send({message: 'Utilisateur introuvable'})
}
if (Object.values(pokerRooms).find(room => room.host_id === creatorId)) {
return res.status(403).send({message: 'Tu ne peux créer qu\'une seule table à la fois'})
}
pokerRooms[id] = {
id: id,
host_id: creatorId,
host_name: creator.globalName,
name: name,
created_at: Date.now(),
last_move_at: Date.now(),
players: {}
}
io.emit('new-poker-room')
return res.status(200).send({ roomId: id })
});
app.get('/poker-rooms', (req, res) => {
return res.status(200).send({ rooms: pokerRooms })
})
app.get('/poker-rooms/:id', (req, res) => {
return res.status(200).send({ room: pokerRooms[req.params.id] })
})
app.post('/poker-room/join', async (req, res) => {
const { userId, roomId } = req.body
const user = await client.users.fetch(userId)
try {
pokerRooms[roomId].players[userId] = user
} catch (e) {
//
}
io.emit('player-joined')
});
import http from 'http';
import { Server } from 'socket.io';
const server = http.createServer(app);
@@ -3434,7 +3492,6 @@ let queue = []
let playingArray = []
io.on('connection', (socket) => {
console.log(`socket connection at ${new Date().toLocaleString()}`);
socket.on('user-connected', async (user) => {
const username = getUser.get(user)
@@ -3460,25 +3517,77 @@ io.on('connection', (socket) => {
})
socket.on('tictactoequeue', async (e) => {
console.log(`tictactoequeue: ${e.playerId}`);
console.log(`${e.playerId} in tic tac toe queue`);
let msgId;
if (!queue.find(obj => obj === e.playerId)) {
queue.push(e.playerId)
if (queue.length === 1) {
try {
const guild = await client.guilds.fetch(process.env.GUILD_ID);
const generalChannel = guild.channels.cache.find(
ch => ch.name === 'général' || ch.name === 'general'
);
const user = await client.users.fetch(e.playerId)
const embed = new EmbedBuilder()
.setTitle(`Tic Tac Toe`)
.setDescription(`**${user.username}** est dans la file d'attente`)
.setColor('#5865f2')
.setTimestamp(new Date());
const row = new ActionRowBuilder()
.addComponents(
new ButtonBuilder()
.setLabel(`Jouer contre ${user.username}`)
.setURL(`${process.env.DEV_SITE === 'true' ? process.env.FLAPI_URL_DEV : process.env.FLAPI_URL}/tic-tac-toe`)
.setStyle(ButtonStyle.Link)
)
await generalChannel.send({ embeds: [embed], components: [row] });
} catch (e) {
console.log(e)
}
}
}
if (queue.length >= 2) {
let p1 = await client.users.fetch(queue[0])
let p2 = await client.users.fetch(queue[1])
let msgId
try {
const guild = await client.guilds.fetch(process.env.GUILD_ID);
const generalChannel = guild.channels.cache.find(
ch => ch.name === 'général' || ch.name === 'general'
);
const embed = new EmbedBuilder()
.setTitle(`Tic Tac Toe`)
.setDescription(`### **❌ ${p1.globalName}** vs **${p2.globalName} ⭕**\n` +
`🟦🟦🟦\n` +
`🟦🟦🟦\n` +
`🟦🟦🟦\n`)
.setColor('#5865f2')
.setTimestamp(new Date());
const msg = await generalChannel.send({ embeds: [embed] });
msgId = msg.id
} catch (e) {
console.log(e)
}
let p1obj = {
id: queue[0],
name: p1.username,
name: p1.globalName,
val: 'X',
move: "",
}
let p2obj = {
id: queue[1],
name: p2.username,
name: p2.globalName,
val: 'O',
move: "",
}
@@ -3490,6 +3599,7 @@ io.on('connection', (socket) => {
xs: [],
os: [],
lastmove: Date.now(),
msgId: msgId,
}
playingArray.push(lobby)
@@ -3500,15 +3610,16 @@ io.on('connection', (socket) => {
let names = [];
for (const n of queue) {
let name = await client.users.fetch(n)
names.push(name?.username)
names.push(name?.globalName)
}
io.emit('tictactoequeue', { allPlayers: playingArray, queue: names })
})
socket.on('tictactoeplaying', (e) => {
socket.on('tictactoeplaying', async (e) => {
let lobbyToChange;
if (e.value === 'X') {
let lobbyToChange = playingArray.find(obj => obj.p1.id === e.playerId)
lobbyToChange = playingArray.find(obj => obj.p1.id === e.playerId)
lobbyToChange.p2.move = ''
lobbyToChange.p1.move = e.boxId
@@ -3517,7 +3628,7 @@ io.on('connection', (socket) => {
lobbyToChange.lastmove = Date.now()
}
else if (e.value === 'O') {
let lobbyToChange = playingArray.find(obj => obj.p2.id === e.playerId)
lobbyToChange = playingArray.find(obj => obj.p2.id === e.playerId)
lobbyToChange.p1.move = ''
lobbyToChange.p2.move = e.boxId
@@ -3526,19 +3637,107 @@ io.on('connection', (socket) => {
lobbyToChange.lastmove = Date.now()
}
let gridText = ''
for (let i = 1; i <= 9; i++) {
if (lobbyToChange.os.includes(i)) {
gridText += '⭕'
} else if (lobbyToChange.xs.includes(i)) {
gridText += '❌'
} else {
gridText += '🟦'
}
if (i%3 === 0) {
gridText += '\n'
}
}
try {
const guild = await client.guilds.fetch(process.env.GUILD_ID);
const generalChannel = await guild.channels.cache.find(
ch => ch.name === 'général' || ch.name === 'general'
);
const message = await generalChannel.messages.fetch(lobbyToChange.msgId)
const embed = new EmbedBuilder()
.setTitle(`Tic Tac Toe`)
.setDescription(`### **❌ ${lobbyToChange.p1.name}** vs **${lobbyToChange.p2.name} ⭕**\n` + gridText)
.setColor('#5865f2')
.setTimestamp(new Date());
await message.edit({ embeds: [embed] });
} catch (e) {
console.log(e)
}
io.emit('tictactoeplaying', { allPlayers: playingArray })
})
socket.on('tictactoegameOver', async (e) => {
const winner = e.winner
const game = playingArray.find(obj => obj.p1.id === e.playerId)
if (game) {
let gridText = ''
for (let i = 1; i <= 9; i++) {
if (game.os.includes(i)) {
gridText += '⭕'
} else if (game.xs.includes(i)) {
gridText += '❌'
} else {
gridText += '🟦'
}
if (i%3 === 0) {
gridText += '\n'
}
}
if (winner === null) {
await eloHandler(game.p1.id, game.p2.id, 0, 0, 'TICTACTOE')
try {
const guild = await client.guilds.fetch(process.env.GUILD_ID);
const generalChannel = await guild.channels.cache.find(
ch => ch.name === 'général' || ch.name === 'general'
);
const message = await generalChannel.messages.fetch(game.msgId)
const embed = new EmbedBuilder()
.setTitle(`Tic Tac Toe`)
.setDescription(`### **❌ ${game.p1.name}** vs **${game.p2.name} ⭕**\n` + gridText + `\n### Égalité`)
.setColor('#5865f2')
.setTimestamp(new Date());
await message.edit({ embeds: [embed] });
} catch (e) {
console.log(e)
}
} else {
await eloHandler(game.p1.id, game.p2.id, game.p1.id === winner ? 1 : 0, game.p2.id === winner ? 1 : 0, 'TICTACTOE')
try {
const guild = await client.guilds.fetch(process.env.GUILD_ID);
const generalChannel = await guild.channels.cache.find(
ch => ch.name === 'général' || ch.name === 'general'
);
const message = await generalChannel.messages.fetch(game.msgId)
const embed = new EmbedBuilder()
.setTitle(`Tic Tac Toe`)
.setDescription(`### **❌ ${game.p1.name}** vs **${game.p2.name} ⭕**\n` + gridText + `\n### Victoire de ${game.p1.id === winner ? game.p1.name : game.p2.name}`)
.setColor('#5865f2')
.setTimestamp(new Date());
await message.edit({ embeds: [embed] });
} catch (e) {
console.log(e)
}
}
}
playingArray = playingArray.filter(obj => obj.p1.id !== e.playerId)
})
});

44
package-lock.json generated
View File

@@ -18,7 +18,10 @@
"express": "^4.18.2",
"node-cron": "^3.0.3",
"openai": "^4.94.0",
"socket.io": "^4.8.1"
"pokersolver": "^2.1.4",
"socket.io": "^4.8.1",
"unique-names-generator": "^4.7.1",
"uuid": "^11.1.0"
},
"devDependencies": {
"nodemon": "^3.0.0"
@@ -1727,6 +1730,15 @@
"node": ">=6.0.0"
}
},
"node_modules/node-cron/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
@@ -1945,6 +1957,15 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pokersolver": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/pokersolver/-/pokersolver-2.1.4.tgz",
"integrity": "sha512-vmgZS+K8H8r1RePQykFM5YyvlKo1v3xVec8FMBjg9N6mR2Tj/n/X415w+lG67FWbrk71D/CADmKFinDgaQlAsw==",
"engines": [
"node >= 4.0.0"
],
"license": "MIT"
},
"node_modules/prebuild-install": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz",
@@ -2606,6 +2627,15 @@
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
"node_modules/unique-names-generator": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz",
"integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -2631,12 +2661,16 @@
}
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
"uuid": "dist/esm/bin/uuid"
}
},
"node_modules/vary": {

View File

@@ -25,7 +25,10 @@
"express": "^4.18.2",
"node-cron": "^3.0.3",
"openai": "^4.94.0",
"socket.io": "^4.8.1"
"pokersolver": "^2.1.4",
"socket.io": "^4.8.1",
"unique-names-generator": "^4.7.1",
"uuid": "^11.1.0"
},
"devDependencies": {
"nodemon": "^3.0.0"