Skip to content

Commit

Permalink
WIP: default map card
Browse files Browse the repository at this point in the history
  • Loading branch information
web-flow committed Nov 25, 2024
1 parent d572508 commit 651db56
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 19 deletions.
103 changes: 98 additions & 5 deletions commands/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,85 @@ const { getTranslations, getTranslation } = require('../languages/setup')
const { getStats } = require('../functions/apiHandler')
const errorCard = require('../templates/errorCard')
const { getInteractionOption, getGameOption } = require('../functions/utility')
const { generateButtons } = require('../functions/customType')
const { TYPES } = require('../templates/customType')

const ALL = 'all'

const buildDefaultMapEmbed = async (interaction, playerId, game, mode, types = null) => {
types ??= [
TYPES.MATCHES,
TYPES.WINRATE,
{
...TYPES.ELO_GAIN,
style: Discord.ButtonStyle.Secondary
}
]

const matchNumber = types.find(t => t.name === TYPES.ELO_GAIN.name)?.style === Discord.ButtonStyle.Secondary ? 20 : 0
const {
playerDatas,
playerStats,
playerHistory
} = await getStats({
playerParam: {
param: playerId,
faceitId: true,
},
matchNumber,
checkElo: true,
game
})

const faceitLevel = playerDatas.games[game].skill_level
const faceitElo = playerDatas.games[game].faceit_elo
const size = 40
const filesAtt = []
const values = {
id: 'mapRadarChartSelector',
playerId,
userId: interaction.user.id,
game,
mode,
map: ALL,
types
}

const buttons = await Promise.all(types.map(t => generateButtons(interaction, values, t)))

const rankImageCanvas = await Graph.getRankImage(faceitLevel, faceitElo, size, game)
playerStats.segments.forEach(s => {
const eloGain = playerHistory
.filter(h => (h.i1 === maps[s.label] || h.i1 === s.label) && s.mode === mode && h.gameMode === s.mode)
.map(h => h.eloGain).filter(h => h).reduce((acc, h) => acc + h, 0)

s.stats['Elo Gain'] = eloGain
})
playerStats.segments.sort((a, b) => a.label.localeCompare(b.label))
const radarChartCanvas = Graph.getMapRadarChart(playerStats.segments, types.filter(t => t.style !== Discord.ButtonStyle.Secondary))
filesAtt.push(new Discord.AttachmentBuilder(radarChartCanvas, { name: 'radar.png' }))
filesAtt.push(new Discord.AttachmentBuilder(rankImageCanvas, { name: 'level.png' }))

const embed = new Discord.EmbedBuilder()
.setAuthor({ name: playerDatas.nickname, iconURL: playerDatas.avatar || null, url: `https://www.faceit.com/en/players/${playerDatas.nickname}` })
.setDescription(`[Steam](https://steamcommunity.com/profiles/${playerDatas.games[game].game_player_id}), [Faceit](https://www.faceit.com/en/players/${playerDatas.nickname})`)
.setColor(color.levels[game][faceitLevel].color)
.setThumbnail('attachment://level.png')
.setImage('attachment://radar.png')

const actionRow = new Discord.ActionRowBuilder()
.addComponents(buttons)

return {
embeds: [embed],
components: [actionRow],
files: filesAtt
}
}

