mirror of
https://github.com/sern-handler/cli
synced 2026-06-06 01:16:53 +00:00
merge n progress
This commit is contained in:
10
README.md
10
README.md
@@ -3,11 +3,11 @@
|
||||
</div>
|
||||
|
||||
# Features
|
||||
|
||||
😁 **User Friendly** <br>
|
||||
💦 **Simple** <br>
|
||||
🌱 **Efficient** <br>
|
||||
💪 **Powerful** <br>
|
||||
- Manage discord application commands from the command line.
|
||||
- Install plugins from the community.
|
||||
- Really fast startup times (I think).
|
||||
- Deploy with premade docker configurations.
|
||||
- Inhouse build tool based on esbuild built for sern applications, nearly **zero** config.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import p from 'node:path';
|
||||
import { glob } from 'glob';
|
||||
import { configDotenv } from 'dotenv';
|
||||
import assert from 'node:assert';
|
||||
import { imageLoader, validExtensions } from '../plugins/imageLoader';
|
||||
import defaultEsbuild from '../utilities/defaultEsbuildConfig';
|
||||
import { require } from '../utilities/require';
|
||||
import { pathExists, pathExistsSync } from 'find-up';
|
||||
@@ -13,6 +12,7 @@ import * as Preprocessor from '../utilities/preprocessor';
|
||||
import { bold, magentaBright } from 'colorette';
|
||||
import { readFile } from 'fs/promises'
|
||||
import { fileURLToPath} from 'node:url'
|
||||
const validExtensions = ['.ts', '.js', '.json', '.png', '.jpg', '.jpeg', '.webp'];
|
||||
type BuildOptions = {
|
||||
/**
|
||||
* Define __VERSION__
|
||||
@@ -159,7 +159,7 @@ export async function build(options: Record<string, any>) {
|
||||
console.log(commandsImports)
|
||||
await esbuild.build({
|
||||
entryPoints: commandsPaths.map(file => p.join("src", "commands", file)),
|
||||
plugins: [imageLoader, ...(buildConfig.esbuildPlugins ?? [])],
|
||||
plugins: [...(buildConfig.esbuildPlugins ?? [])],
|
||||
...defaultEsbuild(buildConfig.format!, buildConfig.tsconfig, "./dist/commands"),
|
||||
outdir: "./dist/commands",
|
||||
dropLabels: [buildConfig.mode === 'production' ? '__DEV__' : '__PROD__', ...buildConfig.dropLabels!],
|
||||
@@ -173,13 +173,13 @@ export async function build(options: Record<string, any>) {
|
||||
.replace("\"use handle\";", `
|
||||
${commandsPaths.map((imp, i) => {
|
||||
if(i === 0) {
|
||||
return `if(interaction.data.name === "${p.parse(imp).name}") {
|
||||
const data = createContext(interaction)
|
||||
const success = await applyPlugins(${p.parse(imp).name}, data);
|
||||
if(success) {
|
||||
await ${p.parse(imp).name}.execute(data);
|
||||
}
|
||||
}`
|
||||
return `if(interaction.data.name === "${p.parse(imp).name}") {
|
||||
const data = createContext(interaction)
|
||||
const success = await applyPlugins(${p.parse(imp).name}, data);
|
||||
if(success) {
|
||||
await ${p.parse(imp).name}.execute(data);
|
||||
}
|
||||
}`
|
||||
}
|
||||
return `else if(interaction.data.name === "${p.parse(imp).name}" ) {
|
||||
const data = createContext(interaction)
|
||||
@@ -192,40 +192,38 @@ export async function build(options: Record<string, any>) {
|
||||
|
||||
await writeFile("./dist/out.js", importedModulesTemplate);
|
||||
} else {
|
||||
|
||||
|
||||
const entryPoints = await glob(`./src/**/*{${validExtensions.join(',')}}`, {
|
||||
//for some reason, my ignore glob wasn't registering correctly'
|
||||
ignore: {
|
||||
ignored: (p) => p.name.endsWith('.d.ts'),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const defVersion = () => JSON.stringify(packageJson().version);
|
||||
const define = {
|
||||
...(buildConfig.define ?? {}),
|
||||
__DEV__: `${buildConfig.mode === 'development'}`,
|
||||
__PROD__: `${buildConfig.mode === 'production'}`,
|
||||
} satisfies Record<string, string>;
|
||||
|
||||
buildConfig.defineVersion && Object.assign(define, { __VERSION__: defVersion() });
|
||||
|
||||
await Preprocessor.writeTsConfig(buildConfig.format!, sernTsConfigPath, writeFile);
|
||||
await Preprocessor.writeAmbientFile(ambientFilePath, define, writeFile);
|
||||
|
||||
//https://esbuild.github.io/content-types/#tsconfig-json
|
||||
await esbuild.build({
|
||||
entryPoints,
|
||||
plugins: [imageLoader, ...(buildConfig.esbuildPlugins ?? [])],
|
||||
...defaultEsbuild(buildConfig.format!, buildConfig.tsconfig),
|
||||
define,
|
||||
dropLabels: [buildConfig.mode === 'production' ? '__DEV__' : '__PROD__', ...buildConfig.dropLabels!],
|
||||
const entryPoints = await glob(`./src/**/*{${validExtensions.join(',')}}`, {
|
||||
//for some reason, my ignore glob wasn't registering correctly'
|
||||
ignore: {
|
||||
ignored: (p) => p.name.endsWith('.d.ts'),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const defVersion = () => JSON.stringify(packageJson().version);
|
||||
const define = {
|
||||
...(buildConfig.define ?? {}),
|
||||
__DEV__: `${buildConfig.mode === 'development'}`,
|
||||
__PROD__: `${buildConfig.mode === 'production'}`,
|
||||
} satisfies Record<string, string>;
|
||||
|
||||
buildConfig.defineVersion && Object.assign(define, { __VERSION__: defVersion() });
|
||||
|
||||
await Preprocessor.writeTsConfig(buildConfig.format!, sernTsConfigPath, writeFile);
|
||||
await Preprocessor.writeAmbientFile(ambientFilePath, define, writeFile);
|
||||
|
||||
//https://esbuild.github.io/content-types/#tsconfig-json
|
||||
await esbuild.build({
|
||||
entryPoints,
|
||||
plugins: [...(buildConfig.esbuildPlugins ?? [])],
|
||||
...defaultEsbuild(buildConfig.format!, buildConfig.tsconfig),
|
||||
define,
|
||||
dropLabels: [buildConfig.mode === 'production' ? '__DEV__' : '__PROD__', ...buildConfig.dropLabels!],
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ export async function publish(commandDir: string | undefined, args: Partial<Publ
|
||||
// assign args.import to empty array if non existent
|
||||
args.import ??= [];
|
||||
|
||||
args.token && console.info('Token passed through command line');
|
||||
args.applicationId && console.info(magentaBright('WARNING')+ ' This option is deprecated. Do not pass applicationId through command line');
|
||||
commandDir && console.info('Publishing with override path: ', commandDir);
|
||||
|
||||
const dotenvLocation = new URL('../node_modules/dotenv/config.js', rootPath),
|
||||
@@ -25,10 +23,6 @@ export async function publish(commandDir: string | undefined, args: Partial<Publ
|
||||
// loader flag to require typescript files
|
||||
const command = fork(fileURLToPath(publishScript), [], {
|
||||
execArgv: ['--loader', esmLoader.toString(), '-r', fileURLToPath(dotenvLocation), '--no-warnings'],
|
||||
env: {
|
||||
token: args.token ?? '',
|
||||
applicationId: args.applicationId ?? '',
|
||||
},
|
||||
});
|
||||
// send paths object so we dont have to recalculate it in script
|
||||
command.send({ config, preloads: args.import, commandDir });
|
||||
|
||||
@@ -10,7 +10,7 @@ import { once } from 'node:events';
|
||||
import * as Rest from './rest';
|
||||
import type { sernConfig } from './utilities/getConfig';
|
||||
import type { PublishableData, PublishableModule, Typeable } from './create-publish.d.ts';
|
||||
import { cyanBright, greenBright, magentaBright, redBright } from 'colorette';
|
||||
import { cyanBright, greenBright, redBright } from 'colorette';
|
||||
import { inspect } from 'node:util'
|
||||
import ora from 'ora';
|
||||
|
||||
@@ -153,6 +153,19 @@ const makePublishData = ({ commandModule, config }: Record<string, Record<string
|
||||
options: optionsTransformer((commandModule?.options ?? []) as Typeable[]),
|
||||
dm_permission: config?.dmPermission,
|
||||
default_member_permissions: serialize(config?.defaultMemberPermissions),
|
||||
//@ts-ignore
|
||||
integration_types: (config?.integrationTypes ?? ['Guild']).map(
|
||||
(s: string) => {
|
||||
if(s === "Guild") {
|
||||
return 0
|
||||
} else if (s == "User") {
|
||||
return 1
|
||||
} else {
|
||||
throw Error("IntegrationType is not one of Guild or User");
|
||||
}
|
||||
}),
|
||||
//@ts-ignore
|
||||
contexts: config?.contexts ? config.contexts : undefined
|
||||
},
|
||||
config,
|
||||
};
|
||||
@@ -160,11 +173,9 @@ const makePublishData = ({ commandModule, config }: Record<string, Record<string
|
||||
|
||||
// We can use these objects to publish to DAPI
|
||||
const publishableData = modules.map(makePublishData),
|
||||
token = process.env.token || process.env.DISCORD_TOKEN,
|
||||
appid = process.env.applicationId || process.env.APPLICATION_ID;
|
||||
token = process.env.token || process.env.DISCORD_TOKEN;
|
||||
|
||||
assert(token, 'Could not find a token for this bot in .env or commandline. Do you have DISCORD_TOKEN in env?');
|
||||
appid && console.warn(`${magentaBright('WARNING')}: APPLICATION_ID is not necessary anymore`);
|
||||
// partition globally published and guilded commands
|
||||
const [globalCommands, guildedCommands] = publishableData.reduce(
|
||||
([globals, guilded], module) => {
|
||||
|
||||
@@ -40,7 +40,6 @@ program //
|
||||
.option('-W --suppress-warnings', 'suppress experimental warning')
|
||||
.option('-i, --import [scriptPath...]', 'Prerequire a script to load into publisher')
|
||||
.option('-t, --token [token]')
|
||||
.option('--appId [applicationId]')
|
||||
.argument('[path]', 'path with respect to current working directory that will locate all published files')
|
||||
.action(async (...args) => importDynamic('publish.js').then((m) => m.publish(...args)))
|
||||
).addCommand(
|
||||
@@ -53,6 +52,14 @@ program //
|
||||
.option('-y, --yes', "Say yes to all prompts")
|
||||
.option('-e, --env [path]', "Supply a path to a .env")
|
||||
.action(async (...args) => importDynamic('command-clear.js').then((m) => m.commandClear(...args))));
|
||||
program
|
||||
.command('app')
|
||||
.description('manage your discord application')
|
||||
.addCommand(
|
||||
new Command('update')
|
||||
.description("Refresh your discord application.")
|
||||
.option('-W --suppress-warnings', 'suppress experimental warning')
|
||||
.action(async (...args) => importDynamic('app-update.js').then(m => m.appUpdate(...args))))
|
||||
|
||||
program
|
||||
.command('build')
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import fs from 'fs/promises';
|
||||
import path from 'node:path';
|
||||
import { require } from '../utilities/require.js';
|
||||
import { type Plugin } from 'esbuild';
|
||||
import { basename } from 'node:path';
|
||||
|
||||
export const validExtensions = ['.ts', '.js', '.json', '.png', '.jpg', '.jpeg', '.webp'];
|
||||
|
||||
//https://github.com/evanw/esbuild/issues/1051
|
||||
export const imageLoader = {
|
||||
name: 'attachment-loader',
|
||||
setup: (b) => {
|
||||
const filter = new RegExp(`\.${validExtensions.slice(3).join('|')}$`);
|
||||
b.onResolve({ filter }, (args) => {
|
||||
//if the module is being imported, resolve the path and transform to the js stub
|
||||
if (args.importer) {
|
||||
const newPath = path
|
||||
.format({ ...path.parse(args.path), base: '', ext: '.js' })
|
||||
.split(path.sep)
|
||||
.join(path.posix.sep);
|
||||
return { path: newPath, namespace: 'attachment-loader', external: true };
|
||||
}
|
||||
// if the file is actually the attachment, resolve the full dir
|
||||
return { path: require.resolve(args.path, { paths: [args.resolveDir] }), namespace: 'attachment-loader' };
|
||||
});
|
||||
|
||||
b.onLoad({ filter: /.*/, namespace: 'attachment-loader' }, async (args) => {
|
||||
const base64 = await fs.readFile(args.path).then((s) => s.toString('base64'));
|
||||
return {
|
||||
contents: `
|
||||
var __toBuffer = (base64) => Buffer.from(base64, "base64");
|
||||
module.exports = {
|
||||
name: '${basename(args.path)}',
|
||||
attachment: __toBuffer("${base64}")
|
||||
}`,
|
||||
};
|
||||
});
|
||||
},
|
||||
} satisfies Plugin;
|
||||
@@ -11,7 +11,6 @@ export async function getConfig(): Promise<sernConfig> {
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export interface sernConfig {
|
||||
type?: "serverless" | "websocket"
|
||||
language: 'typescript' | 'javascript';
|
||||
@@ -21,5 +20,21 @@ export interface sernConfig {
|
||||
commands: string;
|
||||
events?: string;
|
||||
};
|
||||
buildPath: string;
|
||||
app?: {
|
||||
customInstallUrl?: string;
|
||||
description?: string;
|
||||
roleConnectionsVerificationUrl?: string;
|
||||
installParams?: {
|
||||
type: 'install params object';
|
||||
};
|
||||
integrationTypesConfig?: {
|
||||
type: 'dictionary with keys of application integration types';
|
||||
description: 'In preview. Default scopes and permissions for each supported installation context. Value for each key is an integration type configuration object';
|
||||
};
|
||||
flags?: number;
|
||||
icon?: '?image data';
|
||||
coverImage?: '?image data';
|
||||
interactionsEndpointUrl?: string;
|
||||
tags: string[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ class JsonResponse extends Response {
|
||||
super(jsonBody, init);
|
||||
}
|
||||
}
|
||||
|
||||
function createContext(rawcontext) {
|
||||
return rawcontext
|
||||
}
|
||||
@@ -40,7 +41,7 @@ async function executeModule(
|
||||
|
||||
async function applyPlugins(module, payload) {
|
||||
let success = true;
|
||||
for (const plg of module.onEvent){
|
||||
for (const plg of module.onEvent) {
|
||||
const res = await plg.execute(payload);
|
||||
if(!res.isOk()) {
|
||||
success = false;
|
||||
|
||||
Reference in New Issue
Block a user