Skip to content

Commit

Permalink
change the 1337 game to use heeeckers early voting system
Browse files Browse the repository at this point in the history
  • Loading branch information
oglimmer committed Mar 13, 2024
1 parent 1ea0117 commit 8f86edd
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 3 deletions.
199 changes: 199 additions & 0 deletions src/commands/elite-early-bird.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@

import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from 'discord.js'
import LogManager from '../logger/logger'
import { EliteGameDataStorage } from '../service/eliteGameDataStorage'
import { getTodayAsDate } from '../service/promotionService'

function parse(dateToParse: string | number | boolean): undefined | number {
if (typeof dateToParse !== 'string') {
return undefined
}
const now = new Date()
const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0).getTime()
if (/^\d{1,2}:\d{1,2}:\d{1,2}\.\d{1,3}$/g.test(dateToParse)) {
const splitByColon = dateToParse.split(':')
if (splitByColon.length !== 3) {
return undefined
}
const hours = parseInt(splitByColon[0])
const minutes = parseInt(splitByColon[1])
const splitByDot = splitByColon[2].split('.')
if (splitByDot.length !== 2) {
return undefined
}
const seconds = parseInt(splitByDot[0])
const milliseconds = parseInt(splitByDot[1])
if (hours < 0 || hours > 23) {
return undefined
}
if (minutes < 0 || minutes > 59) {
return undefined
}
if (seconds < 0 || seconds > 59) {
return undefined
}
if (milliseconds < 0 || milliseconds > 999) {
return undefined
}
return midnight + milliseconds + (seconds * 1000) + (minutes * 60 * 1000) + (hours * 60 * 60 * 1000)
}
if (/^\d{1,2}:\d{1,2}:\d{1,2}$/g.test(dateToParse)) {
const splitByColon = dateToParse.split(':')
if (splitByColon.length !== 3) {
return undefined
}
const hours = parseInt(splitByColon[0])
const minutes = parseInt(splitByColon[1])
const seconds = parseInt(splitByColon[2])
if (hours < 0 || hours > 23) {
return undefined
}
if (minutes < 0 || minutes > 59) {
return undefined
}
if (seconds < 0 || seconds > 59) {
return undefined
}
return midnight + (seconds * 1000) + (minutes * 60 * 1000) + (hours * 60 * 60 * 1000)
}
if (/^\d{1,2}:\d{1,2}$/g.test(dateToParse)) {
const splitByColon = dateToParse.split(':')
if (splitByColon.length !== 2) {
return undefined
}
const minutes = parseInt(splitByColon[0])
const seconds = parseInt(splitByColon[1])
if (minutes < 0 || minutes > 59) {
return undefined
}
if (seconds < 0 || seconds > 59) {
return undefined
}
return midnight + (seconds * 1000) + (minutes * 60 * 1000) + (13 * 60 * 60 * 1000)
}
if (/^\d{1,2}\.\d{1,3}$/g.test(dateToParse)) {
const splitByDot = dateToParse.split('.')
if (splitByDot.length !== 2) {
return undefined
}
const seconds = parseInt(splitByDot[0])
const milliseconds = parseInt(splitByDot[1])
if (seconds < 0 || seconds > 59) {
return undefined
}
if (milliseconds < 0 || milliseconds > 999) {
return undefined
}
return midnight + milliseconds + (seconds * 1000) + (37 * 60 * 1000) + (13 * 60 * 60 * 1000)
}
if (/^\d{1,2}$/g.test(dateToParse)) {
const seconds = parseInt(dateToParse)
if (seconds < 0 || seconds > 59) {
return undefined
}
return midnight + seconds * 1000 + (37 * 60 * 1000) + (13 * 60 * 60 * 1000)
}
return undefined
}

function calcValidFrom(playTime: number): number {
const now = new Date()
let offset = 999
// rules: Mitternacht tippt sind es 65s, wenn man um 8 Uhr tippt 30s, um 12 Uhr -> 7s, 13 Uhr -> 3s, ab 13:37 dann 0s
if (now.getHours() >= 0 && now.getHours() <= 7) {
offset = 65
} else if (now.getHours() >= 8 && now.getHours() <= 11) {
offset = 30
} else if (now.getHours() === 12) {
offset = 7
} else if (now.getHours() === 13) {
offset = 3
}
return playTime + offset * 1000
}

