mirror of
https://github.com/cassoule/flopobot_v2.git
synced 2026-03-18 21:40:27 +01:00
Merge pull request #4 from cassoule/message-history
valorant + inventory
This commit is contained in:
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="flopobot" uuid="e5d4b6d8-8572-4545-b4a6-246b3cba87a8">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:C:\Users\milog\coding\bot_discord\flopobot_v2\flopobot.db</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
7
.idea/sqldialects.xml
generated
Normal file
7
.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/init_database.js" dialect="SQLite" />
|
||||
<file url="PROJECT" dialect="SQLite" />
|
||||
</component>
|
||||
</project>
|
||||
28
commands.js
28
commands.js
@@ -82,6 +82,32 @@ const TIMEOUT_COMMAND = {
|
||||
contexts: [0, 2],
|
||||
}
|
||||
|
||||
const ALL_COMMANDS = [/*TEST_COMMAND, CHALLENGE_COMMAND, */TIMEOUT_COMMAND];
|
||||
// Valorant
|
||||
const VALORANT_COMMAND = {
|
||||
name: 'valorant',
|
||||
description: 'Ouvrir une caisse valorant',
|
||||
type: 1,
|
||||
integration_types: [0, 1],
|
||||
contexts: [0, 2],
|
||||
}
|
||||
|
||||
// Own inventory command
|
||||
const INVENTORY_COMMAND = {
|
||||
name: 'inventory',
|
||||
description: 'Voir inventaire',
|
||||
options: [
|
||||
{
|
||||
type: 6,
|
||||
name: 'akhy',
|
||||
description: 'Qui ?',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
type: 1,
|
||||
integration_types: [0, 1],
|
||||
contexts: [0, 2],
|
||||
}
|
||||
|
||||
const ALL_COMMANDS = [/*TEST_COMMAND, CHALLENGE_COMMAND, */TIMEOUT_COMMAND, INVENTORY_COMMAND, VALORANT_COMMAND];
|
||||
|
||||
InstallGlobalCommands(process.env.APP_ID, ALL_COMMANDS);
|
||||
|
||||
784
index.js
784
index.js
@@ -17,9 +17,27 @@ import {
|
||||
getRandomHydrateText
|
||||
} from './utils.js';
|
||||
import { getShuffledOptions, getResult } from './game.js';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import { Client, GatewayIntentBits, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
||||
import cron from 'node-cron';
|
||||
import { flopoDB, insertUser, insertManyUsers, updateUser, updateManyUsers, getUser, getAllUsers, stmt } from './init_database.js';
|
||||
import { flopoDB,
|
||||
insertUser,
|
||||
insertManyUsers,
|
||||
updateUser,
|
||||
updateManyUsers,
|
||||
getUser,
|
||||
getAllUsers,
|
||||
stmtUsers,
|
||||
stmtSkins,
|
||||
updateManySkins,
|
||||
insertSkin,
|
||||
updateSkin,
|
||||
insertManySkins,
|
||||
getAllSkins,
|
||||
getSkin,
|
||||
getAllAvailableSkins,
|
||||
getUserInventory
|
||||
} from './init_database.js';
|
||||
import { getValorantSkins, getSkinTiers } from './valo.js';
|
||||
|
||||
// Create an express app
|
||||
const app = express();
|
||||
@@ -28,6 +46,7 @@ const PORT = process.env.PORT || 25578;
|
||||
// To keep track of our active games
|
||||
const activeGames = {};
|
||||
const activePolls = {};
|
||||
const activeInventories = {};
|
||||
let todaysHydrateCron = ''
|
||||
const SPAM_INTERVAL = process.env.SPAM_INTERVAL
|
||||
|
||||
@@ -45,10 +64,11 @@ const requestTimestamps = new Map(); // userId => [timestamp1, timestamp2, ...]
|
||||
const MAX_REQUESTS_PER_INTERVAL = parseInt(process.env.MAX_REQUESTS || "5");
|
||||
|
||||
const akhysData= new Map()
|
||||
const skins = []
|
||||
|
||||
async function getAkhys() {
|
||||
try {
|
||||
stmt.run();
|
||||
stmtUsers.run();
|
||||
const guild = await client.guilds.fetch(process.env.GUILD_ID);
|
||||
const members = await guild.members.fetch(); // Fetch all members
|
||||
|
||||
@@ -79,6 +99,165 @@ async function getAkhys() {
|
||||
} catch (err) {
|
||||
console.error('Error while counting akhys:', err);
|
||||
}
|
||||
try {
|
||||
stmtSkins.run();
|
||||
|
||||
const fetchedSkins = await getValorantSkins()
|
||||
const fetchedTiers = await getSkinTiers()
|
||||
|
||||
fetchedSkins.forEach((skin) => {
|
||||
const chromas = []
|
||||
const levels = []
|
||||
skin.chromas.forEach((chroma) => {
|
||||
chromas.push({
|
||||
uuid: chroma.uuid,
|
||||
displayName: chroma.displayName,
|
||||
displayIcon: chroma.displayIcon,
|
||||
fullRender: chroma.fullRender,
|
||||
swatch: chroma.swatch,
|
||||
streamedVideo: chroma.streamedVideo,
|
||||
})
|
||||
})
|
||||
skin.levels.forEach((level) => {
|
||||
levels.push({
|
||||
uuid: level.uuid,
|
||||
displayName: level.displayName,
|
||||
displayIcon: level.displayIcon,
|
||||
streamedVideo: level.streamedVideo,
|
||||
})
|
||||
})
|
||||
skins.push({
|
||||
uuid: skin.uuid,
|
||||
displayName: skin.displayName,
|
||||
contentTierUuid: skin.contentTierUuid,
|
||||
displayIcon: skin.displayIcon,
|
||||
chromas: chromas,
|
||||
levels: levels,
|
||||
})
|
||||
})
|
||||
|
||||
let newSkinCount = 0;
|
||||
for (const skin of skins) {
|
||||
|
||||
try {
|
||||
if (skin.contentTierUuid !== null) {
|
||||
const tierRank = () => {
|
||||
const tier = fetchedTiers.filter((tier) => { return tier.uuid === skin.contentTierUuid})[0]
|
||||
const rank = tier ? tier['rank'] : null;
|
||||
return rank ? rank + 1 : 0;
|
||||
}
|
||||
const tierColor = () => {
|
||||
const tier = fetchedTiers.filter((tier) => { return tier.uuid === skin.contentTierUuid})[0]
|
||||
return tier ? tier['highlightColor']?.slice(0, 6) : 'F2F3F3'
|
||||
}
|
||||
const tierText = () => {
|
||||
const tier = fetchedTiers.filter((tier) => { return tier.uuid === skin.contentTierUuid})[0]
|
||||
const rank = tier ? tier['rank'] : null;
|
||||
let res;
|
||||
if (rank === null) return 'Pas de tier';
|
||||
switch(rank) {
|
||||
case 0:
|
||||
res = '**<:select:1362964319498670222> Select**'
|
||||
break
|
||||
case 1:
|
||||
res = '**<:deluxe:1362964308094488797> Deluxe**'
|
||||
break
|
||||
case 2:
|
||||
res = '**<:premium:1362964330349330703> Premium**'
|
||||
break
|
||||
case 3:
|
||||
res = '**<:exclusive:1362964427556651098> Exclusive**'
|
||||
break
|
||||
case 4:
|
||||
res = '**<:ultra:1362964339685986314> Ultra**'
|
||||
break
|
||||
default:
|
||||
return 'Pas de tier'
|
||||
}
|
||||
res += skin.displayName.includes('VCT') ? ' | Esports Edition' : ''
|
||||
return res
|
||||
}
|
||||
const basePrice = () => {
|
||||
let res;
|
||||
if (skin.displayName.toLowerCase().includes('classic')){
|
||||
res = 150;
|
||||
} else if (skin.displayName.toLowerCase().includes('shorty')) {
|
||||
res = 300;
|
||||
} else if (skin.displayName.toLowerCase().includes('frenzy')) {
|
||||
res = 450;
|
||||
} else if (skin.displayName.toLowerCase().includes('ghost')) {
|
||||
res = 500;
|
||||
} else if (skin.displayName.toLowerCase().includes('sheriff')) {
|
||||
res = 800;
|
||||
} else if (skin.displayName.toLowerCase().includes('stinger')) {
|
||||
res = 1100;
|
||||
} else if (skin.displayName.toLowerCase().includes('spectre')) {
|
||||
res = 1600;
|
||||
} else if (skin.displayName.toLowerCase().includes('bucky')) {
|
||||
res = 850;
|
||||
} else if (skin.displayName.toLowerCase().includes('judge')) {
|
||||
res = 1850;
|
||||
} else if (skin.displayName.toLowerCase().includes('bulldog')) {
|
||||
res = 2050;
|
||||
} else if (skin.displayName.toLowerCase().includes('guardian')) {
|
||||
res = 2250;
|
||||
} else if (skin.displayName.toLowerCase().includes('phantom')) {
|
||||
res = 2900;
|
||||
} else if (skin.displayName.toLowerCase().includes('vandal')) {
|
||||
res = 2900;
|
||||
} else if (skin.displayName.toLowerCase().includes('marshal')) {
|
||||
res = 950;
|
||||
} else if (skin.displayName.toLowerCase().includes('outlaw')) {
|
||||
res = 2400;
|
||||
} else if (skin.displayName.toLowerCase().includes('operator')) {
|
||||
res = 4700;
|
||||
} else if (skin.displayName.toLowerCase().includes('ares')) {
|
||||
res = 1600;
|
||||
} else if (skin.displayName.toLowerCase().includes('odin')) {
|
||||
res = 3200;
|
||||
} else {
|
||||
res = 6000;
|
||||
}
|
||||
|
||||
res *= (1 + (tierRank()))
|
||||
res *= skin.displayName.includes('VCT') ? 1.25 : 1;
|
||||
|
||||
return (res/111).toFixed(2);
|
||||
}
|
||||
await insertSkin.run(
|
||||
{
|
||||
uuid: skin.uuid,
|
||||
displayName: skin.displayName,
|
||||
contentTierUuid: skin.contentTierUuid,
|
||||
displayIcon: skin.displayIcon,
|
||||
user_id: null,
|
||||
tierRank: tierRank(),
|
||||
tierColor: tierColor(),
|
||||
tierText: tierText(),
|
||||
basePrice: basePrice(),
|
||||
currentLvl: null,
|
||||
currentChroma: null,
|
||||
currentPrice: null,
|
||||
});
|
||||
newSkinCount++;
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
console.log(`New skins : ${newSkinCount}`);
|
||||
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'
|
||||
);
|
||||
if (generalChannel && generalChannel.isTextBased() && newSkinCount > 0) {
|
||||
generalChannel.send(
|
||||
`🔫 ${newSkinCount} nouveau(x) skin(s)`
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error while fetching skins:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function getOnlineUsersWithRole(guild_id=process.env.GUILD_ID, role_id=process.env.VOTING_ROLE_ID) {
|
||||
@@ -100,9 +279,8 @@ client.login(process.env.BOT_TOKEN);
|
||||
client.on('messageCreate', async (message) => {
|
||||
// Ignore messages from bots to avoid feedback loops
|
||||
if (message.author.bot) return;
|
||||
if (message.guildId !== process.env.GUILD_ID) return;
|
||||
|
||||
|
||||
|
||||
if (message.content.toLowerCase().startsWith(`<@${process.env.APP_ID}>`) || message.mentions.repliedUser?.id === process.env.APP_ID) {
|
||||
let startTime = Date.now()
|
||||
console.log('-------------------------------')
|
||||
@@ -230,7 +408,8 @@ client.on('messageCreate', async (message) => {
|
||||
role: "developer",
|
||||
content: `Ton id est : ${process.env.APP_ID}, évite de l'utiliser et ne formatte pas tes messages avec ton propre id, si jamais tu utilises un id formatte le comme suit : <@ID>, en remplacant ID par l'id. Ton username et global_name sont : ${process.env.APP_NAME}`
|
||||
});
|
||||
} else if (process.env.MODEL === 'Mistral') {
|
||||
}
|
||||
else if (process.env.MODEL === 'Mistral') {
|
||||
// Map to Mistral format
|
||||
formatted.push({
|
||||
role: 'system',
|
||||
@@ -316,7 +495,7 @@ client.once('ready', async () => {
|
||||
todaysHydrateCron = `${randomMinute} ${randomHour} * * *`
|
||||
console.log(todaysHydrateCron)
|
||||
await getAkhys();
|
||||
console.log('Akhys ready')
|
||||
console.log('Ready')
|
||||
|
||||
// ─── 💀 Midnight Chaos Timer ──────────────────────
|
||||
cron.schedule(process.env.CRON_EXPR, async () => {
|
||||
@@ -404,55 +583,6 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
|
||||
const { name } = data;
|
||||
console.log(name)
|
||||
|
||||
// "test" command
|
||||
/*if (name === 'test') {
|
||||
// Send a message into the channel where command was triggered from
|
||||
return res.send({
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
// Fetches a random emoji to send from a helper function
|
||||
content: `hello world ${getRandomEmoji()}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// "challenge" command
|
||||
if (name === 'challenge') {
|
||||
// Interaction context
|
||||
const context = req.body.context;
|
||||
// User ID is in user field for (G)DMs, and member for servers
|
||||
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
|
||||
// User's object choice
|
||||
const objectName = req.body.data.options[0].value;
|
||||
|
||||
// Create active game using message ID as the game ID
|
||||
activeGames[id] = {
|
||||
id: userId,
|
||||
objectName,
|
||||
};
|
||||
|
||||
return res.send({
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
content: `Rock papers scissors challenge from <@${userId}>`,
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentTypes.ACTION_ROW,
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentTypes.BUTTON,
|
||||
// Append the game ID to use later on
|
||||
custom_id: `accept_button_${req.body.id}`,
|
||||
label: 'Accept',
|
||||
style: ButtonStyleTypes.PRIMARY,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}*/
|
||||
|
||||
// 'timeout' command
|
||||
if (name === 'timeout') {
|
||||
// Interaction context
|
||||
@@ -645,6 +775,342 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
|
||||
});
|
||||
}
|
||||
|
||||
if (name === 'inventory') {
|
||||
// Interaction context
|
||||
const context = req.body.context;
|
||||
// User ID is in user field for (G)DMs, and member for servers
|
||||
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
|
||||
// User's choices
|
||||
const akhy = req.body.data.options ? req.body.data.options[0].value : userId;
|
||||
|
||||
const guild = await client.guilds.fetch(req.body.guild_id);
|
||||
const completeAkhy = await guild.members.fetch(akhy);
|
||||
|
||||
const invSkins = getUserInventory.all({user_id: akhy});
|
||||
|
||||
const chromaText = (skin) => {
|
||||
let res = ""
|
||||
for (let i = 1; i <= skins.find((s) => s.uuid === skin.uuid).chromas.length; i++) {
|
||||
res += skin.currentChroma === i ? '💠 ' : '◾ '
|
||||
}
|
||||
return res
|
||||
}
|
||||
const chromaName = (skin) => {
|
||||
if (skin.currentChroma >= 2) {
|
||||
const name = skins.find((s) => s.uuid === skin.uuid).chromas[skin.currentChroma-1].displayName.replace(/[\r\n]+/g, '').replace(skin.displayName, '')
|
||||
const match = name.match(/variante\s+[1-4]\s+([^)]+)/)
|
||||
const result = match ? match[2] : null;
|
||||
if (match) {
|
||||
return match[1].trim()
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
if (skin.currentChroma === 1) {
|
||||
return 'Base'
|
||||
}
|
||||
return ''
|
||||
};
|
||||
let content = '';
|
||||
let totalPrice = 0;
|
||||
let fields = [];
|
||||
invSkins.forEach(skin => {
|
||||
content += `- ${skin.displayName} | ${skin.currentPrice.toFixed()}€ \n`;
|
||||
totalPrice += skin.currentPrice;
|
||||
fields.push({
|
||||
name: `${skin.displayName} | ${skin.currentPrice.toFixed(2)}€`,
|
||||
value: `${skin.tierText}\nChroma : ${chromaText(skin)} | ${chromaName(skin)}\nLvl : **${skin.currentLvl}**/${skins.find((s) => s.uuid === skin.uuid).levels.length}\n`,
|
||||
inline: false,
|
||||
})
|
||||
})
|
||||
|
||||
activeInventories[id] = {
|
||||
akhyId: akhy,
|
||||
page: 0,
|
||||
amount: invSkins.length,
|
||||
endpoint: `webhooks/${process.env.APP_ID}/${req.body.token}/messages/@original`,
|
||||
};
|
||||
|
||||
if (invSkins.length === 0) {
|
||||
return res.send({
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
embeds: [
|
||||
{
|
||||
title: `Inventaire de ${completeAkhy.user.username}`,
|
||||
description: "Aucun skin dans l'inventaire",
|
||||
color: 0xF2F3F3,
|
||||
footer: {text: `Total : ${totalPrice.toFixed(2)}€`},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
const trueSkin = skins.find((s) => s.uuid === invSkins[0].uuid);
|
||||
|
||||
const imageUrl = () => {
|
||||
let res;
|
||||
if (invSkins[0].currentLvl === trueSkin.levels.length) {
|
||||
if (invSkins[0].currentChroma === 1) {
|
||||
res = trueSkin.chromas[0].displayIcon
|
||||
|
||||
} else {
|
||||
res = trueSkin.chromas[invSkins[0].currentChroma-1].fullRender ?? trueSkin.chromas[invSkins[0].currentChroma-1].displayIcon
|
||||
}
|
||||
} else if (invSkins[0].currentLvl === 1) {
|
||||
res = trueSkin.levels[0].displayIcon ?? trueSkin.chromas[0].fullRender
|
||||
} else if (invSkins[0].currentLvl === 2 || invSkins[0].currentLvl === 3) {
|
||||
res = trueSkin.displayIcon
|
||||
}
|
||||
if (res) return res;
|
||||
return trueSkin.displayIcon
|
||||
};
|
||||
|
||||
return res.send({
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
embeds: [
|
||||
{
|
||||
title: `Inventaire de ${completeAkhy.user.username}`,
|
||||
description: `${invSkins?.length > 0 ? '' : "Aucun skin dans l'inventaire"}`,
|
||||
color: 0xF2F3F3,
|
||||
footer: {text: `${activeInventories[id].page+1}/${invSkins?.length} | Total : ${totalPrice.toFixed(2)}€`},
|
||||
fields: [fields[activeInventories[id].page]],
|
||||
image: {
|
||||
url: invSkins?.length > 0 ? imageUrl() : '',
|
||||
}
|
||||
},
|
||||
],
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentTypes.ACTION_ROW,
|
||||
components: [
|
||||
{
|
||||
type: MessageComponentTypes.BUTTON,
|
||||
custom_id: `prev_page_${req.body.id}`,
|
||||
label: '⏮️ Préc.',
|
||||
style: ButtonStyleTypes.SECONDARY,
|
||||
},
|
||||
{
|
||||
type: MessageComponentTypes.BUTTON,
|
||||
custom_id: `next_page_${req.body.id}`,
|
||||
label: 'Suiv. ⏭️',
|
||||
style: ButtonStyleTypes.SECONDARY,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (name === 'others_inventory') {
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (name === 'valorant') {
|
||||
// First, send the initial response immediately
|
||||
const initialEmbed = new EmbedBuilder()
|
||||
.setTitle(`\t`)
|
||||
.setImage('https://media.tenor.com/gIWab6ojBnYAAAAd/weapon-line-up-valorant.gif')
|
||||
.setColor(`#F2F3F3`);
|
||||
|
||||
// Send the initial response and store the reply object
|
||||
await res.send({
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: { embeds: [initialEmbed] }
|
||||
});
|
||||
|
||||
// Get a random skin
|
||||
const dbSkins = getAllAvailableSkins.all();
|
||||
const randomIndex = Math.floor(Math.random() * dbSkins.length);
|
||||
let randomSkin;
|
||||
|
||||
try {
|
||||
randomSkin = skins.find((skin) => skin.uuid === dbSkins[randomIndex].uuid);
|
||||
if (!randomSkin) throw new Error("Skin not found");
|
||||
} catch (e) {
|
||||
// Edit the original message if there's an error
|
||||
await DiscordRequest(
|
||||
`webhooks/${process.env.APP_ID}/${req.body.token}/messages/@original`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
content: "Oups, ya eu un ptit problème",
|
||||
embeds: []
|
||||
}
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate random level and chroma
|
||||
const randomLevel = Math.floor(Math.random() * randomSkin.levels.length + 1);
|
||||
const randomChroma = randomLevel === randomSkin.levels.length
|
||||
? Math.floor(Math.random() * randomSkin.chromas.length + 1)
|
||||
: 1;
|
||||
|
||||
const selectedLevel = randomSkin.levels[randomLevel - 1]
|
||||
const selectedChroma = randomSkin.chromas[randomChroma - 1]
|
||||
|
||||
//TODO : add currentLevel, currentChroma (null by default then set when opening) so I can calculate the real price in inventory
|
||||
|
||||
// console.log(randomSkin.chromas)
|
||||
// console.log(randomIndex)
|
||||
|
||||
// Set timeout for the reveal
|
||||
setTimeout(async () => {
|
||||
// Prepare the final embed
|
||||
const selectedLevel = randomSkin.levels[randomLevel - 1];
|
||||
const selectedChroma = randomSkin.chromas[randomChroma - 1];
|
||||
|
||||
// Helper functions (unchanged from your original code)
|
||||
const videoUrl = () => {
|
||||
let res;
|
||||
if (randomLevel === randomSkin.levels.length) {
|
||||
if (randomChroma === 1) {
|
||||
res = randomSkin.levels[randomSkin.levels.length - 1].streamedVideo ?? randomSkin.chromas[0].streamedVideo
|
||||
} else {
|
||||
res = randomSkin.chromas[randomChroma-1].streamedVideo
|
||||
}
|
||||
} else {
|
||||
res = randomSkin.levels[randomLevel-1].streamedVideo
|
||||
}
|
||||
return res;
|
||||
};
|
||||
const imageUrl = () => {
|
||||
let res;
|
||||
if (randomLevel === randomSkin.levels.length) {
|
||||
if (randomChroma === 1) {
|
||||
res = randomSkin.chromas[0].displayIcon
|
||||
|
||||
} else {
|
||||
res = randomSkin.chromas[randomChroma-1].fullRender ?? randomSkin.chromas[randomChroma-1].displayIcon
|
||||
}
|
||||
} else if (randomLevel === 1) {
|
||||
res = randomSkin.levels[0].displayIcon ?? randomSkin.chromas[0].fullRender
|
||||
} else if (randomLevel === 2 || randomLevel === 3) {
|
||||
res = randomSkin.displayIcon
|
||||
}
|
||||
if (res) return res;
|
||||
console.log('default')
|
||||
return randomSkin.displayIcon
|
||||
};
|
||||
const chromaName = () => {
|
||||
if (randomChroma >= 2) {
|
||||
const name = selectedChroma.displayName.replace(/[\r\n]+/g, '').replace(randomSkin.displayName, '')
|
||||
const match = name.match(/variante\s+[1-4]\s+([^)]+)/)
|
||||
const result = match ? match[2] : null;
|
||||
if (match) {
|
||||
return match[1].trim()
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
if (randomChroma === 1) {
|
||||
return 'Base'
|
||||
}
|
||||
return ''
|
||||
};
|
||||
const lvlText = () => {
|
||||
let res = ""
|
||||
if (randomLevel >= 1) {
|
||||
res += '1️⃣ '
|
||||
}
|
||||
if (randomLevel >= 2) {
|
||||
res += '2️⃣ '
|
||||
}
|
||||
if (randomLevel >= 3) {
|
||||
res += '3️⃣ '
|
||||
}
|
||||
if (randomLevel >= 4) {
|
||||
res += '4️⃣ '
|
||||
}
|
||||
if (randomLevel >= 5) {
|
||||
res += '5️⃣ '
|
||||
}
|
||||
for (let i = 0; i < randomSkin.levels.length - randomLevel; i++) {
|
||||
res += '◾ '
|
||||
}
|
||||
return res
|
||||
}
|
||||
const chromaText = () => {
|
||||
let res = ""
|
||||
for (let i = 1; i <= randomSkin.chromas.length; i++) {
|
||||
res += randomChroma === i ? '💠 ' : '◾ '
|
||||
}
|
||||
return res
|
||||
}
|
||||
const price = () => {
|
||||
let res = dbSkins[randomIndex].basePrice;
|
||||
|
||||
res *= (1 + (randomLevel / Math.max(randomSkin.levels.length, 2)))
|
||||
res *= (1 + (randomChroma / 4))
|
||||
|
||||
return res.toFixed(2);
|
||||
}
|
||||
|
||||
// Update the database
|
||||
try {
|
||||
await updateSkin.run({
|
||||
uuid: randomSkin.uuid,
|
||||
user_id: req.body.member.user.id,
|
||||
currentLvl: randomLevel,
|
||||
currentChroma: randomChroma,
|
||||
currentPrice: price()
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Database error', e);
|
||||
}
|
||||
|
||||
// Build the final embed
|
||||
const finalEmbed = new EmbedBuilder()
|
||||
.setTitle(`${randomSkin.displayName} | ${chromaName()}`)
|
||||
.setFields([
|
||||
{ name: '', value: `**Lvl** | ${lvlText()}`, inline: true },
|
||||
{ name: '', value: `**Chroma** | ${chromaText()}`, inline: true },
|
||||
{ name: '', value: `**Prix** | ${price()} <:vp:1362964205808128122>`, inline: true },
|
||||
])
|
||||
.setDescription(dbSkins[randomIndex].tierText)
|
||||
.setImage(imageUrl())
|
||||
.setFooter({ text: 'Ajouté à ton inventaire' })
|
||||
.setColor(`#${dbSkins[randomIndex].tierColor}`);
|
||||
|
||||
// Prepare components if video exists
|
||||
const video = videoUrl();
|
||||
const components = [];
|
||||
|
||||
if (video) {
|
||||
components.push(
|
||||
new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel('🎬 Aperçu vidéo')
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL(video)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Edit the original message
|
||||
try {
|
||||
await DiscordRequest(
|
||||
`webhooks/${process.env.APP_ID}/${req.body.token}/messages/@original`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
embeds: [finalEmbed],
|
||||
components: components
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error('Error editing message:', err);
|
||||
}
|
||||
}, 5500);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(`unknown command: ${name}`);
|
||||
return res.status(400).json({ error: 'unknown command' });
|
||||
}
|
||||
@@ -894,6 +1360,212 @@ app.post('/interactions', verifyKeyMiddleware(process.env.PUBLIC_KEY), async fun
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (componentId.startsWith('prev_page')) {
|
||||
let invId = componentId.replace('prev_page_', '');
|
||||
const context = req.body.context;
|
||||
// User ID is in user field for (G)DMs, and member for servers
|
||||
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
|
||||
|
||||
const guild = await client.guilds.fetch(req.body.guild_id);
|
||||
const completeAkhy = await guild.members.fetch(activeInventories[invId].akhyId);
|
||||
|
||||
const invSkins = getUserInventory.all({user_id: activeInventories[invId].akhyId});
|
||||
|
||||
const chromaText = (skin) => {
|
||||
let res = ""
|
||||
for (let i = 1; i <= skins.find((s) => s.uuid === skin.uuid).chromas.length; i++) {
|
||||
res += skin.currentChroma === i ? '💠 ' : '◾ '
|
||||
}
|
||||
return res
|
||||
}
|
||||
const chromaName = (skin) => {
|
||||
if (skin.currentChroma >= 2) {
|
||||
const name = skins.find((s) => s.uuid === skin.uuid).chromas[skin.currentChroma-1].displayName.replace(/[\r\n]+/g, '').replace(skin.displayName, '')
|
||||
const match = name.match(/variante\s+[1-4]\s+([^)]+)/)
|
||||
const result = match ? match[2] : null;
|
||||
if (match) {
|
||||
return match[1].trim()
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
if (skin.currentChroma === 1) {
|
||||
return 'Base'
|
||||
}
|
||||
return ''
|
||||
};
|
||||
let content = '';
|
||||
let totalPrice = 0;
|
||||
let fields = [];
|
||||
invSkins.forEach(skin => {
|
||||
content += `- ${skin.displayName} | ${skin.currentPrice.toFixed()}€ \n`;
|
||||
totalPrice += skin.currentPrice;
|
||||
fields.push({
|
||||
name: `${skin.displayName} | ${skin.currentPrice.toFixed(2)}€`,
|
||||
value: `${skin.tierText}\nChroma : ${chromaText(skin)} | ${chromaName(skin)}\nLvl : **${skin.currentLvl}**/${skins.find((s) => s.uuid === skin.uuid).levels.length}\n`,
|
||||
inline: false,
|
||||
})
|
||||
})
|
||||
|
||||
if (activeInventories[invId] && activeInventories[invId].akhyId === req.body.member.user.id) {
|
||||
if (activeInventories[invId].page === 0) {
|
||||
activeInventories[invId].page = activeInventories[invId].amount-1
|
||||
} else {
|
||||
activeInventories[invId].page--
|
||||
}
|
||||
} else {return }
|
||||
|
||||
const trueSkin = skins.find((s) => s.uuid === invSkins[activeInventories[invId].page].uuid);
|
||||
const imageUrl = () => {
|
||||
let res;
|
||||
if (invSkins[activeInventories[invId].page].currentLvl === trueSkin.levels.length) {
|
||||
if (invSkins[activeInventories[invId].page].currentChroma === 1) {
|
||||
res = trueSkin.chromas[0].displayIcon
|
||||
|
||||
} else {
|
||||
res = trueSkin.chromas[invSkins[activeInventories[invId].page].currentChroma-1].fullRender ?? trueSkin.chromas[invSkins[activeInventories[invId].page].currentChroma-1].displayIcon
|
||||
}
|
||||
} else if (invSkins[activeInventories[invId].page].currentLvl === 1) {
|
||||
res = trueSkin.levels[0].displayIcon ?? trueSkin.chromas[0].fullRender
|
||||
} else if (invSkins[activeInventories[invId].page].currentLvl === 2 || invSkins[activeInventories[invId].page].currentLvl === 3) {
|
||||
res = trueSkin.displayIcon
|
||||
}
|
||||
if (res) return res;
|
||||
return trueSkin.displayIcon
|
||||
};
|
||||
|
||||
try {
|
||||
await DiscordRequest(
|
||||
activeInventories[invId].endpoint,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
embeds: [
|
||||
{
|
||||
title: `Inventaire de ${completeAkhy.user.username}`,
|
||||
description: `${invSkins?.length > 0 ? '' : "Aucun skin dans l'inventaire"}`,
|
||||
color: 0xF2F3F3,
|
||||
footer: {text: `${activeInventories[invId].page+1}/${invSkins?.length} | Total : ${totalPrice.toFixed(2)}€`},
|
||||
fields: [fields[activeInventories[invId].page]],
|
||||
image: {
|
||||
url: invSkins?.length > 0 ? imageUrl() : '',
|
||||
}
|
||||
},
|
||||
],
|
||||
components: req.body.message.components,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('Pas trouvé : ', err)
|
||||
}
|
||||
return res.send({
|
||||
type: InteractionResponseType.DEFERRED_UPDATE_MESSAGE,
|
||||
});
|
||||
}
|
||||
else if (componentId.startsWith('next_page')) {
|
||||
let invId = componentId.replace('next_page_', '');
|
||||
const context = req.body.context;
|
||||
// User ID is in user field for (G)DMs, and member for servers
|
||||
const userId = context === 0 ? req.body.member.user.id : req.body.user.id;
|
||||
|
||||
const guild = await client.guilds.fetch(req.body.guild_id);
|
||||
const completeAkhy = await guild.members.fetch(activeInventories[invId].akhyId);
|
||||
|
||||
const invSkins = getUserInventory.all({user_id: activeInventories[invId].akhyId});
|
||||
|
||||
const chromaText = (skin) => {
|
||||
let res = ""
|
||||
for (let i = 1; i <= skins.find((s) => s.uuid === skin.uuid).chromas.length; i++) {
|
||||
res += skin.currentChroma === i ? '💠 ' : '◾ '
|
||||
}
|
||||
return res
|
||||
}
|
||||
const chromaName = (skin) => {
|
||||
if (skin.currentChroma >= 2) {
|
||||
const name = skins.find((s) => s.uuid === skin.uuid).chromas[skin.currentChroma-1].displayName.replace(/[\r\n]+/g, '').replace(skin.displayName, '')
|
||||
const match = name.match(/variante\s+[1-4]\s+([^)]+)/)
|
||||
const result = match ? match[2] : null;
|
||||
if (match) {
|
||||
return match[1].trim()
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
if (skin.currentChroma === 1) {
|
||||
return 'Base'
|
||||
}
|
||||
return ''
|
||||
};
|
||||
let content = '';
|
||||
let totalPrice = 0;
|
||||
let fields = [];
|
||||
invSkins.forEach(skin => {
|
||||
content += `- ${skin.displayName} | ${skin.currentPrice.toFixed()}€ \n`;
|
||||
totalPrice += skin.currentPrice;
|
||||
fields.push({
|
||||
name: `${skin.displayName} | ${skin.currentPrice.toFixed(2)}€`,
|
||||
value: `${skin.tierText}\nChroma : ${chromaText(skin)} | ${chromaName(skin)}\nLvl : **${skin.currentLvl}**/${skins.find((s) => s.uuid === skin.uuid).levels.length}\n`,
|
||||
inline: false,
|
||||
})
|
||||
})
|
||||
|
||||
if (activeInventories[invId] && activeInventories[invId].akhyId === req.body.member.user.id) {
|
||||
if (activeInventories[invId].page === activeInventories[invId].amount-1) {
|
||||
activeInventories[invId].page = 0
|
||||
} else {
|
||||
activeInventories[invId].page++
|
||||
}
|
||||
} else {return }
|
||||
|
||||
const trueSkin = skins.find((s) => s.uuid === invSkins[activeInventories[invId].page].uuid);
|
||||
const imageUrl = () => {
|
||||
let res;
|
||||
if (invSkins[activeInventories[invId].page].currentLvl === trueSkin.levels.length) {
|
||||
if (invSkins[activeInventories[invId].page].currentChroma === 1) {
|
||||
res = trueSkin.chromas[0].displayIcon
|
||||
|
||||
} else {
|
||||
res = trueSkin.chromas[invSkins[activeInventories[invId].page].currentChroma-1].fullRender ?? trueSkin.chromas[invSkins[activeInventories[invId].page].currentChroma-1].displayIcon
|
||||
}
|
||||
} else if (invSkins[activeInventories[invId].page].currentLvl === 1) {
|
||||
res = trueSkin.levels[0].displayIcon ?? trueSkin.chromas[0].fullRender
|
||||
} else if (invSkins[activeInventories[invId].page].currentLvl === 2 || invSkins[activeInventories[invId].page].currentLvl === 3) {
|
||||
res = trueSkin.displayIcon
|
||||
}
|
||||
if (res) return res;
|
||||
return trueSkin.displayIcon
|
||||
};
|
||||
|
||||
try {
|
||||
await DiscordRequest(
|
||||
activeInventories[invId].endpoint,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: {
|
||||
embeds: [
|
||||
{
|
||||
title: `Inventaire de ${completeAkhy.user.username}`,
|
||||
description: `${invSkins?.length > 0 ? '' : "Aucun skin dans l'inventaire"}`,
|
||||
color: 0xF2F3F3,
|
||||
footer: {text: `${activeInventories[invId].page+1}/${invSkins?.length} | Total : ${totalPrice.toFixed(2)}€`},
|
||||
fields: [fields[activeInventories[invId].page]],
|
||||
image: {
|
||||
url: invSkins?.length > 0 ? imageUrl() : '',
|
||||
}
|
||||
},
|
||||
],
|
||||
components: req.body.message.components,
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('Pas trouvé : ', err)
|
||||
}
|
||||
return res.send({
|
||||
type: InteractionResponseType.DEFERRED_UPDATE_MESSAGE,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import Database from "better-sqlite3";
|
||||
|
||||
export const flopoDB = new Database('flopobot.db');
|
||||
|
||||
export const stmt = flopoDB.prepare(`
|
||||
export const stmtUsers = flopoDB.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id TEXT PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
@@ -14,20 +14,50 @@ export const stmt = flopoDB.prepare(`
|
||||
totalRequests INTEGER DEFAULT 0
|
||||
)
|
||||
`);
|
||||
stmt.run();
|
||||
stmtUsers.run();
|
||||
export const stmtSkins = flopoDB.prepare(`
|
||||
CREATE TABLE IF NOT EXISTS skins (
|
||||
uuid TEXT PRIMARY KEY,
|
||||
displayName TEXT,
|
||||
contentTierUuid TEXT,
|
||||
displayIcon TEXT,
|
||||
user_id TEXT REFERENCES users,
|
||||
tierRank TEXT,
|
||||
tierColor TEXT,
|
||||
tierText TEXT,
|
||||
basePrice TEXT,
|
||||
currentLvl INTEGER DEFAULT NULL,
|
||||
currentChroma INTEGER DEFAULT NULL,
|
||||
currentPrice INTEGER DEFAULT NULL
|
||||
)
|
||||
`);
|
||||
stmtSkins.run()
|
||||
|
||||
export const insertUser = flopoDB.prepare('INSERT INTO users (id, username, globalName, warned, warns, allTimeWarns, totalRequests) VALUES (@id, @username, @globalName, @warned, @warns, @allTimeWarns, @totalRequests)');
|
||||
export const updateUser = flopoDB.prepare('UPDATE users SET warned = @warned, warns = @warns, allTimeWarns = @allTimeWarns, totalRequests = @totalRequests WHERE id = @id');
|
||||
export const getUser = flopoDB.prepare('SELECT * FROM users WHERE id = ?');
|
||||
export const getAllUsers = flopoDB.prepare('SELECT * FROM users');
|
||||
|
||||
export const insertSkin = flopoDB.prepare('INSERT INTO skins (uuid, displayName, contentTierUuid, displayIcon, user_id, tierRank, tierColor, tierText, basePrice, currentLvl, currentChroma, currentPrice) VALUES (@uuid, @displayName, @contentTierUuid, @displayIcon, @user_id, @tierRank, @tierColor, @tierText, @basePrice, @currentLvl, @currentChroma, @currentPrice)');
|
||||
export const updateSkin = flopoDB.prepare('UPDATE skins SET user_id = @user_id, currentLvl = @currentLvl, currentChroma = @currentChroma, currentPrice = @currentPrice WHERE uuid = @uuid');
|
||||
export const getSkin = flopoDB.prepare('SELECT * FROM skins WHERE uuid = ?');
|
||||
export const getAllSkins = flopoDB.prepare('SELECT * FROM skins');
|
||||
export const getAllAvailableSkins = flopoDB.prepare('SELECT * FROM skins WHERE user_id IS NULL');
|
||||
export const getUserInventory = flopoDB.prepare('SELECT * FROM skins WHERE user_id = @user_id');
|
||||
|
||||
export const insertManyUsers = flopoDB.transaction(async (users) => {
|
||||
for (const user of users) try { await insertUser.run(user) } catch (e) { console.log('users insert failed') };
|
||||
for (const user of users) try { await insertUser.run(user) } catch (e) { console.log('user insert failed') }
|
||||
});
|
||||
export const updateManyUsers = flopoDB.transaction(async (users) => {
|
||||
for (const user of users) try { await updateUser.run(user) } catch (e) { console.log('users update failed') };
|
||||
for (const user of users) try { await updateUser.run(user) } catch (e) { console.log('user update failed') }
|
||||
});
|
||||
|
||||
export const insertManySkins = flopoDB.transaction(async (skins) => {
|
||||
for (const skin of skins) try { await insertSkin.run(skin) } catch (e) { console.log('skin insert failed') }
|
||||
});
|
||||
export const updateManySkins = flopoDB.transaction(async (skins) => {
|
||||
for (const skin of skins) try { await updateSkin.run(skin) } catch (e) { console.log('skin insert failed') }
|
||||
});
|
||||
//const getManyUsers = flopoDB.transaction(())
|
||||
|
||||
|
||||
// insertManyUsers([
|
||||
|
||||
11
valo.js
Normal file
11
valo.js
Normal file
@@ -0,0 +1,11 @@
|
||||
export async function getValorantSkins(locale='fr-FR') {
|
||||
const response = await fetch(`https://valorant-api.com/v1/weapons/skins?language=${locale}`, { method: 'GET' });
|
||||
const data = await response.json();
|
||||
return data.data
|
||||
}
|
||||
|
||||
export async function getSkinTiers(locale='fr-FR') {
|
||||
const response = await fetch(`https://valorant-api.com/v1/contenttiers?language=${locale}`, { method: 'GET'});
|
||||
const data = await response.json();
|
||||
return data.data
|
||||
}
|
||||
Reference in New Issue
Block a user