Group Up V0.4.0

Lotta changes, compiled from months ago. In short:
2 new tables for handling new features
Now runs latest DD on latest Deno
Auto-clean
Alternate=>Joined auto-promote
Join on behalf of someone else
bugfixes!
This commit is contained in:
Ean Milligan (Bastion) 2021-11-11 22:23:36 -05:00
parent c0d9abbe70
commit f244c1891e
14 changed files with 891 additions and 368 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
config.ts
logs
db/update.ts

View File

@ -8,5 +8,8 @@
"spellright.language": [
"en"
],
"spellright.documentTypes": []
"spellright.documentTypes": [],
"deno.suggest.imports.hosts": {
"https://deno.land": true
}
}

View File

@ -1,9 +1,9 @@
export const config = {
"name": "Group Up", // Name of the bot
"version": "0.2.3", // Version of the bot
"version": "0.3.2", // Version of the bot
"token": "the_bot_token", // Discord API Token for this bot
"localtoken": "local_testing_token", // Discord API Token for a secondary OPTIONAL testing bot, THIS MUST BE DIFFERENT FROM "token"
"prefix": "[[", // Prefix for all commands
"prefix": "gu!", // Prefix for all commands
"db": { // Settings for the MySQL database, this is required for use with the API, if you do not want to set this up, you will need to rip all code relating to the DB out of the bot
"host": "", // IP address for the db, usually localhost
"localhost": "", // IP address for a secondary OPTIONAL local testing DB, usually also is localhost, but depends on your dev environment

View File

@ -26,6 +26,8 @@ console.log("Attempt to drop all tables");
await dbClient.execute(`DROP PROCEDURE IF EXISTS INC_CNT;`);
await dbClient.execute(`DROP TABLE IF EXISTS command_cnt;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_prefix;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_mod_role;`);
await dbClient.execute(`DROP TABLE IF EXISTS guild_clean_channel;`);
console.log("Tables dropped");
console.log("Attempting to create table command_cnt");
@ -63,5 +65,26 @@ await dbClient.execute(`
`);
console.log("Table created");
console.log("Attempting to create table guild_mod_role");
await dbClient.execute(`
CREATE TABLE guild_mod_role (
guildId bigint unsigned NOT NULL,
roleId bigint unsigned NOT NULL,
PRIMARY KEY (guildid),
UNIQUE KEY guild_mod_role_guildid_UNIQUE (guildid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`);
console.log("Table created");
console.log("Attempting to create table guild_clean_channel");
await dbClient.execute(`
CREATE TABLE guild_clean_channel (
guildId bigint unsigned NOT NULL,
channelId bigint unsigned NOT NULL,
PRIMARY KEY (guildid, channelId)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`);
console.log("Table created");
await dbClient.close();
console.log("Done!");

16
deps.ts
View File

@ -1,18 +1,18 @@
// All external dependancies are to be loaded here to make updating dependancy versions much easier
export {
startBot, editBotStatus, editBotNickname,
Intents, DiscordActivityTypes, DiscordButtonStyles, DiscordInteractionTypes,
Intents, DiscordActivityTypes, DiscordButtonStyles, DiscordInteractionTypes, DiscordInteractionResponseTypes,
sendMessage, sendDirectMessage, sendInteractionResponse, getMessage, deleteMessage,
getGuild, getUser,
hasGuildPermissions,
cache, botId, structures
} from "https://deno.land/x/discordeno@11.0.0-rc.5/mod.ts";
cache, botId, structures, cacheHandlers
} from "https://deno.land/x/discordeno@12.0.1/mod.ts";
export type {
DiscordenoMessage, DiscordenoMember, DiscordenoGuild, CreateMessage, Interaction, ButtonComponent, ActionRow, EmbedField
} from "https://deno.land/x/discordeno@11.0.0-rc.5/mod.ts"; // https://deno.land/x/discordeno@11.0.0-rc.5/mod.ts
DiscordenoMessage, DiscordenoMember, DiscordenoGuild, ButtonData, DebugArg,
CreateMessage, Interaction, ButtonComponent, ActionRow, Embed, EmbedField
} from "https://deno.land/x/discordeno@12.0.1/mod.ts";
export { Client } from "https://deno.land/x/mysql@v2.9.0/mod.ts";
export { Client } from "https://deno.land/x/mysql@v2.10.1/mod.ts";
export { LogTypes as LT, initLog, log } from "https://raw.githubusercontent.com/Burn-E99/Log4Deno/V1.1.0/mod.ts";
export { nanoid } from "https://deno.land/x/nanoid@v3.0.0/mod.ts";

