Skip to content

Commit af276be

Browse files
TheArmaganErdemGKSL
andcommitted
🥳 v2.0.0 | Plugin Sistemi!
- Plugin sistemi! - Otomatik plugin tip oluşturucu! Co-Authored-By: Erdem <[email protected]>
1 parent 75b30ab commit af276be

File tree

14 files changed

+343
-92
lines changed

14 files changed

+343
-92
lines changed

config.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ module.exports = new (require("./types/Config"))({
107107
async onReady(client) {
108108
console.log("[CONFIG] Discord hesabına giriş yaptıktan sonra çalıştı.");
109109
client.user.setActivity(`/help - Basit Altyapı by TheArmagan`, { type: "WATCHING" });
110-
111110
},
112111
// interaksiyon üzerinde hiçbir kontrol yapılmadan önce çalışır.
113112
// Sadece cevap true ise işleme devam eder.
@@ -141,4 +140,4 @@ module.exports = new (require("./types/Config"))({
141140
async onAfterEvent(eventName, [arg1, arg2], other) {
142141

143142
},
144-
})
143+
})

events/-ornekPluginOlay.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = new Underline.Event({
2+
eventName: "mongooseDatabase:onConnect",
3+
async onEvent(connected) {
4+
console.log(`[DATABASE] ${ connected ? "Bağlandı": "Bağlanamadı" }!`);
5+
}
6+
})

generated/pluginTypes.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export class Types {
2+
["mongooseDatabase"]: { connection: import("mongoose").Connection, connect: import("mongoose").connect, addModel: (name: string, schema: import("mongoose").Schema, collection?: string, options?: import("mongoose").CompileModelOptions)=>any, getModel: (name: string) => import("mongoose").Model, Schema: import("mongoose").Schema };
3+
};
4+
export type TEventNames = "mongooseDatabase:onConnect";
5+
export type TEvents = mongooseDatabase_onConnect;
6+
export interface mongooseDatabase_onConnect { eventName: "mongooseDatabase:onConnect", onEvent: (arg0: boolean) => void }

