djs-builder 0.7.0 → 0.7.1-8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/function/dash.js CHANGED
@@ -1,68 +1,59 @@
1
- /**
2
- * DJS-Builder Dashboard
3
- * لوحة تحكم عصرية وخفيفة متوافقة مع حزمة djs-builder
4
- * @version 2.0.0
5
- * @author DJS-Builder Team
6
- */
1
+
7
2
 
8
3
  const express = require("express");
9
4
  const session = require("express-session");
10
5
  const passport = require("passport");
11
6
  const DiscordStrategy = require("passport-discord").Strategy;
12
7
  const path = require("path");
8
+ const Table = require("cli-table3");
9
+ const chalk = require("chalk");
10
+ const mongoose = require("mongoose");
11
+
12
+ const {
13
+ Level,
14
+ levelGuild,
15
+ resetUser,
16
+ xpNeeded,
17
+ calculateLevel,
18
+ getUserRank,
19
+ getGuildConfig,
20
+ updateGuildConfig,
21
+ deleteGuildConfig,
22
+ clearConfigCache,
23
+ } = require("./level");
24
+ const {
25
+ Blacklist,
26
+ addToBlacklist,
27
+ removeFromBlacklist,
28
+ } = require("./blacklist");
29
+ const {
30
+ giveaway,
31
+ Gcheck,
32
+ Greroll,
33
+ Gpause,
34
+ Gresume,
35
+ Gdelete,
36
+ } = require("./giveaway");
37
+ const { Log, getLogConfigData } = require("./log");
13
38
 
14
- // استيراد موديلات djs-builder
15
- let Level, giveaway, Blacklist, leaderboard, addXP, UserLevel;
16
- let Gstart, Gcheck, Greroll, Glist, Gpause, Gresume, Gdelete, Gdata;
17
- let isBlacklisted, addToBlacklist, removeFromBlacklist, getBlacklist;
18
-
19
- try {
20
- const djsBuilder = require("djs-builder");
21
- Level = djsBuilder.Level;
22
- giveaway = djsBuilder.giveaway;
23
- Blacklist = djsBuilder.Blacklist;
24
- leaderboard = djsBuilder.leaderboard;
25
- addXP = djsBuilder.addXP;
26
- UserLevel = djsBuilder.UserLevel;
27
- Gstart = djsBuilder.Gstart;
28
- Gcheck = djsBuilder.Gcheck;
29
- Greroll = djsBuilder.Greroll;
30
- Glist = djsBuilder.Glist;
31
- Gpause = djsBuilder.Gpause;
32
- Gresume = djsBuilder.Gresume;
33
- Gdelete = djsBuilder.Gdelete;
34
- Gdata = djsBuilder.Gdata;
35
- isBlacklisted = djsBuilder.isBlacklisted;
36
- addToBlacklist = djsBuilder.addToBlacklist;
37
- removeFromBlacklist = djsBuilder.removeFromBlacklist;
38
- getBlacklist = djsBuilder.getBlacklist;
39
- } catch (e) {
40
- console.log("⚠️ djs-builder models not fully loaded:", e.message);
41
- }
42
39
 
43
- /**
44
- * دالة لإنشاء وتشغيل لوحة التحكم
45
- * @param {Client} client - كائن البوت
46
- * @param {Object} options - خيارات اللوحة
47
- */
48
40
  function dashboard(client, options) {
49
41
  const app = express();
50
42
  const {
51
- clientID,
52
43
  clientSecret,
53
- callbackURL,
44
+ callbackURL = "http://localhost:3000/auth/discord/callback",
54
45
  sessionSecret,
55
46
  port = 3000,
56
47
  } = options;
57
48
 
58
- // إعداد المسارات
49
+ clientID = client.user.id;
50
+
59
51
  app.set("view engine", "ejs");
60
- app.set("views", path.join(__dirname, "views"));
61
- app.use(express.static(path.join(__dirname, "public")));
52
+ app.set("views", path.join(__dirname, "..", "views"));
62
53
  app.use(express.json());
63
54
  app.use(express.urlencoded({ extended: true }));
64
55
 
65
- // إعداد الجلسات
56
+
66
57
  app.use(
67
58
  session({
68
59
  secret: sessionSecret,
@@ -74,7 +65,7 @@ app.set("views", path.join(__dirname, "views"));
74
65
  })
75
66
  );
76
67
 
77
- // إعداد Passport
68
+
78
69
  app.use(passport.initialize());
79
70
  app.use(passport.session());
80
71
 
@@ -95,21 +86,22 @@ app.set("views", path.join(__dirname, "views"));
95
86
  )
96
87
  );
97
88
 
98
- // Middleware للتحقق من تسجيل الدخول
89
+
99
90
  function isAuthenticated(req, res, next) {
100
91
  if (req.isAuthenticated()) return next();
101
92
  res.redirect("/login");
102
93
  }
103
94
 
104
- // تمرير بيانات المستخدم والبوت لكل الصفحات
95
+
105
96
  app.use((req, res, next) => {
106
97
  res.locals.user = req.user || null;
107
98
  res.locals.client = client;
108
99
  res.locals.path = req.path;
100
+ res.locals.dashboardEnabled = options.Dashboard !== false;
109
101
  next();
110
102
  });
111
103
 
112
- // ==================== دالة مساعدة للإحصائيات ====================
104
+
113
105
  function getBotStats() {
114
106
  const uniquePrefixCommands = new Set();
115
107
  if (client.prefixCommands) {
@@ -118,12 +110,13 @@ app.set("views", path.join(__dirname, "views"));
118
110
  });
119
111
  }
120
112
 
121
- // محاولة الحصول على البادئة من client
122
- const prefix = client.prefix || options.prefix || '-';
113
+
114
+ const prefix = client.prefix || options.prefix || "-";
123
115
 
124
116
  return {
125
117
  guilds: client.guilds?.cache?.size || 0,
126
- users: client.guilds?.cache?.reduce((acc, g) => acc + g.memberCount, 0) || 0,
118
+ users:
119
+ client.guilds?.cache?.reduce((acc, g) => acc + g.memberCount, 0) || 0,
127
120
  channels: client.channels?.cache?.size || 0,
128
121
  slashCommands: client.slashCommands?.size || 0,
129
122
  prefixCommands: uniquePrefixCommands.size,
@@ -139,22 +132,20 @@ app.set("views", path.join(__dirname, "views"));
139
132
  };