972
mod.ts

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ import config from "../config.ts";
export const constantCmds = {
help: {
embed: {
embeds: [{
title: `${config.name} Help`,
fields: [
{
@ -32,10 +32,10 @@ export const constantCmds = {
`
}
]
}
}]
},
lfgHelp: {
embed: {
embeds: [{
title: `${config.name} LFG Help`,
fields: [
{
@ -74,10 +74,10 @@ export const constantCmds = {
inline: true
}
]
}
}]
},
info: {
embed: {
embeds: [{
fields: [
{
name: "Group Up, the LFG bot",
@ -86,67 +86,67 @@ export const constantCmds = {
Need help with this bot? Join my support server [here](https://discord.gg/peHASXMZYv).`
}
]
}
}]
},
version: {
embed: {
embeds: [{
title: `My current version is ${config.version}`
}
}]
},
report: {
embed: {
embeds: [{
fields: [
{
name: "Failed command has been reported to my developer.",
value: "For more in depth support, and information about planned maintenance, please join the support server [here](https://discord.gg/peHASXMZYv)."
}
]
}
}]
},
lfgDelete1: {
embed: {
embeds: [{
fields: [
{
name: "Could not find any LFGs to delete.",
value: "Make sure you are the owner of the LFG and are running this command in the same channel as the LFG"
}
]
}
}]
},
lfgDelete2: {
embed: {
embeds: [{
fields: [
{
name: `Multiple LFGs found, please run this command again with the two character ID of the LFG you wish to delete.\n\nExample: \`${config.prefix}lfg delete XX\``,
value: "Click on the two character IDs below to view the LFG:\n"
}
]
}
}]
},
lfgDelete3: {
embed: {
embeds: [{
title: "LFG deleted."
}
}]
},
lfgEdit1: {
embed: {
embeds: [{
fields: [
{
name: "Could not find any LFGs to edit.",
value: "Make sure you are the owner of the LFG and are running this command in the same channel as the LFG"
}
]
}
}]
},
lfgEdit2: {
embed: {
embeds: [{
fields: [
{
name: `Multiple LFGs found, please run this command again with the two character ID of the LFG you wish to edit.\n\nExample: \`${config.prefix}lfg edit XX\``,
value: "Click on the two character IDs below to view the LFG:\n"
}
]
}
}]
}
};

View File

@ -37,7 +37,7 @@ export const LFGActivities = {
}
},
"Among Us": {
"Vanilla": 10,
"Modded": 10
"Vanilla": 15,
"Modded": 15
}
};

View File

