Skip to content

Commit

Permalink
refactor: update scripts to be TS
Browse files Browse the repository at this point in the history
  • Loading branch information
favna committed Dec 15, 2023
1 parent df80999 commit b46f036
Show file tree
Hide file tree
Showing 5 changed files with 424 additions and 117 deletions.
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
"build": "tsup",
"watch": "tsup --watch",
"dev": "tsup --watch --onSuccess \"yarn start\"",
"clean": "node scripts/clean.mjs",
"validate-tags": "node scripts/validateTags.mjs"
"clean": "rimraf dist/",
"validate-tags": "tsx scripts/validate-tags.ts"
},
"dependencies": {
"@discordjs/builders": "^1.7.0",
"@discordjs/collection": "^1.5.3",
"@ltd/j-toml": "~1.38.0",
"@sapphire/fetch": "^2.4.2",
"@sapphire/fetch": "^3.0.0",
"@sapphire/result": "^2.6.5",
"@sapphire/utilities": "^3.14.0",
"@skyra/env-utilities": "^1.2.2",
Expand Down Expand Up @@ -58,9 +58,11 @@
"eslint-plugin-prettier": "^5.0.1",
"lint-staged": "^15.2.0",
"prettier": "^3.1.1",
"rimraf": "^5.0.5",
"tsup": "^8.0.1",
"tsx": "^4.6.2",
"typescript": "^5.3.3",
"vite": "^5.0.7",
"vite": "^5.0.9",
"vitest": "^1.0.4"
},
"resolutions": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
process.env.NODE_ENV ??= 'production';

import { fetch, FetchMethods, FetchResultTypes } from '@sapphire/fetch';
import { ApplicationCommandPermissionType, OAuth2Routes, RouteBases, Routes } from 'discord-api-types/v10';
import { config } from 'dotenv-cra';
import { envParseString, setup } from '@skyra/env-utilities';
import {
type APIApplicationCommand,
ApplicationCommandPermissionType,
OAuth2Routes,
RouteBases,
Routes,
type RESTPostOAuth2ClientCredentialsResult
} from 'discord-api-types/v10';
import { stringify } from 'node:querystring';
import { fileURLToPath } from 'node:url';
import { inspect } from 'node:util';

config({ path: fileURLToPath(new URL('../.env', import.meta.url)) });
setup({ path: new URL('../.env', import.meta.url) });

const ApplicationSecret = process.env.DISCORD_APPLICATION_SECRET;
const ApplicationId = process.env.DISCORD_CLIENT_ID;
const ApplicationSecret = envParseString('DISCORD_APPLICATION_SECRET');
const ApplicationId = envParseString('DISCORD_CLIENT_ID');

if (!ApplicationId || !ApplicationSecret) {
throw new Error('Please fill in all env variables in your ".env.local" file');
}

