mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-02 03:06:52 -05:00
313 lines
7.5 KiB
JavaScript
313 lines
7.5 KiB
JavaScript
const {
|
|
UserInputError,
|
|
AuthenticationError,
|
|
gql,
|
|
} = require("apollo-server-express")
|
|
const Maplist = require("../mongoose-models/maplist")
|
|
const MapBallot = require("../mongoose-models/mapballot") //[PositiveVoteCount!]!
|
|
const maps = require("../utils/maps")
|
|
|
|
const typeDef = gql`
|
|
extend type Query {
|
|
maplists(name: String): [Maplist!]!
|
|
plusMaplists: [Maplist!]!
|
|
mapVotes: [MapVote!]
|
|
positiveVotes(mode: Mode = SZ): Boolean
|
|
}
|
|
|
|
input MapVoteInput {
|
|
name: String!
|
|
sz: Int!
|
|
tc: Int!
|
|
rm: Int!
|
|
cb: Int!
|
|
}
|
|
|
|
extend type Mutation {
|
|
addMapVotes(votes: [MapVoteInput!]!): Boolean
|
|
generateMaplistFromVotes: Boolean
|
|
}
|
|
|
|
type MapVoteCount {
|
|
name: String!
|
|
sz: [Int!]!
|
|
tc: [Int!]!
|
|
rm: [Int!]!
|
|
cb: [Int!]!
|
|
}
|
|
|
|
type PlusMaplistInfo {
|
|
month: Int!
|
|
year: Int!
|
|
voter_count: Int!
|
|
vote_counts: [MapVoteCount!]!
|
|
}
|
|
|
|
type Maplist {
|
|
name: String!
|
|
sz: [String!]!
|
|
tc: [String!]!
|
|
rm: [String!]!
|
|
cb: [String!]!
|
|
plus: PlusMaplistInfo
|
|
}
|
|
|
|
type MapVote {
|
|
name: String!
|
|
sz: Int!
|
|
tc: Int!
|
|
rm: Int!
|
|
cb: Int!
|
|
}
|
|
`
|
|
|
|
const resolvers = {
|
|
Query: {
|
|
maplists: (root, args) => {
|
|
const criteria = args.name ? { name: args.name } : {}
|
|
return Maplist.find(criteria)
|
|
.sort({ order: "asc" })
|
|
.catch((e) => {
|
|
throw new UserInputError(e.message, {
|
|
invalidArgs: args,
|
|
})
|
|
})
|
|
},
|
|
plusMaplists: (root, args) => {
|
|
return Maplist.find({ plus: { $ne: null } }).sort({
|
|
"plus.year": "desc",
|
|
"plus.month": "desc",
|
|
})
|
|
},
|
|
mapVotes: async (root, args, ctx) => {
|
|
if (!ctx.user || !ctx.user.plus || !ctx.user.plus.membership_status) {
|
|
return null
|
|
}
|
|
|
|
const mapBallot = await MapBallot.findOne({
|
|
discord_id: ctx.user.discord_id,
|
|
})
|
|
|
|
if (!mapBallot) {
|
|
return maps.map((map) => ({ name: map, sz: 0, tc: 0, rm: 0, cb: 0 }))
|
|
}
|
|
|
|
return mapBallot.maps
|
|
},
|
|
positiveVotes: async (root, args, ctx) => {
|
|
const ballots = await MapBallot.find({})
|
|
|
|
const count = {}
|
|
ballots.forEach((ballot) => {
|
|
ballot.maps.forEach((stage) => {
|
|
let toIcrement = 0
|
|
if (stage[args.mode.toLowerCase()] === 1) toIcrement = 1
|
|
|
|
const votes = count[stage.name] ? count[stage.name] : 0
|
|
count[stage.name] = votes + toIcrement
|
|
})
|
|
})
|
|
|
|
return true
|
|
},
|
|
},
|
|
Mutation: {
|
|
addMapVotes: async (root, args, ctx) => {
|
|
if (!ctx.user || !ctx.user.plus || !ctx.user.plus.membership_status) {
|
|
throw new AuthenticationError("Insufficient access")
|
|
}
|
|
|
|
const legitVotes = [-1, 0, 1]
|
|
|
|
args.votes.forEach((vote, index) => {
|
|
if (vote.name !== maps[index]) {
|
|
throw new UserInputError(
|
|
`Invalid map or position: ${vote.name} on the index ${index}`
|
|
)
|
|
}
|
|
|
|
if (legitVotes.indexOf(vote.sz) === -1) {
|
|
throw new UserInputError(`Invalid vote for sz: ${vote.sz}`)
|
|
}
|
|
|
|
if (legitVotes.indexOf(vote.tc) === -1) {
|
|
throw new UserInputError(`Invalid vote for sz: ${vote.tc}`)
|
|
}
|
|
|
|
if (legitVotes.indexOf(vote.rm) === -1) {
|
|
throw new UserInputError(`Invalid vote for sz: ${vote.rm}`)
|
|
}
|
|
|
|
if (legitVotes.indexOf(vote.cb) === -1) {
|
|
throw new UserInputError(`Invalid vote for sz: ${vote.cb}`)
|
|
}
|
|
})
|
|
|
|
const discord_id = ctx.user.discord_id
|
|
await MapBallot.findOneAndUpdate(
|
|
{ discord_id },
|
|
{ discord_id, maps: args.votes },
|
|
{ upsert: true }
|
|
)
|
|
|
|
return true
|
|
},
|
|
generateMaplistFromVotes: async (root, args, ctx) => {
|
|
if (!ctx.user) throw new AuthenticationError("Not logged in")
|
|
if (ctx.user.discord_id !== process.env.ADMIN_ID)
|
|
throw new AuthenticationError("Not admin")
|
|
|
|
const ballots = await MapBallot.find({}).populate("discord_user")
|
|
|
|
const validBallots = ballots.filter(
|
|
(ballot) => !!ballot.discord_user.plus.membership_status
|
|
)
|
|
|
|
const voter_count = validBallots.length
|
|
const vote_counts = maps.map((stage) => ({
|
|
name: stage,
|
|
sz: [0, 0, 0],
|
|
tc: [0, 0, 0],
|
|
rm: [0, 0, 0],
|
|
cb: [0, 0, 0],
|
|
}))
|
|
|
|
validBallots.forEach((ballot) =>
|
|
ballot.maps.forEach((stage, index) => {
|
|
const { sz, tc, rm, cb } = stage
|
|
// one is added to the index so -1 vote goes to 0 index, 0 to 1 and 1 to 2
|
|
vote_counts[index].sz[sz + 1] = vote_counts[index].sz[sz + 1] + 1
|
|
vote_counts[index].tc[tc + 1] = vote_counts[index].tc[tc + 1] + 1
|
|
vote_counts[index].rm[rm + 1] = vote_counts[index].rm[rm + 1] + 1
|
|
vote_counts[index].cb[cb + 1] = vote_counts[index].cb[cb + 1] + 1
|
|
})
|
|
)
|
|
|
|
let allMaps = []
|
|
vote_counts.forEach((count) => {
|
|
;["sz", "tc", "rm", "cb"].forEach((mode) => {
|
|
allMaps.push({
|
|
name: count.name,
|
|
score: +(
|
|
(count[mode][0] * -1 + count[mode][2]) /
|
|
voter_count
|
|
).toFixed(2),
|
|
mode,
|
|
})
|
|
})
|
|
})
|
|
|
|
allMaps = allMaps.sort((a, b) => b.score - a.score)
|
|
|
|
szMapPool = []
|
|
tcMapPool = []
|
|
rmMapPool = []
|
|
cbMapPool = []
|
|
|
|
const pools = {
|
|
sz: szMapPool,
|
|
tc: tcMapPool,
|
|
rm: rmMapPool,
|
|
cb: cbMapPool,
|
|
}
|
|
|
|
const mapCount = {}
|
|
|
|
// we switch monthly to add variance
|
|
const now = new Date()
|
|
const month = now.getMonth() + 1
|
|
const monthModes = [
|
|
null,
|
|
"sz",
|
|
"tc",
|
|
"rm",
|
|
"cb",
|
|
"sz",
|
|
"tc",
|
|
"rm",
|
|
"cb",
|
|
"sz",
|
|
"tc",
|
|
"rm",
|
|
"cb",
|
|
]
|
|
let modeToAddTo = monthModes[month]
|
|
const nextModeDict = {
|
|
sz: "tc",
|
|
tc: "rm",
|
|
rm: "cb",
|
|
cb: "sz",
|
|
}
|
|
|
|
while (
|
|
szMapPool.length < 8 ||
|
|
tcMapPool.length < 8 ||
|
|
rmMapPool.length < 8 ||
|
|
cbMapPool.length < 8
|
|
) {
|
|
for (const stageObj of allMaps) {
|
|
const pool = pools[modeToAddTo]
|
|
|
|
if (pool.length === 8) {
|
|
break
|
|
}
|
|
|
|
const alreadyInCount = mapCount[stageObj.name]
|
|
? mapCount[stageObj.name]
|
|
: 0
|
|
|
|
if (alreadyInCount === 2) {
|
|
continue
|
|
}
|
|
|
|
if (stageObj.mode !== modeToAddTo) {
|
|
continue
|
|
}
|
|
|
|
if (pool.indexOf(stageObj.name) !== -1) {
|
|
continue
|
|
}
|
|
|
|
mapCount[stageObj.name] = alreadyInCount + 1
|
|
pool.push(stageObj.name)
|
|
break
|
|
}
|
|
|
|
modeToAddTo = nextModeDict[modeToAddTo]
|
|
}
|
|
|
|
szMapPool.sort((a, b) => maps.indexOf(a) - maps.indexOf(b))
|
|
tcMapPool.sort((a, b) => maps.indexOf(a) - maps.indexOf(b))
|
|
rmMapPool.sort((a, b) => maps.indexOf(a) - maps.indexOf(b))
|
|
cbMapPool.sort((a, b) => maps.indexOf(a) - maps.indexOf(b))
|
|
|
|
const maplist = {
|
|
name: `Plus Server ${now.toLocaleString("default", {
|
|
month: "long",
|
|
})} ${now.getFullYear()}`,
|
|
sz: szMapPool,
|
|
tc: tcMapPool,
|
|
rm: rmMapPool,
|
|
cb: cbMapPool,
|
|
order: 0,
|
|
plus: {
|
|
month,
|
|
year: now.getFullYear(),
|
|
voter_count,
|
|
vote_counts,
|
|
},
|
|
}
|
|
|
|
await Maplist.updateMany({ order: 0 }, { order: 1 })
|
|
await Maplist.create(maplist)
|
|
|
|
return true
|
|
},
|
|
},
|
|
}
|
|
|
|
module.exports = {
|
|
Maplist: typeDef,
|
|
maplistResolvers: resolvers,
|
|
}
|