parent
690b9db45b
commit
89215ef8e9
@ -0,0 +1,137 @@ |
|||||||
|
--! Previous: sha1:2a39b3b006a8d5ec5d30ac1e5c2158d282b99acb |
||||||
|
--! Hash: sha1:1390238abcc8e013a87b670fbcb3ae127a51ece8 |
||||||
|
--! Message: daily and VGNYJ error functions |
||||||
|
|
||||||
|
--- Calculates the date at which /daily can be used again after the given timestamp. |
||||||
|
CREATE OR REPLACE FUNCTION NextDailyCommand(IN lastDaily timestamp with time zone) |
||||||
|
RETURNS timestamp with time zone |
||||||
|
CALLED ON NULL INPUT |
||||||
|
IMMUTABLE |
||||||
|
AS |
||||||
|
$$ |
||||||
|
SELECT (COALESCE(lastDaily, TIMESTAMP '-infinity' AT TIME ZONE 'UTC')::date + 1)::timestamp AT TIME ZONE 'UTC' |
||||||
|
$$ LANGUAGE 'sql'; |
||||||
|
|
||||||
|
--- Raises the VGNYJ error. |
||||||
|
CREATE OR REPLACE PROCEDURE Error_NotYetJoined() AS |
||||||
|
$$ |
||||||
|
BEGIN |
||||||
|
RAISE EXCEPTION USING |
||||||
|
ERRCODE = 'VGNYJ', |
||||||
|
MESSAGE = 'Not yet joined', |
||||||
|
DETAIL = 'You haven''t joined the game yet, and can''t use this command until you do.', |
||||||
|
HINT = 'Use the /join command to join the game!'; |
||||||
|
END; |
||||||
|
$$ LANGUAGE 'plpgsql'; |
||||||
|
|
||||||
|
--- Runs the full /daily command. |
||||||
|
--- Error codes: |
||||||
|
--- VGBCG: Bad channel (game). This is not a valid channel to send game commands in. |
||||||
|
--- VGBGG: Bad guild (game). This is not a valid guild to send game commands in. |
||||||
|
--- VGNYJ: Not yet joined. The Discord user using has not joined the game yet. |
||||||
|
--- VGDLY: Already used today. |
||||||
|
CREATE OR REPLACE FUNCTION Command_Daily( |
||||||
|
IN requestedChannel DiscordChannel.discordId%TYPE, |
||||||
|
IN requestedGuild DiscordChannel.guildId%TYPE, |
||||||
|
IN forId DiscordUser.discordId%TYPE, |
||||||
|
IN newUsername DiscordUser.username%TYPE, |
||||||
|
IN newDiscriminator DiscordUser.discriminator%TYPE, |
||||||
|
OUT newLastDaily Player.lastDaily%TYPE, |
||||||
|
OUT newNextDaily Player.lastDaily%TYPE, |
||||||
|
OUT newCurrency Player.currency%TYPE, |
||||||
|
OUT bonus Player.currency%TYPE) |
||||||
|
STRICT |
||||||
|
VOLATILE |
||||||
|
AS |
||||||
|
$$ |
||||||
|
DECLARE |
||||||
|
playerId Player.id%TYPE; |
||||||
|
playerLastDaily Player.lastDaily%TYPE; |
||||||
|
playerNextDaily Player.lastDaily%TYPE; |
||||||
|
BEGIN |
||||||
|
CALL CheckGameCommandIn(requestedChannel, requestedGuild); |
||||||
|
SELECT InvokingPlayer.id, InvokingPlayer.lastDaily, NextDailyCommand(InvokingPlayer.lastdaily) |
||||||
|
INTO playerId, playerLastDaily, playerNextDaily |
||||||
|
FROM GetInvokingPlayer(forId, newUsername, newDiscriminator) AS InvokingPlayer; |
||||||
|
IF playerId IS NULL THEN |
||||||
|
CALL Error_NotYetJoined(); |
||||||
|
END IF; |
||||||
|
bonus = 100; |
||||||
|
IF playerNextDaily > NOW() THEN |
||||||
|
RAISE EXCEPTION USING |
||||||
|
ERRCODE = 'VGDLY', |
||||||
|
MESSAGE = 'Already used today', |
||||||
|
DETAIL = format('You last used your daily command at <t:%s>. You can use it again at <t:%s>.', |
||||||
|
extract(EPOCH FROM playerLastDaily)::INT, |
||||||
|
extract(EPOCH FROM playerNextDaily)::INT), |
||||||
|
HINT = format('Wait <t:%s:R> and you can use the /daily command again to get some more currency!', |
||||||
|
extract(EPOCH FROM playerNextDaily)::INT); |
||||||
|
ELSE |
||||||
|
UPDATE Player |
||||||
|
SET currency = currency + bonus, |
||||||
|
lastDaily = NOW() |
||||||
|
WHERE id = playerId |
||||||
|
RETURNING lastDaily, currency INTO newLastDaily, newCurrency; |
||||||
|
newNextDaily = NextDailyCommand(newLastDaily); |
||||||
|
END IF; |
||||||
|
END; |
||||||
|
$$ LANGUAGE 'plpgsql'; |
||||||
|
|
||||||
|
--- Runs the full /pull command. |
||||||
|
--- Error codes: |
||||||
|
--- VGBCG: Bad channel (game). This is not a valid channel to send game commands in. |
||||||
|
--- VGBGG: Bad guild (game). This is not a valid guild to send game commands in. |
||||||
|
--- VGNYJ: Not yet joined. The Discord user using has not joined the game yet. |
||||||
|
--- VGNEC: Not enough currency. |
||||||
|
CREATE OR REPLACE FUNCTION Command_Pull( |
||||||
|
IN requestedChannel DiscordChannel.discordId%TYPE, |
||||||
|
IN requestedGuild DiscordChannel.guildId%TYPE, |
||||||
|
IN forId DiscordUser.discordId%TYPE, |
||||||
|
IN newUsername DiscordUser.username%TYPE, |
||||||
|
IN newDiscriminator DiscordUser.discriminator%TYPE, |
||||||
|
IN count INT |
||||||
|
) |
||||||
|
RETURNS TABLE |
||||||
|
( |
||||||
|
summonedUnitInstanceId SummonedUnit.instanceId%TYPE, |
||||||
|
summonedUnitId Unit.id%TYPE, |
||||||
|
summonedUnitName Unit.name%TYPE, |
||||||
|
summonedUnitSubtitle Unit.subtitle%TYPE, |
||||||
|
summonedUnitTierName UnitTier.name%TYPE, |
||||||
|
firstTimePull BOOLEAN, |
||||||
|
wasAlreadySummoned BOOLEAN |
||||||
|
) |
||||||
|
STRICT |
||||||
|
VOLATILE |
||||||
|
ROWS 10 |
||||||
|
AS |
||||||
|
$$ |
||||||
|
DECLARE |
||||||
|
playerId Player.id%TYPE; |
||||||
|
cost Player.currency%TYPE; |
||||||
|
oldCurrency Player.currency%TYPE; |
||||||
|
playerLastDaily Player.lastDaily%TYPE; |
||||||
|
BEGIN |
||||||
|
CALL CheckGameCommandIn(requestedChannel, requestedGuild); |
||||||
|
SELECT InvokingPlayer.id, InvokingPlayer.currency, InvokingPlayer.lastDaily |
||||||
|
INTO playerId, oldCurrency, playerLastDaily |
||||||
|
FROM GetInvokingPlayer(forId, newUsername, newDiscriminator) AS InvokingPlayer; |
||||||
|
IF playerId IS NULL THEN |
||||||
|
CALL Error_NotYetJoined(); |
||||||
|
END IF; |
||||||
|
cost = 10 * count; |
||||||
|
UPDATE Player SET currency = currency - cost WHERE id = playerId AND currency >= cost; |
||||||
|
IF NOT FOUND THEN |
||||||
|
RAISE EXCEPTION USING |
||||||
|
ERRCODE = 'VGNEC', |
||||||
|
MESSAGE = 'Not enough currency', |
||||||
|
DETAIL = format('Pulling %s heroines would cost %s currency, but you only have %s currency.', |
||||||
|
count, cost, oldCurrency), |
||||||
|
HINT = CASE NextDailyCommand(playerLastDaily) > NOW() |
||||||
|
WHEN FALSE THEN 'You can use the /daily command to get some more currency for today!' |
||||||
|
ELSE format('You can use the /daily command again <t:%s:R> to get some more currency!', |
||||||
|
extract(EPOCH FROM NextDailyCommand(playerLastDaily))::INT) |
||||||
|
END; |
||||||
|
END IF; |
||||||
|
END; |
||||||
|
$$ LANGUAGE 'plpgsql'; |
@ -0,0 +1,47 @@ |
|||||||
|
import {CommandContext, SlashCommand, SlashCreator} from "slash-create"; |
||||||
|
import {Snowflake} from "discord-api-types"; |
||||||
|
import {Pool} from "pg"; |
||||||
|
import {sendErrorMessage} from "../../queries/ErrorCodes.js"; |
||||||
|
import {singleRowQueryResult} from "../../queries/QueryHelpers.js"; |
||||||
|
|
||||||
|
export class DailyCommand extends SlashCommand { |
||||||
|
readonly pool: Pool |
||||||
|
|
||||||
|
constructor(creator: SlashCreator, {pool, gameGuildIds}: { |
||||||
|
pool: Pool, |
||||||
|
gameGuildIds: Snowflake[] |
||||||
|
}) { |
||||||
|
super(creator, { |
||||||
|
name: "daily", |
||||||
|
guildIDs: gameGuildIds, |
||||||
|
description: "Gathers your daily currency once per day, resetting at midnight UTC.", |
||||||
|
options: [] |
||||||
|
}); |
||||||
|
|
||||||
|
this.pool = pool |
||||||
|
} |
||||||
|
|
||||||
|
async run(ctx: CommandContext): Promise<any> { |
||||||
|
try { |
||||||
|
const result = singleRowQueryResult(await this.pool.query<{ newlastdaily: Date, newnextdaily: Date, newcurrency: number, bonus: number }>({ |
||||||
|
text: `SELECT *
|
||||||
|
FROM Command_Daily($1, $2, $3, $4, $5)`,
|
||||||
|
values: [ctx.channelID, ctx.guildID, ctx.user.id, ctx.user.username, ctx.user.discriminator], |
||||||
|
})) |
||||||
|
if (result === undefined) { |
||||||
|
await ctx.send({ |
||||||
|
content: "Unexpectedly got no results!!", |
||||||
|
ephemeral: true, |
||||||
|
}) |
||||||
|
console.log("Unexpectedly empty Command_Join result!") |
||||||
|
return; |
||||||
|
} |
||||||
|
return ctx.send({ |
||||||
|
content: `Nice! You reap ${result.bonus} currency from the void, leaving you with a total of ${result.newcurrency}!\n\nYou collected your currency now at <t:${Math.floor(result.newlastdaily.getTime() / 1000)}> and can gather it again in <t:${Math.floor(result.newnextdaily.getTime() / 1000)}:R> at <t:${Math.floor(result.newnextdaily.getTime() / 1000)}>.`, |
||||||
|
ephemeral: true, |
||||||
|
}) |
||||||
|
} catch (e) { |
||||||
|
await sendErrorMessage(ctx, e) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue