@santerzet/botkit
Version:
CLI for Discord Bot
213 lines (212 loc) • 8.13 kB
JavaScript
import { fromAsync, isOk, isErr } from '@sapphire/result';
import { blueBright, red } from 'colorette';
import { execa } from 'execa';
import { cp, readFile, rm, writeFile } from 'node:fs/promises';
import prompts from 'prompts';
import { PromptNew } from '#prompts/PromptNew';
import { Spinner } from '#functions/Spinner';
import { fileExists } from '#functions/FileExists';
import { repoUrl } from '#constants';
import { CommandExists } from '#functions/CommandExists';
async function editPackageJson(location, name, description, version, heroku) {
const pjLocation = `${location}/package.json`;
const output = JSON.parse(await readFile(pjLocation, 'utf8'));
if (!output)
throw new Error("Can't read file.");
output.name = name.toLowerCase();
output.description = description;
output.version = version;
if (heroku)
output.scripts.heroku = `git add . && git commit -m \"Update bot\" && git push heroku main`;
const result = await fromAsync(() => writeFile(pjLocation, JSON.stringify(output, null, 2)));
return isOk(result);
}
async function cloneRepo(location, verbose) {
const result = await fromAsync(async () => execa('git', ['clone', repoUrl, `${location}/ghr`], {
stdio: verbose ? 'inherit' : undefined,
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw new Error('An unknown error occurred while cloning the repository. Try running BotKit CLI with "--verbose" flag.');
}
}
async function initializeGitRepo(location) {
await execa('git', ['init'], { cwd: `./${location}/` });
return true;
}
async function herokuLogin() {
const result = await fromAsync(() => execa('heroku', ['login'], {
stdin: 'inherit',
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw result.error;
}
return true;
}
async function initializeHeroku(location, verbose) {
const result = await fromAsync(() => execa('heroku', ['create'], {
stdio: verbose ? 'inherit' : undefined,
cwd: `./${location}/`,
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw new Error('An unknown error occurred while creating Heroku app. Try running BotKit CLI with "--verbose" flag.');
}
return true;
}
async function pushToHeroku(location, verbose) {
await execa('git', ['add', '.'], { cwd: `./${location}/` });
await execa('git', ['commit', '-m', '"Initial commit"'], {
cwd: `./${location}/`,
});
const result = await fromAsync(async () => execa('git', ['push', 'heroku', 'main'], {
stdio: verbose ? 'inherit' : undefined,
cwd: `./${location}/`,
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw new Error('An unknown error occurred while pushing to Heroku. Try running BotKit CLI with "--verbose" flag.');
}
return true;
}
async function configureHeroku(location, verbose) {
const result = await fromAsync(async () => execa('heroku', ['ps:scale', 'web=0', 'worker=1'], {
stdio: verbose ? 'inherit' : undefined,
cwd: `./${location}/`,
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw new Error('An unknown error occurred while configuring Heroku. Try running BotKit CLI with "--verbose" flag.');
}
return true;
}
async function installDeps(location, verbose) {
const result = await fromAsync(() => execa('npm', ['install'], {
stdio: verbose ? 'inherit' : undefined,
cwd: `./${location}/`,
}));
if (isErr(result)) {
throw result.error;
}
if (result.value.exitCode !== 0) {
throw new Error('An unknown error occurred while installing dependencies. Try running BotKit CLI with "--verbose" flag.');
}
const oppositeLockfile = `./${location}/package-lock.json`;
if (await fileExists(oppositeLockfile)) {
await rm(oppositeLockfile);
}
return true;
}
async function runJob(job, name, showError = true) {
const spinner = new Spinner(name).start();
const result = await fromAsync(async () => job());
if (isErr(result)) {
spinner.error();
if (showError) {
console.error(red(result.error.message));
}
process.exit(1);
}
spinner.success();
return true;
}
export default async (name, flags) => {
const response = await prompts(PromptNew(name, await CommandExists('heroku')));
if (!response.botName || !response.botStructure || !response.commands) {
process.exit(1);
}
let botTemplate = '';
if (response.botStructure === 'basic') {
if (response.commands === 'slashCmd') {
botTemplate = 'with-basic-slash-commands';
}
else if (response.commands === 'prefixCmd') {
botTemplate = 'with-basic-prefix-commands';
}
else if (response.commands === 'bothCmd') {
botTemplate = 'with-basic-both-commands';
}
}
else if (response.botStructure === 'advanced') {
if (response.commands === 'slashCmd') {
botTemplate = 'with-advanced-slash-commands';
}
else if (response.commands === 'prefixCmd') {
botTemplate = 'with-advanced-prefix-commands';
}
else if (response.commands === 'bothCmd') {
botTemplate = 'with-advanced-both-commands';
}
}
if (response.heroku)
response.git = true;
const botName = response.botName === '.' ? '' : response.botName;
const stpJob = async () => {
await cp(`./${response.botName}/ghr/templates/${response.botStructure}/${botTemplate}/.`, `./${response.botName}/`, { recursive: true });
if (response.git && !response.heroku) {
await cp(`./${response.botName}/ghr/.gitignore`, `./${response.botName}/.gitignore`);
}
if (response.heroku) {
for (const p of ['.gitignore', 'Procfile', 'HEROKU.md']) {
await cp(`./${response.botName}/ghr/resources/with-heroku/${p}`, `./${response.botName}/${p}`);
}
}
await rm(`./${response.botName}/ghr`, { recursive: true, force: true });
await editPackageJson(response.botName, botName, response.botDescription, response.botVersion, response.heroku);
};
const jobs = [];
if (response.heroku) {
jobs.push([
() => herokuLogin(),
'Logging in to Heroku, press any key to open the browser, or q to quit',
false,
]);
}
jobs.push([
() => cloneRepo(response.botName, flags.verbose),
'Cloning repository',
]);
jobs.push([stpJob, 'Setting up project']);
if (response.git) {
jobs.push([() => initializeGitRepo(response.botName), 'Initializing git']);
}
if (response.heroku) {
jobs.push([
() => initializeHeroku(response.botName, flags.verbose),
'Initializing Heroku',
]);
jobs.push([
() => pushToHeroku(response.botName, flags.verbose),
'Pushing to Heroku',
]);
jobs.push([
() => configureHeroku(response.botName, flags.verbose),
'Configuring Heroku',
]);
}
jobs.push([
() => installDeps(response.botName, flags.verbose),
'Installing dependencies',
]);
for (const [job, name, showError] of jobs) {
await runJob(job, name, showError).catch(() => process.exit(1));
}
if (response.heroku) {
const { stdout } = await execa('heroku', ['info'], {
cwd: `./${response.botName}/`,
});
console.log(`Information about your bot in Heroku:\n${stdout}`);
}
console.log(blueBright('Done!'));
};