Initial Commit

This commit is contained in:
2025-06-01 12:14:53 -07:00
commit fcfd6066f7
8 changed files with 1216 additions and 0 deletions

57
src/cacheController.js Normal file
View File

@@ -0,0 +1,57 @@
const fs = require('fs/promises');
const fsSync = require('fs');
const path = require('path')
const { request } = require('undici')
const { cacheInfo } = require('../config.json');
const AVAILABLE_TAGS = {
SPC_OUTLOOK: "spcoutlook"
}
class CacheController {
constructor(options) {
}
initCache() {
Object.values(AVAILABLE_TAGS).forEach(tag => {
if(!fsSync.existsSync(path.join(cacheInfo.path, tag))) {
fsSync.mkdirSync(path.join(cacheInfo.path, tag))
}
});
}
async storeFile(fileData, fileName, tag) {
return fs.writeFile(path.join(cacheInfo.path, tag, fileName), fileData)
}
async fetchFile(fileName, tag) {
try {
const fileData = await fs.readFile(path.join(cacheInfo.path, tag, fileName));
return {fileExists: true, fileData}
}
catch (err) {
return {fileExists: false, error: err}
}
}
async getOrCacheBinary(url, fileName, tag) {
let results = await this.fetchFile(fileName, tag)
if(results.fileExists) {
console.log("File exists!")
return results.fileData
}
console.log("We gotta fetch it!")
let response = await request(url);
let requestFileData = await response.body.bytes();
await this.storeFile(Buffer.from(requestFileData.buffer), fileName, tag)
return Buffer.from(requestFileData.buffer)
}
}
const Cache = new CacheController();
Cache.initCache()
module.exports = {
Cache,
AVAILABLE_TAGS
}

47
src/commands/ping.js Normal file
View File