export default {
data: new SlashCommandBuilder()
.setName('1337-early-bird')
.setDescription('Plays the 1337 game as an early bird.')
.addStringOption((opt) => opt.setName('time').setDescription('The time (HH:mm:ss.sss) you would have /1337`ed').setRequired(true)),
async execute(interaction: CommandInteraction) {
const logger = LogManager.getInstance().logger('EliteEarlyBirdCommand')
if (!interaction.isRepliable()) {
logger.logSync('ERROR', 'Gegebene interaction kann nicht beantwortet werden.')
return
}
const berlinDate = getTodayAsDate()

const userId = interaction.member?.user.id ?? '<ERROR>'
const time = interaction.options.get('time', true).value
if (!time) {
throw new Error('No time provided')
}

const rows = await EliteGameDataStorage.instance().loadGamePlayForUser(userId, berlinDate)
try {
if (rows.length > 0) {
if (rows[0].player === '\uFFFF') {
// the player '\uFFFF' indicates that the game has been played for this day
await interaction.reply({
embeds: [new EmbedBuilder()
.setColor(0x0099ff)
.setTitle('Too late!')
.setDescription(
'Es ist nach 13:37. Morgen kannst du wieder mitmachen.'
)
.setTimestamp()],
ephemeral: true
})
} else {
await interaction.reply({
embeds: [new EmbedBuilder()
.setColor(0x0099ff)
.setTitle('That`s not going to happen!')
.setDescription(
'Du hast heute bereits deinen Tipp abgegeben. Warte auf das Ergebnis.'
)
.setTimestamp()],
ephemeral: true
})
}
} else {
const playTime = parse(time)
if (!playTime) {
await interaction.reply({
embeds: [new EmbedBuilder()
.setColor(0x0099ff)
.setTitle('Invalid time!')
.setDescription(
'Die Zeit muss im Format `HH:mm:ss.sss` sein.'
)
.setTimestamp()],
ephemeral: true
})
return
}
const validFrom = calcValidFrom(playTime)
await EliteGameDataStorage.instance().writeGamePlayEarlyBird(
userId, // discord-id user
berlinDate, // german format date of play e.g. 17.06.2024
playTime, // submission time (the guess) in millis since epoc e.g. 1710419768333 for 13.03.2024 23:05
validFrom // valid starting time in millis since epoc e.g. 1710419768333 for 13.03.2024 23:05
)
await interaction.reply({
embeds: [new EmbedBuilder()
.setColor(0x0099ff)
.setTitle('Got it early bird!')
.setDescription(
'Dein Tipp wurde registriert. Warte auf das Ergebnis.'
)
.setTimestamp()],
ephemeral: true
})
}
} catch (err) {
console.log(err)
logger.logSync('ERROR', `Reply to elite command konnte nicht gesendet werden. ${JSON.stringify(err)}`)
}
}
}
24 changes: 21 additions & 3 deletions src/service/eliteGameDataStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export class EliteGameDataStorage {
register_date TEXT NOT NULL,
play_timestamp INTEGER NOT NULL
)`)
const rowST = await this.db.getAsyncT<{ count: number }>(`SELECT 1 as count FROM sqlite_master WHERE type='table' AND name='elite_game' AND sql LIKE '%submission_time%';`, [])
if (rowST?.count !== 1) {
await this.db.runAsync(`ALTER TABLE elite_game ADD COLUMN submission_time INTEGER NOT NULL DEFAULT 0`)
await this.db.runAsync(`ALTER TABLE elite_game ADD COLUMN valid_starting_at INTEGER NOT NULL DEFAULT 0`)
}
await this.db.runAsync(`CREATE TABLE IF NOT EXISTS elite_game_winner (
identifier INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
player TEXT NOT NULL,
Expand Down Expand Up @@ -84,7 +89,15 @@ export class EliteGameDataStorage {
if (!this.db) {
throw Error('initEliteGame was not called!')
}
await this.db.runAsync(`INSERT INTO elite_game(register_date, player, play_timestamp) VALUES(?,?,?)`, [berlinDate, userId, Date.now()])
const now = Date.now()
await this.db.runAsync(`INSERT INTO elite_game(register_date, player, play_timestamp, submission_time, valid_starting_at) VALUES(?,?,?,?,0)`, [berlinDate, userId, now, now])
}

public async writeGamePlayEarlyBird(userId: string, berlinDate: string, tippTime: number, validFrom: number): Promise<void> {
if (!this.db) {
throw Error('initEliteGame was not called!')
}
await this.db.runAsync(`INSERT INTO elite_game(register_date, player, play_timestamp, submission_time, valid_starting_at) VALUES(?,?,?,?,?)`, [berlinDate, userId, tippTime, Date.now(), validFrom])
}

public async loadGamePlayForUser(userId: string, berlinDate: string): Promise<EliteGame[]> {
Expand All @@ -98,7 +111,8 @@ export class EliteGameDataStorage {
if (!this.db) {
throw Error('initEliteGame was not called!')
}
return await this.db.allAsyncT<EliteGame>(`select * from elite_game where register_date = ? and player != "\uFFFF" order by play_timestamp desc`, [berlinDate])
const now = Date.now()
return await this.db.allAsyncT<EliteGame>(`select * from elite_game where register_date = ? and valid_starting_at <= ? and player != "\uFFFF" order by play_timestamp desc`, [berlinDate, now])
}

public async writeGameWinner(userId: string, berlinDate: string, playerTimestamp: number): Promise<void> {
Expand Down Expand Up @@ -132,6 +146,10 @@ export class EliteGameDataStorage {
if (!this.db) {
throw Error('initEliteGame was not called!')
}
return await this.db.allAsyncT<{ userId: string }>(`select player as userId from elite_game where register_date = (select register_date from elite_game where identifier = (select max(identifier) from elite_game where player = "\uFFFF")) and player != "\uFFFF" order by play_timestamp desc`, [])
return await this.db.allAsyncT<{ userId: string }>(`select player as userId from elite_game where register_date = ` +
`(select register_date from elite_game where identifier = (select max(identifier) from elite_game where player = "\uFFFF")) ` +
`and player != "\uFFFF" and valid_starting_at <= ` +
`(select play_timestamp from elite_game where identifier = (select max(identifier) from elite_game where player = "\uFFFF")) ` +
`order by play_timestamp desc`, [])
}
}

0 comments on commit 8f86edd

Please sign in to comment.