const buildEmbed = async (interaction, playerId, map, mode, game) => {
if (!map) return
if (map === ALL) return buildDefaultMapEmbed(interaction, playerId, game, mode)

const {
playerDatas,
Expand Down Expand Up @@ -89,7 +165,7 @@ const buildEmbed = async (interaction, playerId, map, mode, game) => {
}
}

const sendCardWithInfo = async (interaction, playerParam, map = null, mode = null, game = null) => {
const sendCardWithInfo = async (interaction, playerParam, map = ALL, mode = null, game = null) => {
map ??= getInteractionOption(interaction, 'map')
game ??= getGameOption(interaction)
mode ??= '5v5'
Expand All @@ -108,17 +184,33 @@ const sendCardWithInfo = async (interaction, playerParam, map = null, mode = nul
if (!playerStats.segments.length) throw getTranslation('error.user.noMatches', interaction.locale, { playerName: playerDatas.nickname })

const playerId = playerDatas.player_id
const defaultValues = {
playerId,
userId: interaction.user.id,
game,
}

let content = getTranslation('strings.selectMapDescription', interaction.locale, { playerName: playerDatas.nickname })
let options = []
const totalMatches = playerStats.segments.reduce((acc, e) => acc + parseInt(e.stats.Matches), 0)

const defaultId = (await Interaction.create({
...defaultValues,
map: ALL,
mode: '5v5'
})).id
const defaultOption = new Discord.StringSelectMenuOptionBuilder()
.setLabel(getTranslation('strings.allMaps', interaction.locale))
.setDescription(getTranslation('strings.allMapsDescription', interaction.locale))
.setValue(defaultId)
.setDefault(true)
options.push(defaultOption)

const totalMatches = playerStats.segments.reduce((acc, e) => acc + parseInt(e.stats.Matches), 0)
await Promise.all(playerStats.segments.map(async (e) => {
e.label = game === 'cs2' ? maps[e.label] : e.label
const label = `${e.label} ${e.mode}`
const values = {
playerId,
userId: interaction.user.id,
game,
...defaultValues,
map: e.label,
mode: e.mode
}
Expand Down Expand Up @@ -158,6 +250,7 @@ const sendCardWithInfo = async (interaction, playerParam, map = null, mode = nul
const resp = await buildEmbed(interaction, playerId, map, mode, game)
embeds = resp.embeds
files = resp.files
if (resp.components) components.push(...resp.components)
content = ''
}

Expand Down
19 changes: 19 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,25 @@
"color": "#6B9B46"
}
},
"charts": {
"text": "#c9d1d9",
"grid": "#3c3c3c",
"background": "#2f3136",
"radarCategories": {
"Matches": {
"border": "rgb(76, 191, 192)",
"background": "rgba(76, 191, 192, 0.2)"
},
"Win Rate %": {
"border": "rgb(54, 162, 235)",
"background": "rgba(54, 162, 235, 0.2)"
},
"Elo Gain": {
"border": "rgb(153, 102, 255)",
"background": "rgba(153, 102, 255, 0.2)"
}
}
},
"error": "#770000"
},
"emojis": {
Expand Down
7 changes: 5 additions & 2 deletions functions/customType.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ const generateButtons = async (interaction, values, type, disabledType = null) =
userId: interaction.user.id
}))).id

return new ButtonBuilder()
const button = new ButtonBuilder()
.setCustomId(customId)
.setLabel(name)
.setEmoji(type.emoji)
.setStyle(type.style)
.setDisabled(type.name === disabledType?.name)

if (type.emoji) button.setEmoji(type.emoji)

return button
}