@@ -0,0 +1,47 @@
const { isMessageInstance } = require('@sapphire/discord.js-utilities');
const { Command } = require('@sapphire/framework');
class PingCommand extends Command {
constructor(context, options) {
super(context, {
...options,
name: 'ping',
aliases: ['pong'],
description: 'ping pong'
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder //
.setName(this.name)
.setDescription(this.description)
,{
idHints: '1355583305029783573'
});
}
async messageRun(message) {
const msg = await message.channel.send('Ping?');
const content = `Pong from JavaScript! Bot Latency ${Math.round(this.container.client.ws.ping)}ms. API Latency ${msg.createdTimestamp - message.createdTimestamp}ms.`;
return msg.edit(content);
}
async chatInputRun(interaction) {
const currentDate = new Date();
const msg = await interaction.reply({ content: `Ping? Current UTC time is ${currentDate.getUTCHours()}:${currentDate.getUTCMinutes()}:${currentDate.getUTCSeconds()}`, ephemeral: true, fetchReply: true });
if (isMessageInstance(msg)) {
const diff = msg.createdTimestamp - interaction.createdTimestamp;
const ping = Math.round(this.container.client.ws.ping);
return interaction.editReply(`Pong 🏓! (Round trip took: ${diff}ms. Heartbeat: ${ping}ms.)`);
}
return interaction.editReply('Failed to retrieve ping :(');
}
}
module.exports = {
PingCommand
};

135
src/commands/spc.js Normal file
View File

@@ -0,0 +1,135 @@
const { Subcommand } = require('@sapphire/plugin-subcommands');
const { AVAILABLE_TAGS, Cache } = require('../cacheController')
const { request } = require('undici')
const { spcGenerationTimes } = require('../../config.json')
// Extend `Subcommand` instead of `Command`
class UserCommand extends Subcommand {
constructor(context, options) {
super(context, {
...options,
name: 'spc',
subcommands: [
{
name: 'getoutlook',
chatInputRun: 'getOutlook'
}
]
});
}
registerApplicationCommands(registry) {
registry.registerChatInputCommand((builder) =>
builder
.setName('spc')
.setDescription('Get SPC Information') // Needed even though base command isn't displayed to end user
.addSubcommand((command) =>
command.setName('getoutlook')
.setDescription('Gets SPC Outlooks')
.addStringOption((option) =>
option.setName("outlookday")
.setDescription("Outlook Day(s) to pull")
.setRequired(true)
.addChoices(
{ name: 'Day 1 (Today)', value: 'day1'},
{ name: 'Day 2 (Tomorrow)', value: 'day2'},
{ name: 'Day 3', value: 'day3'},
{ name: 'Days 4-7', value: 'day4'}
)
)
)
, {
idHints: '1355696856037986435'
});
}
async getOutlook(interaction) {
await interaction.deferReply()
const day = interaction.options.getString('outlookday');
const currentDate = new Date();
const cacheDateString = currentDate.getMonth() + "" + currentDate.getDate() + "" + currentDate.getFullYear()
if(day == "day4" ) {
console.log("Day is day4")
let fileData = await Cache.getOrCacheBinary('https://www.spc.noaa.gov/products/exper/day4-8/day48prob.gif',
cacheDateString.concat("_", day), AVAILABLE_TAGS.SPC_OUTLOOK);
return interaction.editReply({
files: [{
attachment: fileData, name: `SPC_Day4Outlook_${cacheDateString}.gif`
}]
})
}
let timeText = this.normalizeTime(day);
console.log(`The timeText is ${timeText}`)
if(day == "day1" || day == "day2") {
console.log(`It's day1 or day2! It's ${day}`)
const cacheDateString = currentDate.getMonth() + "" + currentDate.getDate() + "" + currentDate.getFullYear()
// Day 1 and Day 2 have probabalistic that we want to grab.
try {
console.log("Trying!")
let files = await Promise.all([
Cache.getOrCacheBinary(`https://www.spc.noaa.gov/products/outlook/${day}otlk_${timeText}.gif`,
cacheDateString.concat("_", day, timeText), AVAILABLE_TAGS.SPC_OUTLOOK),
Cache.getOrCacheBinary(`https://www.spc.noaa.gov/products/outlook/${day}probotlk_${timeText}_torn.gif`,
cacheDateString.concat("_", day, timeText, "torn"), AVAILABLE_TAGS.SPC_OUTLOOK),
Cache.getOrCacheBinary(`https://www.spc.noaa.gov/products/outlook/${day}probotlk_${timeText}_wind.gif`,
cacheDateString.concat("_", day, timeText, "wind"), AVAILABLE_TAGS.SPC_OUTLOOK),
Cache.getOrCacheBinary(`https://www.spc.noaa.gov/products/outlook/${day}probotlk_${timeText}_hail.gif`,
cacheDateString.concat("_", day, timeText, "hail"), AVAILABLE_TAGS.SPC_OUTLOOK)
])
return interaction.editReply({
files: [{
attachment: files[0], name: `SPC_${day}Outlook_${timeText}_${cacheDateString}.gif`
}, {
attachment: files[1], name: `SPC_${day}Outlook_${timeText}_Tornado_${cacheDateString}.gif`
}, {
attachment: files[2], name: `SPC_${day}Outlook_${timeText}_Wind_${cacheDateString}.gif`
}, {
attachment: files[3], name: `SPC_${day}Outlook_${timeText}_Hail_${cacheDateString}.gif`
}]
})
}
catch (err) {
console.log(err);
return interaction.editReply("Oh shit something happened!")
}
}
else {
console.log("It's day3!")
const cacheDateString = currentDate.getMonth() + "" + currentDate.getDate() + "" + currentDate.getFullYear()
let file = await Cache.getOrCacheBinary(`https://www.spc.noaa.gov/products/outlook/day3otlk_${timeText}.gif`,
cacheDateString.concat("_", day, timeText), AVAILABLE_TAGS.SPC_OUTLOOK)
return interaction.editReply({
files: [{
attachment: file, name: `SPC_Day3Outlook_${timeText}_${cacheDateString}.gif`
}]
})
}
}
normalizeTime(day) {
const currentUTCHour = (new Date()).getUTCHours();
const currentUTCMinutes = (new Date()).getUTCMinutes();
const dateNumber = (currentUTCHour * 100) + currentUTCMinutes
let generationTimes = spcGenerationTimes[day];
let timeLiteral = ""
generationTimes.forEach((timeObject) => {
if(dateNumber >= timeObject.startTime && dateNumber < timeObject.endTime) {
timeLiteral = timeObject.timeText
}
});
return timeLiteral;
}
}
module.exports = {
UserCommand
};

11
src/index.js Normal file
View File

@@ -0,0 +1,11 @@
const { LogLevel, SapphireClient } = require('@sapphire/framework');
const { GatewayIntentBits } = require('discord.js');
const { token } = require('../config.json')
const client = new SapphireClient({
intents: [GatewayIntentBits.MessageContent, GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
loadMessageCommandListeners: true,
logger: { level: LogLevel.Debug }
});
client.login(token);