Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Renew Development Mode #102

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"password": ""
},
"myriad": "",
"debug": false
"dev": false
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"lint:fix": "eslint --ignore-path .gitignore \"**/*.+(ts|js)\" --fix",
"start": "node dist",
"start:dev": "node dist/yumeko.js dev",
"create:cmd": "node dist/util/CreateCommand.js",
"config:sync": "node dist/util/ConfigTypeSync.js",
"test": "yarn lint && yarn build"
},
"repository": "https://github.com/youKnowOwO/yumeko-ts.git",
Expand Down
3 changes: 2 additions & 1 deletion src/classes/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import Logger from "@yumeko/libs/Logger";
import eventLoader from "@yumeko/libs/EventLoader";
import nowPlayMoe from "@yumeko/libs/NowplayMoeWS";
import { langCollector } from "@yumeko/libs/Localization";
import { Config } from "@yumeko/interfaces";
import { Client } from "discord.js";
import { Node as Lavalink } from "lavalink";

import "../extension";
import { hide } from "@yumeko/decorators";

// i don't want compiler compile these one
const config = require("../../config.json");
const config: Config = require("../../config.json");

export default class YumekoClient extends Client {
@hide
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 4 additions & 2 deletions src/events/Debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { Event } from "@yumeko/interfaces";

export default class DebugEvent implements Event {
public readonly listener = "debug";
public readonly devOnly = true;
public constructor(public readonly client: YumekoClient) {}
public exec(msg: string): void {
if (this.client.config.debug) {
if (this.client.config.dev) {
this.client.log.info(msg);
}
}
}
}

4 changes: 0 additions & 4 deletions src/events/Ready.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type YumekoClient from "@yumeko/classes/Client";
import { Event } from "@yumeko/interfaces";
import { stripIndents } from "common-tags";
import { constantly } from "@yumeko/decorators";

const presences = require("../../assets/json/presence.json");
Expand All @@ -12,9 +11,6 @@ export default class ReadyEvent implements Event {
@constantly
public exec (): void {
assignDB(this.client);
this.client.log.info(stripIndents`
${this.client.log.color(this.client.user!.tag, "FFFFFF")} is Ready to play. ${this.client.shard ? this.client.shard.ids.map(x => this.client.log.color(`#${x + 1}`, "00FFFF")).join(", ") : ""}
`);
this.client.lavalink.userID = this.client.user!.id;
presence.call(null, this.client);
setInterval(presence.bind(null, this.client), 60000);
Expand Down
16 changes: 16 additions & 0 deletions src/interfaces/Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.

export interface Config {
owners: string[];
prefix: string;
color: string;
lavalink: {
hosts: {
rest: string;
ws: string;
};
password: string;
};
myriad: string;
dev: boolean;
}
17 changes: 12 additions & 5 deletions src/interfaces/Event.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import type Command from "@yumeko/classes/Command";
import type { ClientEvents } from "discord.js";

export interface YumekoClientEvents extends ClientEvents {
raw: [any];
}
type EventKeys = keyof ClientEvents;

export interface Event {
readonly listener: keyof YumekoClientEvents;
exec(...args: YumekoClientEvents[Event["listener"]]): any;
readonly listener: EventKeys;
readonly devOnly?: boolean;
exec(...args: ClientEvents[EventKeys]): any;
}

declare module "discord.js" {
interface ClientEvents {
raw: [any];
commandStored: [Command?];
}
}
3 changes: 2 additions & 1 deletion src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "@yumeko/interfaces/Command";
export * from "@yumeko/interfaces/Other";
export * from "@yumeko/interfaces/Event";
export * from "@yumeko/interfaces/HTTPResponse";
export * from "@yumeko/interfaces/HTTPResponse";
export * from "@yumeko/interfaces/Config";
8 changes: 3 additions & 5 deletions src/libs/CommandCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,23 @@ export default class CommandCollector {

public constructor(public client: YumekoClient) {}

public loadAll(log = true): void {
public loadAll(): void {
const path = join(__dirname, "../commands");
const files = readdirRecursive(path);
const { print, color, equal, date } = this.client.log;
if (log) print(equal(color("▶️ Collecting Command", "00C2FF")));
for (const file of files) {
const load = require(file).default;
if (!load || !(load.prototype instanceof Command)) continue;
const command = this.getCommand(file);
this.registry(command);
if (log) print(`+ ${color(command.identifier, "FE9DFF")} (${color(file, "A20092")})`);
}
if (log) print(equal(color(date(), "505050")));
this.client.emit("commandStored");
}

public registry(command: string | Command): void {
if (typeof command === "string") command = this.getCommand(command);
this.addToCategory(command);
this.commands.set(command.identifier, command);
this.client.emit("commandStored", command);
}

public getCommand(path: string): Command {
Expand Down
1 change: 1 addition & 0 deletions src/libs/EventLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default function EventLoader (client: YumekoClient): void {
const files = readdirRecursive(path);
for (const file of files) {
const event: Event = new (require(file).default)(client);
if (event.devOnly && !client.config.dev) continue;
client.addListener(event.listener, event.exec.bind(event) as any);
}
}
2 changes: 1 addition & 1 deletion src/util/CodeLinter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type YumekoClient from "@yumeko/classes/Client";
import LintCommand from "@yumeko/commands/Util/Lint";
import LintCommand from "@yumeko/commands/Utility/Lint";
import type { Message } from "discord.js";
import { TypeCodeReturn } from "@yumeko/interfaces";
import { Linter, Rule } from "eslint";
Expand Down
32 changes: 32 additions & 0 deletions src/util/ConfigTypeSync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* eslint-disable @typescript-eslint/ban-types */
import chalk from "chalk";
import { promises } from "fs";

const config: object = require("../../config.json");

function parse(data: any, indent = 1): string {
const type = typeof data;
if (data instanceof Array) return parseArray(data, indent);
if (type === "object") return parseObject(data, indent);
return type;
}

function parseObject(data: Record<string, never> | null, indent: number): string {
if (data === null) return "null";
const space = " ".repeat(indent * 4);
const types = [];
for (const x of Object.keys(data)) types.push(`${space}${x}: ${parse(data[x], indent + 1)};`);
return types.length ? `{\n${types.join("\n")}\n${space.slice(4)}}` : "{}";
}

function parseArray<T>(data: T[], indent: number): string {
const parsed = parse(data[0], indent);
return `${parsed}[]`;
}

void async function exec(): Promise<void> {
const toWrite = `// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.\n\nexport interface Config ${parse(config)}`;
await promises.writeFile("./src/interfaces/Config.ts", toWrite);
process.stdout.write(chalk`{bgGreen {black \ DONE }} Config sucessfully sync`);
process.stdout.write("\n");
}();
82 changes: 82 additions & 0 deletions src/util/CreateCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { createInterface, ReadLineOptions } from "readline";
import { existsSync, promises } from "fs";
import { join } from "path";
import { firstUpperCase } from "./Util";
import chalk from "chalk";

const categories = Object.keys(require("../../assets/json/help.json"));

const rl = createInterface({
input: process.stdin,
output: process.stdout
} as ReadLineOptions);

function awaitQuestion(query: string): Promise<string> {
return new Promise(resolve => rl.question(query, resolve));
}

function end(prop?: string, error = false): void {
if (error) rl.write(chalk`{bgRed {black \ ERROR }} {red ${prop}}`);
else if (prop) rl.write(chalk`{bgRed {black \ ERROR }} Command {blue ${prop}} is required`);
rl.write("\n");
process.exit(0);
}

export async function exec(): Promise<void> {
rl.write(chalk`\n{bgBlue {black \ INFO }} Please describe your {blue command} !\n\n`);
let aliases: string[] = [];
const id = await awaitQuestion(chalk` {blue •} What {blue identifier} for this command ? `);
if (!id.length) return end("identifier");
aliases.push(id);
await awaitQuestion(chalk` {blue •} Please insert {blue aliases} you want splited by comma (,) ! `).then(x => aliases.push(...x.split(",")));
aliases = aliases.filter(x => x.length);
const descriptionContent = await awaitQuestion(chalk` {blue •} What {blue description} of the command ? {gray (if came from localization please insert the id between <>)} `)
.then(x => {
if (!x.length) return "";
if (x.startsWith("<") && x.endsWith(">"))
return `(msg): string => msg.ctx.lang("${x.replace(/<|>/g, "")}")`;
return `"${x}"`;
});
if (!descriptionContent.length) return end("description");
const usageDescription = await awaitQuestion(chalk` {blue •} How you {blue usage} this command ? `);
if (!usageDescription.length) return end("usage");
const exampleDescription = await awaitQuestion(chalk` {blue •} Please write at least 1 {blue example} ! `);
if (!exampleDescription.length) return end("example");
const category = await awaitQuestion(chalk` {blue •} Which {blue category} that ship this command ? {gray (${categories.join(", ")})} `);
if (!category.length) return end("categroy");
const filename = await awaitQuestion(chalk` {blue •} What the {blue filename} do you want ? {gray (${firstUpperCase(id)})} `).then(x => x.length ? x : firstUpperCase(id));
const dirname = await awaitQuestion(chalk` {blue •} What the {blue dirname} do you want ? {gray (${firstUpperCase(category)})} `).then(x => x.length ? x : firstUpperCase(category));
const toWrite = `import Command from "@yumeko/classes/Command";
import type { Message } from "discord.js";
import { DeclareCommand, constantly } from "@yumeko/decorators";

@DeclareCommand("${id}", {
aliases: [${aliases.map(x => `"${x}"`).join(", ")}],
description: {
content: ${descriptionContent},
usage: "${usageDescription}",
examples: ["${exampleDescription}"]
},
category: "${category}"
})
export default class extends Command {
@constantly
public async exec(msg: Message): Promise<Message> {
return msg;
}
}`;
const path = join("./src/commands", dirname);
rl.write(chalk`\n{bgBlue {black \ INFO }} Checking directory...\n`);
if (!existsSync(path)) {
rl.write(chalk`{bgBlue {black \ INFO }} Directory isn't exist try to create...\n`);
await promises.mkdir(path, { recursive: true });
rl.write(chalk`{bgGreen {black \ DONE }} Directory Created !\n`);
}
rl.write(chalk`{bgBlue {black \ INFO }} Writting command...\n`);
const file = join(path, `${filename}.ts`);
await promises.writeFile(file, toWrite, { encoding: "utf-8" });
rl.write(chalk`{bgGreen {black \ DONE }} Succes write command to {green ${file}}\n`);
return end();
}

exec().catch((e: Error) => end(String(e), true));
2 changes: 1 addition & 1 deletion src/util/Myriad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import request from "node-superfetch";
import { Message, MessageReaction, User } from "discord.js";
import { TypeCodeReturn } from "@yumeko/interfaces";
import { join } from "path";
import type CodeCommand from "@yumeko/commands/Util/Code";
import type CodeCommand from "@yumeko/commands/Utility/Code";

export async function exec(client: YumekoClient, code: string, language: string): Promise<[boolean, string]> {
const endpoint = join(client.config.myriad, "eval");
Expand Down
2 changes: 1 addition & 1 deletion src/yumeko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import YumekoClient from "./classes/Client";

if (process.argv[2] === "dev") {
require("./util/EnvLoader");
require("../config.json").debug = true;
require("../config.json").dev = true;
}

const client = new YumekoClient();
Expand Down