140
133
  }
141
134
 
142
- // ==================== المسارات الرئيسية ====================
143
135
 
144
- // الصفحة الرئيسية
145
136
  app.get("/", (req, res) => {
146
137
  res.render("index", {
147
138
  botStats: getBotStats(),
148
139
  });
149
140
  });
150
141
 
151
- // صفحة تسجيل الدخول
142
+
152
143
  app.get("/login", (req, res) => {
153
144
  if (req.isAuthenticated()) return res.redirect("/dashboard");
154
145
  res.render("login", { botStats: getBotStats() });
155
146
  });
156
147
 
157
- // OAuth2 مع Discord
148
+
158
149
  app.get("/auth/discord", passport.authenticate("discord"));
159
150
 
160
151
  app.get(
@@ -167,28 +158,25 @@ app.set("views", path.join(__dirname, "views"));
167
158
  }
168
159
  );
169
160
 
170
- // تسجيل الخروج
161
+
171
162
  app.get("/logout", (req, res) => {
172
163
  req.logout(() => {
173
164
  res.redirect("/");
174
165
  });
175
166
  });
176
167
 
177
- // ==================== لوحة التحكم ====================
178
168
 
179
- // لوحة التحكم الرئيسية
180
169
  app.get("/dashboard", isAuthenticated, (req, res) => {
181
170
  const userGuilds = req.user.guilds.filter(
182
- (g) =>
183
- (g.permissions & 0x20) === 0x20 && client.guilds.cache.has(g.id)
171
+ (g) => (g.permissions & 0x20) === 0x20 && client.guilds.cache.has(g.id)
184
172
  );
185
- res.render("dashboard", {
173
+ res.render("dashboard", {
186
174
  guilds: userGuilds,
187
- botStats: getBotStats()
175
+ botStats: getBotStats(),
188
176
  });
189
177
  });
190
178
 
191
- // إدارة سيرفر معين - نظرة عامة
179
+
192
180
  app.get("/dashboard/:guildId", isAuthenticated, async (req, res) => {
193
181
  const guild = client.guilds.cache.get(req.params.guildId);
194
182
  if (!guild) return res.redirect("/dashboard");
@@ -198,15 +186,20 @@ app.set("views", path.join(__dirname, "views"));
198
186
  return res.redirect("/dashboard");
199
187
  }
200
188
 
201
- // جمع إحصائيات السيرفر
189
+
202
190
  let levelCount = 0;
203
191
  let giveawayCount = 0;
204
192
  let blacklistCount = 0;
205
193
 
206
194
  try {
207
195
  if (Level) levelCount = await Level.countDocuments({ guildId: guild.id });
208
- if (giveaway) giveawayCount = await giveaway.countDocuments({ guildId: guild.id, ended: false });
209
- if (Blacklist) blacklistCount = await Blacklist.countDocuments({ guild: guild.id });
196
+ if (giveaway)
197
+ giveawayCount = await giveaway.countDocuments({
198
+ guildId: guild.id,
199
+ ended: false,
200
+ });
201
+ if (Blacklist)
202
+ blacklistCount = await Blacklist.countDocuments({ guild: guild.id });
210
203
  } catch (e) {
211
204
  console.error("Stats error:", e);
212
205
  }
@@ -223,11 +216,11 @@ app.set("views", path.join(__dirname, "views"));
223
216
  activeGiveaways: giveawayCount,
224
217
  blacklisted: blacklistCount,
225
218
  },
226
- botStats: getBotStats()
219
+ botStats: getBotStats(),
227
220
  });
228
221
  });
229
222
 
230
- // ==================== صفحة الأوامر ====================
223
+
231
224
  app.get("/dashboard/:guildId/commands", isAuthenticated, async (req, res) => {
232
225
  const guild = client.guilds.cache.get(req.params.guildId);
233
226
  if (!guild) return res.redirect("/dashboard");
@@ -235,8 +228,8 @@ app.set("views", path.join(__dirname, "views"));
235
228
  const slashCommands = client.slashCommands
236
229
  ? Array.from(client.slashCommands.values())
237
230
  : [];
238
-
239
- // إزالة التكرارات من prefix commands
231
+
232
+
240
233
  const uniquePrefixCommands = [];
241
234
  const seen = new Set();
242
235
  if (client.prefixCommands) {
@@ -253,35 +246,99 @@ app.set("views", path.join(__dirname, "views"));
253
246
  slashCommands,
254
247
  prefixCommands: uniquePrefixCommands,
255
248
  page: "commands",
256
- botStats: getBotStats()
249
+ botStats: getBotStats(),
257
250
  });
258
251
  });
259
252
 