globals.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ interface Underline {
1616
Button: typeof import("./types/Button"),
1717
SelectMenu: typeof import("./types/SelectMenu"),
1818
Locale: typeof import("./types/Locale"),
19+
plugins: import("./generated/pluginTypes").Types,
20+
Plugin: typeof import("./types/Plugin"),
1921

2022
reload(): Promise<any>;
2123

index.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ let onFunctions = {
2323
onInteraction: [config.onInteraction],
2424
onAfterInteraction: [config.onAfterInteraction],
2525
onEvent: [config.onEvent],
26-
onAfterEvent: [config.onAfterEvent]
26+
onAfterEvent: [config.onAfterEvent],
27+
onReady: [config.onReady],
2728
};
2829
globalThis.Underline = {
2930
...config.globalObjects,
@@ -44,6 +45,7 @@ globalThis.Underline = {
4445
SelectMenu: require("./types/SelectMenu"),
4546
Button: require("./types/Button"),
4647
Locale: require("./types/Locale"),
48+
Plugin: require("./types/Plugin"),
4749
}
4850

4951
const extractZip = require("extract-zip");
@@ -65,7 +67,7 @@ async function getPluginFilePaths() {
6567
let folderPath = path.resolve(pluginsPath, folderOrZip.name.replace(".up.zip", ".up"));
6668
let zipPath = path.resolve(pluginsPath, folderOrZip.name);
6769

68-
await fs.promises.rmdir(folderPath, {recursive: true}).catch(() => {});
70+
await fs.promises.rm(folderPath, {recursive: true}).catch(() => {});
6971
await makeSureFolderExists(folderPath);
7072
await extractZip(zipPath, { dir: folderPath });
7173
fs.promises.unlink(zipPath).catch(() => null);
@@ -119,7 +121,8 @@ async function load() {
119121
onInteraction: [config.onInteraction],
120122
onAfterInteraction: [config.onAfterInteraction],
121123
onEvent: [config.onEvent],
122-
onAfterEvent: [config.onAfterEvent]
124+
onAfterEvent: [config.onAfterEvent],
125+
onReady: [config.onReady],
123126
};
124127

125128
let loadStart = Date.now();
@@ -144,6 +147,7 @@ async function load() {
144147
})
145148

146149
pluginFiles = await getPluginFilePaths();
150+
let pluginCache = [];
147151
await chillout.forEach(pluginFiles, (pluginFile) => {
148152
let start = Date.now();
149153
let rltPath = path.relative(__dirname, pluginFile);
@@ -158,10 +162,25 @@ async function load() {
158162
if (Underline.plugins[plugin.namespace])
159163
return console.warn(`[UYARI] ${plugin.name} plugini zaten yüklenmiş. Atlanıyor..`);
160164

165+
pluginCache.push(plugin);
166+
console.info(`[BİLGİ] "${plugin.name}" plugini yüklenme sırasına alındı. (${Date.now() - start}ms sürdü.)`);
167+
168+
})
169+
170+
pluginCache = pluginCache.sort((plugin, dependency) => plugin?.requires?.plugins?.includes(dependency) ? 1 : 0);
171+
172+
let pluginSort = utils.sortDependant(pluginCache.map(i=>i.namespace), Object.fromEntries(pluginCache.map(i => [i.namespace, i?.requires?.plugins || []])))
173+
174+
await chillout.forEach(pluginSort, async (pluginNamespace) => {
175+
let start = Date.now();
176+
177+
const plugin = pluginCache.find(x => x.namespace === pluginNamespace);
178+
161179
const pluginApi = {};
180+
let isReady = false;
162181

163-
pluginApi.ready = async () => {
164-
if (isReady) throw new Error("Plugin already ready!")
182+
pluginApi.setPluginReady = async () => {
183+
if (isReady) throw new Error("Plugin is already ready!")
165184
isReady = true;
166185
}
167186

@@ -181,18 +200,20 @@ async function load() {
181200

182201
pluginApi.onEvent = onFunctions.onEvent.push;
183202
pluginApi.onAfterEvent = onFunctions.onAfterEvent.push;
203+
pluginApi.onBotReady = onFunctions.onReady.push;
184204

185205
pluginApi.client = client;
186206

187207
plugin.onLoad(pluginApi);
208+
console.info(`[BİLGİ] "${plugin.name}" pluginin yüklenmesi bekleniyor!`);
188209
await chillout.waitUntil(() => {
189210
if (isReady) return chillout.StopIteration;
190211
})
191-
192-
console.info(`[BİLGİ] "${plugin.name}" plugini yüklendi. (${Date.now() - start}ms sürdü.)`);
212+
console.info(`[BİLGİ] "${plugin.name}" plugini yüklendi! (${Date.now() - start}ms sürdü.)`);
193213
})
194214

195-
215+
216+
for (let ind in onFunctions) onFunctions[ind] = onFunctions[ind].filter(x => typeof x === "function");
196217

197218
interactionFiles = await getInteractionFilePaths();
198219
await chillout.forEach(interactionFiles, (interactionFile) => {
@@ -612,7 +633,7 @@ client.on("interactionCreate", async (interaction) => {
612633
let now = Date.now();
613634

614635
for (let k in converter) {
615-
let keyCooldown = uInter.coolDowns.get(key);
636+
let keyCooldown = uInter.coolDowns.get(k);
616637
if (now < keyCooldown) {
617638
config.userErrors.coolDown(interaction, uInter, keyCooldown - now, k, other);
618639
return;
@@ -677,6 +698,16 @@ client.on("interactionCreate", async (interaction) => {
677698
await client.login(config.clientToken);
678699

679700
config.onReady(client);
701+
quickForEach(onFunctions.onReady, async (func) => {
702+
try {
703+
func?.catch?.(() => {
704+
705+
});
706+
} catch (err) {
707+
708+
}
709+
710+
})
680711
})();
681712

682713
Underline.reload = reload;

other/pluginTypesGenerator.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
require("./patchConsoleLog");
2+
3+
const chillout = require("chillout");
4+
const fs = require("fs");
5+
const extractZip = require("extract-zip");
6+
const { makeSureFolderExists } = require("stuffs");
7+
const path = require("path");
8+
9+
globalThis.Underline = {
10+
Plugin: require("../types/Plugin")
11+
};
12+
13+
(async () => {
14+
let pluginFiles = await getPluginFilePaths();
15+
let pluginTypes = [];
16+
let TEventNames = [];
17+
let TEvents = [];
18+
let TInterfaces = [];
19+
let loadedNamespaces = [];
20+
await chillout.forEach(pluginFiles, async (pluginFile) => {
21+
let rltPath = path.relative(__dirname, pluginFile);
22+
console.info(`[BİLGİ] "${rltPath}" konumundaki plugin yükleniyor..`)
23+
/** @type {import("../types/Plugin")} */
24+
let plugin = require(pluginFile);
25+
console.info("Plugin:", plugin._type, plugin.namespace,plugin);
26+
// console.log(plugin)
27+
28+
if (plugin._type != "plugin")
29+
return console.warn(`[UYARI] "${rltPath}" plugin dosyası boş. Atlanıyor..`);
30+
31+
if (loadedNamespaces.find(x => x == plugin.namespace))
32+
return console.warn(`[UYARI] ${plugin.name} plugini zaten yüklenmiş. Atlanıyor..`);
33+
34+
loadedNamespaces.push(plugin.namespace);
35+
36+
let parsedPluginPath = path.parse(pluginFile);
37+
// console.log(parsedPluginPath)
38+
39+
let dtsPath = "";
40+
41+
switch (parsedPluginPath.dir.split(path.sep).pop()) {
42+
case "plugins": {
43+
dtsPath = path.resolve(parsedPluginPath.dir, parsedPluginPath.base.replace(".up.js", ".up.d.ts"));
44+
break;
45+
}
46+
default: {
47+
dtsPath = path.resolve(parsedPluginPath.dir, "index.d.ts");
48+
break;
49+
}
50+
}
51+
console.log(dtsPath);
52+
let isDTS = fs.existsSync(dtsPath);
53+
54+
if (isDTS) {
55+
pluginTypes.push(`["${plugin.namespace}"]: import("${path.relative(process.cwd(), dtsPath).replace(".d.ts", "").replaceAll(path.sep, "/")}").Plugin`);
56+
} else {
57+
// let implements = Object.entries(plugin.implements.properties);
58+
if (!plugin?.implements?.properties) return;
59+
let result = []
60+
for (let property in plugin.implements.properties) {
61+
let returns = plugin.implements.properties[property];
62+
let returnsString = generateReturn(returns);
63+
result.push(`${property}: ${returnsString}`)
64+
}
65+
result = `{ ${result.join(", ")} }`;
66+
pluginTypes.push(`["${plugin.namespace}"]: ${result}`);
67+
}
68+
69+
if (plugin?.implements?.events) {
70+
for (let eventName in plugin.implements.events) {
71+
let args = plugin.implements.events[eventName].split(/ *, */).map(x => x.includes(":") ? (x.split(":")[0] + ":" + generateReturn(x.split(":")[1])) : generateReturn(x));
72+
TEventNames.push(`"${plugin.namespace}:${eventName}"`);
73+
TInterfaces.push(`export interface ${plugin.namespace}_${eventName.replace(/ |-/, "")} { eventName: "${plugin.namespace}:${eventName}", onEvent: (${!args?.[0] ? "" : args.map((v, i) => v.includes(":") ? v : `arg${i}: ${v}`).join(", ")}) => void }`);
74+
TEvents.push(`${plugin.namespace}_${eventName.replace(/ |-/, "")}`);
75+
76+
}
77+
}
78+
79+
console.info(`[BİLGİ] "${plugin.name}" plugini tipi çıkartıldı.`);
80+
});
81+
82+
await makeSureFolderExists(path.resolve(__dirname, "../generated"));
83+
let result = `export class Types {\n${pluginTypes.map(i => ` ${i};`).join("\n")}\n};\n${TEventNames.length ? `export type TEventNames = ${TEventNames.join(" | ")};` : ""}\n${TEvents.length ? `export type TEvents = ${TEvents.join(" | ")};` : ""}\n${TInterfaces.join("\n")}\n`.trim();
84+
console.info(result);
85+
await fs.promises.writeFile(path.resolve(__dirname, "../generated/pluginTypes.d.ts"), result);
86+
87+
88+
})();
89+
90+
function generateReturn(returns) {
91+
let returnsString = "";
92+
switch (returns) {
93+
case "function": {
94+
returnsString = "(...args: any[]) => any";
95+
break;
96+
}
97+
case "object": {
98+
returnsString = "{ [key:string|number]: any }";
99+
break;
100+
}
101+
case "array": {
102+
returnsString = "any[]";
103+
break;
104+
}
105+
default: {
106+
returnsString = returns;
107+
}
108+
}
109+
return returnsString;
110+
}
111+
112+
async function getPluginFilePaths() {
113+
let pluginsPath = path.resolve(__dirname, "../plugins");
114+
await makeSureFolderExists(pluginsPath);
115+
let folderOrZips = await fs.promises.readdir(pluginsPath, { withFileTypes: true });
116+
let result = [];
117+
for (let i = 0; i < folderOrZips.length; i++) {
118+
const folderOrZip = folderOrZips[i];
119+
if (folderOrZip.isDirectory() && folderOrZip.name.endsWith(".up")) {
120+
if (folderOrZip.name.startsWith("-")) continue;
121+
result.push(path.resolve(pluginsPath, folderOrZip.name, "index.js"));
122+
} else if (folderOrZip.name.endsWith(".up.js")) {
123+
if (folderOrZip.name.startsWith("-")) continue;
124+
result.push(path.resolve(pluginsPath, folderOrZip.name));
125+
} else if (folderOrZip.name.endsWith(".up.zip")) {
126+
let folderPath = path.resolve(pluginsPath, folderOrZip.name.replace(".up.zip", ".up"));
127+
let zipPath = path.resolve(pluginsPath, folderOrZip.name);
128+
129+
await fs.promises.rm(folderPath, {recursive: true}).catch(() => {});
130+
await makeSureFolderExists(folderPath);
131+
await extractZip(zipPath, { dir: folderPath });
132+
fs.promises.unlink(zipPath).catch(() => null);
133+
if (folderOrZip.name.startsWith("-")) continue;
134+
result.push(path.resolve(folderPath, "index.js"));
135+
}
136+
}
137+
console.log(result)
138+
return result;
139+
}

other/utils.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,21 @@ module.exports = {
99
},
1010
});
1111
return true;
12+
},
13+
/**
14+
* @param {string[]} names
15+
* @param {{[key: string]: string[]}} obj
16+
* @param {string[]?} start
17+
* @param {number?} depth
18+
* @returns {string[]}
19+
*/
20+
sortDependant(names, obj, start=[], depth = 0) {
21+
const processed = names.reduce((a, b, i) => {
22+
if (obj[b].every(Array.prototype.includes, a)) a.push(b)
23+
return a
24+
}, start)
25+
const nextNames = names.filter(n => !processed.includes(n)),
26+
goAgain = nextNames.length && depth <= names.length
27+
return goAgain ? this.sortDependant(nextNames, obj, processed, depth + 1) : processed
1228
}
1329
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
"scripts": {
3434
"interaksiyon": "node ./other/generator.js interaction",
3535
"olay": "node ./other/generator.js event",
36-
"baslat": "node index.js"
36+
"baslat": "node index.js",
37+
"plugin-tipleri": "node ./other/pluginTypesGenerator.js"
3738
},
3839
"bugs": {
3940
"url": "https://github.com/TheArmagan/basit-altyapi/issues"

plugins/database.up.zip

623 Bytes
Binary file not shown.

readme.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# Basit Altyapı (Versiyon 1.9.9) (v13.x)
1+
# Basit Altyapı (Versiyon 2.0.0) (v13.x)
22

33
Kullanımı basit ancak bir yandanda içinde birçek özellik barındıran discord bot altyapısı. Sık sık güncelleme alıyor. (Slash Commands)
44

55
## Özellikler
66

7+
-**Plugin sistemi!** *- Altyapıya istediğiniz özellikleri parça parça ekleyin!*
8+
- ✅ Pluginler TAM tip desteği! `yarn plugin-tipleri`
79
-**Çoklu dil desteği.**
810
-**Sunucuya özel komut paylaşma tipleri.**
911
-**SelectMenu ve Button desteği.**
@@ -62,10 +64,6 @@ Kullanımı basit ancak bir yandanda içinde birçek özellik barındıran disco
6264
- Olaylar için `events` klasörünün içindeki [`-ornekOlay.js`](./events/-ornekOlay.js) dosyasını dikkatlice okuyabilirsiniz.
6365
- Yeni bir olay dosyası oluşturmak isterseniz `yarn olay` interaksiyonunu kullanabilsiniz. Bu sayede sizi ilk olay altyapısını yazma derdinden kurtaracak ve olay hakkında her türlü soruyu soracaktır.
6466

65-
## Güncelleme
66-
67-
- Güncelleme yaparken yapmanız gereken sadece `types klasörünü`, `other klasörünü`, `index.js dosyası`, `package.json dosyasını` eskisi ile değiştirmek.
68-
6967
## Yardım
7068

7169
- Eğer buradaki herşeyi okudunuz ama bir şey anlamadıysanız, salaklar için tasarlanmış olan [Basit Altyapı Rehberi](https://www.youtube.com/watch?v=bQ9ZLpfyCyY&list=PLONuYdOGaaL3m705lBXLAp2okk0TE5EiO) serimizi izleyebilirsiniz.

0 commit comments

Comments
 (0)