@ -2,18 +2,20 @@ import {
// Discordeno deps
cache,
sendMessage, getMessage, deleteMessage, sendDirectMessage,
getGuild,
// Log4Deno deps
LT, log
} from "../deps.ts";
import { jsonStringifyBig } from "./utils.ts";
import { BuildingLFG, ActiveLFG } from "./mod.d.ts";
import config from "../config.ts";
// getRandomStatus() returns status as string
// Gets a new random status for the bot
const getRandomStatus = (): string => {
const getRandomStatus = (cachedGuilds: number): string => {
let status = "";
switch (Math.floor((Math.random() * 5) + 1)) {
case 1:
@ -29,7 +31,7 @@ const getRandomStatus = (): string => {
status = "Mention me to check my prefix!";
break;
default:
status = `Running LFGs in ${cache.guilds.size} servers`;
status = `Running LFGs in ${cachedGuilds + cache.dispatchedGuildIds.size} servers`;
break;
}
@ -41,7 +43,7 @@ const getRandomStatus = (): string => {
const updateListStatistics = (botID: BigInt, serverCount: number): void => {
config.botLists.forEach(async e => {
if (e.enabled) {
log(LT.LOG, `Updating statistics for ${JSON.stringify(e)}`);
log(LT.LOG, `Updating statistics for ${jsonStringifyBig(e)}`);
try {
const tempHeaders = new Headers();
tempHeaders.append(e.headers[0].header, e.headers[0].value);
@ -50,12 +52,12 @@ const updateListStatistics = (botID: BigInt, serverCount: number): void => {
const response = await fetch(e.apiUrl.replace("?{bot_id}", botID.toString()), {
"method": 'POST',
"headers": tempHeaders,
"body": JSON.stringify(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number
"body": jsonStringifyBig(e.body).replace('"?{server_count}"', serverCount.toString()) // ?{server_count} needs the "" removed from around it aswell to make sure its sent as a number
});
log(LT.INFO, `Posted server count to ${e.name}. Results: ${JSON.stringify(response)}`);
log(LT.INFO, `Posted server count to ${e.name}. Results: ${jsonStringifyBig(response)}`);
}
catch (e) {
log(LT.WARN, `Failed to post statistics to ${e.name} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to post statistics to ${e.name} | ${jsonStringifyBig(e)}`);
}
}
});
@ -65,26 +67,29 @@ const buildingTimeout = async (activeBuilders: Array<BuildingLFG>): Promise<void
const currentTime = new Date().getTime();
for (let i = 0; i < activeBuilders.length; i++) {
if (activeBuilders[i].lastTouch.getTime() + (activeBuilders[i].maxIdle * 1000) < currentTime) {
activeBuilders[i].questionMsg.delete();
activeBuilders[i].questionMsg.delete().catch(e => {
log(LT.WARN, `Failed to clean up active builder | edit | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${jsonStringifyBig(e)}`);
});
if (activeBuilders[i].editing) {
activeBuilders[i].lfgMsg.edit({
content: ""
}).catch(e => {
log(LT.WARN, `Failed to clean up active builder | edit | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to clean up active builder | edit | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${jsonStringifyBig(e)}`);
});
} else {
activeBuilders[i].lfgMsg.delete().catch(e => {
log(LT.WARN, `Failed to clean up active builder | delete | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to clean up active builder | delete | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${jsonStringifyBig(e)}`);
});
}
try {
const m = await sendMessage(activeBuilders[i].channelId, `<@${activeBuilders[i].userId}>, your LFG ${activeBuilders[i].editing ? "editing" : "creation"} has timed out. Please try again.`);
setTimeout(() => {
m.delete();
}, 30000);
m.delete("Channel Cleanup", 30000).catch(e =>{
log(LT.WARN, `Failed to delete message | ${jsonStringifyBig(e)}`);
});
}
catch (e) {
log(LT.WARN, `Failed to clean up active builder | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to clean up active builder | ${activeBuilders[i].userId}-${activeBuilders[i].channelId} | ${jsonStringifyBig(e)}`);
}
finally {
activeBuilders.splice(i, 1);
@ -92,10 +97,10 @@ const buildingTimeout = async (activeBuilders: Array<BuildingLFG>): Promise<void
}
}
}
}
};
const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
log(LT.INFO, "Checking for LFG posts to notify/delete/lock")
log(LT.INFO, "Checking for LFG posts to notify/delete/lock");
const tenMin = 10 * 60 * 1000;
const now = new Date().getTime();
for (let i = 0; i < activeLFGPosts.length; i++) {
@ -106,7 +111,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId);
const lfg = message.embeds[0].fields || [];
const lfgActivity = `${lfg[0].name.substr(0, lfg[0].name.length - 1)} - ${lfg[0].value}`;
const guildName = message.guild?.name || "unknown";
const guildName = message.guild?.name || (await getGuild(message.guildId, {counts:false, addToCache: false})).name;
const members = lfg[4].value;
let editMsg = "";
members.split("\n").forEach(async m => {
@ -115,7 +120,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
const userId = BigInt(tmpId.substr(0, tmpId.length - 1));
editMsg += `<@${userId}>, `;
await sendDirectMessage(userId, {
embed: {
embeds: [{
title: `Hello ${name}! Your event in ${guildName} starts in less than 10 minutes.`,
fields: [
lfg[0],
@ -124,7 +129,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
value: members
}
]
}
}]
});
}
});
@ -137,7 +142,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
activeLFGPosts[i].notified = true;
}
catch (err) {
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${JSON.stringify(err)}`);
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${jsonStringifyBig(err)}`);
activeLFGPosts.splice(i, 1);
i--;
@ -145,7 +150,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
}
// Lock LFG from editing/Joining/Leaving
if (!activeLFGPosts[i].locked && activeLFGPosts[i].lfgTime < now) {
else if (!activeLFGPosts[i].locked && activeLFGPosts[i].lfgTime < now) {
log(LT.INFO, `Locking LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`);
try {
const message = await getMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId);
@ -157,7 +162,7 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
activeLFGPosts[i].locked = true;
}
catch (err) {
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${JSON.stringify(err)}`);
log(LT.WARN, `Failed to find LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${jsonStringifyBig(err)}`);
activeLFGPosts.splice(i, 1);
i--;
@ -165,19 +170,17 @@ const lfgNotifier = async (activeLFGPosts: Array<ActiveLFG>): Promise<void> => {
}
// Delete old LFG post
if (activeLFGPosts[i].lfgTime < (now - tenMin)) {
else if (activeLFGPosts[i].lfgTime < (now - tenMin)) {
log(LT.INFO, `Deleting LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid}`);
await deleteMessage(activeLFGPosts[i].channelId, activeLFGPosts[i].messageId, "LFG post expired").catch(e => {
log(LT.WARN, `Failed to delete LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to delete LFG ${activeLFGPosts[i].ownerId}-${activeLFGPosts[i].lfgUid} | ${jsonStringifyBig(e)}`);
});
activeLFGPosts.splice(i, 1);
i--;
}
}
localStorage.setItem("activeLFGPosts", JSON.stringify(activeLFGPosts, (_key, value) =>
typeof value === "bigint" ? value.toString() + "n" : value
));
}
localStorage.setItem("activeLFGPosts", jsonStringifyBig(activeLFGPosts));
};
export default { getRandomStatus, updateListStatistics, buildingTimeout, lfgNotifier };

View File

@ -5,5 +5,12 @@ import {
export type JoinLeaveType = {
embed: EmbedField[],
success: boolean,
full: boolean
full: boolean,
justFilled: boolean
}
export type UrlIds = {
guildId: bigint,
channelId: bigint,
messageId: bigint
}

View File

@ -4,11 +4,12 @@ import {
LT, log
} from "../deps.ts";
import { JoinLeaveType } from "./lfgHandlers.d.ts";
import { JoinLeaveType, UrlIds } from "./lfgHandlers.d.ts";
import { BuildingLFG } from "./mod.d.ts";
import { LFGActivities } from "./games.ts";
import { determineTZ } from "./timeUtils.ts";
import { lfgStepQuestions } from "./constantCmds.ts";
import { jsonStringifyBig } from "./utils.ts";
export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise<BuildingLFG> => {
const currentLFG = (wipLFG.lfgMsg.embeds[0] || { fields: undefined }).fields || [
@ -225,7 +226,8 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
let lfgDate = `${today.getMonth() + 1}/${today.getDate()}`,
lfgTime = "",
lfgTZ = "",
lfgPeriod = "";
lfgPeriod = "",
overrodeTZ = false;
input.split(" ").forEach(c => {
if (c.includes("/")) {
@ -258,14 +260,14 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
} else if (c.match(/^\d/)) {
const tzIdx = c.search(/[a-zA-Z]/);
lfgTime = c.substr(0, tzIdx);
lfgTZ = determineTZ(c.substr(tzIdx));
[lfgTZ, overrodeTZ] = determineTZ(c.substr(tzIdx));
} else {
lfgTZ = determineTZ(c);
[lfgTZ, overrodeTZ] = determineTZ(c);
}
});
if (!lfgTZ) {
lfgTZ = determineTZ("ET");
[lfgTZ, overrodeTZ] = determineTZ("ET");
}
if (!lfgTime.includes(":")) {
@ -324,9 +326,9 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
try {
if (editFlag) {
wipLFG.lfgMsg = await wipLFG.lfgMsg.edit({
embed: {
embeds: [{
fields: currentLFG
}
}]
});
}
@ -336,7 +338,7 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
});
}
catch (e) {
log(LT.WARN, `Failed to edit active builder | ${wipLFG.userId}-${wipLFG.channelId} | ${JSON.stringify(e)}`);
log(LT.WARN, `Failed to edit active builder | ${wipLFG.userId}-${wipLFG.channelId} | ${jsonStringifyBig(e)}`);
}
return wipLFG;
@ -344,6 +346,7 @@ export const handleLFGStep = async (wipLFG: BuildingLFG, input: string): Promise
export const handleMemberJoin = (lfg: EmbedField[], member: DiscordenoMember, alternate: boolean): JoinLeaveType => {
let success = false;
let justFilled = false;
const userStr = `${member.username} - <@${member.id}>`;
@ -352,6 +355,7 @@ export const handleMemberJoin = (lfg: EmbedField[], member: DiscordenoMember, al
const maxMembers = parseInt(tempMembers[1]);
if (alternate && !lfg[5].value.includes(member.id.toString())) {
// remove from joined list
if (lfg[4].value.includes(member.id.toString())) {
const tempArr = lfg[4].value.split("\n");
const memberIdx = tempArr.findIndex(m => m.includes(member.id.toString()));
@ -371,11 +375,12 @@ export const handleMemberJoin = (lfg: EmbedField[], member: DiscordenoMember, al
}
success = true;
} else if (!alternate &&currentMembers < maxMembers && !lfg[4].value.includes(member.id.toString())) {
} else if (!alternate && currentMembers < maxMembers && !lfg[4].value.includes(member.id.toString())) {
// remove from alternate list
if (lfg[5].value.includes(member.id.toString())) {
const tempArr = lfg[5].value.split("\n");
const memberIdx = tempArr.findIndex(m => m.includes(member.id.toString()));
tempArr.splice(memberIdx, 1)
tempArr.splice(memberIdx, 1);
lfg[5].value = tempArr.join("\n") || "None";
}
@ -385,16 +390,34 @@ export const handleMemberJoin = (lfg: EmbedField[], member: DiscordenoMember, al
lfg[4].value += `\n${userStr}`;
}
currentMembers++;
justFilled = currentMembers === maxMembers;
lfg[4].name = `Members Joined: ${currentMembers}/${maxMembers}`;
success = true;
} else if (!alternate && currentMembers === maxMembers && !lfg[4].value.includes(member.id.toString())) {
// update user in alternate list to include the * to make them autojoin
if (lfg[5].value.includes(member.id.toString())) {
const tempArr = lfg[5].value.split("\n");
const memberIdx = tempArr.findIndex(m => m.includes(member.id.toString()));
tempArr[memberIdx] = `${tempArr[memberIdx]} *`;
lfg[5].value = tempArr.join("\n");
} else {
if (lfg[5].value === "None") {
lfg[5].value = `${userStr} *`;
} else {
lfg[5].value += `\n${userStr} *`;
}
success = true;
}
}
return {
embed: lfg,
success: success,
full: currentMembers === maxMembers
full: currentMembers === maxMembers,
justFilled: justFilled
};
};
@ -413,10 +436,25 @@ export const handleMemberLeave = (lfg: EmbedField[], member: DiscordenoMember):
tempArr.splice(memberIdx, 1);
lfg[4].value = tempArr.join("\n") || "None";
if (currentMembers) {
currentMembers--;
if (lfg[5].value.includes("*")) {
// find first * user and move them to the joined list
const tempArr2 = lfg[5].value.split("\n");
const memberToMoveIdx = tempArr2.findIndex(m => m.includes("*"))
let memberToMove = tempArr2[memberToMoveIdx];
memberToMove = memberToMove.substr(0, memberToMove.length - 2);
tempArr.push(memberToMove);
lfg[4].value = tempArr.join("\n") || "None";
// Remove them from the alt list
tempArr2.splice(memberToMoveIdx, 1);
lfg[5].value = tempArr2.join("\n") || "None";
} else {
// update count since no users were marked as *
if (currentMembers) {
currentMembers--;
}
lfg[4].name = `Members Joined: ${currentMembers}/${maxMembers}`;
}
lfg[4].name = `Members Joined: ${currentMembers}/${maxMembers}`;
success = true;
}
@ -433,6 +471,25 @@ export const handleMemberLeave = (lfg: EmbedField[], member: DiscordenoMember):
return {
embed: lfg,
success: success,
full: currentMembers === maxMembers
full: currentMembers === maxMembers,
justFilled: false
};
};
export const urlToIds = (url: string): UrlIds => {
const strIds = {
guildId: "",
channelId: "",
messageId: ""
};
url = url.toLowerCase();
[strIds.guildId, strIds.channelId, strIds.messageId] = url.substr((url.indexOf("channels") + 9)).split("/");
return {
guildId: BigInt(strIds.guildId),
channelId: BigInt(strIds.channelId),
messageId: BigInt(strIds.messageId)
};
};

14
src/mod.d.ts vendored
View File

@ -28,10 +28,12 @@ export type GuildPrefixes = {
prefix: string
}
// TEMP
export interface ButtonData {
/** with the value you defined for this component */
customId: string;
/** The type of this component */
componentType: 2;
export type GuildModRoles = {
guildId: bigint,
roleId: bigint
}
export type GuildCleanChannels = {
guildId: bigint,
channelId: bigint
}

View File

@ -1,16 +1,22 @@
export const determineTZ = (tz: string): string => {
export const determineTZ = (tz: string, userOverride = false): [string, boolean] => {
tz = tz.toUpperCase();
if (tz === "ET" || tz === "CT" || tz === "MT" || tz === "PT") {
let overrode = false;
const shortHandUSTZ = (tz === "ET" || tz === "CT" || tz === "MT" || tz === "PT");
const fullUSTZ = (tz.length === 3 && (tz.startsWith("E") || tz.startsWith("C") || tz.startsWith("M") || tz.startsWith("P")) && (tz.endsWith("DT") || tz.endsWith("ST")));
if (!userOverride && (shortHandUSTZ || fullUSTZ)) {
const today = new Date();
const jan = new Date(today.getFullYear(), 0, 1);
const jul = new Date(today.getFullYear(), 6, 1);
if (today.getTimezoneOffset() < Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset())) {
if (tz.includes("S")) overrode = true;
tz = `${tz.substr(0, 1)}DT`;
} else {
if (tz.includes("D")) overrode = true;
tz = `${tz.substr(0, 1)}ST`;
}
}
return tz;
return [tz, overrode];
};

14
src/utils.ts Normal file
View File

@ -0,0 +1,14 @@
export const jsonParseBig = (input: string) => {
return JSON.parse(input, (_key, value) => {
if (typeof value === "string" && /^\d+n$/.test(value)) {
return BigInt(value.substr(0, value.length - 1));
}
return value;
});
};
export const jsonStringifyBig = (input: any) => {
return JSON.stringify(input, (_key, value) =>
typeof value === "bigint" ? value.toString() + "n" : value
);
};