generated from nhcarrigan/template
feat: update this highly outdated app to use latest packages and custom configs (#1)
Reviewed-on: https://codeberg.org/nhcarrigan/tingle-bot/pulls/1 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit is contained in:
parent
1339b63378
commit
7c0bd7ad10
@ -1,73 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"es2020": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"plugin:prettier/recommended",
|
|
||||||
"plugin:jsdoc/recommended",
|
|
||||||
"plugin:import/recommended",
|
|
||||||
"plugin:import/typescript"
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 11,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": ["@typescript-eslint", "jsdoc", "import"],
|
|
||||||
"rules": {
|
|
||||||
"linebreak-style": ["error", "unix"],
|
|
||||||
"quotes": ["error", "double", { "allowTemplateLiterals": true }],
|
|
||||||
"semi": ["error", "always"],
|
|
||||||
"prefer-const": "error",
|
|
||||||
"eqeqeq": ["error", "always"],
|
|
||||||
"curly": ["error"],
|
|
||||||
"require-atomic-updates": ["error"],
|
|
||||||
"no-var": ["error"],
|
|
||||||
"camelcase": ["error"],
|
|
||||||
"init-declarations": ["error", "always"],
|
|
||||||
"require-await": ["error"],
|
|
||||||
"no-param-reassign": ["error"],
|
|
||||||
"jsdoc/require-jsdoc": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"require": {
|
|
||||||
"ArrowFunctionExpression": true,
|
|
||||||
"ClassDeclaration": true,
|
|
||||||
"ClassExpression": true,
|
|
||||||
"FunctionDeclaration": true,
|
|
||||||
"FunctionExpression": true,
|
|
||||||
"MethodDefinition": true
|
|
||||||
},
|
|
||||||
"publicOnly": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"jsdoc/require-description-complete-sentence": "error",
|
|
||||||
"import/first": "error",
|
|
||||||
"import/exports-last": "error",
|
|
||||||
"import/newline-after-import": "error",
|
|
||||||
"import/order": [
|
|
||||||
"error",
|
|
||||||
{
|
|
||||||
"groups": [
|
|
||||||
"builtin",
|
|
||||||
"external",
|
|
||||||
"internal",
|
|
||||||
"parent",
|
|
||||||
"sibling",
|
|
||||||
"index",
|
|
||||||
"object",
|
|
||||||
"type",
|
|
||||||
"unknown"
|
|
||||||
],
|
|
||||||
"newlines-between": "always",
|
|
||||||
"alphabetize": {
|
|
||||||
"order": "asc",
|
|
||||||
"caseInsensitive": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
.env
|
.env
|
||||||
/prod/
|
/prod/
|
||||||
|
/coverage/
|
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
},
|
||||||
|
"eslint.validate": ["typescript"]
|
||||||
|
}
|
13
eslint.config.js
Normal file
13
eslint.config.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import NaomisConifg from "@nhcarrigan/eslint-config";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...NaomisConifg,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/naming-convention": "off",
|
||||||
|
"max-lines": "off",
|
||||||
|
"max-lines-per-function": "off",
|
||||||
|
"max-statements": "off",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
7722
package-lock.json
generated
7722
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
53
package.json
53
package.json
@ -3,47 +3,34 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "eslint src --max-warnings 0",
|
"lint": "eslint src test --max-warnings 0",
|
||||||
"start": "node -r dotenv/config prod/index.js",
|
"start": "op run --env-file=./prod.env -- node prod/index.js",
|
||||||
"test": "ts-mocha -u tdd tests/**/*.spec.ts"
|
"test": "vitest run --coverage"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "See license in LICENSE.md",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "16.11.0",
|
"node": "20",
|
||||||
"npm": "8.0.0"
|
"pnpm": "9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.0",
|
"@nhcarrigan/eslint-config": "5.0.0-rc1",
|
||||||
"@types/mocha": "^9.1.0",
|
"@nhcarrigan/typescript-config": "4.0.0",
|
||||||
"@types/node-schedule": "^1.3.2",
|
"@types/node-schedule": "2.1.7",
|
||||||
"@types/sharp": "^0.29.5",
|
"@types/uuid": "10.0.0",
|
||||||
"@types/uuid": "^8.3.4",
|
"@vitest/coverage-istanbul": "2.1.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
"eslint": "9.11.1",
|
||||||
"@typescript-eslint/parser": "^5.10.2",
|
"typescript": "5.6.2",
|
||||||
"chai": "^4.3.6",
|
"vitest": "2.1.1"
|
||||||
"eslint": "^8.8.0",
|
|
||||||
"eslint-config-prettier": "^8.3.0",
|
|
||||||
"eslint-plugin-import": "^2.25.4",
|
|
||||||
"eslint-plugin-jsdoc": "^37.7.1",
|
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
|
||||||
"mocha": "^9.2.0",
|
|
||||||
"prettier": "^2.5.1",
|
|
||||||
"ts-mocha": "^9.0.2",
|
|
||||||
"typescript": "^4.5.5"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@discordjs/builders": "^0.12.0",
|
"discord.js": "14.16.2",
|
||||||
"@discordjs/rest": "^0.3.0",
|
"node-schedule": "2.1.1",
|
||||||
"@sentry/integrations": "^6.17.7",
|
"sharp": "0.33.5",
|
||||||
"@sentry/node": "^6.17.7",
|
"uuid": "10.0.0",
|
||||||
"discord.js": "^13.6.0",
|
"winston": "3.14.2"
|
||||||
"dotenv": "^16.0.0",
|
|
||||||
"node-schedule": "^2.1.0",
|
|
||||||
"sharp": "^0.30.1",
|
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"winston": "^3.6.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4999
pnpm-lock.yaml
generated
Normal file
4999
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
prod.env
Normal file
6
prod.env
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
DISCORD_TOKEN="op://Environment Variables - Naomi/Tingle Bot/token"
|
||||||
|
HOME_GUILD_ID="op://Environment Variables - Naomi/Tingle Bot/home_guild"
|
||||||
|
RUDANIA_ID="op://Environment Variables - Naomi/Tingle Bot/rudania_channel"
|
||||||
|
INARIKO_ID="op://Environment Variables - Naomi/Tingle Bot/inariko_channel"
|
||||||
|
VHINTL_ID="op://Environment Variables - Naomi/Tingle Bot/vhintl_channel"
|
||||||
|
DEBUG_HOOK="op://Environment Variables - Naomi/Tingle Bot/debug_hook"
|
@ -1,6 +0,0 @@
|
|||||||
import { forecast } from "./forecast";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: Migrate this to an automated import.
|
|
||||||
*/
|
|
||||||
export const CommandList = [forecast];
|
|
11
src/commands/_commandList.ts
Normal file
11
src/commands/_commandList.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { forecast } from "./forecast.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Migrate this to an automated import.
|
||||||
|
*/
|
||||||
|
export const commandList = [ forecast ];
|
@ -1,35 +1,44 @@
|
|||||||
import { SlashCommandBuilder } from "@discordjs/builders";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { SlashCommandBuilder } from "discord.js";
|
||||||
|
import { forecastChoices } from "../config/forecastChoices.js";
|
||||||
|
import { generateWeatherEmbed } from "../modules/generateWeatherEmbed.js";
|
||||||
|
import { getWeatherForecast } from "../modules/getWeatherForecast.js";
|
||||||
|
import { errorHandler } from "../utils/errorHandler.js";
|
||||||
|
import type { Command } from "../interfaces/commands/command.js";
|
||||||
|
import type { RegionName } from "../interfaces/weather/names/regionName.js";
|
||||||
|
|
||||||
import { ForecastChoices } from "../config/ForecastChoices";
|
const isRegionName = (region: string): region is RegionName => {
|
||||||
import { Command } from "../interfaces/commands/Command";
|
return [ "Rudania", "Inariko", "Vhintl" ].includes(region);
|
||||||
import { RegionName } from "../interfaces/weather/names/RegionName";
|
};
|
||||||
import { generateWeatherEmbed } from "../modules/generateWeatherEmbed";
|
|
||||||
import { getWeatherForecast } from "../modules/getWeatherForecast";
|
|
||||||
import { errorHandler } from "../utils/errorHandler";
|
|
||||||
|
|
||||||
export const forecast: Command = {
|
export const forecast: Command = {
|
||||||
data: new SlashCommandBuilder()
|
data: new SlashCommandBuilder().
|
||||||
.setName("forecast")
|
setName("forecast").
|
||||||
.setDescription("Get the weather forecast for a specific region.")
|
setDescription("Get the weather forecast for a specific region.").
|
||||||
.addStringOption((option) =>
|
addStringOption((option) => {
|
||||||
option
|
return option.
|
||||||
.setName("region")
|
setName("region").
|
||||||
.setDescription("The region to get a forecast for.")
|
setDescription("The region to get a forecast for.").
|
||||||
.setRequired(true)
|
setRequired(true).
|
||||||
.addChoices(ForecastChoices)
|
addChoices(forecastChoices);
|
||||||
),
|
}),
|
||||||
run: async (interaction, CACHE) => {
|
run: async(interaction, cache) => {
|
||||||
try {
|
try {
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
const region = interaction.options.getString(
|
const region = interaction.options.getString("region", true);
|
||||||
"region",
|
if (!isRegionName(region)) {
|
||||||
true
|
await interaction.editReply({ content: `Invalid region: ${region}` });
|
||||||
) as RegionName;
|
return;
|
||||||
const forecast = CACHE[region] || getWeatherForecast(region);
|
}
|
||||||
const response = await generateWeatherEmbed(forecast);
|
const forecastResult = cache[region] ?? getWeatherForecast(region);
|
||||||
|
const response = await generateWeatherEmbed(forecastResult);
|
||||||
await interaction.editReply(response);
|
await interaction.editReply(response);
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
const response = await errorHandler(err, "forecast command");
|
const response = await errorHandler(error, "forecast command");
|
||||||
await interaction.editReply(response);
|
await interaction.editReply(response);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
import { RegionName } from "../interfaces/weather/names/RegionName";
|
|
||||||
|
|
||||||
export const ForecastChoices: [string, RegionName][] = [
|
|
||||||
["Rudania", "Rudania"],
|
|
||||||
["Inariko", "Inariko"],
|
|
||||||
["Vhintl", "Vhintl"],
|
|
||||||
];
|
|
@ -1,3 +0,0 @@
|
|||||||
import { IntentsString } from "discord.js";
|
|
||||||
|
|
||||||
export const IntentOptions: IntentsString[] = ["GUILDS"];
|
|
14
src/config/forecastChoices.ts
Normal file
14
src/config/forecastChoices.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { RegionName } from "../interfaces/weather/names/regionName.js";
|
||||||
|
import type { APIApplicationCommandOptionChoice } from "discord.js";
|
||||||
|
|
||||||
|
export const forecastChoices:
|
||||||
|
Array<APIApplicationCommandOptionChoice<RegionName>> = [
|
||||||
|
{ name: "Rudania", value: "Rudania" },
|
||||||
|
{ name: "Inariko", value: "Inariko" },
|
||||||
|
{ name: "Vhintl", value: "Vhintl" },
|
||||||
|
];
|
8
src/config/intentOptions.ts
Normal file
8
src/config/intentOptions.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { GatewayIntentBits } from "discord.js";
|
||||||
|
|
||||||
|
export const intentOptions = [ GatewayIntentBits.Guilds ];
|
@ -1,65 +1,50 @@
|
|||||||
import { Precipitation } from "../../interfaces/weather/Precipitation";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { Precipitation } from "../../interfaces/weather/precipitation.js";
|
||||||
|
|
||||||
export const precipitations: Precipitation[] = [
|
export const precipitations: Array<Precipitation> = [
|
||||||
{
|
{
|
||||||
name: "Blizzard",
|
|
||||||
temps: ["Cold", "Freezing", "Frigid"],
|
|
||||||
winds: ["Strong", "Gale", "Storm", "Hurricane"],
|
|
||||||
emote: "❄️",
|
emote: "❄️",
|
||||||
|
name: "Blizzard",
|
||||||
|
temps: [ "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: [ "Strong", "Gale", "Storm", "Hurricane" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cinder Storm",
|
|
||||||
temps: "any",
|
|
||||||
winds: ["Strong", "Gale", "Storm", "Hurricane"],
|
|
||||||
emote: "🔥",
|
emote: "🔥",
|
||||||
|
name: "Cinder Storm",
|
||||||
|
temps: "any",
|
||||||
|
winds: [ "Strong", "Gale", "Storm", "Hurricane" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cloudy",
|
|
||||||
temps: "any",
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "☁️",
|
emote: "☁️",
|
||||||
|
name: "Cloudy",
|
||||||
|
temps: "any",
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fog",
|
|
||||||
temps: "any",
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌫️",
|
emote: "🌫️",
|
||||||
|
name: "Fog",
|
||||||
|
temps: "any",
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Hail",
|
emote: "☁️🧊",
|
||||||
|
name: "Hail",
|
||||||
temps: "any",
|
temps: "any",
|
||||||
winds: "any",
|
winds: "any",
|
||||||
emote: "☁️🧊",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Heat Lightning",
|
|
||||||
temps: ["Warm", "Hot", "Scorching", "Heat Wave"],
|
|
||||||
winds: "any",
|
|
||||||
emote: "🌡️⚡",
|
emote: "🌡️⚡",
|
||||||
|
name: "Heat Lightning",
|
||||||
|
temps: [ "Warm", "Hot", "Scorching", "Heat Wave" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Heavy Rain",
|
|
||||||
temps: [
|
|
||||||
"Brisk",
|
|
||||||
"Cool",
|
|
||||||
"Mild",
|
|
||||||
"Perfect",
|
|
||||||
"Warm",
|
|
||||||
"Hot",
|
|
||||||
"Scorching",
|
|
||||||
"Heat Wave",
|
|
||||||
],
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌧️",
|
emote: "🌧️",
|
||||||
},
|
name: "Heavy Rain",
|
||||||
{
|
|
||||||
name: "Heavy Snow",
|
|
||||||
temps: ["Chilly", "Cold", "Freezing", "Frigid"],
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌨️",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Light Rain",
|
|
||||||
temps: [
|
temps: [
|
||||||
"Brisk",
|
"Brisk",
|
||||||
"Cool",
|
"Cool",
|
||||||
@ -70,56 +55,77 @@ export const precipitations: Precipitation[] = [
|
|||||||
"Scorching",
|
"Scorching",
|
||||||
"Heat Wave",
|
"Heat Wave",
|
||||||
],
|
],
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
emote: "🌨️",
|
||||||
|
name: "Heavy Snow",
|
||||||
|
temps: [ "Chilly", "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
|
},
|
||||||
|
{
|
||||||
emote: "☔",
|
emote: "☔",
|
||||||
|
name: "Light Rain",
|
||||||
|
temps: [
|
||||||
|
"Brisk",
|
||||||
|
"Cool",
|
||||||
|
"Mild",
|
||||||
|
"Perfect",
|
||||||
|
"Warm",
|
||||||
|
"Hot",
|
||||||
|
"Scorching",
|
||||||
|
"Heat Wave",
|
||||||
|
],
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Light Snow",
|
|
||||||
temps: ["Chilly", "Cold", "Freezing", "Frigid"],
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌨️",
|
emote: "🌨️",
|
||||||
|
name: "Light Snow",
|
||||||
|
temps: [ "Chilly", "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Partly Cloudy",
|
|
||||||
temps: "any",
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "⛅",
|
emote: "⛅",
|
||||||
|
name: "Partly Cloudy",
|
||||||
|
temps: "any",
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rain",
|
|
||||||
temps: [
|
|
||||||
"Brisk",
|
|
||||||
"Cool",
|
|
||||||
"Mild",
|
|
||||||
"Perfect",
|
|
||||||
"Warm",
|
|
||||||
"Hot",
|
|
||||||
"Scorching",
|
|
||||||
"Heat Wave",
|
|
||||||
],
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌧️",
|
emote: "🌧️",
|
||||||
|
name: "Rain",
|
||||||
|
temps: [
|
||||||
|
"Brisk",
|
||||||
|
"Cool",
|
||||||
|
"Mild",
|
||||||
|
"Perfect",
|
||||||
|
"Warm",
|
||||||
|
"Hot",
|
||||||
|
"Scorching",
|
||||||
|
"Heat Wave",
|
||||||
|
],
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Rainbow",
|
|
||||||
temps: "any",
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌈",
|
emote: "🌈",
|
||||||
|
name: "Rainbow",
|
||||||
|
temps: "any",
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sleet",
|
|
||||||
temps: ["Brisk", "Chilly"],
|
|
||||||
winds: "any",
|
|
||||||
emote: "☁️🧊",
|
emote: "☁️🧊",
|
||||||
|
name: "Sleet",
|
||||||
|
temps: [ "Brisk", "Chilly" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Snow",
|
|
||||||
temps: ["Chilly", "Cold", "Freezing", "Frigid"],
|
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
|
||||||
emote: "🌨️",
|
emote: "🌨️",
|
||||||
|
name: "Snow",
|
||||||
|
temps: [ "Chilly", "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sun Shower",
|
emote: "🌦️",
|
||||||
|
name: "Sun Shower",
|
||||||
temps: [
|
temps: [
|
||||||
"Brisk",
|
"Brisk",
|
||||||
"Cool",
|
"Cool",
|
||||||
@ -130,23 +136,23 @@ export const precipitations: Precipitation[] = [
|
|||||||
"Scorching",
|
"Scorching",
|
||||||
"Heat Wave",
|
"Heat Wave",
|
||||||
],
|
],
|
||||||
winds: ["Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm"],
|
winds: [ "Gale", "Strong", "Fresh", "Moderate", "Breeze", "Calm" ],
|
||||||
emote: "🌦️",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sunny",
|
emote: "☀️",
|
||||||
|
name: "Sunny",
|
||||||
temps: "any",
|
temps: "any",
|
||||||
winds: "any",
|
winds: "any",
|
||||||
emote: "☀️",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Thundersnow",
|
|
||||||
temps: ["Chilly", "Cold", "Freezing", "Frigid"],
|
|
||||||
winds: "any",
|
|
||||||
emote: "🌨️⚡",
|
emote: "🌨️⚡",
|
||||||
|
name: "Thundersnow",
|
||||||
|
temps: [ "Chilly", "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Thunderstorm",
|
emote: "⛈️",
|
||||||
|
name: "Thunderstorm",
|
||||||
temps: [
|
temps: [
|
||||||
"Brisk",
|
"Brisk",
|
||||||
"Cool",
|
"Cool",
|
||||||
@ -158,6 +164,5 @@ export const precipitations: Precipitation[] = [
|
|||||||
"Heat Wave",
|
"Heat Wave",
|
||||||
],
|
],
|
||||||
winds: "any",
|
winds: "any",
|
||||||
emote: "⛈️",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { RegionRestriction } from "../../../interfaces/weather/regions/RegionRestriction";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { RegionRestriction }
|
||||||
|
from "../../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
|
||||||
export const inarikoSeasons: RegionRestriction[] = [
|
export const inarikoSeasons: Array<RegionRestriction> = [
|
||||||
{
|
{
|
||||||
season: "Winter",
|
|
||||||
temps: ["Frigid", "Freezing", "Cold", "Chilly", "Brisk"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
|
||||||
"Breeze",
|
|
||||||
"Moderate",
|
|
||||||
"Fresh",
|
|
||||||
"Strong",
|
|
||||||
"Gale",
|
|
||||||
"Storm",
|
|
||||||
"Hurricane",
|
|
||||||
],
|
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Blizzard",
|
"Blizzard",
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
@ -31,12 +25,10 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Thundersnow",
|
"Thundersnow",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
special: ["Avalanche", "Meteor Shower", "Blight Rain"],
|
season: "Winter",
|
||||||
},
|
special: [ "Avalanche", "Meteor Shower", "Blight Rain" ],
|
||||||
{
|
temps: [ "Frigid", "Freezing", "Cold", "Chilly", "Brisk" ],
|
||||||
season: "Spring",
|
wind: [
|
||||||
temps: ["Chilly", "Brisk", "Cool", "Mild", "Perfect", "Warm"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -46,6 +38,8 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -62,6 +56,7 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Spring",
|
||||||
special: [
|
special: [
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
"Flood",
|
"Flood",
|
||||||
@ -70,11 +65,8 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Meteor Shower",
|
"Meteor Shower",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Chilly", "Brisk", "Cool", "Mild", "Perfect", "Warm" ],
|
||||||
{
|
wind: [
|
||||||
season: "Summer",
|
|
||||||
temps: ["Mild", "Perfect", "Warm", "Hot", "Scorching"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -84,6 +76,8 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -98,6 +92,7 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Summer",
|
||||||
special: [
|
special: [
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
"Flood",
|
"Flood",
|
||||||
@ -107,11 +102,8 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Mild", "Perfect", "Warm", "Hot", "Scorching" ],
|
||||||
{
|
wind: [
|
||||||
season: "Fall",
|
|
||||||
temps: ["Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -121,6 +113,8 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Blizzard",
|
"Blizzard",
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
@ -140,6 +134,7 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Thundersnow",
|
"Thundersnow",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Fall",
|
||||||
special: [
|
special: [
|
||||||
"Avalanche",
|
"Avalanche",
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
@ -150,5 +145,16 @@ export const inarikoSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
|
temps: [ "Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect" ],
|
||||||
|
wind: [
|
||||||
|
"Calm",
|
||||||
|
"Breeze",
|
||||||
|
"Moderate",
|
||||||
|
"Fresh",
|
||||||
|
"Strong",
|
||||||
|
"Gale",
|
||||||
|
"Storm",
|
||||||
|
"Hurricane",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { RegionRestriction } from "../../../interfaces/weather/regions/RegionRestriction";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { RegionRestriction }
|
||||||
|
from "../../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
|
||||||
export const rudaniaSeasons: RegionRestriction[] = [
|
export const rudaniaSeasons: Array<RegionRestriction> = [
|
||||||
{
|
{
|
||||||
season: "Winter",
|
|
||||||
temps: ["Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
|
||||||
"Breeze",
|
|
||||||
"Moderate",
|
|
||||||
"Fresh",
|
|
||||||
"Strong",
|
|
||||||
"Gale",
|
|
||||||
"Storm",
|
|
||||||
"Hurricane",
|
|
||||||
],
|
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -30,12 +24,10 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
special: ["Flood", "Meteor Shower", "Rock Slide", "Blight Rain"],
|
season: "Winter",
|
||||||
},
|
special: [ "Flood", "Meteor Shower", "Rock Slide", "Blight Rain" ],
|
||||||
{
|
temps: [ "Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect" ],
|
||||||
season: "Spring",
|
wind: [
|
||||||
temps: ["Brisk", "Cool", "Mild", "Perfect", "Warm", "Hot", "Scorching"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -45,6 +37,8 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cinder Storm",
|
"Cinder Storm",
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
@ -59,6 +53,7 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Spring",
|
||||||
special: [
|
special: [
|
||||||
"Drought",
|
"Drought",
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
@ -68,11 +63,8 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Brisk", "Cool", "Mild", "Perfect", "Warm", "Hot", "Scorching" ],
|
||||||
{
|
wind: [
|
||||||
season: "Summer",
|
|
||||||
temps: ["Mild", "Perfect", "Warm", "Hot", "Scorching", "Heat Wave"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -82,6 +74,8 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cinder Storm",
|
"Cinder Storm",
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
@ -98,6 +92,7 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Summer",
|
||||||
special: [
|
special: [
|
||||||
"Drought",
|
"Drought",
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
@ -109,11 +104,8 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Rock Slide",
|
"Rock Slide",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Mild", "Perfect", "Warm", "Hot", "Scorching", "Heat Wave" ],
|
||||||
{
|
wind: [
|
||||||
season: "Fall",
|
|
||||||
temps: ["Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -123,6 +115,8 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cinder Storm",
|
"Cinder Storm",
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
@ -140,6 +134,7 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Fall",
|
||||||
special: [
|
special: [
|
||||||
"Drought",
|
"Drought",
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
@ -149,5 +144,16 @@ export const rudaniaSeasons: RegionRestriction[] = [
|
|||||||
"Rock Slide",
|
"Rock Slide",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
|
temps: [ "Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect" ],
|
||||||
|
wind: [
|
||||||
|
"Calm",
|
||||||
|
"Breeze",
|
||||||
|
"Moderate",
|
||||||
|
"Fresh",
|
||||||
|
"Strong",
|
||||||
|
"Gale",
|
||||||
|
"Storm",
|
||||||
|
"Hurricane",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { RegionRestriction } from "../../../interfaces/weather/regions/RegionRestriction";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { RegionRestriction }
|
||||||
|
from "../../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
|
||||||
export const vhintlSeasons: RegionRestriction[] = [
|
export const vhintlSeasons: Array<RegionRestriction> = [
|
||||||
{
|
{
|
||||||
season: "Winter",
|
|
||||||
temps: ["Cold", "Chilly", "Brisk", "Cool", "Mild"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
|
||||||
"Breeze",
|
|
||||||
"Moderate",
|
|
||||||
"Fresh",
|
|
||||||
"Strong",
|
|
||||||
"Gale",
|
|
||||||
"Storm",
|
|
||||||
"Hurricane",
|
|
||||||
],
|
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -29,12 +23,10 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Thundersnow",
|
"Thundersnow",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
special: ["Fairy Circle", "Meteor Shower", "Blight Rain"],
|
season: "Winter",
|
||||||
},
|
special: [ "Fairy Circle", "Meteor Shower", "Blight Rain" ],
|
||||||
{
|
temps: [ "Cold", "Chilly", "Brisk", "Cool", "Mild" ],
|
||||||
season: "Spring",
|
wind: [
|
||||||
temps: ["Chilly", "Brisk", "Cool", "Mild", "Perfect", "Warm", "Hot"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -44,6 +36,8 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -59,6 +53,7 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Spring",
|
||||||
special: [
|
special: [
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
"Flood",
|
"Flood",
|
||||||
@ -68,11 +63,8 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Chilly", "Brisk", "Cool", "Mild", "Perfect", "Warm", "Hot" ],
|
||||||
{
|
wind: [
|
||||||
season: "Summer",
|
|
||||||
temps: ["Mild", "Perfect", "Warm", "Hot", "Scorching", "Heat Wave"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -82,6 +74,8 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -96,6 +90,7 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Sunny",
|
"Sunny",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Summer",
|
||||||
special: [
|
special: [
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
"Flood",
|
"Flood",
|
||||||
@ -105,11 +100,8 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
},
|
temps: [ "Mild", "Perfect", "Warm", "Hot", "Scorching", "Heat Wave" ],
|
||||||
{
|
wind: [
|
||||||
season: "Fall",
|
|
||||||
temps: ["Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect"],
|
|
||||||
wind: [
|
|
||||||
"Calm",
|
"Calm",
|
||||||
"Breeze",
|
"Breeze",
|
||||||
"Moderate",
|
"Moderate",
|
||||||
@ -119,6 +111,8 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Storm",
|
"Storm",
|
||||||
"Hurricane",
|
"Hurricane",
|
||||||
],
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
precipitation: [
|
precipitation: [
|
||||||
"Cloudy",
|
"Cloudy",
|
||||||
"Fog",
|
"Fog",
|
||||||
@ -134,6 +128,7 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Thundersnow",
|
"Thundersnow",
|
||||||
"Thunderstorm",
|
"Thunderstorm",
|
||||||
],
|
],
|
||||||
|
season: "Fall",
|
||||||
special: [
|
special: [
|
||||||
"Fairy Circle",
|
"Fairy Circle",
|
||||||
"Flood",
|
"Flood",
|
||||||
@ -143,5 +138,16 @@ export const vhintlSeasons: RegionRestriction[] = [
|
|||||||
"Muggy",
|
"Muggy",
|
||||||
"Blight Rain",
|
"Blight Rain",
|
||||||
],
|
],
|
||||||
|
temps: [ "Cold", "Chilly", "Brisk", "Cool", "Mild", "Perfect" ],
|
||||||
|
wind: [
|
||||||
|
"Calm",
|
||||||
|
"Breeze",
|
||||||
|
"Moderate",
|
||||||
|
"Fresh",
|
||||||
|
"Strong",
|
||||||
|
"Gale",
|
||||||
|
"Storm",
|
||||||
|
"Hurricane",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
import { Special } from "../../interfaces/weather/Special";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { Special } from "../../interfaces/weather/special.js";
|
||||||
|
|
||||||
export const specials: Special[] = [
|
export const specials: Array<Special> = [
|
||||||
{
|
{
|
||||||
name: "Avalanche",
|
|
||||||
temps: ["Chilly", "Cold", "Freezing", "Frigid"],
|
|
||||||
winds: "any",
|
|
||||||
precipitations: ["Snow"],
|
|
||||||
description:
|
description:
|
||||||
"There has been an avalanche and some roads are blocked! Travel to and from this village today is impossible.",
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
emote: "🏔️",
|
"There has been an avalanche and some roads are blocked! Travel to and from this village today is impossible.",
|
||||||
|
emote: "🏔️",
|
||||||
|
name: "Avalanche",
|
||||||
|
precipitations: [ "Snow" ],
|
||||||
|
temps: [ "Chilly", "Cold", "Freezing", "Frigid" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Blight Rain",
|
description:
|
||||||
temps: [
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
|
"Blighted rain falls from the sky, staining the ground and creating sickly maroon-tinged puddles... if you roll for gathering today you must also `/tableroll blightrain` to see if you get infected! If you miss doing this roll with your gather, blighting will be automatic.",
|
||||||
|
emote: "🌧️🧿",
|
||||||
|
name: "Blight Rain",
|
||||||
|
precipitations: [ "Rain" ],
|
||||||
|
temps: [
|
||||||
"Brisk",
|
"Brisk",
|
||||||
"Cool",
|
"Cool",
|
||||||
"Mild",
|
"Mild",
|
||||||
@ -23,32 +34,35 @@ export const specials: Special[] = [
|
|||||||
"Heat Wave",
|
"Heat Wave",
|
||||||
],
|
],
|
||||||
winds: "any",
|
winds: "any",
|
||||||
precipitations: ["Rain"],
|
|
||||||
description:
|
|
||||||
"Blighted rain falls from the sky, staining the ground and creating sickly maroon-tinged puddles... if you roll for gathering today you must also `/tableroll blightrain` to see if you get infected! If you miss doing this roll with your gather, blighting will be automatic.",
|
|
||||||
emote: "🌧️🧿",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Drought",
|
|
||||||
temps: ["Scorching", "Heat Wave"],
|
|
||||||
winds: "any",
|
|
||||||
precipitations: ["Sunny"],
|
|
||||||
description:
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
"A drought has dried up the smaller vegetation surrounding the village... any plants or mushrooms rolled today are found dead and will not be gathered.",
|
"A drought has dried up the smaller vegetation surrounding the village... any plants or mushrooms rolled today are found dead and will not be gathered.",
|
||||||
emote: "🌵",
|
emote: "🌵",
|
||||||
|
name: "Drought",
|
||||||
|
precipitations: [ "Sunny" ],
|
||||||
|
temps: [ "Scorching", "Heat Wave" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fairy Circle",
|
|
||||||
temps: "any",
|
|
||||||
winds: "any",
|
|
||||||
precipitations: "any",
|
|
||||||
description:
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
"Fairy circles have popped up all over Hyrule! All residents and visitors may use `/tableroll fairycircle` to gather mushrooms today!",
|
"Fairy circles have popped up all over Hyrule! All residents and visitors may use `/tableroll fairycircle` to gather mushrooms today!",
|
||||||
emote: "🍄",
|
emote: "🍄",
|
||||||
|
name: "Fairy Circle",
|
||||||
|
precipitations: "any",
|
||||||
|
temps: "any",
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Flood",
|
description:
|
||||||
temps: [
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
|
"There has been a Flood! Travelling to and from this village is impossible today due to the danger.",
|
||||||
|
emote: "🌊",
|
||||||
|
name: "Flood",
|
||||||
|
precipitations: [ "Rain" ],
|
||||||
|
temps: [
|
||||||
"Cold",
|
"Cold",
|
||||||
"Chilly",
|
"Chilly",
|
||||||
"Brisk",
|
"Brisk",
|
||||||
@ -61,54 +75,55 @@ export const specials: Special[] = [
|
|||||||
"Heat Wave",
|
"Heat Wave",
|
||||||
],
|
],
|
||||||
winds: "any",
|
winds: "any",
|
||||||
precipitations: ["Rain"],
|
|
||||||
description:
|
|
||||||
"There has been a Flood! Travelling to and from this village is impossible today due to the danger.",
|
|
||||||
emote: "🌊",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Flower Bloom",
|
|
||||||
temps: ["Perfect", "Warm", "Hot", "Scorching", "Heat Wave"],
|
|
||||||
winds: "any",
|
|
||||||
precipitations: "any",
|
|
||||||
description:
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
"An overabundance of plants and flowers have been spotted growing in and around the village! All residents and visitors may `/tableroll flowerbloom` to gather today!",
|
"An overabundance of plants and flowers have been spotted growing in and around the village! All residents and visitors may `/tableroll flowerbloom` to gather today!",
|
||||||
emote: "🌼",
|
emote: "🌼",
|
||||||
|
name: "Flower Bloom",
|
||||||
|
precipitations: "any",
|
||||||
|
temps: [ "Perfect", "Warm", "Hot", "Scorching", "Heat Wave" ],
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Jubilee",
|
|
||||||
temps: "any",
|
|
||||||
winds: "any",
|
|
||||||
precipitations: "any",
|
|
||||||
description:
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
"Fish are practically jumping out of the water! All residents and visitors may `/tableroll jubilee` to catch some fish!",
|
"Fish are practically jumping out of the water! All residents and visitors may `/tableroll jubilee` to catch some fish!",
|
||||||
emote: "🐟",
|
emote: "🐟",
|
||||||
},
|
name: "Jubilee",
|
||||||
{
|
|
||||||
name: "Meteor Shower",
|
|
||||||
temps: "any",
|
|
||||||
winds: "any",
|
|
||||||
precipitations: ["Sunny"],
|
|
||||||
description:
|
|
||||||
"Shooting starts have been spotted streaking through the sky! Quick, all residents and visitors make a wish and use `/tableroll meteorshower` for a chance to find a star fragment!",
|
|
||||||
emote: "☄️",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Muggy",
|
|
||||||
temps: ["Perfect", "Warm", "Hot", "Scorching", "Heat Wave"],
|
|
||||||
winds: "any",
|
|
||||||
precipitations: ["Rain", "Fog", "Cloudy"],
|
|
||||||
description:
|
|
||||||
"Oof! Sure is humid today! Critters are out and about more than usual. All residents and visitors may use `/tableroll muggy` to catch some critters!",
|
|
||||||
emote: "🐛",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Rock Slide",
|
|
||||||
temps: "any",
|
|
||||||
winds: "any",
|
|
||||||
precipitations: "any",
|
precipitations: "any",
|
||||||
|
temps: "any",
|
||||||
|
winds: "any",
|
||||||
|
},
|
||||||
|
{
|
||||||
description:
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
|
"Shooting starts have been spotted streaking through the sky! Quick, all residents and visitors make a wish and use `/tableroll meteorshower` for a chance to find a star fragment!",
|
||||||
|
emote: "☄️",
|
||||||
|
name: "Meteor Shower",
|
||||||
|
precipitations: [ "Sunny" ],
|
||||||
|
temps: "any",
|
||||||
|
winds: "any",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
|
"Oof! Sure is humid today! Critters are out and about more than usual. All residents and visitors may use `/tableroll muggy` to catch some critters!",
|
||||||
|
emote: "🐛",
|
||||||
|
name: "Muggy",
|
||||||
|
precipitations: [ "Rain", "Fog", "Cloudy" ],
|
||||||
|
temps: [ "Perfect", "Warm", "Hot", "Scorching", "Heat Wave" ],
|
||||||
|
winds: "any",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
// eslint-disable-next-line stylistic/max-len -- We can't break this string up.
|
||||||
"Oh no there's been a rock slide! Travelling to and from this village is impossible today. All residents and visitors may use `/tableroll rockslide` to help clear the road! You might just find something interesting while you work...",
|
"Oh no there's been a rock slide! Travelling to and from this village is impossible today. All residents and visitors may use `/tableroll rockslide` to help clear the road! You might just find something interesting while you work...",
|
||||||
emote: "⛏️",
|
emote: "⛏️",
|
||||||
|
name: "Rock Slide",
|
||||||
|
precipitations: "any",
|
||||||
|
temps: "any",
|
||||||
|
winds: "any",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,76 +1,81 @@
|
|||||||
import { Temperature } from "../../interfaces/weather/Temperature";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { Temperature } from "../../interfaces/weather/temperature.js";
|
||||||
|
|
||||||
export const temperatures: Temperature[] = [
|
export const temperatures: Array<Temperature> = [
|
||||||
{
|
{
|
||||||
|
celsius: -18,
|
||||||
|
emote: "🥶",
|
||||||
fahrenheit: 0,
|
fahrenheit: 0,
|
||||||
celsius: -18,
|
name: "Frigid",
|
||||||
name: "Frigid",
|
|
||||||
emote: "🥶",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: -14,
|
||||||
|
emote: "🐧",
|
||||||
fahrenheit: 8,
|
fahrenheit: 8,
|
||||||
celsius: -14,
|
name: "Freezing",
|
||||||
name: "Freezing",
|
|
||||||
emote: "🐧",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: -4,
|
||||||
|
emote: "☃️",
|
||||||
fahrenheit: 24,
|
fahrenheit: 24,
|
||||||
celsius: -4,
|
name: "Cold",
|
||||||
name: "Cold",
|
|
||||||
emote: "☃️",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 2,
|
||||||
|
emote: "🧊",
|
||||||
fahrenheit: 36,
|
fahrenheit: 36,
|
||||||
celsius: 2,
|
name: "Chilly",
|
||||||
name: "Chilly",
|
|
||||||
emote: "🧊",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 6,
|
||||||
|
emote: "🔷",
|
||||||
fahrenheit: 44,
|
fahrenheit: 44,
|
||||||
celsius: 6,
|
name: "Brisk",
|
||||||
name: "Brisk",
|
|
||||||
emote: "🔷",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 11,
|
||||||
|
emote: "🆒",
|
||||||
fahrenheit: 52,
|
fahrenheit: 52,
|
||||||
celsius: 11,
|
name: "Cool",
|
||||||
name: "Cool",
|
|
||||||
emote: "🆒",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 16,
|
||||||
|
emote: "😐",
|
||||||
fahrenheit: 61,
|
fahrenheit: 61,
|
||||||
celsius: 16,
|
name: "Mild",
|
||||||
name: "Mild",
|
|
||||||
emote: "😐",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 22,
|
||||||
|
emote: "👌",
|
||||||
fahrenheit: 72,
|
fahrenheit: 72,
|
||||||
celsius: 22,
|
name: "Perfect",
|
||||||
name: "Perfect",
|
|
||||||
emote: "👌",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 28,
|
||||||
|
emote: "🌡️",
|
||||||
fahrenheit: 24,
|
fahrenheit: 24,
|
||||||
celsius: 28,
|
name: "Warm",
|
||||||
name: "Warm",
|
|
||||||
emote: "🌡️",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 32,
|
||||||
|
emote: "🌶️",
|
||||||
fahrenheit: 89,
|
fahrenheit: 89,
|
||||||
celsius: 32,
|
name: "Hot",
|
||||||
name: "Hot",
|
|
||||||
emote: "🌶️",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 36,
|
||||||
|
emote: "🥵",
|
||||||
fahrenheit: 97,
|
fahrenheit: 97,
|
||||||
celsius: 36,
|
name: "Scorching",
|
||||||
name: "Scorching",
|
|
||||||
emote: "🥵",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
celsius: 38,
|
||||||
|
emote: "💯",
|
||||||
fahrenheit: 100,
|
fahrenheit: 100,
|
||||||
celsius: 38,
|
name: "Heat Wave",
|
||||||
name: "Heat Wave",
|
|
||||||
emote: "💯",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,52 +1,57 @@
|
|||||||
import { Wind } from "../../interfaces/weather/Wind";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { Wind } from "../../interfaces/weather/wind.js";
|
||||||
|
|
||||||
export const winds: Wind[] = [
|
export const winds: Array<Wind> = [
|
||||||
{
|
{
|
||||||
name: "Calm",
|
emote: "😌",
|
||||||
lowSpeed: 0,
|
|
||||||
highSpeed: 1,
|
highSpeed: 1,
|
||||||
emote: "😌",
|
lowSpeed: 0,
|
||||||
|
name: "Calm",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Breeze",
|
emote: "🎐",
|
||||||
lowSpeed: 2,
|
|
||||||
highSpeed: 12,
|
highSpeed: 12,
|
||||||
emote: "🎐",
|
lowSpeed: 2,
|
||||||
|
name: "Breeze",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Moderate",
|
emote: "🍃",
|
||||||
lowSpeed: 13,
|
|
||||||
highSpeed: 30,
|
highSpeed: 30,
|
||||||
emote: "🍃",
|
lowSpeed: 13,
|
||||||
|
name: "Moderate",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fresh",
|
emote: "🌬️",
|
||||||
lowSpeed: 31,
|
|
||||||
highSpeed: 40,
|
highSpeed: 40,
|
||||||
emote: "🌬️",
|
lowSpeed: 31,
|
||||||
|
name: "Fresh",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Strong",
|
emote: "💫",
|
||||||
lowSpeed: 41,
|
|
||||||
highSpeed: 62,
|
highSpeed: 62,
|
||||||
emote: "💫",
|
lowSpeed: 41,
|
||||||
|
name: "Strong",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Gale",
|
emote: "💨",
|
||||||
lowSpeed: 63,
|
|
||||||
highSpeed: 87,
|
highSpeed: 87,
|
||||||
emote: "💨",
|
lowSpeed: 63,
|
||||||
|
name: "Gale",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Storm",
|
emote: "🌀",
|
||||||
lowSpeed: 88,
|
|
||||||
highSpeed: 117,
|
highSpeed: 117,
|
||||||
emote: "🌀",
|
lowSpeed: 88,
|
||||||
|
name: "Storm",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Hurricane",
|
emote: "🌪️",
|
||||||
lowSpeed: 118,
|
|
||||||
highSpeed: 150,
|
highSpeed: 150,
|
||||||
emote: "🌪️",
|
lowSpeed: 118,
|
||||||
|
name: "Hurricane",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -1,21 +1,27 @@
|
|||||||
import { Client } from "discord.js";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { WeatherCache } from "../interfaces/WeatherCache";
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
import { onInteraction } from "./handlers/onInteraction";
|
*/
|
||||||
import { onReady } from "./handlers/onReady";
|
import { onInteraction } from "./handlers/onInteraction.js";
|
||||||
|
import { onReady } from "./handlers/onReady.js";
|
||||||
|
import type { WeatherCache } from "../interfaces/weatherCache.js";
|
||||||
|
import type { Client } from "discord.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mounts listeners for the Discord gateway events.
|
* Mounts listeners for the Discord gateway events.
|
||||||
*
|
* @param bot - The bot's discord instance.
|
||||||
* @param {Client} BOT The bot's discord instance.
|
* @param cache - The cache of weather data.
|
||||||
* @param { WeatherCache } CACHE The cache of weather data.
|
|
||||||
*/
|
*/
|
||||||
export const handleEvents = (BOT: Client, CACHE: WeatherCache) => {
|
export const handleEvents = (bot: Client, cache: WeatherCache): void => {
|
||||||
BOT.on("ready", async () => await onReady(BOT, CACHE));
|
bot.on("ready", async() => {
|
||||||
|
await onReady(bot, cache);
|
||||||
|
});
|
||||||
|
|
||||||
BOT.on(
|
bot.on(
|
||||||
"interactionCreate",
|
"interactionCreate",
|
||||||
async (interaction) => await onInteraction(interaction, CACHE)
|
async(interaction) => {
|
||||||
|
await onInteraction(interaction, cache);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
import { Interaction } from "discord.js";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { CommandList } from "../../commands/_CommandList";
|
* @license Naomi's Public License
|
||||||
import { WeatherCache } from "../../interfaces/WeatherCache";
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { commandList } from "../../commands/_commandList.js";
|
||||||
|
import type { WeatherCache } from "../../interfaces/weatherCache.js";
|
||||||
|
import type { Interaction } from "discord.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the INTERACTION_CREATE event from Discord. Checks if the interaction is
|
* Handles the INTERACTION_CREATE event from Discord. Checks if the interaction is
|
||||||
* an application command, and if there is a matching command, and runs it.
|
* an application command, and if there is a matching command, and runs it.
|
||||||
*
|
* @param interaction - The interaction payload from Discord.
|
||||||
* @param {Interaction} interaction The interaction payload from Discord.
|
* @param cache - The cache of weather data.
|
||||||
* @param { WeatherCache } CACHE The cache of weather data.
|
|
||||||
*/
|
*/
|
||||||
export const onInteraction = async (
|
export const onInteraction = async(
|
||||||
interaction: Interaction,
|
interaction: Interaction,
|
||||||
CACHE: WeatherCache
|
cache: WeatherCache,
|
||||||
) => {
|
): Promise<void> => {
|
||||||
if (!interaction.isCommand()) {
|
if (!interaction.isChatInputCommand()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = CommandList.find(
|
const target = commandList.find(
|
||||||
(command) => command.data.name === interaction.commandName
|
(command) => {
|
||||||
|
return command.data.name === interaction.commandName;
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
@ -27,5 +32,5 @@ export const onInteraction = async (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await target.run(interaction, CACHE);
|
await target.run(interaction, cache);
|
||||||
};
|
};
|
||||||
|
@ -1,40 +1,46 @@
|
|||||||
import { REST } from "@discordjs/rest";
|
/**
|
||||||
import { Routes } from "discord-api-types/v9";
|
* @copyright nhcarrigan
|
||||||
import { Client, WebhookClient } from "discord.js";
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
import { CommandList } from "../../commands/_CommandList";
|
*/
|
||||||
import { WeatherCache } from "../../interfaces/WeatherCache";
|
import { REST, Routes, type Client, WebhookClient } from "discord.js";
|
||||||
import { scheduleForecasts } from "../../modules/scheduleForecasts";
|
import { commandList } from "../../commands/_commandList.js";
|
||||||
import { logHandler } from "../../utils/logHandler";
|
import { scheduleForecasts } from "../../modules/scheduleForecasts.js";
|
||||||
|
import { logHandler } from "../../utils/logHandler.js";
|
||||||
|
import type { WeatherCache } from "../../interfaces/weatherCache.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for the READY event from Discord. Logs that the bot is connected,
|
* Handler for the READY event from Discord. Logs that the bot is connected,
|
||||||
* then registers the guild slash commands.
|
* then registers the guild slash commands.
|
||||||
*
|
* @param bot - The bot's Discord instance.
|
||||||
* @param {Client} BOT The bot's Discord instance.
|
* @param cache - The cache of weather data.
|
||||||
* @param {WeatherCache} CACHE The cache of weather data.
|
|
||||||
*/
|
*/
|
||||||
export const onReady = async (BOT: Client, CACHE: WeatherCache) => {
|
export const onReady = async (
|
||||||
|
bot: Client,
|
||||||
|
cache: WeatherCache
|
||||||
|
): Promise<void> => {
|
||||||
const webhook = new WebhookClient({ url: process.env.DEBUG_HOOK as string });
|
const webhook = new WebhookClient({ url: process.env.DEBUG_HOOK as string });
|
||||||
|
|
||||||
await webhook.send("Ruu Bot is online!");
|
await webhook.send("Ruu Bot is online!");
|
||||||
logHandler.log("info", "Connected to Discord!");
|
logHandler.log("info", "Connected to Discord!");
|
||||||
|
|
||||||
const rest = new REST({ version: "9" }).setToken(
|
const rest = new REST({ version: "9" }).setToken(
|
||||||
process.env.DISCORD_TOKEN as string
|
process.env.DISCORD_TOKEN ?? ""
|
||||||
);
|
);
|
||||||
|
|
||||||
const commandData = CommandList.map((command) => command.data.toJSON());
|
const commandData = commandList.map((command) => {
|
||||||
|
return command.data.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
await rest.put(
|
await rest.put(
|
||||||
Routes.applicationGuildCommands(
|
Routes.applicationGuildCommands(
|
||||||
BOT.user?.id || "oopsie whoopsie",
|
bot.user?.id ?? "oopsie whoopsie",
|
||||||
process.env.HOME_GUILD_ID as string
|
process.env.HOME_GUILD_ID ?? ""
|
||||||
),
|
),
|
||||||
{ body: commandData }
|
{ body: commandData }
|
||||||
);
|
);
|
||||||
|
|
||||||
logHandler.log("info", "Registered commands!");
|
logHandler.log("info", "Registered commands!");
|
||||||
|
|
||||||
scheduleForecasts(CACHE);
|
scheduleForecasts(cache);
|
||||||
};
|
};
|
||||||
|
54
src/index.ts
54
src/index.ts
@ -1,37 +1,27 @@
|
|||||||
import { RewriteFrames } from "@sentry/integrations";
|
/**
|
||||||
import * as Sentry from "@sentry/node";
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
import { Client } from "discord.js";
|
import { Client } from "discord.js";
|
||||||
|
import { intentOptions } from "./config/intentOptions.js";
|
||||||
|
import { handleEvents } from "./events/handleEvents.js";
|
||||||
|
import { getWeatherForecast } from "./modules/getWeatherForecast.js";
|
||||||
|
import { loadChannels } from "./modules/loadChannels.js";
|
||||||
|
import { errorHandler } from "./utils/errorHandler.js";
|
||||||
|
import type { WeatherCache } from "./interfaces/weatherCache.js";
|
||||||
|
|
||||||
import { IntentOptions } from "./config/IntentOptions";
|
const bot = new Client({ intents: intentOptions });
|
||||||
import { handleEvents } from "./events/handleEvents";
|
|
||||||
import { WeatherCache } from "./interfaces/WeatherCache";
|
|
||||||
import { getWeatherForecast } from "./modules/getWeatherForecast";
|
|
||||||
import { loadChannels } from "./modules/loadChannels";
|
|
||||||
import { errorHandler } from "./utils/errorHandler";
|
|
||||||
|
|
||||||
(async () => {
|
const cache: WeatherCache = {
|
||||||
Sentry.init({
|
Inariko: getWeatherForecast("Inariko"),
|
||||||
dsn: process.env.SENTRY_DSN,
|
Rudania: getWeatherForecast("Rudania"),
|
||||||
tracesSampleRate: 1.0,
|
Vhintl: getWeatherForecast("Vhintl"),
|
||||||
integrations: [
|
channels: await loadChannels(bot),
|
||||||
new RewriteFrames({
|
};
|
||||||
root: global.__dirname,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const BOT = new Client({ intents: IntentOptions });
|
handleEvents(bot, cache);
|
||||||
|
|
||||||
const CACHE: WeatherCache = {
|
await bot.login(process.env.DISCORD_TOKEN ?? "").catch(async(error: unknown) => {
|
||||||
Rudania: getWeatherForecast("Rudania"),
|
return await errorHandler(error, "login");
|
||||||
Inariko: getWeatherForecast("Inariko"),
|
});
|
||||||
Vhintl: getWeatherForecast("Vhintl"),
|
|
||||||
channels: await loadChannels(BOT),
|
|
||||||
};
|
|
||||||
|
|
||||||
handleEvents(BOT, CACHE);
|
|
||||||
|
|
||||||
await BOT.login(process.env.DISCORD_TOKEN as string).catch(
|
|
||||||
async (err) => await errorHandler(err, "login")
|
|
||||||
);
|
|
||||||
})();
|
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
import { NewsChannel, TextChannel } from "discord.js";
|
|
||||||
|
|
||||||
import { WeatherForecast } from "./weather/WeatherForecast";
|
|
||||||
|
|
||||||
export interface WeatherCache {
|
|
||||||
Rudania: WeatherForecast | null;
|
|
||||||
Inariko: WeatherForecast | null;
|
|
||||||
Vhintl: WeatherForecast | null;
|
|
||||||
channels: {
|
|
||||||
Rudania: TextChannel | NewsChannel;
|
|
||||||
Inariko: TextChannel | NewsChannel;
|
|
||||||
Vhintl: TextChannel | NewsChannel;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
export interface AttachmentData {
|
|
||||||
attachmentString: string;
|
|
||||||
filePath: string;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
import {
|
|
||||||
SlashCommandBuilder,
|
|
||||||
SlashCommandSubcommandsOnlyBuilder,
|
|
||||||
} from "@discordjs/builders";
|
|
||||||
import { CommandInteraction } from "discord.js";
|
|
||||||
|
|
||||||
import { WeatherCache } from "../WeatherCache";
|
|
||||||
|
|
||||||
export interface Command {
|
|
||||||
data:
|
|
||||||
| Omit<SlashCommandBuilder, "addSubcommand" | "addSubcommandGroup">
|
|
||||||
| SlashCommandSubcommandsOnlyBuilder;
|
|
||||||
run: (interaction: CommandInteraction, CACHE: WeatherCache) => Promise<void>;
|
|
||||||
}
|
|
9
src/interfaces/commands/attachmentData.ts
Normal file
9
src/interfaces/commands/attachmentData.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
export interface AttachmentData {
|
||||||
|
attachmentString: string;
|
||||||
|
filePath: string;
|
||||||
|
}
|
20
src/interfaces/commands/command.ts
Normal file
20
src/interfaces/commands/command.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { WeatherCache } from "../weatherCache.js";
|
||||||
|
import type {
|
||||||
|
SlashCommandSubcommandsOnlyBuilder,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
SlashCommandOptionsOnlyBuilder,
|
||||||
|
} from "discord.js";
|
||||||
|
|
||||||
|
export interface Command {
|
||||||
|
data:
|
||||||
|
SlashCommandOptionsOnlyBuilder | SlashCommandSubcommandsOnlyBuilder;
|
||||||
|
run: (
|
||||||
|
interaction: ChatInputCommandInteraction,
|
||||||
|
cache: WeatherCache
|
||||||
|
)=> Promise<void>;
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
import { PrecipitationName } from "./names/PrecipitationName";
|
|
||||||
import { TemperatureName } from "./names/TemperatureName";
|
|
||||||
import { WindName } from "./names/WindName";
|
|
||||||
|
|
||||||
export interface Precipitation {
|
|
||||||
name: PrecipitationName;
|
|
||||||
temps: TemperatureName[] | "any";
|
|
||||||
winds: WindName[] | "any";
|
|
||||||
emote: string;
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { PrecipitationName } from "./names/PrecipitationName";
|
|
||||||
import { SpecialName } from "./names/SpecialName";
|
|
||||||
import { TemperatureName } from "./names/TemperatureName";
|
|
||||||
import { WindName } from "./names/WindName";
|
|
||||||
|
|
||||||
export interface Special {
|
|
||||||
name: SpecialName;
|
|
||||||
temps: TemperatureName[] | "any";
|
|
||||||
winds: WindName[] | "any";
|
|
||||||
precipitations: PrecipitationName[] | "any";
|
|
||||||
description: string;
|
|
||||||
emote: string;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { TemperatureName } from "./names/TemperatureName";
|
|
||||||
|
|
||||||
export interface Temperature {
|
|
||||||
fahrenheit: number;
|
|
||||||
celsius: number;
|
|
||||||
name: TemperatureName;
|
|
||||||
emote: string;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { RegionName } from "./names/RegionName";
|
|
||||||
import { Season } from "./names/Season";
|
|
||||||
import { Precipitation } from "./Precipitation";
|
|
||||||
import { Special } from "./Special";
|
|
||||||
import { Temperature } from "./Temperature";
|
|
||||||
import { Wind } from "./Wind";
|
|
||||||
|
|
||||||
export interface WeatherForecast {
|
|
||||||
region: RegionName;
|
|
||||||
season: Season;
|
|
||||||
temperature: Temperature;
|
|
||||||
wind: Wind;
|
|
||||||
precipitation: Precipitation | null;
|
|
||||||
special: Special | null;
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import { WindName } from "./names/WindName";
|
|
||||||
|
|
||||||
export interface Wind {
|
|
||||||
lowSpeed: number;
|
|
||||||
highSpeed: number;
|
|
||||||
name: WindName;
|
|
||||||
emote: string;
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export type RegionName = "Rudania" | "Inariko" | "Vhintl";
|
|
@ -1 +0,0 @@
|
|||||||
export type Season = "Spring" | "Summer" | "Fall" | "Winter";
|
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
export type PrecipitationName =
|
export type PrecipitationName =
|
||||||
| "Blizzard"
|
| "Blizzard"
|
||||||
| "Cinder Storm"
|
| "Cinder Storm"
|
6
src/interfaces/weather/names/regionName.ts
Normal file
6
src/interfaces/weather/names/regionName.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
export type RegionName = "Rudania" | "Inariko" | "Vhintl";
|
6
src/interfaces/weather/names/season.ts
Normal file
6
src/interfaces/weather/names/season.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
export type Season = "Spring" | "Summer" | "Fall" | "Winter";
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
export type SpecialName =
|
export type SpecialName =
|
||||||
| "Avalanche"
|
| "Avalanche"
|
||||||
| "Blight Rain"
|
| "Blight Rain"
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
export type TemperatureName =
|
export type TemperatureName =
|
||||||
| "Frigid"
|
| "Frigid"
|
||||||
| "Freezing"
|
| "Freezing"
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
export type WindName =
|
export type WindName =
|
||||||
| "Calm"
|
| "Calm"
|
||||||
| "Breeze"
|
| "Breeze"
|
15
src/interfaces/weather/precipitation.ts
Normal file
15
src/interfaces/weather/precipitation.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { PrecipitationName } from "./names/precipitationName.js";
|
||||||
|
import type { TemperatureName } from "./names/temperatureName.js";
|
||||||
|
import type { WindName } from "./names/windName.js";
|
||||||
|
|
||||||
|
export interface Precipitation {
|
||||||
|
name: PrecipitationName;
|
||||||
|
temps: Array<TemperatureName> | "any";
|
||||||
|
winds: Array<WindName> | "any";
|
||||||
|
emote: string;
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
import { PrecipitationName } from "../names/PrecipitationName";
|
|
||||||
import { Season } from "../names/Season";
|
|
||||||
import { SpecialName } from "../names/SpecialName";
|
|
||||||
import { TemperatureName } from "../names/TemperatureName";
|
|
||||||
import { WindName } from "../names/WindName";
|
|
||||||
|
|
||||||
export interface RegionRestriction {
|
|
||||||
season: Season;
|
|
||||||
temps: TemperatureName[];
|
|
||||||
wind: WindName[];
|
|
||||||
precipitation: PrecipitationName[];
|
|
||||||
special: SpecialName[];
|
|
||||||
}
|
|
18
src/interfaces/weather/regions/regionRestriction.ts
Normal file
18
src/interfaces/weather/regions/regionRestriction.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { PrecipitationName } from "../names/precipitationName.js";
|
||||||
|
import type { Season } from "../names/season.js";
|
||||||
|
import type { SpecialName } from "../names/specialName.js";
|
||||||
|
import type { TemperatureName } from "../names/temperatureName.js";
|
||||||
|
import type { WindName } from "../names/windName.js";
|
||||||
|
|
||||||
|
export interface RegionRestriction {
|
||||||
|
season: Season;
|
||||||
|
temps: Array<TemperatureName>;
|
||||||
|
wind: Array<WindName>;
|
||||||
|
precipitation: Array<PrecipitationName>;
|
||||||
|
special: Array<SpecialName>;
|
||||||
|
}
|
18
src/interfaces/weather/special.ts
Normal file
18
src/interfaces/weather/special.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { PrecipitationName } from "./names/precipitationName.js";
|
||||||
|
import type { SpecialName } from "./names/specialName.js";
|
||||||
|
import type { TemperatureName } from "./names/temperatureName.js";
|
||||||
|
import type { WindName } from "./names/windName.js";
|
||||||
|
|
||||||
|
export interface Special {
|
||||||
|
name: SpecialName;
|
||||||
|
temps: Array<TemperatureName> | "any";
|
||||||
|
winds: Array<WindName> | "any";
|
||||||
|
precipitations: Array<PrecipitationName> | "any";
|
||||||
|
description: string;
|
||||||
|
emote: string;
|
||||||
|
}
|
13
src/interfaces/weather/temperature.ts
Normal file
13
src/interfaces/weather/temperature.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { TemperatureName } from "./names/temperatureName.js";
|
||||||
|
|
||||||
|
export interface Temperature {
|
||||||
|
fahrenheit: number;
|
||||||
|
celsius: number;
|
||||||
|
name: TemperatureName;
|
||||||
|
emote: string;
|
||||||
|
}
|
20
src/interfaces/weather/weatherForecast.ts
Normal file
20
src/interfaces/weather/weatherForecast.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { RegionName } from "./names/regionName.js";
|
||||||
|
import type { Season } from "./names/season.js";
|
||||||
|
import type { Precipitation } from "./precipitation.js";
|
||||||
|
import type { Special } from "./special.js";
|
||||||
|
import type { Temperature } from "./temperature.js";
|
||||||
|
import type { Wind } from "./wind.js";
|
||||||
|
|
||||||
|
export interface WeatherForecast {
|
||||||
|
region: RegionName;
|
||||||
|
season: Season;
|
||||||
|
temperature: Temperature;
|
||||||
|
wind: Wind;
|
||||||
|
precipitation: Precipitation | null;
|
||||||
|
special: Special | null;
|
||||||
|
}
|
13
src/interfaces/weather/wind.ts
Normal file
13
src/interfaces/weather/wind.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { WindName } from "./names/windName.js";
|
||||||
|
|
||||||
|
export interface Wind {
|
||||||
|
lowSpeed: number;
|
||||||
|
highSpeed: number;
|
||||||
|
name: WindName;
|
||||||
|
emote: string;
|
||||||
|
}
|
18
src/interfaces/weatherCache.ts
Normal file
18
src/interfaces/weatherCache.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { WeatherForecast } from "./weather/weatherForecast.js";
|
||||||
|
import type { NewsChannel, TextChannel } from "discord.js";
|
||||||
|
|
||||||
|
export interface WeatherCache {
|
||||||
|
Rudania: WeatherForecast | null;
|
||||||
|
Inariko: WeatherForecast | null;
|
||||||
|
Vhintl: WeatherForecast | null;
|
||||||
|
channels: {
|
||||||
|
Rudania: TextChannel | NewsChannel;
|
||||||
|
Inariko: TextChannel | NewsChannel;
|
||||||
|
Vhintl: TextChannel | NewsChannel;
|
||||||
|
};
|
||||||
|
}
|
@ -1,49 +1,61 @@
|
|||||||
import { MessageEmbed, MessagePayload } from "discord.js";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { WeatherForecast } from "../interfaces/weather/WeatherForecast";
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
import { generateBanner } from "./images/generateBanner";
|
*/
|
||||||
import { getSeasonIcon } from "./images/getSeasonIcon";
|
import { EmbedBuilder, type APIEmbedField, type MessageCreateOptions }
|
||||||
|
from "discord.js";
|
||||||
|
import { generateBanner } from "./images/generateBanner.js";
|
||||||
|
import { getSeasonIcon } from "./images/getSeasonIcon.js";
|
||||||
|
import type { WeatherForecast } from "../interfaces/weather/weatherForecast.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a weather forecast into a Discord message embed.
|
* Parses a weather forecast into a Discord message embed.
|
||||||
*
|
* @param forecast - The forecast to parse.
|
||||||
* @param {WeatherForecast} forecast The forecast to parse.
|
* @returns A Discord message embed.
|
||||||
* @returns {MessageEmbed} A Discord message embed.
|
|
||||||
*/
|
*/
|
||||||
export const generateWeatherEmbed = async (
|
export const generateWeatherEmbed = async(
|
||||||
forecast: WeatherForecast | null
|
forecast: WeatherForecast | null,
|
||||||
): Promise<MessagePayload["options"]> => {
|
): Promise<MessageCreateOptions> => {
|
||||||
if (!forecast) {
|
if (!forecast) {
|
||||||
const embed = new MessageEmbed()
|
const embed = new EmbedBuilder().
|
||||||
.setTitle("Error")
|
setTitle("Error").
|
||||||
.setDescription("No forecast was generated.");
|
setDescription("No forecast was generated.");
|
||||||
return { embeds: [embed] };
|
return { embeds: [ embed ] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const weatherEmbed = new MessageEmbed();
|
const weatherEmbed = new EmbedBuilder();
|
||||||
weatherEmbed.setTitle(`${forecast.region}'s Daily Weather Forecast`);
|
weatherEmbed.setTitle(`${forecast.region}'s Daily Weather Forecast`);
|
||||||
|
|
||||||
let emoteString = forecast.temperature.emote + forecast.wind.emote;
|
let emoteString = forecast.temperature.emote + forecast.wind.emote;
|
||||||
|
|
||||||
weatherEmbed.addField(
|
const fields: Array<APIEmbedField> = [
|
||||||
"Temperature",
|
{
|
||||||
`${forecast.temperature.fahrenheit}°F / ${forecast.temperature.celsius}°C [${forecast.temperature.name}]`
|
name: "Temperature",
|
||||||
);
|
value: `${String(forecast.temperature.fahrenheit)}°F / ${String(forecast.temperature.celsius)}°C [${forecast.temperature.name}]`,
|
||||||
weatherEmbed.addField(
|
},
|
||||||
"Wind",
|
{
|
||||||
`${forecast.wind.name} [${forecast.wind.lowSpeed} - ${forecast.wind.highSpeed}kph]`
|
name: "Wind",
|
||||||
);
|
value: `${forecast.wind.name} [${String(forecast.wind.lowSpeed)} - ${String(forecast.wind.highSpeed)}kph]`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (forecast.precipitation) {
|
if (forecast.precipitation) {
|
||||||
weatherEmbed.addField("Precipitation", `${forecast.precipitation.name}`);
|
fields.push({
|
||||||
emoteString += forecast.precipitation.emote;
|
name: "Precipitation",
|
||||||
|
value: forecast.precipitation.name,
|
||||||
|
});
|
||||||
|
emoteString = emoteString + forecast.precipitation.emote;
|
||||||
}
|
}
|
||||||
if (forecast.special) {
|
if (forecast.special) {
|
||||||
weatherEmbed.addField("Description", `${forecast.special.description}`);
|
fields.push({
|
||||||
emoteString += forecast.special.emote;
|
name: "Special Conditions",
|
||||||
|
value: forecast.special.name,
|
||||||
|
});
|
||||||
|
emoteString = emoteString + forecast.special.emote;
|
||||||
}
|
}
|
||||||
weatherEmbed.setDescription(emoteString);
|
weatherEmbed.setDescription(emoteString);
|
||||||
|
weatherEmbed.addFields(fields);
|
||||||
|
|
||||||
const seasonIcon = getSeasonIcon(forecast.season);
|
const seasonIcon = getSeasonIcon(forecast.season);
|
||||||
weatherEmbed.setThumbnail(seasonIcon.attachmentString);
|
weatherEmbed.setThumbnail(seasonIcon.attachmentString);
|
||||||
@ -52,18 +64,20 @@ export const generateWeatherEmbed = async (
|
|||||||
|
|
||||||
switch (forecast.region) {
|
switch (forecast.region) {
|
||||||
case "Rudania":
|
case "Rudania":
|
||||||
weatherEmbed.setColor("RED");
|
weatherEmbed.setColor(0xFF_00_00);
|
||||||
break;
|
break;
|
||||||
case "Inariko":
|
case "Inariko":
|
||||||
weatherEmbed.setColor("BLUE");
|
weatherEmbed.setColor(0x00_00_FF);
|
||||||
break;
|
break;
|
||||||
case "Vhintl":
|
case "Vhintl":
|
||||||
weatherEmbed.setColor("GREEN");
|
weatherEmbed.setColor(0x00_FF_00);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
weatherEmbed.setColor(0x00_00_00);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
embeds: [weatherEmbed],
|
embeds: [ weatherEmbed ],
|
||||||
files: [seasonIcon.filePath, banner.filePath],
|
files: [ seasonIcon.filePath, banner.filePath ],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,26 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Module to select a random value from an array.
|
* Module to select a random value from an array.
|
||||||
*
|
* @param array - The array to select from.
|
||||||
* @param {any[]} array The array to select from.
|
* @param weight - Optional parameter to prefer lower or higher values.
|
||||||
* @param {"low" | "high"} weight Optional parameter to prefer lower or higher values.
|
* @returns The selected value.
|
||||||
* @returns {any} The selected value.
|
|
||||||
*/
|
*/
|
||||||
export const getRandomValue = <T>(array: T[], weight?: "low" | "high"): T => {
|
export const getRandomValue
|
||||||
let random = Math.floor(Math.random() * array.length);
|
= <T>(array: Array<T>, weight?: "low" | "high"): T => {
|
||||||
switch (weight) {
|
let random = Math.floor(Math.random() * array.length);
|
||||||
case "low":
|
switch (weight) {
|
||||||
random = Math.min(
|
case "low":
|
||||||
Math.floor(Math.random() * array.length),
|
random = Math.min(
|
||||||
Math.floor(Math.random() * array.length),
|
Math.floor(Math.random() * array.length),
|
||||||
Math.floor(Math.random() * array.length)
|
Math.floor(Math.random() * array.length),
|
||||||
);
|
Math.floor(Math.random() * array.length),
|
||||||
break;
|
);
|
||||||
case "high":
|
break;
|
||||||
random = Math.max(
|
case "high":
|
||||||
Math.floor(Math.random() * array.length),
|
random = Math.max(
|
||||||
Math.floor(Math.random() * array.length),
|
Math.floor(Math.random() * array.length),
|
||||||
Math.floor(Math.random() * array.length)
|
Math.floor(Math.random() * array.length),
|
||||||
);
|
Math.floor(Math.random() * array.length),
|
||||||
}
|
);
|
||||||
return array[random];
|
break;
|
||||||
};
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return array[random] as T;
|
||||||
|
};
|
||||||
|
@ -1,24 +1,27 @@
|
|||||||
import { temperatures } from "../data/weather/temperatures";
|
/**
|
||||||
import { winds } from "../data/weather/winds";
|
* @copyright nhcarrigan
|
||||||
import { RegionName } from "../interfaces/weather/names/RegionName";
|
* @license Naomi's Public License
|
||||||
import { WeatherForecast } from "../interfaces/weather/WeatherForecast";
|
* @author Naomi Carrigan
|
||||||
import { errorHandler } from "../utils/errorHandler";
|
*/
|
||||||
|
import { temperatures } from "../data/weather/temperatures.js";
|
||||||
import { getRandomValue } from "./getRandomValue";
|
import { winds } from "../data/weather/winds.js";
|
||||||
import { getPrecipitation } from "./weather/getPrecipitation";
|
import { errorHandler } from "../utils/errorHandler.js";
|
||||||
import { getRegionRestrictions } from "./weather/getRegionRestrictions";
|
import { getRandomValue } from "./getRandomValue.js";
|
||||||
import { getSeason } from "./weather/getSeason";
|
import { getPrecipitation } from "./weather/getPrecipitation.js";
|
||||||
import { getSpecial } from "./weather/getSpecial";
|
import { getRegionRestrictions } from "./weather/getRegionRestrictions.js";
|
||||||
|
import { getSeason } from "./weather/getSeason.js";
|
||||||
|
import { getSpecial } from "./weather/getSpecial.js";
|
||||||
|
import type { RegionName } from "../interfaces/weather/names/regionName.js";
|
||||||
|
import type { WeatherForecast } from "../interfaces/weather/weatherForecast.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a weather forecast for the specified region, using the current
|
* Generates a weather forecast for the specified region, using the current
|
||||||
* date to determine the season.
|
* date to determine the season.
|
||||||
*
|
* @param region - The name of the region to forecast for. Must be one of "Rudania", "Inariko", or "Vhintl".
|
||||||
* @param {RegionName} region The name of the region to forecast for.
|
* @returns The weather forecast.
|
||||||
* @returns {WeatherForecast | null} The weather forecast.
|
|
||||||
*/
|
*/
|
||||||
export const getWeatherForecast = (
|
export const getWeatherForecast = (
|
||||||
region: RegionName
|
region: RegionName,
|
||||||
): WeatherForecast | null => {
|
): WeatherForecast | null => {
|
||||||
try {
|
try {
|
||||||
const season = getSeason();
|
const season = getSeason();
|
||||||
@ -28,26 +31,30 @@ export const getWeatherForecast = (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tempName = getRandomValue(allowedWeather.temps);
|
const temporaryName = getRandomValue(allowedWeather.temps);
|
||||||
const temperature = temperatures.find((el) => el.name === tempName);
|
const temperature = temperatures.find((element) => {
|
||||||
|
return element.name === temporaryName;
|
||||||
|
});
|
||||||
const windName = getRandomValue(allowedWeather.wind, "low");
|
const windName = getRandomValue(allowedWeather.wind, "low");
|
||||||
const wind = winds.find((el) => el.name === windName);
|
const wind = winds.find((element) => {
|
||||||
|
return element.name === windName;
|
||||||
|
});
|
||||||
|
|
||||||
if (!temperature || !wind) {
|
if (!temperature || !wind) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const precipitation = getPrecipitation(allowedWeather, tempName, windName);
|
const precipitation = getPrecipitation(allowedWeather, temporaryName, windName);
|
||||||
const special = getSpecial(
|
const special = getSpecial(
|
||||||
allowedWeather,
|
allowedWeather,
|
||||||
tempName,
|
temporaryName,
|
||||||
windName,
|
windName,
|
||||||
precipitation?.name
|
precipitation?.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
return { region, season, temperature, wind, precipitation, special };
|
return { precipitation, region, season, special, temperature, wind };
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
errorHandler(err, "get weather forecast");
|
void errorHandler(error, "get weather forecast");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,37 +1,34 @@
|
|||||||
import { AttachmentData } from "../../interfaces/commands/AttachmentData";
|
/**
|
||||||
import { WeatherForecast } from "../../interfaces/weather/WeatherForecast";
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
import { getBannerImage } from "./getBannerImage";
|
* @author Naomi Carrigan
|
||||||
import { getOverlayImage } from "./getOverlayImage";
|
*/
|
||||||
import { overlayImages } from "./overlayImages";
|
import { getBannerImage } from "./getBannerImage.js";
|
||||||
|
import { getOverlayImage } from "./getOverlayImage.js";
|
||||||
|
import { overlayImages } from "./overlayImages.js";
|
||||||
|
import type { AttachmentData }
|
||||||
|
from "../../interfaces/commands/attachmentData.js";
|
||||||
|
import type { WeatherForecast }
|
||||||
|
from "../../interfaces/weather/weatherForecast.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the banner image. If an overlay is available, constructs a new banner image by overlaying the overlay on the banner.
|
* Generates the banner image. If an overlay is available, constructs a new banner image by overlaying the overlay on the banner.
|
||||||
* Otherwise, returns the banner itself.
|
* Otherwise, returns the banner itself.
|
||||||
*
|
* @param forecast - The weather forecast.
|
||||||
* @param {WeatherForecast} forecast The weather forecast.
|
* @returns The banner image attachment data.
|
||||||
* @returns {AttachmentData} The banner image attachment data.
|
|
||||||
*/
|
*/
|
||||||
export const generateBanner = async (
|
export const generateBanner = async(
|
||||||
forecast: WeatherForecast
|
forecast: WeatherForecast,
|
||||||
): Promise<AttachmentData> => {
|
): Promise<AttachmentData> => {
|
||||||
const background = getBannerImage(forecast.region);
|
const background = getBannerImage(forecast.region);
|
||||||
const overlayQuery =
|
const overlayQuery
|
||||||
forecast.special?.name === "Blight Rain"
|
= forecast.special?.name === "Blight Rain"
|
||||||
? "Blight Rain"
|
? "Blight Rain"
|
||||||
: forecast.precipitation?.name;
|
: forecast.precipitation?.name;
|
||||||
|
|
||||||
if (!overlayQuery) {
|
const overlayPath = getOverlayImage(overlayQuery ?? null);
|
||||||
return background;
|
|
||||||
}
|
|
||||||
|
|
||||||
const overlayPath = getOverlayImage(overlayQuery);
|
const overlay = await overlayImages(background.filePath, overlayPath ?? "");
|
||||||
|
|
||||||
if (!overlayPath) {
|
|
||||||
return background;
|
|
||||||
}
|
|
||||||
|
|
||||||
const overlay = await overlayImages(background.filePath, overlayPath);
|
|
||||||
|
|
||||||
return overlay;
|
return overlay;
|
||||||
};
|
};
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
import { join } from "path";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { AttachmentData } from "../../interfaces/commands/AttachmentData";
|
* @license Naomi's Public License
|
||||||
import { RegionName } from "../../interfaces/weather/names/RegionName";
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { join } from "node:path";
|
||||||
|
import type { AttachmentData }
|
||||||
|
from "../../interfaces/commands/attachmentData.js";
|
||||||
|
import type { RegionName } from "../../interfaces/weather/names/regionName.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects one of the random banner images from the banner folder.
|
* Selects one of the random banner images from the banner folder.
|
||||||
*
|
* @param region - The name of the region.
|
||||||
* @param {RegionName} region The name of the region.
|
* @returns The banner image attachment data.
|
||||||
* @returns {AttachmentData} The banner image attachment data.
|
|
||||||
*/
|
*/
|
||||||
export const getBannerImage = (region: RegionName): AttachmentData => {
|
export const getBannerImage = (region: RegionName): AttachmentData => {
|
||||||
const fileName = `${region}${Math.ceil(Math.random() * 3)}.png`;
|
const fileName
|
||||||
|
= `${region}${String(Math.ceil(Math.random() * 3))}.png`;
|
||||||
const filePath = join(process.cwd(), "src", "assets", "banners", fileName);
|
const filePath = join(process.cwd(), "src", "assets", "banners", fileName);
|
||||||
return {
|
return {
|
||||||
attachmentString: `attachment://${fileName}`,
|
attachmentString: `attachment://${fileName}`,
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
import { join } from "path";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { PrecipitationName } from "../../interfaces/weather/names/PrecipitationName";
|
* @license Naomi's Public License
|
||||||
import { SpecialName } from "../../interfaces/weather/names/SpecialName";
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { join } from "node:path";
|
||||||
|
import type { PrecipitationName }
|
||||||
|
from "../../interfaces/weather/names/precipitationName.js";
|
||||||
|
import type { SpecialName }
|
||||||
|
from "../../interfaces/weather/names/specialName.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the current weather conditions have an overlay.
|
* Checks if the current weather conditions have an overlay.
|
||||||
*
|
* @param name - The name of the weather condition to look for.
|
||||||
* @param {SpecialName | PrecipitationName} name The name of the weather condition to look for.
|
* @returns The file path to the overlay, or null if there is no overlay.
|
||||||
* @returns {string | null} The file path to the overlay, or null if there is no overlay.
|
|
||||||
*/
|
*/
|
||||||
export const getOverlayImage = (
|
export const getOverlayImage = (
|
||||||
name: SpecialName | PrecipitationName
|
name: SpecialName | PrecipitationName | null,
|
||||||
): string | null => {
|
): string | null => {
|
||||||
let fileName = null;
|
let fileName: string | null = null;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "Blight Rain":
|
case "Blight Rain":
|
||||||
fileName = "ROOTS-blightrain.png";
|
fileName = "ROOTS-blightrain.png";
|
||||||
@ -58,8 +63,10 @@ export const getOverlayImage = (
|
|||||||
case "Thunderstorm":
|
case "Thunderstorm":
|
||||||
fileName = "ROOTS-thunderstorm.png";
|
fileName = "ROOTS-thunderstorm.png";
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
fileName = null;
|
||||||
}
|
}
|
||||||
if (!fileName) {
|
if (fileName === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { join } from "path";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { AttachmentData } from "../../interfaces/commands/AttachmentData";
|
* @license Naomi's Public License
|
||||||
import { Season } from "../../interfaces/weather/names/Season";
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { join } from "node:path";
|
||||||
|
import type { AttachmentData } from "../../interfaces/commands/attachmentData.js";
|
||||||
|
import type { Season }
|
||||||
|
from "../../interfaces/weather/names/season.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to generate the season icon attachment.
|
* Module to generate the season icon attachment.
|
||||||
*
|
* @param season - The season for which to get the icon.
|
||||||
* @param {Season} season The season.
|
* @returns The icon attachment data.
|
||||||
* @returns {AttachmentData} The icon attachment data.
|
|
||||||
*/
|
*/
|
||||||
export const getSeasonIcon = (season: Season): AttachmentData => {
|
export const getSeasonIcon = (season: Season): AttachmentData => {
|
||||||
const fileName = `${season.toLowerCase()}.png`;
|
const fileName = `${season.toLowerCase()}.png`;
|
||||||
|
@ -1,25 +1,28 @@
|
|||||||
import { join } from "path";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { join } from "node:path";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
import type { AttachmentData }
|
||||||
import { AttachmentData } from "../../interfaces/commands/AttachmentData";
|
from "../../interfaces/commands/attachmentData.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to combine an overlay and a banner.
|
* Module to combine an overlay and a banner.
|
||||||
*
|
* @param banner - The file path for the banner.
|
||||||
* @param {string} banner The file path for the banner.
|
* @param overlay - The file path for the overlay.
|
||||||
* @param {string} overlay The file path for the overlay.
|
* @returns The attachment data for the new banner.
|
||||||
* @returns {AttachmentData} The attachment data for the new banner.
|
|
||||||
*/
|
*/
|
||||||
export const overlayImages = async (
|
export const overlayImages = async(
|
||||||
banner: string,
|
banner: string,
|
||||||
overlay: string
|
overlay: string,
|
||||||
): Promise<AttachmentData> => {
|
): Promise<AttachmentData> => {
|
||||||
await sharp(banner)
|
await sharp(banner).
|
||||||
.composite([{ input: overlay, gravity: "center" }])
|
composite([ { gravity: "center", input: overlay } ]).
|
||||||
.toFile(join(process.cwd(), "src", "assets", "overlay.png"));
|
toFile(join(process.cwd(), "src", "assets", "overlay.png"));
|
||||||
return {
|
return {
|
||||||
attachmentString: `attachment://overlay.png`,
|
attachmentString: `attachment://overlay.png`,
|
||||||
filePath: join(process.cwd(), "src", "assets", "overlay.png"),
|
filePath: join(process.cwd(), "src", "assets", "overlay.png"),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,42 +1,47 @@
|
|||||||
import { Client } from "discord.js";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
import { WeatherCache } from "../interfaces/WeatherCache";
|
* @license Naomi's Public License
|
||||||
import { logHandler } from "../utils/logHandler";
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { ChannelType, type Client } from "discord.js";
|
||||||
|
import { logHandler } from "../utils/logHandler.js";
|
||||||
|
import type { WeatherCache } from "../interfaces/weatherCache.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to fetch the configured channels for sending weather forecasts.
|
* Module to fetch the configured channels for sending weather forecasts.
|
||||||
*
|
* @param bot - The bot's Discord instance.
|
||||||
* @param {Client} BOT The bot's Discord instance.
|
* @returns The configured channels for sending weather forecasts.
|
||||||
* @returns {WeatherCache["channels"]} The configured channels for sending weather forecasts.
|
|
||||||
*/
|
*/
|
||||||
export const loadChannels = async (
|
export const loadChannels = async(
|
||||||
BOT: Client
|
bot: Client,
|
||||||
): Promise<WeatherCache["channels"]> => {
|
): Promise<WeatherCache["channels"]> => {
|
||||||
try {
|
try {
|
||||||
const guildId = process.env.HOME_GUILD_ID as string;
|
const guildId = process.env.HOME_GUILD_ID ?? "";
|
||||||
const rudaniaId = process.env.RUDANIA_ID as string;
|
const rudaniaId = process.env.RUDANIA_ID ?? "";
|
||||||
const inarikoId = process.env.INARIKO_ID as string;
|
const inarikoId = process.env.INARIKO_ID ?? "";
|
||||||
const vhintlId = process.env.VHINTL_ID as string;
|
const vhintlId = process.env.VHINTL_ID ?? "";
|
||||||
|
|
||||||
const guild = await BOT.guilds.fetch(guildId);
|
const guild = await bot.guilds.fetch(guildId);
|
||||||
const Rudania = await guild.channels.fetch(rudaniaId);
|
const Rudania = await guild.channels.fetch(rudaniaId);
|
||||||
const Inariko = await guild.channels.fetch(inarikoId);
|
const Inariko = await guild.channels.fetch(inarikoId);
|
||||||
const Vhintl = await guild.channels.fetch(vhintlId);
|
const Vhintl = await guild.channels.fetch(vhintlId);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!guild ||
|
!Rudania
|
||||||
!Rudania ||
|
|| !Inariko
|
||||||
!Inariko ||
|
|| !Vhintl
|
||||||
!Vhintl ||
|
|| !("send" in Rudania && "send" in Inariko && "send" in Vhintl)
|
||||||
!("send" in Rudania && "send" in Inariko && "send" in Vhintl)
|
|| Rudania.type !== ChannelType.GuildText
|
||||||
|
|| Inariko.type !== ChannelType.GuildText
|
||||||
|
|| Vhintl.type !== ChannelType.GuildText
|
||||||
) {
|
) {
|
||||||
logHandler.log("error", "Cannot locate the forecast channels!");
|
logHandler.log("error", "Cannot locate the forecast channels!");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { Rudania, Inariko, Vhintl };
|
return { Inariko, Rudania, Vhintl };
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
logHandler.log("error", err);
|
logHandler.log("error", error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,35 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
import { scheduleJob } from "node-schedule";
|
import { scheduleJob } from "node-schedule";
|
||||||
|
import { errorHandler } from "../utils/errorHandler.js";
|
||||||
import { WeatherCache } from "../interfaces/WeatherCache";
|
import { logHandler } from "../utils/logHandler.js";
|
||||||
import { errorHandler } from "../utils/errorHandler";
|
import { generateWeatherEmbed } from "./generateWeatherEmbed.js";
|
||||||
import { logHandler } from "../utils/logHandler";
|
import { getWeatherForecast } from "./getWeatherForecast.js";
|
||||||
|
import type { WeatherCache } from "../interfaces/weatherCache.js";
|
||||||
import { generateWeatherEmbed } from "./generateWeatherEmbed";
|
|
||||||
import { getWeatherForecast } from "./getWeatherForecast";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules a CRON job for sending weather forecasts to the appropriate channels.
|
* Schedules a CRON job for sending weather forecasts to the appropriate channels.
|
||||||
*
|
* @param cache - The weather cache.
|
||||||
* @param {WeatherCache} CACHE The weather cache.
|
|
||||||
*/
|
*/
|
||||||
export const scheduleForecasts = (CACHE: WeatherCache) => {
|
export const scheduleForecasts = (cache: WeatherCache): void => {
|
||||||
logHandler.log("info", "Scheduling weather forecasts...");
|
logHandler.log("info", "Scheduling weather forecasts...");
|
||||||
// Run daily at 5AM PST to get 8AM EST.
|
// Run daily at 5AM PST to get 8AM EST.
|
||||||
scheduleJob("0 0 5 * * *", async () => {
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||||
|
scheduleJob("0 0 5 * * *", async() => {
|
||||||
try {
|
try {
|
||||||
CACHE.Rudania = getWeatherForecast("Rudania");
|
cache.Rudania = getWeatherForecast("Rudania");
|
||||||
CACHE.Inariko = getWeatherForecast("Inariko");
|
cache.Inariko = getWeatherForecast("Inariko");
|
||||||
CACHE.Vhintl = getWeatherForecast("Vhintl");
|
cache.Vhintl = getWeatherForecast("Vhintl");
|
||||||
|
|
||||||
const rudania = await generateWeatherEmbed(CACHE.Rudania);
|
const rudania = await generateWeatherEmbed(cache.Rudania);
|
||||||
await CACHE.channels.Rudania.send(rudania);
|
await cache.channels.Rudania.send(rudania);
|
||||||
const inariko = await generateWeatherEmbed(CACHE.Inariko);
|
const inariko = await generateWeatherEmbed(cache.Inariko);
|
||||||
await CACHE.channels.Inariko.send(inariko);
|
await cache.channels.Inariko.send(inariko);
|
||||||
const vhintl = await generateWeatherEmbed(CACHE.Vhintl);
|
const vhintl = await generateWeatherEmbed(cache.Vhintl);
|
||||||
await CACHE.channels.Vhintl.send(vhintl);
|
await cache.channels.Vhintl.send(vhintl);
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
const errorId = await errorHandler(err, "scheduled forecast");
|
const errorId = await errorHandler(error, "scheduled forecast");
|
||||||
await CACHE.channels.Rudania.send(errorId);
|
await cache.channels.Rudania.send(errorId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,32 +1,40 @@
|
|||||||
import { precipitations } from "../../data/weather/precipitations";
|
/**
|
||||||
import { TemperatureName } from "../../interfaces/weather/names/TemperatureName";
|
* @copyright nhcarrigan
|
||||||
import { WindName } from "../../interfaces/weather/names/WindName";
|
* @license Naomi's Public License
|
||||||
import { Precipitation } from "../../interfaces/weather/Precipitation";
|
* @author Naomi Carrigan
|
||||||
import { RegionRestriction } from "../../interfaces/weather/regions/RegionRestriction";
|
*/
|
||||||
import { getRandomValue } from "../getRandomValue";
|
import { precipitations } from "../../data/weather/precipitations.js";
|
||||||
|
import { getRandomValue } from "../getRandomValue.js";
|
||||||
|
import type { TemperatureName }
|
||||||
|
from "../../interfaces/weather/names/temperatureName.js";
|
||||||
|
import type { WindName } from "../../interfaces/weather/names/windName.js";
|
||||||
|
import type { Precipitation }
|
||||||
|
from "../../interfaces/weather/precipitation.js";
|
||||||
|
import type { RegionRestriction } from "../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Get a precipitation forecast for the region and season.
|
||||||
* @param {RegionRestriction} options The allowed weather for the region + season.
|
* @param options - The allowed weather for the region + season.
|
||||||
* @param {TemperatureName} temp The current temperature.
|
* @param temperature - The current temperature.
|
||||||
* @param {WindName} wind The current wind.
|
* @param wind - The current wind.
|
||||||
* @returns {Precipitation | null} The precipitation, or null if there are no valid forecasts.
|
* @returns The precipitation, or null if there are no valid forecasts.
|
||||||
*/
|
*/
|
||||||
export const getPrecipitation = (
|
export const getPrecipitation = (
|
||||||
options: RegionRestriction,
|
options: RegionRestriction,
|
||||||
temp: TemperatureName,
|
temperature: TemperatureName,
|
||||||
wind: WindName
|
wind: WindName,
|
||||||
): Precipitation | null => {
|
): Precipitation | null => {
|
||||||
const restrictedPrecipitation = precipitations.filter((el) =>
|
const restrictedPrecipitation = precipitations.filter((element) => {
|
||||||
options.precipitation.includes(el.name)
|
return options.precipitation.includes(element.name);
|
||||||
);
|
});
|
||||||
const validPrecipitations = restrictedPrecipitation.filter(
|
const validPrecipitations = restrictedPrecipitation.filter(
|
||||||
(el) =>
|
(element) => {
|
||||||
(el.temps.includes(temp) || el.temps === "any") &&
|
return (element.temps.includes(temperature) || element.temps === "any")
|
||||||
(el.winds.includes(wind) || el.winds === "any")
|
&& (element.winds.includes(wind) || element.winds === "any");
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!validPrecipitations.length) {
|
if (validPrecipitations.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,21 +1,25 @@
|
|||||||
import { inarikoSeasons } from "../../data/weather/regions/inarikoSeasons";
|
/**
|
||||||
import { rudaniaSeasons } from "../../data/weather/regions/rudaniaSeasons";
|
* @copyright nhcarrigan
|
||||||
import { vhintlSeasons } from "../../data/weather/regions/vhintlSeasons";
|
* @license Naomi's Public License
|
||||||
import { RegionName } from "../../interfaces/weather/names/RegionName";
|
* @author Naomi Carrigan
|
||||||
import { Season } from "../../interfaces/weather/names/Season";
|
*/
|
||||||
import { RegionRestriction } from "../../interfaces/weather/regions/RegionRestriction";
|
import { inarikoSeasons } from "../../data/weather/regions/inarikoSeasons.js";
|
||||||
|
import { rudaniaSeasons } from "../../data/weather/regions/rudaniaSeasons.js";
|
||||||
|
import { vhintlSeasons } from "../../data/weather/regions/vhintlSeasons.js";
|
||||||
|
import type { RegionName } from "../../interfaces/weather/names/regionName.js";
|
||||||
|
import type { Season } from "../../interfaces/weather/names/season.js";
|
||||||
|
import type { RegionRestriction } from "../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to get the allowed weather for a region based on the season.
|
* Module to get the allowed weather for a region based on the season.
|
||||||
* Will throw an error if the data is not found.
|
* Will throw an error if the data is not found.
|
||||||
*
|
* @param region - The name of the region.
|
||||||
* @param {RegionName} region The name of the region.
|
* @param season - The season the region is in.
|
||||||
* @param {Season} season The season.
|
* @returns The allowed weather for the region.
|
||||||
* @returns {RegionRestriction} The allowed weather for the region.
|
|
||||||
*/
|
*/
|
||||||
export const getRegionRestrictions = (
|
export const getRegionRestrictions = (
|
||||||
region: RegionName,
|
region: RegionName,
|
||||||
season: Season
|
season: Season,
|
||||||
): RegionRestriction | null => {
|
): RegionRestriction | null => {
|
||||||
let restrictions = null;
|
let restrictions = null;
|
||||||
switch (region) {
|
switch (region) {
|
||||||
@ -32,7 +36,13 @@ export const getRegionRestrictions = (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const seasonalRestriction = restrictions.find((el) => el.season === season);
|
if (restrictions === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const seasonalRestriction = restrictions.find((element) => {
|
||||||
|
return element.season === season;
|
||||||
|
});
|
||||||
|
|
||||||
if (!seasonalRestriction) {
|
if (!seasonalRestriction) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { Season } from "../../interfaces/weather/names/Season";
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import type { Season } from "../../interfaces/weather/names/season.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to get the season based on today's date.
|
* Module to get the season based on today's date.
|
||||||
@ -6,21 +11,20 @@ import { Season } from "../../interfaces/weather/names/Season";
|
|||||||
* Spring: March 21 - June 20.
|
* Spring: March 21 - June 20.
|
||||||
* Summer: June 21st - September 22.
|
* Summer: June 21st - September 22.
|
||||||
* Fall: September 23 - December 21.
|
* Fall: September 23 - December 21.
|
||||||
*
|
* @returns The season name.
|
||||||
* @returns {Season} The season name.
|
|
||||||
*/
|
*/
|
||||||
export const getSeason = (): Season => {
|
export const getSeason = (): Season => {
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const month = date.getMonth();
|
const month = date.getMonth();
|
||||||
const day = date.getDate();
|
const day = date.getDate();
|
||||||
|
|
||||||
if (month < 2 || (month === 2 && day < 21) || (month === 11 && day > 20)) {
|
if (month < 2 || month === 2 && day < 21 || month === 11 && day > 20) {
|
||||||
return "Winter";
|
return "Winter";
|
||||||
}
|
}
|
||||||
if (month < 5 || (month === 5 && day < 21)) {
|
if (month < 5 || month === 5 && day < 21) {
|
||||||
return "Spring";
|
return "Spring";
|
||||||
}
|
}
|
||||||
if (month < 8 || (month === 8 && day < 23)) {
|
if (month < 8 || month === 8 && day < 23) {
|
||||||
return "Summer";
|
return "Summer";
|
||||||
}
|
}
|
||||||
return "Fall";
|
return "Fall";
|
||||||
|
@ -1,45 +1,51 @@
|
|||||||
import { specials } from "../../data/weather/specials";
|
/**
|
||||||
import { PrecipitationName } from "../../interfaces/weather/names/PrecipitationName";
|
* @copyright nhcarrigan
|
||||||
import { TemperatureName } from "../../interfaces/weather/names/TemperatureName";
|
* @license Naomi's Public License
|
||||||
import { WindName } from "../../interfaces/weather/names/WindName";
|
* @author Naomi Carrigan
|
||||||
import { RegionRestriction } from "../../interfaces/weather/regions/RegionRestriction";
|
*/
|
||||||
import { Special } from "../../interfaces/weather/Special";
|
import { specials } from "../../data/weather/specials.js";
|
||||||
import { getRandomValue } from "../getRandomValue";
|
import { getRandomValue } from "../getRandomValue.js";
|
||||||
|
import type { PrecipitationName } from "../../interfaces/weather/names/precipitationName.js";
|
||||||
|
import type { TemperatureName } from "../../interfaces/weather/names/temperatureName.js";
|
||||||
|
import type { WindName } from "../../interfaces/weather/names/windName.js";
|
||||||
|
import type { RegionRestriction } from "../../interfaces/weather/regions/regionRestriction.js";
|
||||||
|
import type { Special } from "../../interfaces/weather/special.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module to get a special weather event for a region. Checks if the temp, wind, and precipitation
|
* Module to get a special weather event for a region. Checks if the temp, wind, and precipitation
|
||||||
* allow for a special event. If so, has a 30% chance to trigger one.
|
* allow for a special event. If so, has a 30% chance to trigger one.
|
||||||
*
|
* @param options - The allowed weather for the region + season.
|
||||||
* @param {RegionRestriction} options The allowed weather for the region + season.
|
* @param temperature - The current temperature.
|
||||||
* @param {TemperatureName} temp The current temperature.
|
* @param wind - The current wind.
|
||||||
* @param {WindName} wind The current wind.
|
* @param precipitation - The current precipitation.
|
||||||
* @param {PrecipitationName | undefined} precipitation The current precipitation.
|
* @returns The special event, or null if there are no valid forecasts.
|
||||||
* @returns {Special | null} The special event, or null if there are no valid forecasts.
|
|
||||||
*/
|
*/
|
||||||
export const getSpecial = (
|
export const getSpecial = (
|
||||||
options: RegionRestriction,
|
options: RegionRestriction,
|
||||||
temp: TemperatureName,
|
temperature: TemperatureName,
|
||||||
wind: WindName,
|
wind: WindName,
|
||||||
precipitation: PrecipitationName | undefined
|
precipitation: PrecipitationName | undefined,
|
||||||
): Special | null => {
|
): Special | null => {
|
||||||
const restrictedSpecial = specials.filter((el) =>
|
const restrictedSpecial = specials.filter((element) => {
|
||||||
options.special.includes(el.name)
|
return options.special.includes(element.name);
|
||||||
);
|
});
|
||||||
const validSpecials = precipitation
|
const validSpecials = precipitation !== undefined
|
||||||
? restrictedSpecial.filter(
|
? restrictedSpecial.filter(
|
||||||
(el) =>
|
(element) => {
|
||||||
(el.temps.includes(temp) || el.temps === "any") &&
|
return (element.temps.includes(temperature) || element.temps === "any")
|
||||||
(el.winds.includes(wind) || el.winds === "any") &&
|
&& (element.winds.includes(wind) || element.winds === "any")
|
||||||
(el.precipitations.includes(precipitation) ||
|
&& (element.precipitations.includes(precipitation)
|
||||||
el.precipitations === "any")
|
|| element.precipitations === "any");
|
||||||
)
|
},
|
||||||
|
)
|
||||||
: restrictedSpecial.filter(
|
: restrictedSpecial.filter(
|
||||||
(el) =>
|
(element) => {
|
||||||
(el.temps.includes(temp) || el.temps === "any") &&
|
return (element.temps.includes(temperature) || element.temps === "any")
|
||||||
(el.winds.includes(wind) || el.winds === "any")
|
&& (element.winds.includes(wind) || element.winds === "any");
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
if (!validSpecials.length) {
|
if (validSpecials.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,49 +1,50 @@
|
|||||||
import { captureException } from "@sentry/node";
|
/**
|
||||||
import { MessageEmbed, WebhookClient } from "discord.js";
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
|
import { EmbedBuilder, WebhookClient } from "discord.js";
|
||||||
import { v4 } from "uuid";
|
import { v4 } from "uuid";
|
||||||
|
import { logHandler } from "./logHandler.js";
|
||||||
import { logHandler } from "./logHandler";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats an error into an embed and sends it to the developer debug webhook.
|
* Formats an error into an embed and sends it to the developer debug webhook.
|
||||||
*
|
* @param error - The error instance from Node.
|
||||||
* @param {Error} err The error.
|
* @param context - A description of where the error occurred.
|
||||||
* @param {string} context A description of where the error occurred.
|
* @returns The UUID tied to the error.
|
||||||
* @returns {string} The UUID tied to the error.
|
|
||||||
*/
|
*/
|
||||||
export const errorHandler = async (
|
export const errorHandler = async(
|
||||||
err: unknown,
|
error: unknown,
|
||||||
context: string
|
context: string,
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
const error = err as Error;
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
const typedError = error as Error;
|
||||||
logHandler.log("error", `There was an error in the ${context}:`);
|
logHandler.log("error", `There was an error in the ${context}:`);
|
||||||
logHandler.log(
|
logHandler.log(
|
||||||
"error",
|
"error",
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
{ errorMessage: error.message, errorStack: error.stack },
|
{ errorMessage: typedError.message, errorStack: typedError.stack },
|
||||||
null,
|
null,
|
||||||
2
|
2,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
captureException(error);
|
|
||||||
|
|
||||||
const errorId = v4();
|
const errorId = v4();
|
||||||
|
|
||||||
const errorEmbed = new MessageEmbed();
|
const errorEmbed = new EmbedBuilder();
|
||||||
errorEmbed.setTitle(`RuuBot had a ${context} error!`);
|
errorEmbed.setTitle(`RuuBot had a ${context} error!`);
|
||||||
errorEmbed.setColor("DARK_RED");
|
errorEmbed.setColor(0xFF_00_00);
|
||||||
errorEmbed.setDescription(error.message.substring(0, 4000));
|
errorEmbed.setDescription(typedError.message.slice(0, 4000));
|
||||||
errorEmbed.addField(
|
errorEmbed.addFields([
|
||||||
"Stack Trace:",
|
{ name: "Stack Trace",
|
||||||
`\`\`\`${error.stack?.slice(0, 1000)}\`\`\``
|
value: typedError.stack?.slice(0, 1000) ?? "No stack trace available." },
|
||||||
);
|
{ name: "Error ID", value: errorId },
|
||||||
errorEmbed.addField("Error ID:", errorId);
|
]);
|
||||||
errorEmbed.setTimestamp();
|
errorEmbed.setTimestamp();
|
||||||
|
|
||||||
const webhook = new WebhookClient({ url: process.env.DEBUG_HOOK as string });
|
const webhook = new WebhookClient({ url: process.env.DEBUG_HOOK ?? "" });
|
||||||
|
|
||||||
await webhook.send({ embeds: [errorEmbed] });
|
await webhook.send({ embeds: [ errorEmbed ] });
|
||||||
|
|
||||||
return `The ${context} logic had an error. Please contact the developer with this ID: \`${errorId}\``;
|
return `The ${context} logic had an error. Please contact the developer with this ID: \`${errorId}\``;
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* @copyright nhcarrigan
|
||||||
|
* @license Naomi's Public License
|
||||||
|
* @author Naomi Carrigan
|
||||||
|
*/
|
||||||
import { createLogger, format, transports, config } from "winston";
|
import { createLogger, format, transports, config } from "winston";
|
||||||
|
|
||||||
const { combine, timestamp, colorize, printf } = format;
|
const { combine, timestamp, colorize, printf } = format;
|
||||||
@ -5,20 +10,21 @@ const { combine, timestamp, colorize, printf } = format;
|
|||||||
/**
|
/**
|
||||||
* Standard log handler, using winston to wrap and format
|
* Standard log handler, using winston to wrap and format
|
||||||
* messages. Call with `logHandler.log(level, message)`.
|
* messages. Call with `logHandler.log(level, message)`.
|
||||||
*
|
|
||||||
* @param {string} level - The log level to use.
|
* @param {string} level - The log level to use.
|
||||||
* @param {string} message - The message to log.
|
* @param {string} message - The message to log.
|
||||||
*/
|
*/
|
||||||
export const logHandler = createLogger({
|
export const logHandler = createLogger({
|
||||||
levels: config.npm.levels,
|
exitOnError: false,
|
||||||
level: "silly",
|
format: combine(
|
||||||
transports: [new transports.Console()],
|
|
||||||
format: combine(
|
|
||||||
timestamp({
|
timestamp({
|
||||||
format: "YYYY-MM-DD HH:mm:ss",
|
format: "YYYY-MM-DD HH:mm:ss",
|
||||||
}),
|
}),
|
||||||
colorize(),
|
colorize(),
|
||||||
printf((info) => `${info.level}: ${[info.timestamp]}: ${info.message}`)
|
printf((info) => {
|
||||||
|
return `${info.level}: ${info.timestamp}: ${info.message}`;
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
exitOnError: false,
|
level: "silly",
|
||||||
|
levels: config.npm.levels,
|
||||||
|
transports: [ new transports.Console() ],
|
||||||
});
|
});
|
||||||
|
132
test/regions.spec.ts
Normal file
132
test/regions.spec.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { inarikoSeasons } from "../src/data/weather/regions/inarikoSeasons.ts";
|
||||||
|
import { rudaniaSeasons } from "../src/data/weather/regions/rudaniaSeasons.ts";
|
||||||
|
import { vhintlSeasons } from "../src/data/weather/regions/vhintlSeasons.ts";
|
||||||
|
|
||||||
|
describe("region Tests", () => {
|
||||||
|
describe("inariko", () => {
|
||||||
|
it("seasons should be unique", () => {
|
||||||
|
const seasons = new Set(inarikoSeasons.map((element) => {
|
||||||
|
return element.season;
|
||||||
|
}));
|
||||||
|
expect(seasons.size, "seasons are not unique!").toBe(
|
||||||
|
inarikoSeasons.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const season of inarikoSeasons) {
|
||||||
|
it(`${season.season} should have unique temps`, () => {
|
||||||
|
const temps = new Set(season.temps);
|
||||||
|
expect(temps.size, `${season.season} temps are not unique!`).toBe(
|
||||||
|
season.temps.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique winds`, () => {
|
||||||
|
const winds = new Set(season.wind);
|
||||||
|
expect(winds.size, `${season.season} winds are not unique!`).toBe(
|
||||||
|
season.wind.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique precipitation`, () => {
|
||||||
|
const precipitation = new Set(season.precipitation);
|
||||||
|
expect(
|
||||||
|
precipitation.size,
|
||||||
|
`${season.season} precipitation is not unique!`,
|
||||||
|
).toBe(season.precipitation.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique specials`, () => {
|
||||||
|
const specials = new Set(season.special);
|
||||||
|
expect(specials.size, `${season.season} specials are not unique!`).toBe(
|
||||||
|
season.special.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("rudania", () => {
|
||||||
|
it("seasons should be unique", () => {
|
||||||
|
const seasons = new Set(rudaniaSeasons.map((element) => {
|
||||||
|
return element.season;
|
||||||
|
}));
|
||||||
|
expect(seasons.size, "seasons are not unique!").toBe(
|
||||||
|
rudaniaSeasons.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const season of rudaniaSeasons) {
|
||||||
|
it(`${season.season} should have unique temps`, () => {
|
||||||
|
const temps = new Set(season.temps);
|
||||||
|
expect(temps.size, `${season.season} temps are not unique!`).toBe(
|
||||||
|
season.temps.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique winds`, () => {
|
||||||
|
const winds = new Set(season.wind);
|
||||||
|
expect(winds.size, `${season.season} winds are not unique!`).toBe(
|
||||||
|
season.wind.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique precipitation`, () => {
|
||||||
|
const precipitation = new Set(season.precipitation);
|
||||||
|
expect(
|
||||||
|
precipitation.size,
|
||||||
|
`${season.season} precipitation is not unique!`,
|
||||||
|
).toBe(season.precipitation.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique specials`, () => {
|
||||||
|
const specials = new Set(season.special);
|
||||||
|
expect(specials.size, `${season.season} specials are not unique!`).toBe(
|
||||||
|
season.special.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("vhintl", () => {
|
||||||
|
it("seasons should be unique", () => {
|
||||||
|
const seasons = new Set(vhintlSeasons.map((element) => {
|
||||||
|
return element.season;
|
||||||
|
}));
|
||||||
|
expect(seasons.size, "seasons are not unique!").toBe(
|
||||||
|
vhintlSeasons.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const season of vhintlSeasons) {
|
||||||
|
it(`${season.season} should have unique temps`, () => {
|
||||||
|
const temps = new Set(season.temps);
|
||||||
|
expect(temps.size, `${season.season} temps are not unique!`).toBe(
|
||||||
|
season.temps.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique winds`, () => {
|
||||||
|
const winds = new Set(season.wind);
|
||||||
|
expect(winds.size, `${season.season} winds are not unique!`).toBe(
|
||||||
|
season.wind.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique precipitation`, () => {
|
||||||
|
const precipitation = new Set(season.precipitation);
|
||||||
|
expect(
|
||||||
|
precipitation.size,
|
||||||
|
`${season.season} precipitation is not unique!`,
|
||||||
|
).toBe(season.precipitation.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${season.season} should have unique specials`, () => {
|
||||||
|
const specials = new Set(season.special);
|
||||||
|
expect(specials.size, `${season.season} specials are not unique!`).toBe(
|
||||||
|
season.special.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
102
test/weather.spec.ts
Normal file
102
test/weather.spec.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { precipitations } from "../src/data/weather/precipitations.ts";
|
||||||
|
import { specials } from "../src/data/weather/specials.ts";
|
||||||
|
import { temperatures } from "../src/data/weather/temperatures.ts";
|
||||||
|
import { winds } from "../src/data/weather/winds.ts";
|
||||||
|
|
||||||
|
describe("weather Tests", () => {
|
||||||
|
describe("temperature", () => {
|
||||||
|
const temps = new Set(temperatures.map((element) => {
|
||||||
|
return element.name;
|
||||||
|
}));
|
||||||
|
it("temperatures should be unique", () => {
|
||||||
|
expect(temps.size, "temperatures are not unique!").toBe(
|
||||||
|
temperatures.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("winds", () => {
|
||||||
|
const wind = new Set(winds.map((element) => {
|
||||||
|
return element.name;
|
||||||
|
}));
|
||||||
|
it("winds should be unique", () => {
|
||||||
|
expect(wind.size, "winds are not unique!").toBe(winds.length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("precipitations", () => {
|
||||||
|
const precip = new Set(precipitations.map((element) => {
|
||||||
|
return element.name;
|
||||||
|
}));
|
||||||
|
it("precipitations should be unique", () => {
|
||||||
|
expect(precip.size, "precipitations are not unique!").toBe(
|
||||||
|
precipitations.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const precipitation of precipitations) {
|
||||||
|
it(`${precipitation.name} should have unique temps`, () => {
|
||||||
|
if (precipitation.temps === "any") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const temps = new Set(precipitation.temps);
|
||||||
|
expect(temps.size, `${precipitation.name} temps are not unique!`).toBe(
|
||||||
|
precipitation.temps.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${precipitation.name} should have unique winds`, () => {
|
||||||
|
if (precipitation.winds === "any") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const winds = new Set(precipitation.winds);
|
||||||
|
expect(winds.size, `${precipitation.name} winds are not unique!`).toBe(
|
||||||
|
precipitation.winds.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("specials", () => {
|
||||||
|
const spec = new Set(specials.map((element) => {
|
||||||
|
return element.name;
|
||||||
|
}));
|
||||||
|
it("specials should be unique", () => {
|
||||||
|
expect(spec.size, "specials are not unique!").toBe(specials.length);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const special of specials) {
|
||||||
|
it(`${special.name} should have unique temps`, () => {
|
||||||
|
if (special.temps === "any") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const temps = new Set(special.temps);
|
||||||
|
expect(temps.size, `${special.name} temps are not unique!`).toBe(
|
||||||
|
special.temps.length,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${special.name} should have unique winds`, () => {
|
||||||
|
if (special.winds === "any") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const winds = new Set(special.winds);
|
||||||
|
expect(special.winds, "Special winds are not unique!").toHaveLength(
|
||||||
|
winds.size,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`${special.name} should have unique precipitation`, () => {
|
||||||
|
if (special.precipitations === "any") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const precip = new Set(special.precipitations);
|
||||||
|
expect(
|
||||||
|
precip.size,
|
||||||
|
`${special.name} precipitation is not unique!`,
|
||||||
|
).toBe(special.precipitations.length);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
@ -1,154 +0,0 @@
|
|||||||
import { assert } from "chai";
|
|
||||||
|
|
||||||
import { inarikoSeasons } from "../src/data/weather/regions/inarikoSeasons";
|
|
||||||
import { rudaniaSeasons } from "../src/data/weather/regions/rudaniaSeasons";
|
|
||||||
import { vhintlSeasons } from "../src/data/weather/regions/vhintlSeasons";
|
|
||||||
|
|
||||||
suite("Region Tests", () => {
|
|
||||||
suite("Inariko", () => {
|
|
||||||
test("Seasons should be unique", () => {
|
|
||||||
const seasons = new Set(inarikoSeasons.map((el) => el.season));
|
|
||||||
assert.equal(
|
|
||||||
seasons.size,
|
|
||||||
inarikoSeasons.length,
|
|
||||||
"seasons are not unique!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const season of inarikoSeasons) {
|
|
||||||
test(`${season.season} should have unique temps`, () => {
|
|
||||||
const temps = new Set(season.temps);
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
season.temps.length,
|
|
||||||
`${season.season} temps are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique winds`, () => {
|
|
||||||
const winds = new Set(season.wind);
|
|
||||||
assert.equal(
|
|
||||||
winds.size,
|
|
||||||
season.wind.length,
|
|
||||||
`${season.season} winds are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique precipitation`, () => {
|
|
||||||
const precipitation = new Set(season.precipitation);
|
|
||||||
assert.equal(
|
|
||||||
precipitation.size,
|
|
||||||
season.precipitation.length,
|
|
||||||
`${season.season} precipitation is not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique specials`, () => {
|
|
||||||
const specials = new Set(season.special);
|
|
||||||
assert.equal(
|
|
||||||
specials.size,
|
|
||||||
season.special.length,
|
|
||||||
`${season.season} specials are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite("Rudania", () => {
|
|
||||||
test("Seasons should be unique", () => {
|
|
||||||
const seasons = new Set(rudaniaSeasons.map((el) => el.season));
|
|
||||||
assert.equal(
|
|
||||||
seasons.size,
|
|
||||||
rudaniaSeasons.length,
|
|
||||||
"seasons are not unique!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const season of rudaniaSeasons) {
|
|
||||||
test(`${season.season} should have unique temps`, () => {
|
|
||||||
const temps = new Set(season.temps);
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
season.temps.length,
|
|
||||||
`${season.season} temps are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique winds`, () => {
|
|
||||||
const winds = new Set(season.wind);
|
|
||||||
assert.equal(
|
|
||||||
winds.size,
|
|
||||||
season.wind.length,
|
|
||||||
`${season.season} winds are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique precipitation`, () => {
|
|
||||||
const precipitation = new Set(season.precipitation);
|
|
||||||
assert.equal(
|
|
||||||
precipitation.size,
|
|
||||||
season.precipitation.length,
|
|
||||||
`${season.season} precipitation is not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique specials`, () => {
|
|
||||||
const specials = new Set(season.special);
|
|
||||||
assert.equal(
|
|
||||||
specials.size,
|
|
||||||
season.special.length,
|
|
||||||
`${season.season} specials are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite("Vhintl", () => {
|
|
||||||
test("Seasons should be unique", () => {
|
|
||||||
const seasons = new Set(vhintlSeasons.map((el) => el.season));
|
|
||||||
assert.equal(
|
|
||||||
seasons.size,
|
|
||||||
vhintlSeasons.length,
|
|
||||||
"seasons are not unique!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const season of vhintlSeasons) {
|
|
||||||
test(`${season.season} should have unique temps`, () => {
|
|
||||||
const temps = new Set(season.temps);
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
season.temps.length,
|
|
||||||
`${season.season} temps are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique winds`, () => {
|
|
||||||
const winds = new Set(season.wind);
|
|
||||||
assert.equal(
|
|
||||||
winds.size,
|
|
||||||
season.wind.length,
|
|
||||||
`${season.season} winds are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique precipitation`, () => {
|
|
||||||
const precipitation = new Set(season.precipitation);
|
|
||||||
assert.equal(
|
|
||||||
precipitation.size,
|
|
||||||
season.precipitation.length,
|
|
||||||
`${season.season} precipitation is not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${season.season} should have unique specials`, () => {
|
|
||||||
const specials = new Set(season.special);
|
|
||||||
assert.equal(
|
|
||||||
specials.size,
|
|
||||||
season.special.length,
|
|
||||||
`${season.season} specials are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,108 +0,0 @@
|
|||||||
import { assert } from "chai";
|
|
||||||
|
|
||||||
import { precipitations } from "../src/data/weather/precipitations";
|
|
||||||
import { specials } from "../src/data/weather/specials";
|
|
||||||
import { temperatures } from "../src/data/weather/temperatures";
|
|
||||||
import { winds } from "../src/data/weather/winds";
|
|
||||||
|
|
||||||
suite("Weather Tests", () => {
|
|
||||||
suite("Temperature", () => {
|
|
||||||
const temps = new Set(temperatures.map((el) => el.name));
|
|
||||||
test("Temperatures should be unique", () => {
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
temperatures.length,
|
|
||||||
"temperatures are not unique!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite("Winds", () => {
|
|
||||||
const wind = new Set(winds.map((el) => el.name));
|
|
||||||
test("Winds should be unique", () => {
|
|
||||||
assert.equal(wind.size, winds.length, "winds are not unique!");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
suite("Precipitations", () => {
|
|
||||||
const precip = new Set(precipitations.map((el) => el.name));
|
|
||||||
test("Precipitations should be unique", () => {
|
|
||||||
assert.equal(
|
|
||||||
precip.size,
|
|
||||||
precipitations.length,
|
|
||||||
"precipitations are not unique!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const precipitation of precipitations) {
|
|
||||||
test(`${precipitation.name} should have unique temps`, () => {
|
|
||||||
if (precipitation.temps === "any") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const temps = new Set(precipitation.temps);
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
precipitation.temps.length,
|
|
||||||
`${precipitation.name} temps are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${precipitation.name} should have unique winds`, () => {
|
|
||||||
if (precipitation.winds === "any") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const winds = new Set(precipitation.winds);
|
|
||||||
assert.equal(
|
|
||||||
winds.size,
|
|
||||||
precipitation.winds.length,
|
|
||||||
`${precipitation.name} winds are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
suite("Specials", () => {
|
|
||||||
const spec = new Set(specials.map((el) => el.name));
|
|
||||||
test("Specials should be unique", () => {
|
|
||||||
assert.equal(spec.size, specials.length, "specials are not unique!");
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const special of specials) {
|
|
||||||
test(`${special.name} should have unique temps`, () => {
|
|
||||||
if (special.temps === "any") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const temps = new Set(special.temps);
|
|
||||||
assert.equal(
|
|
||||||
temps.size,
|
|
||||||
special.temps.length,
|
|
||||||
`${special.name} temps are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${special.name} should have unique winds`, () => {
|
|
||||||
if (special.winds === "any") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const winds = new Set(special.winds);
|
|
||||||
assert.equal(
|
|
||||||
winds.size,
|
|
||||||
special.winds.length,
|
|
||||||
`${special.name} winds are not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test(`${special.name} should have unique precipitation`, () => {
|
|
||||||
if (special.precipitations === "any") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const precip = new Set(special.precipitations);
|
|
||||||
assert.equal(
|
|
||||||
precip.size,
|
|
||||||
special.precipitations.length,
|
|
||||||
`${special.name} precipitation is not unique!`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,14 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"extends": "@nhcarrigan/typescript-config",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES6",
|
|
||||||
"module": "CommonJS",
|
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./prod",
|
"outDir": "./prod",
|
||||||
"strict": true,
|
"exactOptionalPropertyTypes": false
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"resolveJsonModule": true
|
|
||||||
},
|
},
|
||||||
"exclude": ["tests/**/*.spec.ts"]
|
"exclude": ["./test", "vitest.config.ts"]
|
||||||
}
|
}
|
15
vitest.config.ts
Normal file
15
vitest.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
coverage: {
|
||||||
|
provider: "istanbul",
|
||||||
|
reporter: ["text", "html"],
|
||||||
|
all: true,
|
||||||
|
allowExternal: true,
|
||||||
|
thresholds: {
|
||||||
|
lines: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user