260
- // ==================== نظام اللفل ====================
261
253
  app.get("/dashboard/:guildId/levels", isAuthenticated, async (req, res) => {
262
254
  const guild = client.guilds.cache.get(req.params.guildId);
263
255
  if (!guild) return res.redirect("/dashboard");
264
256
 
257
+ const mongoConnected = mongoose.connection.readyState === 1;
258
+ let levelConfigDb = null;
259
+ let configSource = null;
260
+ let databaseEnabled = false;
261
+
262
+ const canModify = options.Dashboard !== false;
263
+
264
+
265
+ const usesDashboardMode = client.levelDashboardGuilds && client.levelDashboardGuilds.has(guild.id);
266
+
267
+
268
+ if (mongoConnected) {
269
+ try {
270
+ levelConfigDb = await getGuildConfig(guild.id);
271
+ if (levelConfigDb && usesDashboardMode) {
272
+ configSource = "database";
273
+ databaseEnabled = true;
274
+ }
275
+ } catch (e) {
276
+ console.error("Error fetching level config:", e);
277
+ }
278
+ }
279
+
265
280
  let leaderboardData = [];
266
281
  if (Level) {
267
282
  try {
268
- leaderboardData = await Level.find({ guildId: guild.id })
283
+ const rawLeaderboard = await Level.find({ guildId: guild.id })
269
284
  .sort({ totalXP: -1 })
270
285
  .limit(100);
286
+
287
+
288
+ const memberIds = rawLeaderboard.map(u => u.userId);
289
+ const fetchedMembers = await guild.members.fetch({ user: memberIds }).catch(() => new Map());
290
+
291
+ leaderboardData = rawLeaderboard.map(u => {
292
+ const member = fetchedMembers.get(u.userId) || guild.members.cache.get(u.userId);
293
+ return {
294
+ userId: u.userId,
295
+ totalXP: u.totalXP,
296
+ level: u.level,
297
+ username: member ? member.user.username : `User ${u.userId.substring(0, 5)}`,
298
+ avatar: member ? member.user.displayAvatarURL({ size: 64, forceStatic: true }) : `https://cdn.discordapp.com/embed/avatars/${Math.floor(Math.random() * 6)}.png`,
299
+ };
300
+ });
271
301
  } catch (e) {
272
302
  console.error("Error fetching levels:", e);
273
303
  }
274
304
  }
275
305
 
306
+
307
+ const roles = guild.roles.cache
308
+ .filter((r) => r.id !== guild.id && !r.managed)
309
+ .map((r) => ({ id: r.id, name: r.name, color: r.hexColor }))
310
+ .sort((a, b) => a.name.localeCompare(b.name));
311
+
312
+
313
+ const channels = guild.channels.cache
314
+ .filter((c) => c.type === 0)
315
+ .map((c) => ({ id: c.id, name: c.name }))
316
+ .sort((a, b) => a.name.localeCompare(b.name));
317
+
276
318
  res.render("levels", {
277
319
  guild,
278
320
  leaderboard: leaderboardData,
279
321
  page: "levels",
280
- botStats: getBotStats()
322
+ botStats: getBotStats(),
323
+ databaseEnabled,
324
+ mongoConnected,
325
+ configSource,
326
+ levelConfigDb,
327
+ roles,
328
+ channels,
329
+ canModify,
281
330
  });
282
331
  });
283
332
 
284
- // API للحصول على بيانات مستخدم
333
+
334
+ function canModifySettings(req, res, next) {
335
+ if (options.Dashboard === false) {
336
+ return res.status(403).json({ success: false, error: "تم تعطيل التعديل من الداش بورد في ملف الإعدادات الرئيسي للبوت." });
337
+ }
338
+ next();
339
+ }
340
+
341
+
285
342
  app.get("/api/:guildId/user/:userId", isAuthenticated, async (req, res) => {
286
343
  if (!Level) return res.json({ error: "Level system not available" });
287
344
 
@@ -296,122 +353,380 @@ app.set("views", path.join(__dirname, "views"));
296
353
  }
297
354
  });
298
355
 