/**
* Retrieves a client_credentials access token from the Discord OAUTH API
* @returns {Promise<string>} The access token to be used
* @returns The access token to be used
*/
async function getBearerToken() {
/** @type {import('discord-api-types/v10').RESTPostOAuth2ClientCredentialsResult} */
const body = await fetch(
async function getBearerToken(): Promise<string> {
const body = await fetch<RESTPostOAuth2ClientCredentialsResult>(
OAuth2Routes.tokenURL,
{
headers: {
Expand All @@ -45,10 +50,11 @@ async function getBearerToken() {

/**
* Updates permissions for the reload tags chat input command
* @param {string} token The authentication token from Discord
* @param {import('discord-api-types/v10').APIApplicationCommand} reloadTagsData The data from Discord for the reloadtags command
* @param token The authentication token from Discord
* @param staffId The ID of the Sapphire Staff role
* @param reloadTagsData The data from Discord for the reloadtags command
*/
async function allowSapphireStaffToUseCommand(token, reloadTagsData) {
async function allowSapphireStaffToUseCommand(token: string, staffId: string, reloadTagsData: APIApplicationCommand) {
try {
const res = await fetch(
`${RouteBases.api}${Routes.applicationCommandPermissions(ApplicationId, '737141877803057244', reloadTagsData.id)}`,
Expand All @@ -61,7 +67,7 @@ async function allowSapphireStaffToUseCommand(token, reloadTagsData) {
body: JSON.stringify({
permissions: [
{
id: '868612689977569341',
id: staffId,
type: ApplicationCommandPermissionType.Role,
permission: true
}
Expand All @@ -79,12 +85,11 @@ async function allowSapphireStaffToUseCommand(token, reloadTagsData) {

/**
* Updates global chat input commands
* @param {string} token The authentication token from Discord
* @param token The authentication token from Discord
*/
async function getReloadTagsCommand(token) {
async function getReloadTagsCommand(token: string) {
try {
/** @type {Array<import('discord-api-types/v10').APIApplicationCommand>} */
const res = await fetch(
const res = await fetch<APIApplicationCommand[]>(
`${RouteBases.api}${Routes.applicationCommands(ApplicationId)}`,
{
headers: {
Expand All @@ -106,8 +111,17 @@ const token = await getBearerToken();

const allCommandsData = await getReloadTagsCommand(token);

const reloadTagCommand = allCommandsData.find((command) => command.name === 'reload-tags');
if (allCommandsData) {
const reloadTagCommand = allCommandsData.find((command) => command.name === 'reload-tags');

if (reloadTagCommand) {
await allowSapphireStaffToUseCommand(token, reloadTagCommand);
if (reloadTagCommand) {
await allowSapphireStaffToUseCommand(token, '868612689977569341', reloadTagCommand);
}
}

declare module '@skyra/env-utilities' {
interface Env {
DISCORD_CLIENT_ID: string;
DISCORD_APPLICATION_SECRET: string;
}
}
6 changes: 0 additions & 6 deletions scripts/clean.mjs

This file was deleted.

62 changes: 32 additions & 30 deletions scripts/validateTags.mjs → scripts/validate-tags.ts
Original file line number Diff line number Diff line change
@@ -1,76 +1,77 @@
import { parse as parseToml } from '@ltd/j-toml';
import { objectEntries } from '@sapphire/utilities';
import { green, red } from 'colorette';
import { readFile } from 'node:fs/promises';
import { Conflict, Tag } from '../src/lib/types/Tags';

const tagsDir = new URL('../src/tags/', import.meta.url);

const file = await readFile(new URL('tags.toml', tagsDir), { encoding: 'utf-8' });
const data = parseToml(file, 1.0, '\n');

/** @type {import('../src/lib/types/Tags').Conflict[]} */
const conflicts = [];
const conflicts: Conflict[] = [];
let hoisted = 0;

for (const [key, value] of Object.entries(data)) {
/** @type {import('../src/lib/types/Tags').Tag} */
const v = value;
for (const [key, value] of objectEntries(data)) {
const typedValue = value as unknown as Tag;
const typedKey = key as string;

const codeBlockRegex = /(`{1,3}).+?\1/gs;
const detectionRegex = /\[[^\[\]]+?\]\([^<][^\(\)]+?[^>]\)/g;
const cleanedContent = v.content.replace(codeBlockRegex, '');
const cleanedContent = typedValue.content.replace(codeBlockRegex, '');

const conflictLinks = [];
/** @type {RegExpExecArray | null} */
let result;
const conflictLinks: string[] = [];
let result: RegExpExecArray | null;

while ((result = detectionRegex.exec(cleanedContent)) !== null) {
conflictLinks.push(result[0]);
}

if (conflictLinks.length) {
conflicts.push({
firstName: key,
firstName: typedKey,
secondName: '',
conflictKeyWords: conflictLinks,
type: 'unescapedLink'
});
}

if (v.hoisted) {
if (typedValue.hoisted) {
hoisted++;
}

for (const [otherKey, otherValue] of Object.entries(data)) {
/** @type {import('../src/lib/types/Tags').Tag} */
const oV = otherValue;
for (const [otherKey, otherValue] of objectEntries(data)) {
const typedOtherValue = otherValue as unknown as Tag;
const typedOtherKey = otherKey as string;

if (
(v.keywords.some((k) => !k.replace(/\s+/g, '').length) || !v.content.replace(/\s+/g, '').length) &&
!conflicts.some((c) => c.type === 'emptyKeyword' && c.firstName === key)
(typedValue.keywords.some((k) => !k.replace(/\s+/g, '').length) || !typedValue.content.replace(/\s+/g, '').length) &&
!conflicts.some((c) => c.type === 'emptyKeyword' && c.firstName === typedKey)
) {
conflicts.push({
firstName: key,
firstName: typedKey,
secondName: '',
conflictKeyWords: [],
type: 'emptyKeyword'
});
}

if (key !== otherKey) {
if (!v.keywords.includes(key) && !conflicts.some((c) => c.type === 'headerInKeywords' && c.firstName === key)) {
if (typedKey !== typedOtherKey) {
if (!typedValue.keywords.includes(typedKey) && !conflicts.some((c) => c.type === 'headerInKeywords' && c.firstName === typedKey)) {
conflicts.push({
firstName: key,
firstName: typedKey,
secondName: '',
conflictKeyWords: [],
type: 'headerInKeywords'
});
}

const conflictKeyWords = v.keywords.filter((k) => oV.keywords.includes(k));
const conflictKeyWords = typedValue.keywords.filter((k) => typedOtherValue.keywords.includes(k));

if (conflictKeyWords.length && !conflicts.some((c) => [c.firstName, c.secondName].every((e) => [key, otherKey].includes(e)))) {
if (conflictKeyWords.length && !conflicts.some((c) => [c.firstName, c.secondName].every((e) => [typedKey, typedOtherKey].includes(e)))) {
conflicts.push({
firstName: key,
secondName: otherKey,
firstName: typedKey,
secondName: typedOtherKey,
conflictKeyWords,
type: 'uniqueKeywords'
});
Expand All @@ -80,8 +81,13 @@ for (const [key, value] of Object.entries(data)) {
}

if (conflicts.length || hoisted > 20) {
const parts = [];
const { uniqueConflicts, headerConflicts, emptyConflicts, linkConflicts } = conflicts.reduce(
const parts: string[] = [];
const { uniqueConflicts, headerConflicts, emptyConflicts, linkConflicts } = conflicts.reduce<{
uniqueConflicts: Conflict[];
headerConflicts: Conflict[];
emptyConflicts: Conflict[];
linkConflicts: Conflict[];
}>(
(a, c) => {
switch (c.type) {
case 'uniqueKeywords':
Expand All @@ -99,13 +105,9 @@ if (conflicts.length || hoisted > 20) {
return a;
},
{
/** @type {import('../src/lib/types/Tags').Conflict[]} */
uniqueConflicts: [],
/** @type {import('../src/lib/types/Tags').Conflict[]} */
headerConflicts: [],
/** @type {import('../src/lib/types/Tags').Conflict[]} */
emptyConflicts: [],
/** @type {import('../src/lib/types/Tags').Conflict[]} */
linkConflicts: []
}
);
Expand Down
Loading

0 comments on commit b46f036

Please sign in to comment.