const updateButtons = (components, type, jsonData = null) => {
Expand Down
71 changes: 63 additions & 8 deletions functions/graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,16 @@ const getChart = (datasets, labels, datasetFunc, displayY1, game) => {
const canvas = Canvas.createCanvas(600, 400)
const ctx = canvas.getContext('2d')

const color = '#c9d1d9', gridColor = '#3c3c3c'
const yAxisBase = {
border: {
width: 1,
},
grid: {
color: gridColor,
color: color.charts.grid,
},
ticks: {
beginAtZero: false,
color: color,
color: color.charts.text,
}
}

Expand All @@ -60,17 +59,17 @@ const getChart = (datasets, labels, datasetFunc, displayY1, game) => {
},
x: {
grid: {
color: gridColor,
color: color.charts.grid,
},
ticks: {
color: color,
color: color.charts.text,
}
}
},
plugins: {
legend: {
labels: {
color: color,
color: color.charts.text,
borderWidth: 1,
}
}
Expand All @@ -81,7 +80,7 @@ const getChart = (datasets, labels, datasetFunc, displayY1, game) => {
const ctx = chart.canvas.getContext('2d')
ctx.save()
ctx.globalCompositeOperation = 'source-over'
ctx.fillStyle = '#2f3136'
ctx.fillStyle = color.charts.background
ctx.fillRect(0, 0, chart.width, chart.height)
ctx.restore()
}
Expand Down Expand Up @@ -223,6 +222,61 @@ const getGraph = (locale, playerName, type, matchHistory, maxMatch) => {
}
}

const getMapRadarChart = (segments, types) => {
const canvas = Canvas.createCanvas(600, 600)
const ctx = canvas.getContext('2d')
const datasetsKeys = types.map(e => e.name)
const datasets = datasetsKeys.map(key => {
return {
label: key,
data: segments.map(e => e.stats[key]),
backgroundColor: color.charts.radarCategories[key].background,
borderColor: color.charts.radarCategories[key].border,
borderWidth: 2
}
})

new Chart(ctx, {
type: 'radar',
data: {
labels: segments.map(e => e.label),
datasets: datasets
},
options: {
scales: {
r: {
grid: {
color: color.charts.grid,
},
ticks: {
color: color.charts.text,
backdropColor: color.charts.background,
},
backgroundColor: color.charts.background,
pointLabels: {
display: true,
font: {
size: 16
},
color: color.charts.text,
padding: 10
}
}
},
plugins: {
legend: {
labels: {
color: color.charts.text,
borderWidth: 2,
}
}
}
}
})

return canvas.toBuffer()
}

module.exports = {
generateChart,
getRankImage,
Expand All @@ -231,5 +285,6 @@ module.exports = {
getChart,
getGraph,
getCompareDatasets,
getEloGain
getEloGain,
getMapRadarChart
}
Empty file.
2 changes: 2 additions & 0 deletions languages/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ module.exports = {
verifyDescription: '',
verify: '',
premiumDesc: '',
allMaps: '',
allMapsDescription: '',
},
error: {
user: {
Expand Down
3 changes: 3 additions & 0 deletions languages/en-US/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ base.strings.pagination = {
}
base.strings.donate = 'Support the project'
base.strings.fullHistory = 'Full history'
base.strings.allMaps = 'All maps'
// Radar chart
base.strings.allMapsDescription = 'Sneak peek of different maps statistics'
base.strings.premiumDesc = 'This feature is only available for premium guilds. Become a premium guild by clicking on the button below'
base.error.user.missing = 'It seems like there is a user missing'
base.error.user.compareSame = 'You can\'t compare the same user'
Expand Down
1 change: 1 addition & 0 deletions languages/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const getTranslation = (key, language, replace) => {
try {
languageConf = require(`./${language}/translations`)
string = languageConf[key]
if (!string.length) throw new Error('Empty string')
} catch (error) {
languageConf = require('./en-US/translations')
string = languageConf[key]
Expand Down
3 changes: 3 additions & 0 deletions templates/customType.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ module.exports.TYPES = {
PREV: { name: 'strings.pagination.prev', emoji: '◀', style: ButtonStyle.Primary, translate: true },
LAST: { name: 'strings.pagination.last', emoji: '⏭', style: ButtonStyle.Primary, translate: true },
FIRST: { name: 'strings.pagination.first', emoji: '⏮', style: ButtonStyle.Primary, translate: true },
MATCHES: { name: 'Matches', style: ButtonStyle.Success, translate: false },
WINRATE: { name: 'Win Rate %', style: ButtonStyle.Success, translate: false },
ELO_GAIN: { name: 'Elo Gain', style: ButtonStyle.Success, translate: false },
}

module.exports.getType = (t) => Object.entries(this.TYPES).filter(e => e[1].name.normalize() === t)[0][1]
Expand Down
11 changes: 7 additions & 4 deletions templates/errorCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ module.exports = (description, lang) => {
if (typeof description === 'string' && keyReg.test(description)) description = getTranslation(description, lang)

return {
embeds: [new Discord.EmbedBuilder()
.setColor(color.error)
.setDescription(description ?? getTranslation('error.execution.command', lang))
.setFooter({ text: `${name} ${getTranslation('strings.error', lang)}` })]
embeds: [
new Discord.EmbedBuilder()
.setColor(color.error)
.setDescription(description ?? getTranslation('error.execution.command', lang))
.setFooter({ text: `${name} ${getTranslation('strings.error', lang)}` })
],
files: []
}
}

0 comments on commit 651db56

Please sign in to comment.