299
- // ==================== نظام الجيف اوي ====================
300
- app.get("/dashboard/:guildId/giveaways", isAuthenticated, async (req, res) => {
356
+
357
+ app.get(
358
+ "/dashboard/:guildId/giveaways",
359
+ isAuthenticated,
360
+ async (req, res) => {
361
+ const guild = client.guilds.cache.get(req.params.guildId);
362
+ if (!guild) return res.redirect("/dashboard");
363
+
364
+ let giveaways = [];
365
+ if (giveaway) {
366
+ try {
367
+ giveaways = await giveaway
368
+ .find({ guildId: guild.id })
369
+ .sort({ endTime: -1 });
370
+ } catch (e) {
371
+ console.error("Error fetching giveaways:", e);
372
+ }
373
+ }
374
+
375
+ res.render("giveaways", {
376
+ guild,
377
+ giveaways,
378
+ page: "giveaways",
379
+ botStats: getBotStats(),
380
+ });
381
+ }
382
+ );
383
+
384
+
385
+ app.post(
386
+ "/api/:guildId/giveaway/:action",
387
+ isAuthenticated,
388
+ async (req, res) => {
389
+ const { guildId, action } = req.params;
390
+ const { messageId } = req.body;
391
+
392
+ try {
393
+ let result = { success: true };
394
+ switch (action) {
395
+ case "pause":
396
+ if (Gpause) await Gpause(client, messageId);
397
+ break;
398
+ case "resume":
399
+
400
+ if (Gresume) await Gresume(client, messageId);
401
+ break;
402
+ case "end":
403
+
404
+ if (giveaway) {
405
+ const g = await giveaway.findOne({ messageId });
406
+ if (g && !g.ended) {
407
+
408
+ await giveaway.findOneAndUpdate(
409
+ { messageId },
410
+ { paused: false, endTime: Date.now().toString() }
411
+ );
412
+
413
+ setTimeout(() => Gcheck && Gcheck(client), 1000);
414
+ }
415
+ }
416
+ break;
417
+ case "reroll":
418
+ if (Greroll) await Greroll(client, messageId);
419
+ break;
420
+ case "delete":
421
+ if (Gdelete) await Gdelete(messageId);
422
+ break;
423
+ default:
424
+ return res.json({ success: false, error: "Invalid action" });
425
+ }
426
+ res.json({ success: true, message: `تم تنفيذ ${action} بنجاح` });
427
+ } catch (e) {
428
+ console.error("Giveaway action error:", e);
429
+ res.json({ success: false, error: e.message });
430
+ }
431
+ }
432
+ );
433
+
434
+
435
+ const LOG_EVENT_TYPES = [
436
+ { name: "حذف رسالة", value: "messageDelete", icon: "ri-delete-bin-line" },
437
+ { name: "تعديل رسالة", value: "messageUpdate", icon: "ri-edit-line" },
438
+ { name: "إنشاء قناة", value: "channelCreate", icon: "ri-add-circle-line" },
439
+ { name: "حذف قناة", value: "channelDelete", icon: "ri-close-circle-line" },
440
+ { name: "تعديل قناة", value: "channelUpdate", icon: "ri-settings-3-line" },
441
+ { name: "انضمام عضو", value: "guildMemberAdd", icon: "ri-user-add-line" },
442
+ {
443
+ name: "مغادرة عضو",
444
+ value: "guildMemberRemove",
445
+ icon: "ri-user-unfollow-line",
446
+ },
447
+ { name: "حظر عضو", value: "guildBanAdd", icon: "ri-forbid-line" },
448
+ {
449
+ name: "رفع الحظر",
450
+ value: "guildBanRemove",
451
+ icon: "ri-checkbox-circle-line",
452
+ },
453
+ { name: "إنشاء رتبة", value: "roleCreate", icon: "ri-shield-star-line" },
454
+ { name: "حذف رتبة", value: "roleDelete", icon: "ri-shield-cross-line" },
455
+ { name: "تعديل رتبة", value: "roleUpdate", icon: "ri-shield-line" },
456
+ {
457
+ name: "تغيير رتب العضو",
458
+ value: "guildMemberUpdate",
459
+ icon: "ri-user-settings-line",
460
+ },
461
+ { name: "نشاط صوتي", value: "voiceStateUpdate", icon: "ri-mic-line" },
462
+ { name: "إنشاء دعوة", value: "inviteCreate", icon: "ri-links-line" },
463
+ {
464
+ name: "إضافة إيموجي",
465
+ value: "emojiCreate",
466
+ icon: "ri-emotion-happy-line",
467
+ },
468
+ {
469
+ name: "حذف إيموجي",
470
+ value: "emojiDelete",
471
+ icon: "ri-emotion-unhappy-line",
472
+ },
473
+ { name: "تعديل إيموجي", value: "emojiUpdate", icon: "ri-emotion-line" },
474
+ {
475
+ name: "إضافة ستيكر",
476
+ value: "stickerCreate",
477
+ icon: "ri-sticky-note-line",
478
+ },
479
+ {
480
+ name: "حذف ستيكر",
481
+ value: "stickerDelete",
482
+ icon: "ri-sticky-note-2-line",
483
+ },
484
+ { name: "تعديل ستيكر", value: "stickerUpdate", icon: "ri-file-edit-line" },
485
+ ];
486
+
487
+
488
+ app.get("/dashboard/:guildId/logs", isAuthenticated, async (req, res) => {
301
489
  const guild = client.guilds.cache.get(req.params.guildId);
302
490
  if (!guild) return res.redirect("/dashboard");
303
491
 
304
- let giveaways = [];
305
- if (giveaway) {
492
+ const userGuild = req.user.guilds.find((g) => g.id === req.params.guildId);
493
+ if (!userGuild || (userGuild.permissions & 0x20) !== 0x20) {
494
+ return res.redirect("/dashboard");
495
+ }
496
+
497
+
498
+ const logData = getLogConfigData();
499
+ const databaseEnabled = logData.databaseEnabled;
500
+ let logConfig = null;
501
+ let configSource = null;
502
+
503
+ if (databaseEnabled) {
504
+
306
505
  try {
307
- giveaways = await giveaway.find({ guildId: guild.id }).sort({ endTime: -1 });
506
+ logConfig = await Log.findOne({ guildId: guild.id });
507
+ if (logConfig) configSource = "database";
308
508
  } catch (e) {
309
- console.error("Error fetching giveaways:", e);
509
+ console.error("Error fetching log config:", e);
510
+ }
511
+ } else {
512
+
513
+ const codeConfig = logData.configs.find((c) => c.guildId === guild.id);
514
+ if (codeConfig) {
515
+ logConfig = codeConfig;
516
+ configSource = "code";
310
517
  }
311
518
  }
312
519
 
313
- res.render("giveaways", {
520
+
521
+ const channels = guild.channels.cache
522
+ .filter((c) => c.type === 0)
523
+ .map((c) => ({ id: c.id, name: c.name }))
524
+ .sort((a, b) => a.name.localeCompare(b.name));
525
+
526
+
527
+ const disabledEvents = logConfig?.disable?.length || 0;
528
+ const enabledEvents = LOG_EVENT_TYPES.length - disabledEvents;
529
+ const customColors = logConfig?.colors
530
+ ? Object.keys(logConfig.colors).length
531
+ : 0;
532
+ const customChannels = logConfig?.channels
533
+ ? Object.keys(logConfig.channels).length
534
+ : 0;
535
+
536
+ res.render("logs", {
314
537
  guild,
315
- giveaways,
316
- page: "giveaways",
317
- botStats: getBotStats()
538
+ page: "logs",
539
+ botStats: getBotStats(),
540
+ databaseEnabled,
541
+ configSource,
542
+ logConfig,
543
+ channels,
544
+ eventTypes: LOG_EVENT_TYPES,
545
+ enabledEvents,
546
+ disabledEvents,
547
+ customColors,
548
+ customChannels,
318
549
  });
319
550
  });
320
551
 
321
- // API للجيف اوي
322
- app.post("/api/:guildId/giveaway/:action", isAuthenticated, async (req, res) => {
323
- const { guildId, action } = req.params;
324
- const { messageId } = req.body;
552
+
553
+ app.post("/api/:guildId/logs/channel", isAuthenticated, canModifySettings, async (req, res) => {
554
+ const logData = getLogConfigData();
555
+ if (!logData.databaseEnabled)
556
+ return res.json({ error: "يجب تفعيل وضع Database لتعديل الإعدادات" });
557
+
558
+ const { channelId } = req.body;
559
+ const guildId = req.params.guildId;
325
560
 
326
561
  try {
327
- let result = { success: true };
328
- switch (action) {
329
- case "pause":
330
- if (Gpause) await Gpause(client, messageId);
331
- break;
332
- case "resume":
333
- // تنفيذ يدوي للاستئناف لأن Gresume في djs-builder يحتوي على خطأ
334
- if (Gresume) await Gresume(client, messageId);
335
- break;
336
- case "end":
337
- // إنهاء الجيف اوي فوراً بتعديل وقت الانتهاء للوقت الحالي
338
- if (giveaway) {
339
- const g = await giveaway.findOne({ messageId });
340
- if (g && !g.ended) {
341
- // إذا كان متوقفاً مؤقتاً، نلغي الإيقاف أولاً
342
- await giveaway.findOneAndUpdate(
343
- { messageId },
344
- { paused: false, endTime: Date.now().toString() }
345
- );
346
- // إعطاء Gcheck فرصة للتشغيل
347
- setTimeout(() => Gcheck && Gcheck(client), 1000);
348
- }
349
- }
350
- break;
351
- case "reroll":
352
- if (Greroll) await Greroll(client, messageId);
353
- break;
354
- case "delete":
355
- if (Gdelete) await Gdelete(messageId);
356
- break;
357
- default:
358
- return res.json({ success: false, error: "Invalid action" });
359
- }
360
- res.json({ success: true, message: `تم تنفيذ ${action} بنجاح` });
562
+ await Log.findOneAndUpdate(
563
+ { guildId },
564
+ { guildId, channelId },
565
+ { upsert: true, new: true }
566
+ );
567
+
568
+ if (logData.clearCache) logData.clearCache(guildId);
569
+ res.json({ success: true });
361
570
  } catch (e) {
362
- console.error("Giveaway action error:", e);
363
571
  res.json({ success: false, error: e.message });
364
572
  }
365
573
  });
366
574
 
367
- // ==================== نظام البلاك ليست ====================
368
- // API لإضافة للبلاك ليست
369
- app.post("/api/guild/:guildId/blacklist", isAuthenticated, async (req, res) => {
370
- const { type, id } = req.body;
575
+
576
+ app.post("/api/:guildId/logs/toggle", isAuthenticated, canModifySettings, async (req, res) => {
577
+ const logData = getLogConfigData();
578
+ if (!logData.databaseEnabled)
579
+ return res.json({ error: "يجب تفعيل وضع Database لتعديل الإعدادات" });
580
+
581
+ const { eventType, enable } = req.body;
371
582
  const guildId = req.params.guildId;
372
583
 
373
584
  try {
374
- if (addToBlacklist) {
375
- const result = await addToBlacklist(guildId, type, id);
376
- return res.json({ success: result });
377
- } else if (Blacklist) {
378
- const exists = await Blacklist.findOne({ guild: guildId, type, id });
379
- if (!exists) {
380
- await Blacklist.create({ guild: guildId, type, id });
381
- }
382
- return res.json({ success: true });
585
+ if (enable) {
586
+
587
+ await Log.findOneAndUpdate(
588
+ { guildId },
589
+ { $pull: { disable: eventType } },
590
+ { upsert: true }
591
+ );
592
+ } else {
593
+
594
+ await Log.findOneAndUpdate(
595
+ { guildId },
596
+ { $addToSet: { disable: eventType } },
597
+ { upsert: true }
598
+ );
383
599
  }
384
- res.json({ error: "Blacklist system not available" });
600
+
601
+ const logData = getLogConfigData();
602
+ if (logData.clearCache) logData.clearCache(guildId);
603
+ res.json({ success: true });
385
604
  } catch (e) {
386
605
  res.json({ success: false, error: e.message });
387
606
  }
388
607
  });
389
608
 
390
- // API لحذف من البلاك ليست
391
- app.delete("/api/guild/:guildId/blacklist", isAuthenticated, async (req, res) => {
392
- const { type, id } = req.body;
609
+
610
+ app.post("/api/:guildId/logs/event", isAuthenticated, canModifySettings, async (req, res) => {
611
+ const logData = getLogConfigData();
612
+ if (!logData.databaseEnabled)
613
+ return res.json({ error: "يجب تفعيل وضع Database لتعديل الإعدادات" });
614
+
615
+ const { eventType, channel, color } = req.body;
393
616
  const guildId = req.params.guildId;
394
617
 
395
618
  try {
396
- if (removeFromBlacklist) {
397
- const result = await removeFromBlacklist(guildId, type, id);
398
- return res.json({ success: result });
399
- } else if (Blacklist) {
400
- await Blacklist.deleteOne({ guild: guildId, type, id });
401
- return res.json({ success: true });
619
+ const updateObj = {};
620
+
621
+ if (channel) {
622
+ updateObj[`channels.${eventType}`] = channel;
623
+ } else {
624
+
625
+ await Log.findOneAndUpdate(
626
+ { guildId },
627
+ { $unset: { [`channels.${eventType}`]: "" } }
628
+ );
402
629
  }
403
- res.json({ error: "Blacklist system not available" });
630
+
631
+ if (color) {
632
+ updateObj[`colors.${eventType}`] = color;
633
+ } else {
634
+
635
+ await Log.findOneAndUpdate(
636
+ { guildId },
637
+ { $unset: { [`colors.${eventType}`]: "" } }
638
+ );
639
+ }
640
+
641
+ if (Object.keys(updateObj).length > 0) {
642
+ await Log.findOneAndUpdate(
643
+ { guildId },
644
+ { $set: updateObj },
645
+ { upsert: true }
646
+ );
647
+ }
648
+
649
+
650
+ const logData2 = getLogConfigData();
651
+ if (logData2.clearCache) logData2.clearCache(guildId);
652
+ res.json({ success: true });
404
653
  } catch (e) {
405
654
  res.json({ success: false, error: e.message });
406
655
  }
407
656
  });
408
657
 
409
- // ==================== API القائمة السوداء العامة ====================
658
+
659
+ app.post("/api/:guildId/logs/reset", isAuthenticated, canModifySettings, async (req, res) => {
660
+ const logData = getLogConfigData();
661
+ if (!logData.databaseEnabled)
662
+ return res.json({ error: "يجب تفعيل وضع Database لتعديل الإعدادات" });
663
+
664
+ const guildId = req.params.guildId;
665
+
666
+ try {
667
+ await Log.findOneAndDelete({ guildId });
668
+
669
+ if (logData.clearCache) logData.clearCache(guildId);
670
+ res.json({ success: true });
671
+ } catch (e) {
672
+ res.json({ success: false, error: e.message });
673
+ }
674
+ });
675
+
676
+
677
+ app.post(
678
+ "/api/guild/:guildId/blacklist",
679
+ isAuthenticated,
680
+ async (req, res) => {
681
+ const { type, id } = req.body;
682
+ const guildId = req.params.guildId;
683
+
684
+ try {
685
+ if (addToBlacklist) {
686
+ const result = await addToBlacklist(guildId, type, id);
687
+ return res.json({ success: result });
688
+ } else if (Blacklist) {
689
+ const exists = await Blacklist.findOne({ guild: guildId, type, id });
690
+ if (!exists) {
691
+ await Blacklist.create({ guild: guildId, type, id });
692
+ }
693
+ return res.json({ success: true });
694
+ }
695
+ res.json({ error: "Blacklist system not available" });
696
+ } catch (e) {
697
+ res.json({ success: false, error: e.message });
698
+ }
699
+ }
700
+ );
701
+
702
+
703
+ app.delete(
704
+ "/api/guild/:guildId/blacklist",
705
+ isAuthenticated,
706
+ async (req, res) => {
707
+ const { type, id } = req.body;
708
+ const guildId = req.params.guildId;
709
+
710
+ try {
711
+ if (removeFromBlacklist) {
712
+ const result = await removeFromBlacklist(guildId, type, id);
713
+ return res.json({ success: result });
714
+ } else if (Blacklist) {
715
+ await Blacklist.deleteOne({ guild: guildId, type, id });
716
+ return res.json({ success: true });
717
+ }
718
+ res.json({ error: "Blacklist system not available" });
719
+ } catch (e) {
720
+ res.json({ success: false, error: e.message });
721
+ }
722
+ }
723
+ );
724
+
410
725
  app.get("/api/blacklist/:type", isAuthenticated, async (req, res) => {
411
726
  const { type } = req.params;
412
727
  try {
413
728
  if (Blacklist) {
414
- const typeMap = { users: 'user', roles: 'role', channels: 'channel' };
729
+ const typeMap = { users: "user", roles: "role", channels: "channel" };
415
730
  const items = await Blacklist.find({ type: typeMap[type] || type });
416
731
  return res.json({ items });
417
732
  }
@@ -427,7 +742,7 @@ app.set("views", path.join(__dirname, "views"));
427
742
  if (Blacklist) {
428
743
  const exists = await Blacklist.findOne({ type, id });
429
744
  if (!exists) {
430
- await Blacklist.create({ type, id, guild: 'global' });
745
+ await Blacklist.create({ type, id, guild: "global" });
431
746
  }
432
747
  return res.json({ success: true });
433
748
  }
@@ -450,17 +765,19 @@ app.set("views", path.join(__dirname, "views"));
450
765
  }
451
766
  });
452
767
 
453
- // API لجلب بيانات مستخدم (عام)
768
+
454
769
  app.get("/api/user/:userId", isAuthenticated, async (req, res) => {
455
770
  try {
456
- const user = await client.users.fetch(req.params.userId).catch(() => null);
771
+ const user = await client.users
772
+ .fetch(req.params.userId)
773
+ .catch(() => null);
457
774
  if (user) {
458
775
  return res.json({
459
776
  id: user.id,
460
777
  username: user.username,
461
778
  global_name: user.globalName,
462
779
  avatar: user.avatar,
463
- discriminator: user.discriminator
780
+ discriminator: user.discriminator,
464
781
  });
465
782
  }
466
783
  res.json({ error: "User not found" });
@@ -469,14 +786,15 @@ app.set("views", path.join(__dirname, "views"));
469
786
  }
470
787
  });
471
788
 
472
- // ==================== API للأعضاء ====================
473
- // الحصول على معلومات عضو
789
+
474
790
  app.get("/api/:guildId/member/:userId", isAuthenticated, async (req, res) => {
475
791
  const guild = client.guilds.cache.get(req.params.guildId);
476
792
  if (!guild) return res.json({ error: "Guild not found" });
477
793
 
478
794
  try {
479
- const member = await guild.members.fetch(req.params.userId).catch(() => null);
795
+ const member = await guild.members
796
+ .fetch(req.params.userId)
797
+ .catch(() => null);
480
798
  if (member) {
481
799
  return res.json({
482
800
  user: {
@@ -484,9 +802,9 @@ app.set("views", path.join(__dirname, "views"));
484
802
  username: member.user.username,
485
803
  global_name: member.user.globalName,
486
804
  avatar: member.user.avatar,
487
- discriminator: member.user.discriminator
805
+ discriminator: member.user.discriminator,
488
806
  },
489
- displayName: member.displayName
807
+ displayName: member.displayName,
490
808
  });
491
809
  }
492
810
  res.json({ error: "Member not found" });
@@ -495,103 +813,280 @@ app.set("views", path.join(__dirname, "views"));
495
813
  }
496
814
  });
497
815
 
498
- // البحث عن أعضاء
816
+
499
817
  app.get("/api/:guildId/members/search", isAuthenticated, async (req, res) => {
500
818
  const guild = client.guilds.cache.get(req.params.guildId);
501
819
  if (!guild) return res.json({ members: [] });
502
820
 
503
- const query = (req.query.q || '').toLowerCase();
821
+ const query = (req.query.q || "").toLowerCase();
504
822
  if (query.length < 2) return res.json({ members: [] });
505
823
 
506
824
  try {
507
- // جلب الأعضاء من الكاش
825
+
508
826
  const members = guild.members.cache
509
- .filter(m =>
510
- m.user.username.toLowerCase().includes(query) ||
511
- m.displayName.toLowerCase().includes(query) ||
512
- m.user.id.includes(query)
827
+ .filter(
828
+ (m) =>
829
+ m.user.username.toLowerCase().includes(query) ||
830
+ m.displayName.toLowerCase().includes(query) ||
831
+ m.user.id.includes(query)
513
832
  )
514
833
  .first(20)
515
- .map(m => ({
834
+ .map((m) => ({
516
835
  id: m.user.id,
517
836
  username: m.user.username,
518
837
  displayName: m.displayName,
519
838
  avatar: m.user.avatar,
520
- discriminator: m.user.discriminator
839
+ discriminator: m.user.discriminator,
521
840
  }));
522
-
841
+
523
842
  res.json({ members });
524
843
  } catch (e) {
525
844
  res.json({ members: [], error: e.message });
526
845
  }
527
846
  });
528
847
 
529
- // ==================== API للمستويات ====================
530
- // تحديث مستوى مستخدم
531
- app.post("/api/:guildId/level/update", isAuthenticated, async (req, res) => {
848
+
849
+ app.post("/api/:guildId/level/update", isAuthenticated, canModifySettings, async (req, res) => {
532
850
  const { userId, xp, level } = req.body;
533
851
  const guildId = req.params.guildId;
534
852
 
535
853
  try {
536
- if (Level) {
854
+ if (!Level) return res.json({ error: "Level system not available" });
855
+
856
+ if (level !== undefined) {
857
+
858
+ const config = await getGuildConfig(guildId);
859
+ const totalXP = xpNeeded(level - 1, config || {});
537
860
  await Level.findOneAndUpdate(
538
- { guildId, userId },
539
- { totalXP: xp, level, $setOnInsert: { text: 0, voice: 0 } },
861
+ { userId, guildId },
862
+ { userId, guildId, level, totalXP },
863
+ { upsert: true, new: true }
864
+ );
865
+ } else if (xp !== undefined) {
866
+
867
+ const config = await getGuildConfig(guildId);
868
+ const newLevel = calculateLevel(xp, config || {});
869
+ await Level.findOneAndUpdate(
870
+ { userId, guildId },
871
+ { userId, guildId, totalXP: xp, level: newLevel },
540
872
  { upsert: true, new: true }
541
873
  );
542
- return res.json({ success: true });
543
874
  }
544
- res.json({ error: "Level system not available" });
875
+ return res.json({ success: true });
545
876
  } catch (e) {
546
877
  res.json({ success: false, error: e.message });
547
878
  }
548
879
  });
549
880
 
550
- // إضافة XP لمستخدم
551
- app.post("/api/:guildId/level/add", isAuthenticated, async (req, res) => {
552
- const { userId, xp, level } = req.body;
881
+
882
+ app.post("/api/:guildId/level/set", isAuthenticated, canModifySettings, async (req, res) => {
883
+ const { userId, level } = req.body;
553
884
  const guildId = req.params.guildId;
554
885
 
555
886
  try {
556
- if (Level) {
557
- const existing = await Level.findOne({ guildId, userId });
558
- if (existing) {
559
- existing.totalXP += xp;
560
- if (level > 0) existing.level = level;
561
- await existing.save();
562
- } else {
563
- await Level.create({ guildId, userId, totalXP: xp, level: level || 0, text: 0, voice: 0 });
564
- }
565
- return res.json({ success: true });
887
+ if (!Level) return res.json({ error: "Level system not available" });
888
+ const config = await getGuildConfig(guildId);
889
+ const totalXP = xpNeeded(level - 1, config || {});
890
+ await Level.findOneAndUpdate(
891
+ { userId, guildId },
892
+ { userId, guildId, level, totalXP },
893
+ { upsert: true, new: true }
894
+ );
895
+ return res.json({ success: true });
896
+ } catch (e) {
897
+ res.json({ success: false, error: e.message });
898
+ }
899
+ });
900
+
901
+
902
+ app.post("/api/:guildId/level/setxp", isAuthenticated, canModifySettings, async (req, res) => {
903
+ const { userId, xp } = req.body;
904
+ const guildId = req.params.guildId;
905
+
906
+ try {
907
+ if (!Level) return res.json({ error: "Level system not available" });
908
+ const config = await getGuildConfig(guildId);
909
+ const level = calculateLevel(xp, config || {});
910
+ await Level.findOneAndUpdate(
911
+ { userId, guildId },
912
+ { userId, guildId, totalXP: xp, level },
913
+ { upsert: true, new: true }
914
+ );
915
+ return res.json({ success: true });
916
+ } catch (e) {
917
+ res.json({ success: false, error: e.message });
918
+ }
919
+ });
920
+
921
+
922
+ app.post("/api/:guildId/level/add", isAuthenticated, canModifySettings, async (req, res) => {
923
+ const { userId, xp } = req.body;
924
+ const guildId = req.params.guildId;
925
+
926
+ try {
927
+ if (!Level) return res.json({ error: "Level system not available" });
928
+
929
+ const config = await getGuildConfig(guildId);
930
+ const existing = await Level.findOne({ guildId, userId });
931
+ if (existing) {
932
+ existing.totalXP += xp;
933
+ existing.level = calculateLevel(existing.totalXP, config || {});
934
+ await existing.save();
935
+ } else {
936
+ await Level.create({
937
+ guildId,
938
+ userId,
939
+ totalXP: xp,
940
+ level: calculateLevel(xp, config || {}),
941
+ text: xp,
942
+ });
566
943
  }
567
- res.json({ error: "Level system not available" });
944
+ return res.json({ success: true });
568
945
  } catch (e) {
569
946
  res.json({ success: false, error: e.message });
570
947
  }
571
948
  });
572
949
 
573
- // إعادة تعيين مستوى مستخدم
574
- app.post("/api/:guildId/level/reset", isAuthenticated, async (req, res) => {
950
+
951
+ app.post("/api/:guildId/level/reset", isAuthenticated, canModifySettings, async (req, res) => {
575
952
  const { userId } = req.body;
576
953
  const guildId = req.params.guildId;
577
954
 
578
955
  try {
579
- if (Level) {
580
- await Level.deleteOne({ guildId, userId });
581
- return res.json({ success: true });
956
+ if (!Level) return res.json({ error: "Level system not available" });
957
+ await resetUser(userId, guildId);
958
+ return res.json({ success: true });
959
+ } catch (e) {
960
+ res.json({ success: false, error: e.message });
961
+ }
962
+ });
963
+
964
+
965
+ app.get("/api/:guildId/level/rank/:userId", isAuthenticated, async (req, res) => {
966
+ const { guildId, userId } = req.params;
967
+
968
+ try {
969
+ if (!Level) return res.json({ error: "Level system not available" });
970
+ const rank = await getUserRank(userId, guildId);
971
+ const userData = await Level.findOne({ guildId, userId });
972
+ res.json({
973
+ rank,
974
+ level: userData?.level || 0,
975
+ totalXP: userData?.totalXP || 0,
976
+ xpForNext: xpNeeded((userData?.level || 0) + 1)
977
+ });
978
+ } catch (e) {
979
+ res.json({ error: e.message });
980
+ }
981
+ });
982
+
983
+
984
+ app.get("/api/:guildId/level/config", isAuthenticated, async (req, res) => {
985
+ try {
986
+ const config = await getGuildConfig(req.params.guildId);
987
+ const usesDashboardMode = client.levelDashboardGuilds && client.levelDashboardGuilds.has(req.params.guildId);
988
+ res.json({ databaseEnabled: usesDashboardMode, config: config || null });
989
+ } catch (e) {
990
+ res.json({ error: e.message });
991
+ }
992
+ });
993
+
994
+
995
+ app.post("/api/:guildId/level/config/role", isAuthenticated, canModifySettings, async (req, res) => {
996
+ const { level, roleId } = req.body;
997
+ const guildId = req.params.guildId;
998
+
999
+ try {
1000
+ const config = await getGuildConfig(guildId) || {};
1001
+ const roleReward = config.roleReward || {};
1002
+
1003
+ if (roleId) {
1004
+ roleReward[level] = roleId;
1005
+ } else {
1006
+ delete roleReward[level];
582
1007
  }
583
- res.json({ error: "Level system not available" });
1008
+
1009
+ await updateGuildConfig(guildId, { roleReward });
1010
+ res.json({ success: true, roleReward });
1011
+ } catch (e) {
1012
+ res.json({ success: false, error: e.message });
1013
+ }
1014
+ });
1015
+
1016
+
1017
+ app.post("/api/:guildId/level/config/xp", isAuthenticated, canModifySettings, async (req, res) => {
1018
+ const { minXP, maxXP, xpMultiplier, maxLevel, cooldown, leveling } = req.body;
1019
+ const guildId = req.params.guildId;
1020
+
1021
+ try {
1022
+ const updates = {};
1023
+ if (minXP !== undefined) updates.minXP = minXP;
1024
+ if (maxXP !== undefined) updates.maxXP = maxXP;
1025
+ if (xpMultiplier !== undefined) updates.xpMultiplier = xpMultiplier;
1026
+ if (maxLevel !== undefined) updates.maxLevel = maxLevel;
1027
+ if (cooldown !== undefined) updates.cooldown = cooldown;
1028
+ if (leveling !== undefined) updates.leveling = leveling;
1029
+
1030
+ await updateGuildConfig(guildId, updates);
1031
+ res.json({ success: true });
584
1032
  } catch (e) {
585
1033
  res.json({ success: false, error: e.message });
586
1034
  }
587
1035
  });
588
1036
 
589
- // ==================== API للإحصائيات ====================
1037
+
1038
+ app.post("/api/:guildId/level/config/blacklist", isAuthenticated, canModifySettings, async (req, res) => {
1039
+ const { channelId, action } = req.body;
1040
+ const guildId = req.params.guildId;
1041
+
1042
+ try {
1043
+ const config = await getGuildConfig(guildId) || {};
1044
+ let blacklistedChannels = config.blacklistedChannels || [];
1045
+
1046
+ if (action === "add" && !blacklistedChannels.includes(channelId)) {
1047
+ blacklistedChannels.push(channelId);
1048
+ } else if (action === "remove") {
1049
+ blacklistedChannels = blacklistedChannels.filter(id => id !== channelId);
1050
+ }
1051
+
1052
+ await updateGuildConfig(guildId, { blacklistedChannels });
1053
+ res.json({ success: true, blacklistedChannels });
1054
+ } catch (e) {
1055
+ res.json({ success: false, error: e.message });
1056
+ }
1057
+ });
1058
+
1059
+
1060
+ app.post("/api/:guildId/level/config/toggle", isAuthenticated, canModifySettings, async (req, res) => {
1061
+ const { disabled } = req.body;
1062
+ const guildId = req.params.guildId;
1063
+
1064
+ try {
1065
+ await updateGuildConfig(guildId, { disabled: !!disabled });
1066
+ res.json({ success: true });
1067
+ } catch (e) {
1068
+ res.json({ success: false, error: e.message });
1069
+ }
1070
+ });
1071
+
1072
+
1073
+ app.post("/api/:guildId/level/config/reset", isAuthenticated, canModifySettings, async (req, res) => {
1074
+ const guildId = req.params.guildId;
1075
+
1076
+ try {
1077
+ await deleteGuildConfig(guildId);
1078
+ res.json({ success: true });
1079
+ } catch (e) {
1080
+ res.json({ success: false, error: e.message });
1081
+ }
1082
+ });
1083
+
1084
+
590
1085
  app.get("/api/stats", (req, res) => {
591
1086
  res.json(getBotStats());
592
1087
  });
593
1088
 
594
- // API لإحصائيات السيرفر
1089
+
595
1090
  app.get("/api/:guildId/stats", isAuthenticated, async (req, res) => {
596
1091
  const guild = client.guilds.cache.get(req.params.guildId);
597
1092
  if (!guild) return res.json({ error: "Guild not found" });
@@ -602,8 +1097,13 @@ app.set("views", path.join(__dirname, "views"));
602
1097
 
603
1098
  try {
604
1099
  if (Level) levelCount = await Level.countDocuments({ guildId: guild.id });
605
- if (giveaway) giveawayCount = await giveaway.countDocuments({ guildId: guild.id, ended: false });
606
- if (Blacklist) blacklistCount = await Blacklist.countDocuments({ guild: guild.id });
1100
+ if (giveaway)
1101
+ giveawayCount = await giveaway.countDocuments({
1102
+ guildId: guild.id,
1103
+ ended: false,
1104
+ });
1105
+ if (Blacklist)
1106
+ blacklistCount = await Blacklist.countDocuments({ guild: guild.id });
607
1107
  } catch (e) {
608
1108
  console.error("Stats error:", e);
609
1109
  }
@@ -619,18 +1119,13 @@ app.set("views", path.join(__dirname, "views"));
619
1119
  });
620
1120
  });
621
1121
 
622
- // ==================== صفحة 404 ====================
1122
+
623
1123
  app.use((req, res) => {
624
1124
  res.status(404).render("404", { botStats: getBotStats() });
625
1125
  });
626
1126
 
627
- // تشغيل الخادم
628
- app.listen(port, () => {
629
- console.log(`\n🌐 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
630
- console.log(` DJS-Builder Dashboard v2.0`);
631
- console.log(` 🔗 http://localhost:${port}`);
632
- console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
633
- });
1127
+
1128
+ app.listen(port, () => {});
634
1129
 
635
1130
  return app;
636
1131
  }