djs-builder 0.6.35 → 0.6.37
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/README.md +1059 -4
- package/function/giveaway.js +161 -161
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -656,9 +656,7 @@ module.exports = {
|
|
|
656
656
|
|
|
657
657
|
// 🎉 Level-up notification
|
|
658
658
|
if (result.leveledUp) {
|
|
659
|
-
msg.channel.send(
|
|
660
|
-
`🎊 ${msg.author} new level **${result.newLevel}** ⬆️`
|
|
661
|
-
);
|
|
659
|
+
msg.channel.send(`🎊 ${msg.author} new level **${result.newLevel}** ⬆️`);
|
|
662
660
|
}
|
|
663
661
|
|
|
664
662
|
// 📊 Check your rank
|
|
@@ -699,10 +697,1067 @@ module.exports = {
|
|
|
699
697
|
|
|
700
698
|
---
|
|
701
699
|
|
|
702
|
-
|
|
700
|
+
<details>
|
|
701
|
+
<summary>Giveaway System 🎉</summary>
|
|
702
|
+
|
|
703
|
+
## 🎉 Giveaway System – All-in-One Contest Management 🏆✨
|
|
704
|
+
|
|
705
|
+
This module provides a robust and feature-rich suite of functions to effortlessly launch, monitor, manage, and conclude **Giveaways** on your Discord server. It fully supports both **Reactions** and **Buttons** for entry, featuring advanced controls like pausing, resuming, and rerolling winners. **It is highly recommended to read the Important Notes section below.** 🚨
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
### 📦 Module Exports
|
|
710
|
+
|
|
711
|
+
```js
|
|
712
|
+
const {
|
|
713
|
+
Gstart,
|
|
714
|
+
Gcheck,
|
|
715
|
+
Greroll,
|
|
716
|
+
Glist,
|
|
717
|
+
Gpause,
|
|
718
|
+
Gresume,
|
|
719
|
+
Gdelete,
|
|
720
|
+
GaddUser,
|
|
721
|
+
GremoveUser,
|
|
722
|
+
GaddTime,
|
|
723
|
+
GremoveTime,
|
|
724
|
+
Gdata,
|
|
725
|
+
} = require("djs-builder");
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
---
|
|
729
|
+
|
|
730
|
+
<details>
|
|
731
|
+
<summary>🎬 Gstart – Launch a Brand New Giveaway</summary>
|
|
732
|
+
|
|
733
|
+
### 🎬 **Gstart** – Launch a Brand New Giveaway\! 🚀
|
|
734
|
+
|
|
735
|
+
This is the primary function to kick off a new giveaway. It handles creating the Discord message and persists all necessary data in the database (MongoDB). This function offers deep customization for embeds and entry methods. 🎨
|
|
736
|
+
|
|
737
|
+
**⚙️ Essential Options:**
|
|
738
|
+
|
|
739
|
+
- `context`: The Message or Interaction object that triggered the command.
|
|
740
|
+
- `endTime`: The duration until the giveaway ends (in **milliseconds** ⏱️).
|
|
741
|
+
- `winers`: The number of winners for the contest. 🏅
|
|
742
|
+
- `channelId`: The ID of the channel where the giveaway message will be posted. 📢
|
|
743
|
+
- `embed` / `endEmbed`: Options to fully customize the starting message and the final end message.
|
|
744
|
+
- `reaction`: To specify the entry method (**`button`** or **`reaction`**). 🖱️
|
|
745
|
+
|
|
746
|
+
**⚡ Simple Usage Example (Basic Requirements Only):**
|
|
747
|
+
|
|
748
|
+
This example provides a fast and minimalist way to start a giveaway with default embed colors, a simple title, and the default reaction entry type (if the `reaction` object is omitted or set to `reaction`).
|
|
749
|
+
|
|
750
|
+
```js
|
|
751
|
+
const { Gstart } = require("djs-builder");
|
|
752
|
+
|
|
753
|
+
module.exports = {
|
|
754
|
+
name: "gstart",
|
|
755
|
+
description: "Starts a new simple giveaway.",
|
|
756
|
+
run: async (client, message, args) => {
|
|
757
|
+
// ⏰ Giveaway ends in 1 hour
|
|
758
|
+
const oneHour = 60 * 60 * 1000;
|
|
759
|
+
const channelId = message.channel.id;
|
|
760
|
+
|
|
761
|
+
await Gstart({
|
|
762
|
+
context: message,
|
|
763
|
+
endTime: oneHour,
|
|
764
|
+
winers: 1,
|
|
765
|
+
channelId: channelId,
|
|
766
|
+
|
|
767
|
+
embed: {
|
|
768
|
+
title: "🎉 Simple Test Giveaway",
|
|
769
|
+
description: "React to enter! Prize: Discord Nitro.",
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
message.reply("🎉 Simple Giveaway started successfully!");
|
|
773
|
+
},
|
|
774
|
+
};
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
> 💡 **Tip:** This example provides a fast and minimalist way to start a giveaway with the default reaction type (emoji reaction).
|
|
703
778
|
|
|
704
779
|
---
|
|
705
780
|
|
|
781
|
+
**⚡ Full Customization Example (Demonstrating all options):**
|
|
782
|
+
|
|
783
|
+
This example showcases all available configuration options for deep customization of the embeds and the button entry method.
|
|
784
|
+
|
|
785
|
+
```js
|
|
786
|
+
const { Gstart } = require("djs-builder");
|
|
787
|
+
const { EmbedBuilder } = require("discord.js");
|
|
788
|
+
|
|
789
|
+
module.exports = {
|
|
790
|
+
name: "gstart",
|
|
791
|
+
description: "Starts a new highly-customized giveaway.",
|
|
792
|
+
run: async (client, message, args) => {
|
|
793
|
+
// ⏰ Giveaway ends in 48 hours
|
|
794
|
+
const twoDays = 48 * 60 * 60 * 1000;
|
|
795
|
+
const channelId = 'YOUR_GIVEAWAY_CHANNEL_ID'; // 📢 Target Channel
|
|
796
|
+
|
|
797
|
+
await Gstart({
|
|
798
|
+
context: message,
|
|
799
|
+
endTime: twoDays,
|
|
800
|
+
winers: 5, // 5 lucky winners! 🏆
|
|
801
|
+
channelId: channelId,
|
|
802
|
+
|
|
803
|
+
// 🎨 Customization for the STARTING EMBED
|
|
804
|
+
embed: {
|
|
805
|
+
|
|
806
|
+
custom: new EmbedBuilder().setTitle("Raw Embed").toJSON(),// 💡 You can use 'custom' to pass a raw Discord.js EmbedBuilder JSON
|
|
807
|
+
title: "🎉 **HUGE SERVER BOOST GIVEAWAY!**",
|
|
808
|
+
description: "Click the button below to enter for a chance to win a free server boost!",
|
|
809
|
+
color: "Blue", // Any valid Discord color
|
|
810
|
+
image: "https://yourimage.com/banner.png", 🖼️
|
|
811
|
+
thumbnail: message.guild.iconURL(),
|
|
812
|
+
},
|
|
813
|
+
|
|
814
|
+
// 🛑 Customization for the ENDED EMBED
|
|
815
|
+
endEmbed: {
|
|
816
|
+
custom: new EmbedBuilder().setTitle("Raw Embed").toJSON(),// 💡 You can use 'custom' to pass a raw Discord.js EmbedBuilder JSON
|
|
817
|
+
title: "🛑 Giveaway Has Concluded!",
|
|
818
|
+
description: "Congratulations to the winners! Check the message below.",
|
|
819
|
+
color: "Green",
|
|
820
|
+
// Eimage and Ethumbnail can also be set here
|
|
821
|
+
},
|
|
822
|
+
|
|
823
|
+
// 🖱️ Button Entry Method
|
|
824
|
+
reaction: {
|
|
825
|
+
type: "button", // Use 'reaction' for an emoji reaction
|
|
826
|
+
emoji: "✅", // The emoji displayed on the button
|
|
827
|
+
label: "Enter Giveaway!", // The text label
|
|
828
|
+
style: 3, // Button style: Primary(1), Secondary(2), Success(3), Danger(4)
|
|
829
|
+
id: "giveaway", // Custom ID for the button
|
|
830
|
+
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
message.reply("🎉 Giveaway started successfully!");
|
|
834
|
+
},
|
|
835
|
+
};
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
> 💡 **Flexibility Tip:** You can safely remove any option you do not wish to use (e.g., remove `endEmbed` entirely if you are happy with the default end message structure). 🗑️
|
|
839
|
+
|
|
840
|
+
</details>
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
<details>
|
|
845
|
+
<summary>⚠️ mportant Notes</summary>
|
|
846
|
+
|
|
847
|
+
### ⚠️ **Important Notes** – _Read Before Use\!_ 🚨
|
|
848
|
+
|
|
849
|
+
This module requires specific setup steps to function correctly. Pay attention to the following crucial points:
|
|
850
|
+
|
|
851
|
+
- **1. The Gcheck Requirement (Monitor):**
|
|
852
|
+
|
|
853
|
+
- The **`Gcheck(client)`** function is responsible for **monitoring** and **ending** the giveaways once their time runs out.
|
|
854
|
+
- **Failure to call `Gcheck(client)`** when your bot starts (e.g., in the `clientReady` event) will result in **giveaways never ending automatically\!** 🛑
|
|
855
|
+
|
|
856
|
+
- **2. Button Entry vs. InteractionCreate:**
|
|
857
|
+
|
|
858
|
+
- If you set `reaction.type` to **`button`** in `Gstart`, the system will create the button but **will NOT automatically register the participants.**
|
|
859
|
+
- You **MUST** manually implement a listener for the **`interactionCreate`** event and use the **`GaddUser`** function to register the user entry when the button is clicked. 🖱️
|
|
860
|
+
- _See the `GaddUser` section for a detailed code example._
|
|
861
|
+
|
|
862
|
+
</details>
|
|
863
|
+
|
|
864
|
+
---
|
|
865
|
+
|
|
866
|
+
<details>
|
|
867
|
+
<summary>📆 Gcheck – Monitor and End Time-Expired Giveaways</summary>
|
|
868
|
+
|
|
869
|
+
### 📆 **Gcheck** – Monitor and End Time-Expired Giveaways 🔎
|
|
870
|
+
|
|
871
|
+
This function runs periodically (every 10 seconds ⏱️) to check all active, non-paused giveaways. It automatically concludes contests that have passed their `endTime`. **_This function must be called once when the bot starts up._** 🚀
|
|
872
|
+
|
|
873
|
+
**⚡ Simple Example (in your `clientReady` event):**
|
|
874
|
+
|
|
875
|
+
```js
|
|
876
|
+
const { Gcheck } = require("djs-builder");
|
|
877
|
+
|
|
878
|
+
module.exports = {
|
|
879
|
+
name: "clientReady",
|
|
880
|
+
once: true,
|
|
881
|
+
run: (client) => {
|
|
882
|
+
console.log(`🤖 Bot is ready and monitoring giveaways...`);
|
|
883
|
+
Gcheck(client); // 👈 Start the continuous check loop!
|
|
884
|
+
},
|
|
885
|
+
};
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
</details>
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
892
|
+
<details>
|
|
893
|
+
<summary>🔄 Greroll – Redraw Winners for an Ended Contest</summary>
|
|
894
|
+
|
|
895
|
+
### 🔄 **Greroll** – Redraw Winners for an Ended Contest 🎲
|
|
896
|
+
|
|
897
|
+
Used to redraw one or more new winners for a **completed** giveaway. It sends a new announcement message with the updated winners.
|
|
898
|
+
|
|
899
|
+
**⚙️ Options:**
|
|
900
|
+
|
|
901
|
+
- `client`: The Discord Client object. 🤖
|
|
902
|
+
- `messageId`: The Message ID of the giveaway post. 🆔
|
|
903
|
+
|
|
904
|
+
**⚡ Simple Example:**
|
|
905
|
+
|
|
906
|
+
```js
|
|
907
|
+
const { Greroll } = require("djs-builder");
|
|
908
|
+
|
|
909
|
+
module.exports = {
|
|
910
|
+
name: "reroll",
|
|
911
|
+
description: "Redraws winners for an ended giveaway.",
|
|
912
|
+
Permissions: ["MANAGE_MESSAGES"], // 🛡️ Permission check
|
|
913
|
+
run: async (client, message, args) => {
|
|
914
|
+
const id = args[0];
|
|
915
|
+
if (!id)
|
|
916
|
+
return message.reply(
|
|
917
|
+
"❌ **Error:** Please provide the Giveaway Message ID."
|
|
918
|
+
);
|
|
919
|
+
const result = await Greroll(client, id);
|
|
920
|
+
if (result?.error) {
|
|
921
|
+
return message.reply(`⚠️ **Reroll Failed:** ${result.error}`);
|
|
922
|
+
} else {
|
|
923
|
+
message.reply(`✅ **Success!** Reroll initiated for giveaway \`${id}\`.`);
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
};
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
</details>
|
|
930
|
+
|
|
931
|
+
---
|
|
932
|
+
|
|
933
|
+
<details>
|
|
934
|
+
<summary>📜 Glist – Retrieve List of Giveaways</summary>
|
|
935
|
+
|
|
936
|
+
### 📜 **Glist** – Retrieve List of Giveaways 📋
|
|
937
|
+
|
|
938
|
+
Fetches a list of all saved giveaways based on their current status.
|
|
939
|
+
|
|
940
|
+
**⚙️ Options:**
|
|
941
|
+
|
|
942
|
+
- `type`: Can be **`ended`**, **`active`**, **`paused`**, or **`all`**. 🌐
|
|
943
|
+
|
|
944
|
+
**⚡ Simple Example:**
|
|
945
|
+
|
|
946
|
+
```js
|
|
947
|
+
const { Glist } = require("djs-builder");
|
|
948
|
+
|
|
949
|
+
// Retrieve all currently active (non-paused, non-ended) giveaways
|
|
950
|
+
const activeGiveaways = await Glist("active");
|
|
951
|
+
|
|
952
|
+
if (activeGiveaways.length > 0) {
|
|
953
|
+
console.log(`✅ Found ${activeGiveaways.length} Active Giveaways.`); // ... You can format and display this list to the user
|
|
954
|
+
} else {
|
|
955
|
+
console.log("No active giveaways found.");
|
|
956
|
+
}
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
</details>
|
|
960
|
+
|
|
961
|
+
---
|
|
962
|
+
|
|
963
|
+
<details>
|
|
964
|
+
<summary>⏸️ Gpause &▶️ Gresume – Stop and Continue Contests</summary>
|
|
965
|
+
|
|
966
|
+
### ⏸️ **Gpause** & ▶️ **Gresume** – Stop and Continue Contests 🛑
|
|
967
|
+
|
|
968
|
+
Allows you to temporarily halt an ongoing giveaway, preserving the remaining time, and then resume it later. The message embed is automatically updated to reflect the pause/resume status.
|
|
969
|
+
|
|
970
|
+
**⚙️ Options:**
|
|
971
|
+
|
|
972
|
+
- `client`: The Discord Client object. 🤖
|
|
973
|
+
- `messageId`: The Message ID of the giveaway post. 🆔
|
|
974
|
+
|
|
975
|
+
**⚡ Simple Example (for a management command):**
|
|
976
|
+
|
|
977
|
+
```js
|
|
978
|
+
const { Gpause, Gresume } = require("djs-builder");
|
|
979
|
+
|
|
980
|
+
module.exports = {
|
|
981
|
+
name: "g_manage",
|
|
982
|
+
description: "Pause or resume a giveaway.",
|
|
983
|
+
devOnly: true, // 👑 Developer only access
|
|
984
|
+
run: async (client, message, args) => {
|
|
985
|
+
const [action, id] = args;
|
|
986
|
+
if (!action || !id) return message.reply("⚠️ Missing action or ID.");
|
|
987
|
+
|
|
988
|
+
if (action === "pause") {
|
|
989
|
+
const result = await Gpause(client, id);
|
|
990
|
+
if (result?.error) return message.reply(`❌ ${result.error}`);
|
|
991
|
+
message.reply("⏸️ Giveaway successfully **paused**!");
|
|
992
|
+
} else if (action === "resume") {
|
|
993
|
+
const result = await Gresume(client, id);
|
|
994
|
+
if (result?.error) return message.reply(`❌ ${result.error}`);
|
|
995
|
+
message.reply("▶️ Giveaway successfully **resumed**!");
|
|
996
|
+
}
|
|
997
|
+
},
|
|
998
|
+
};
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
</details>
|
|
1002
|
+
|
|
1003
|
+
---
|
|
1004
|
+
|
|
1005
|
+
<details>
|
|
1006
|
+
<summary>🗑️ Gdelete – Permanently Remove Giveaway Data</summary>
|
|
1007
|
+
|
|
1008
|
+
### 🗑️ **Gdelete** – Permanently Remove Giveaway Data 🗄️
|
|
1009
|
+
|
|
1010
|
+
Deletes the giveaway record entirely from the database. _Note: This does not delete the Discord message itself, only the data._
|
|
1011
|
+
|
|
1012
|
+
**⚙️ Options:**
|
|
1013
|
+
|
|
1014
|
+
- `messageId`: The Message ID of the giveaway post. 🆔
|
|
1015
|
+
|
|
1016
|
+
**⚡ Simple Example:**
|
|
1017
|
+
|
|
1018
|
+
```js
|
|
1019
|
+
const { Gdelete } = require("djs-builder");
|
|
1020
|
+
|
|
1021
|
+
module.exports = {
|
|
1022
|
+
name: "gdelete",
|
|
1023
|
+
run: async (client, message, args) => {
|
|
1024
|
+
const messageId = args[0];
|
|
1025
|
+
const result = await Gdelete(messageId);
|
|
1026
|
+
if (result?.delete) {
|
|
1027
|
+
message.reply(
|
|
1028
|
+
`✅ **Success:** Giveaway data for \`${messageId}\` has been permanently deleted.`
|
|
1029
|
+
);
|
|
1030
|
+
} else {
|
|
1031
|
+
message.reply(
|
|
1032
|
+
`❌ **Error:** ${result?.error || "Could not delete giveaway data."}`
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
};
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
</details>
|
|
1040
|
+
|
|
1041
|
+
---
|
|
1042
|
+
|
|
1043
|
+
<details>
|
|
1044
|
+
<summary>👤 GaddUser & GremoveUser – Manual Participant Control</summary>
|
|
1045
|
+
|
|
1046
|
+
### ➕ **GaddUser** & ➖ **GremoveUser** – Manual Participant Control 🧑🤝🧑
|
|
1047
|
+
|
|
1048
|
+
These functions allow you to manually add or remove user IDs from the participant list. This is **crucial** when setting the giveaway `reaction` type to **`button`**.
|
|
1049
|
+
|
|
1050
|
+
**⚙️ Options:**
|
|
1051
|
+
|
|
1052
|
+
- `messageId`: The Message ID of the giveaway. 🆔
|
|
1053
|
+
- `userId`: The ID of the user to add/remove. 👤
|
|
1054
|
+
|
|
1055
|
+
---
|
|
1056
|
+
|
|
1057
|
+
#### 💡 **InteractionCreate Setup (for Button Entry)** 🖱️
|
|
1058
|
+
|
|
1059
|
+
When using the `button` type, you **MUST** implement the following listener to handle join/leave actions dynamically.
|
|
1060
|
+
|
|
1061
|
+
**⚡ Example Code for Dynamic Join/Leave in `interactionCreate` Event:**
|
|
1062
|
+
|
|
1063
|
+
```js
|
|
1064
|
+
const { GaddUser, GremoveUser, CreateRow } = require("djs-builder");
|
|
1065
|
+
|
|
1066
|
+
module.exports = {
|
|
1067
|
+
name: "interactionCreate",
|
|
1068
|
+
run: async (interaction) => {
|
|
1069
|
+
// 1. Handle Join/Entry Button Click
|
|
1070
|
+
if (interaction.customId === "djs-builder-giveaway") {
|
|
1071
|
+
const result = await GaddUser(
|
|
1072
|
+
interaction.message.id,
|
|
1073
|
+
interaction.user.id
|
|
1074
|
+
);
|
|
1075
|
+
|
|
1076
|
+
if (result?.error === "❌ User Already Joined") {
|
|
1077
|
+
// User already joined, offer a 'Leave' button dynamically
|
|
1078
|
+
const row = await CreateRow([
|
|
1079
|
+
[
|
|
1080
|
+
{
|
|
1081
|
+
label: "Leave Giveaway",
|
|
1082
|
+
id: "djs-builder-giveaway-leave-" + interaction.message.id, // Dynamic ID
|
|
1083
|
+
style: 4, // Danger Red
|
|
1084
|
+
},
|
|
1085
|
+
],
|
|
1086
|
+
]);
|
|
1087
|
+
return interaction.reply({
|
|
1088
|
+
content:
|
|
1089
|
+
"⚠️ You have **already joined** this giveaway! You can **leave** by clicking the button below.",
|
|
1090
|
+
components: row,
|
|
1091
|
+
flags: 64, // Ephemeral reply
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
await interaction.reply({
|
|
1096
|
+
content: "🎉 **Success!** Your entry has been registered!",
|
|
1097
|
+
flags: 64,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// 2. Handle Leave Button Click (Dynamically Generated ID)
|
|
1102
|
+
if (
|
|
1103
|
+
interaction.customId &&
|
|
1104
|
+
interaction.customId.startsWith("djs-builder-giveaway-leave-")
|
|
1105
|
+
) {
|
|
1106
|
+
const id = interaction.customId.split("-")[4]; // Extract message ID
|
|
1107
|
+
const result = await GremoveUser(id, interaction.user.id);
|
|
1108
|
+
|
|
1109
|
+
// Note: The error message should ideally reflect the module's logic (User Not Joined)
|
|
1110
|
+
if (result?.error && result.error.includes("User Not Joined")) {
|
|
1111
|
+
return interaction.reply({
|
|
1112
|
+
content: "⚠️ You have **already left** this giveaway!",
|
|
1113
|
+
flags: 64,
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
await interaction.reply({
|
|
1118
|
+
content: "🎉 **Success!** Your entry has been removed!",
|
|
1119
|
+
flags: 64,
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
};
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
</details>
|
|
1127
|
+
|
|
1128
|
+
---
|
|
1129
|
+
|
|
1130
|
+
<details>
|
|
1131
|
+
<summary>⏰ GaddTime & GremoveTime – Adjust the End Time</summary>
|
|
1132
|
+
|
|
1133
|
+
### ➕ **GaddTime** & ➖ **GremoveTime** – Adjust the End Time 🕰️
|
|
1134
|
+
|
|
1135
|
+
Allows you to extend or shorten the duration of an active giveaway.
|
|
1136
|
+
|
|
1137
|
+
**⚙️ Options:**
|
|
1138
|
+
|
|
1139
|
+
- `messageId`: The Message ID of the giveaway. 🆔
|
|
1140
|
+
- `client`: The Discord Client object. 🤖
|
|
1141
|
+
- `time`: The amount of time (in **milliseconds** ⏳) to add or remove.
|
|
1142
|
+
|
|
1143
|
+
**⚡ Simple Example (Extending the Giveaway):**
|
|
1144
|
+
|
|
1145
|
+
```js
|
|
1146
|
+
const { GaddTime } = require("djs-builder");
|
|
1147
|
+
|
|
1148
|
+
// Extend the contest by 30 minutes (1,800,000 MS)
|
|
1149
|
+
const halfHour = 30 * 60 * 1000;
|
|
1150
|
+
const result = await GaddTime(messageId, client, halfHour);
|
|
1151
|
+
|
|
1152
|
+
if (result?.error) {
|
|
1153
|
+
console.log(`Error adding time: ${result.error}`);
|
|
1154
|
+
} else {
|
|
1155
|
+
console.log("Time successfully added!");
|
|
1156
|
+
}
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
</details>
|
|
1160
|
+
|
|
1161
|
+
---
|
|
1162
|
+
|
|
1163
|
+
<details>
|
|
1164
|
+
<summary>📊 Gdata – Retrieve Giveaway Data</summary>
|
|
1165
|
+
|
|
1166
|
+
### 📊 **Gdata** – Retrieve Giveaway Data 💾
|
|
1167
|
+
|
|
1168
|
+
Fetches all saved data for a specific giveaway message.
|
|
1169
|
+
|
|
1170
|
+
**⚙️ Options:**
|
|
1171
|
+
|
|
1172
|
+
- `messageId`: The Message ID of the giveaway. 🆔
|
|
1173
|
+
|
|
1174
|
+
**⚡ Simple Example:**
|
|
1175
|
+
|
|
1176
|
+
```js
|
|
1177
|
+
const { Gdata } = require("djs-builder");
|
|
1178
|
+
|
|
1179
|
+
const giveawayData = await Gdata(messageId);
|
|
1180
|
+
|
|
1181
|
+
if (giveawayData) {
|
|
1182
|
+
console.log(`Giveaway hosted by: <@${giveawayData.hoster}>`);
|
|
1183
|
+
console.log(`Current status: ${giveawayData.ended ? "Ended" : "Active"}`);
|
|
1184
|
+
} else {
|
|
1185
|
+
console.log("No data found for this message ID.");
|
|
1186
|
+
}
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
📊 **`Gdata(messageId)`** – Returned Object Structure
|
|
1192
|
+
|
|
1193
|
+
- it like :
|
|
1194
|
+
|
|
1195
|
+
```js
|
|
1196
|
+
{
|
|
1197
|
+
"_id": "651234567890abcdef12345678", // 🔑 MongoDB unique ID
|
|
1198
|
+
"ended": false, // 💡 Current status of the giveaway
|
|
1199
|
+
"guildId": "123456789012345678", // 🏠 Server ID
|
|
1200
|
+
"channelId": "876543210987654321", // 📢 Channel ID
|
|
1201
|
+
"messageId": "987654321098765432", // 💬 Message ID of the giveaway post
|
|
1202
|
+
"hoster": "112233445566778899", // 👤 User ID of the giveaway host
|
|
1203
|
+
"winwesNumber": 1, // 🔢 Number of winners set
|
|
1204
|
+
"winers": [], // 🏆 Array of user IDs who won (empty if not ended)
|
|
1205
|
+
"paused": false, // ⏸️ True if the giveaway is paused
|
|
1206
|
+
"pausedTime": [], // ⏱️ Array of saved remaining times (used for resume)
|
|
1207
|
+
"endTime": "1730995200000", // 📅 Timestamp (MS) of when the giveaway should end
|
|
1208
|
+
"endType": "Time End", // 📝 How the giveaway ended (e.g., Time End, Reroll: 1 time(s))
|
|
1209
|
+
"reaction": "button", // 🖱️ Entry method: "reaction" or "button"
|
|
1210
|
+
"users": ["111111111111111111", "222222222222222222"], // 🧑🤝🧑 Array of user IDs who entered (used for button type)
|
|
1211
|
+
"endEmbed": {
|
|
1212
|
+
"title": "🛑 Giveaway Has Concluded!",
|
|
1213
|
+
"description": "Congratulations to the winners!",
|
|
1214
|
+
"color": 3066993, // Green color code
|
|
1215
|
+
"fields": [
|
|
1216
|
+
{
|
|
1217
|
+
"name": "🎉 Giveaway",
|
|
1218
|
+
"value": "- Winer(s): 1\n- Time : <t:1730995200:R>\n- Hosted By : <@112233445566778899>",
|
|
1219
|
+
"inline": true
|
|
1220
|
+
}
|
|
1221
|
+
]
|
|
1222
|
+
},
|
|
1223
|
+
"__v": 0 // 🌐 Mongoose version key (internal)
|
|
1224
|
+
}
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
</details>
|
|
1228
|
+
|
|
1229
|
+
---
|
|
1230
|
+
|
|
1231
|
+
<details>
|
|
1232
|
+
<summary>✨ General Code Example</summary>
|
|
1233
|
+
|
|
1234
|
+
### 💡 General Code Example – The All-in-One `/giveaway` Command 🛠️
|
|
1235
|
+
|
|
1236
|
+
This complete example demonstrates how to integrate **all** functions of the Giveaway System into a single Discord Slash Command, `/giveaway`, using subcommands for clean management.
|
|
1237
|
+
|
|
1238
|
+
```js
|
|
1239
|
+
const {
|
|
1240
|
+
Gstart,
|
|
1241
|
+
Greroll,
|
|
1242
|
+
Glist,
|
|
1243
|
+
Gpause,
|
|
1244
|
+
Gresume,
|
|
1245
|
+
Gdelete,
|
|
1246
|
+
GaddUser,
|
|
1247
|
+
GremoveUser,
|
|
1248
|
+
GaddTime,
|
|
1249
|
+
GremoveTime,
|
|
1250
|
+
Gdata,
|
|
1251
|
+
} = require("djs-builder");
|
|
1252
|
+
const {
|
|
1253
|
+
SlashCommandBuilder,
|
|
1254
|
+
EmbedBuilder,
|
|
1255
|
+
AttachmentBuilder,
|
|
1256
|
+
} = require("discord.js");
|
|
1257
|
+
const ms = require("ms");
|
|
1258
|
+
|
|
1259
|
+
module.exports = {
|
|
1260
|
+
data: new SlashCommandBuilder()
|
|
1261
|
+
.setName("giveaway")
|
|
1262
|
+
.setDescription("Comprehensive giveaway system management")
|
|
1263
|
+
|
|
1264
|
+
// 🎬 /giveaway start
|
|
1265
|
+
.addSubcommand((subcommand) =>
|
|
1266
|
+
subcommand
|
|
1267
|
+
.setName("start")
|
|
1268
|
+
.setDescription("Starts a new giveaway")
|
|
1269
|
+
.addStringOption((option) =>
|
|
1270
|
+
option
|
|
1271
|
+
.setName("prize")
|
|
1272
|
+
.setDescription("The prize for the winner(s)")
|
|
1273
|
+
.setRequired(true)
|
|
1274
|
+
)
|
|
1275
|
+
.addStringOption((option) =>
|
|
1276
|
+
option
|
|
1277
|
+
.setName("time")
|
|
1278
|
+
.setDescription("Duration (e.g., 1h, 30m, 7d)")
|
|
1279
|
+
.setRequired(true)
|
|
1280
|
+
)
|
|
1281
|
+
.addStringOption((option) =>
|
|
1282
|
+
option
|
|
1283
|
+
.setName("winner")
|
|
1284
|
+
.setDescription("Number of winners (e.g., 1, 3)")
|
|
1285
|
+
.setRequired(true)
|
|
1286
|
+
)
|
|
1287
|
+
.addChannelOption((option) =>
|
|
1288
|
+
option
|
|
1289
|
+
.setName("channel")
|
|
1290
|
+
.setDescription("The channel to post the giveaway in")
|
|
1291
|
+
)
|
|
1292
|
+
.addStringOption((option) =>
|
|
1293
|
+
option
|
|
1294
|
+
.setName("description")
|
|
1295
|
+
.setDescription("Custom description for the giveaway embed")
|
|
1296
|
+
)
|
|
1297
|
+
.addStringOption((option) =>
|
|
1298
|
+
option
|
|
1299
|
+
.setName("image")
|
|
1300
|
+
.setDescription("Image URL for the embed banner")
|
|
1301
|
+
)
|
|
1302
|
+
.addStringOption((option) =>
|
|
1303
|
+
option
|
|
1304
|
+
.setName("thumbnail")
|
|
1305
|
+
.setDescription("Thumbnail URL for the embed")
|
|
1306
|
+
)
|
|
1307
|
+
)
|
|
1308
|
+
|
|
1309
|
+
// 🔄 Management Commands: /giveaway reroll, pause, resume, delete, data
|
|
1310
|
+
.addSubcommand((subcommand) =>
|
|
1311
|
+
subcommand
|
|
1312
|
+
.setName("reroll")
|
|
1313
|
+
.setDescription("Redraw winners for an ended giveaway")
|
|
1314
|
+
.addStringOption((option) =>
|
|
1315
|
+
option
|
|
1316
|
+
.setName("id")
|
|
1317
|
+
.setDescription("Giveaway message ID")
|
|
1318
|
+
.setRequired(true)
|
|
1319
|
+
)
|
|
1320
|
+
)
|
|
1321
|
+
.addSubcommand((subcommand) =>
|
|
1322
|
+
subcommand
|
|
1323
|
+
.setName("pause")
|
|
1324
|
+
.setDescription("Pause an active giveaway")
|
|
1325
|
+
.addStringOption((option) =>
|
|
1326
|
+
option
|
|
1327
|
+
.setName("id")
|
|
1328
|
+
.setDescription("Giveaway message ID")
|
|
1329
|
+
.setRequired(true)
|
|
1330
|
+
)
|
|
1331
|
+
)
|
|
1332
|
+
.addSubcommand((subcommand) =>
|
|
1333
|
+
subcommand
|
|
1334
|
+
.setName("resume")
|
|
1335
|
+
.setDescription("Resume a paused giveaway")
|
|
1336
|
+
.addStringOption((option) =>
|
|
1337
|
+
option
|
|
1338
|
+
.setName("id")
|
|
1339
|
+
.setDescription("Giveaway message ID")
|
|
1340
|
+
.setRequired(true)
|
|
1341
|
+
)
|
|
1342
|
+
)
|
|
1343
|
+
.addSubcommand((subcommand) =>
|
|
1344
|
+
subcommand
|
|
1345
|
+
.setName("delete")
|
|
1346
|
+
.setDescription("Delete a giveaway's data")
|
|
1347
|
+
.addStringOption((option) =>
|
|
1348
|
+
option
|
|
1349
|
+
.setName("id")
|
|
1350
|
+
.setDescription("Giveaway message ID")
|
|
1351
|
+
.setRequired(true)
|
|
1352
|
+
)
|
|
1353
|
+
)
|
|
1354
|
+
.addSubcommand((subcommand) =>
|
|
1355
|
+
subcommand
|
|
1356
|
+
.setName("data")
|
|
1357
|
+
.setDescription("Get raw data for a giveaway")
|
|
1358
|
+
.addStringOption((option) =>
|
|
1359
|
+
option
|
|
1360
|
+
.setName("id")
|
|
1361
|
+
.setDescription("Giveaway message ID")
|
|
1362
|
+
.setRequired(true)
|
|
1363
|
+
)
|
|
1364
|
+
)
|
|
1365
|
+
|
|
1366
|
+
// 🧑🤝🧑 User Entry Management: /giveaway adduser, removeuser
|
|
1367
|
+
.addSubcommand((subcommand) =>
|
|
1368
|
+
subcommand
|
|
1369
|
+
.setName("adduser")
|
|
1370
|
+
.setDescription("Manually add user to giveaway (button entry only)")
|
|
1371
|
+
.addStringOption((option) =>
|
|
1372
|
+
option
|
|
1373
|
+
.setName("id")
|
|
1374
|
+
.setDescription("Giveaway message ID")
|
|
1375
|
+
.setRequired(true)
|
|
1376
|
+
)
|
|
1377
|
+
.addUserOption((option) =>
|
|
1378
|
+
option.setName("user").setDescription("User to add").setRequired(true)
|
|
1379
|
+
)
|
|
1380
|
+
)
|
|
1381
|
+
.addSubcommand((subcommand) =>
|
|
1382
|
+
subcommand
|
|
1383
|
+
.setName("removeuser")
|
|
1384
|
+
.setDescription(
|
|
1385
|
+
"Manually remove user from giveaway (button entry only)"
|
|
1386
|
+
)
|
|
1387
|
+
.addStringOption((option) =>
|
|
1388
|
+
option
|
|
1389
|
+
.setName("id")
|
|
1390
|
+
.setDescription("Giveaway message ID")
|
|
1391
|
+
.setRequired(true)
|
|
1392
|
+
)
|
|
1393
|
+
.addUserOption((option) =>
|
|
1394
|
+
option
|
|
1395
|
+
.setName("user")
|
|
1396
|
+
.setDescription("User to remove")
|
|
1397
|
+
.setRequired(true)
|
|
1398
|
+
)
|
|
1399
|
+
)
|
|
1400
|
+
|
|
1401
|
+
// ⏱️ Time Modification: /giveaway addtime, removetime
|
|
1402
|
+
.addSubcommand((subcommand) =>
|
|
1403
|
+
subcommand
|
|
1404
|
+
.setName("addtime")
|
|
1405
|
+
.setDescription("Add time to an active giveaway")
|
|
1406
|
+
.addStringOption((option) =>
|
|
1407
|
+
option
|
|
1408
|
+
.setName("id")
|
|
1409
|
+
.setDescription("Giveaway message ID")
|
|
1410
|
+
.setRequired(true)
|
|
1411
|
+
)
|
|
1412
|
+
.addStringOption((option) =>
|
|
1413
|
+
option
|
|
1414
|
+
.setName("time")
|
|
1415
|
+
.setDescription("Time to add (e.g., 30m, 1h)")
|
|
1416
|
+
.setRequired(true)
|
|
1417
|
+
)
|
|
1418
|
+
)
|
|
1419
|
+
.addSubcommand((subcommand) =>
|
|
1420
|
+
subcommand
|
|
1421
|
+
.setName("removetime")
|
|
1422
|
+
.setDescription("Remove time from an active giveaway")
|
|
1423
|
+
.addStringOption((option) =>
|
|
1424
|
+
option
|
|
1425
|
+
.setName("id")
|
|
1426
|
+
.setDescription("Giveaway message ID")
|
|
1427
|
+
.setRequired(true)
|
|
1428
|
+
)
|
|
1429
|
+
.addStringOption((option) =>
|
|
1430
|
+
option
|
|
1431
|
+
.setName("time")
|
|
1432
|
+
.setDescription("Time to remove (e.g., 10m)")
|
|
1433
|
+
.setRequired(true)
|
|
1434
|
+
)
|
|
1435
|
+
)
|
|
1436
|
+
// 📜 giveaways list
|
|
1437
|
+
.addSubcommand((subcommand) =>
|
|
1438
|
+
subcommand
|
|
1439
|
+
.setName("list")
|
|
1440
|
+
.setDescription("List giveaways")
|
|
1441
|
+
.addStringOption((option) =>
|
|
1442
|
+
option
|
|
1443
|
+
.setName("type")
|
|
1444
|
+
.setDescription("Type of giveaways to list")
|
|
1445
|
+
.setRequired(true)
|
|
1446
|
+
.addChoices(
|
|
1447
|
+
{ name: "Ended", value: "ended" },
|
|
1448
|
+
{ name: "Active", value: "active" },
|
|
1449
|
+
{ name: "Paused", value: "paused" },
|
|
1450
|
+
{ name: "All", value: "all" }
|
|
1451
|
+
)
|
|
1452
|
+
)
|
|
1453
|
+
),
|
|
1454
|
+
|
|
1455
|
+
// ⚙️ Command Execution
|
|
1456
|
+
run: async (interaction, client) => {
|
|
1457
|
+
await interaction.deferReply({ flags: 64 }); // Acknowledge command immediately
|
|
1458
|
+
|
|
1459
|
+
const command = interaction.options.getSubcommand();
|
|
1460
|
+
const id = interaction.options.getString("id");
|
|
1461
|
+
|
|
1462
|
+
if (command === "start") {
|
|
1463
|
+
// 🚀 START LOGIC
|
|
1464
|
+
const channel =
|
|
1465
|
+
interaction.options.getChannel("channel") || interaction.channel;
|
|
1466
|
+
const description = interaction.options.getString("description");
|
|
1467
|
+
const image = interaction.options.getString("image");
|
|
1468
|
+
const thumbnail = interaction.options.getString("thumbnail");
|
|
1469
|
+
const timeString = interaction.options.getString("time");
|
|
1470
|
+
const winnerCount = parseInt(interaction.options.getString("winner"));
|
|
1471
|
+
const prize = interaction.options.getString("prize");
|
|
1472
|
+
|
|
1473
|
+
const durationMs = ms(timeString);
|
|
1474
|
+
const endTimeMs = Date.now() + durationMs;
|
|
1475
|
+
|
|
1476
|
+
|
|
1477
|
+
if (!durationMs) {
|
|
1478
|
+
return interaction.editReply(
|
|
1479
|
+
"❌ **Error:** Invalid time format provided (e.g., 1h, 30m)."
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
if (isNaN(winnerCount) || winnerCount < 1) {
|
|
1483
|
+
return interaction.editReply(
|
|
1484
|
+
"❌ **Error:** Winner count must be a number greater than 0."
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
const giveaway = await Gstart({
|
|
1489
|
+
context: interaction,
|
|
1490
|
+
channelId: channel.id,
|
|
1491
|
+
winers: winnerCount,
|
|
1492
|
+
endTime: endTimeMs,
|
|
1493
|
+
embed: {
|
|
1494
|
+
title: `🎉 ${prize} Giveaway!`,
|
|
1495
|
+
image: image,
|
|
1496
|
+
thumbnail: thumbnail,
|
|
1497
|
+
description: description,
|
|
1498
|
+
},
|
|
1499
|
+
reaction: {
|
|
1500
|
+
type: "button",
|
|
1501
|
+
label: "Enter Giveaway",
|
|
1502
|
+
style: 1,
|
|
1503
|
+
emoji: "🎉",
|
|
1504
|
+
},
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
if (giveaway?.error) {
|
|
1508
|
+
return interaction.editReply(`❌ **Error:** ${giveaway.error}`);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
return interaction.editReply(
|
|
1512
|
+
`✅ **Success!** Giveaway for **${prize}** has been started in ${channel}!`
|
|
1513
|
+
);
|
|
1514
|
+
} else if (command === "list") {
|
|
1515
|
+
// 📜 LIST LOGIC
|
|
1516
|
+
const type = interaction.options.getString("type");
|
|
1517
|
+
const giveaways = await Glist(type);
|
|
1518
|
+
const MAX_EMBED_LENGTH = 4000;
|
|
1519
|
+
const MAX_MESSAGE_LENGTH = 2000;
|
|
1520
|
+
|
|
1521
|
+
if (!giveaways || giveaways.length === 0) {
|
|
1522
|
+
return interaction.editReply(`❌ No **${type}** giveaways found.`);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
const listContent = giveaways
|
|
1526
|
+
.map(
|
|
1527
|
+
(g, i) =>
|
|
1528
|
+
`**#${i + 1}** | ID: \`${g.messageId}\` | Hoster: <@${
|
|
1529
|
+
g.hoster
|
|
1530
|
+
}> | End: <t:${Math.floor(g.endTime / 1000)}:R>`
|
|
1531
|
+
)
|
|
1532
|
+
.join("\n");
|
|
1533
|
+
|
|
1534
|
+
const title = `📜 **${type.toUpperCase()} Giveaways:** (${
|
|
1535
|
+
giveaways.length
|
|
1536
|
+
} found)`;
|
|
1537
|
+
|
|
1538
|
+
|
|
1539
|
+
if (listContent.length <= MAX_MESSAGE_LENGTH) {
|
|
1540
|
+
// Output as a simple message (Under 2000 chars)
|
|
1541
|
+
return interaction.editReply({
|
|
1542
|
+
content: `${title}\n\n${listContent}`,
|
|
1543
|
+
});
|
|
1544
|
+
} else if (listContent.length <= MAX_EMBED_LENGTH) {
|
|
1545
|
+
// Output as an Embed (Over 2000, under 4096)
|
|
1546
|
+
const embed = new EmbedBuilder()
|
|
1547
|
+
.setTitle(title.replace(/\*\*/g, ""))
|
|
1548
|
+
.setDescription(listContent)
|
|
1549
|
+
.setColor("Blue");
|
|
1550
|
+
|
|
1551
|
+
return interaction.editReply({
|
|
1552
|
+
content: `Too many results for a single message, displaying via Embed.`,
|
|
1553
|
+
embeds: [embed],
|
|
1554
|
+
});
|
|
1555
|
+
} else {
|
|
1556
|
+
// Output as an attachment/JSON file (Over 4096 chars)
|
|
1557
|
+
const jsonFile = new AttachmentBuilder(
|
|
1558
|
+
Buffer.from(JSON.stringify(giveaways, null, 2)),
|
|
1559
|
+
{ name: `${type}_giveaways_list.json` }
|
|
1560
|
+
);
|
|
1561
|
+
|
|
1562
|
+
return interaction.editReply({
|
|
1563
|
+
content: `⚠️ **Warning:** The list is too long! Sending ${giveaways.length} results in a JSON file.`,
|
|
1564
|
+
files: [jsonFile],
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
} else if (command === "pause") {
|
|
1568
|
+
// ⏸️ PAUSE LOGIC
|
|
1569
|
+
const result = await Gpause(client, id);
|
|
1570
|
+
if (result?.error)
|
|
1571
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1572
|
+
return interaction.editReply(
|
|
1573
|
+
`⏸️ Giveaway \`${id}\` paused successfully!`
|
|
1574
|
+
);
|
|
1575
|
+
} else if (command === "resume") {
|
|
1576
|
+
// ▶️ RESUME LOGIC
|
|
1577
|
+
const result = await Gresume(client, id);
|
|
1578
|
+
if (result?.error)
|
|
1579
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1580
|
+
return interaction.editReply(
|
|
1581
|
+
`▶️ Giveaway \`${id}\` resumed successfully!`
|
|
1582
|
+
);
|
|
1583
|
+
} else if (command === "reroll") {
|
|
1584
|
+
// 🔄 REROLL LOGIC
|
|
1585
|
+
const result = await Greroll(client, id);
|
|
1586
|
+
if (result?.error)
|
|
1587
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1588
|
+
return interaction.editReply(
|
|
1589
|
+
`🔄 Reroll command sent for giveaway \`${id}\`! Check the channel for the new winner.`
|
|
1590
|
+
);
|
|
1591
|
+
} else if (command === "delete") {
|
|
1592
|
+
// 🗑️ DELETE LOGIC
|
|
1593
|
+
const result = await Gdelete(id);
|
|
1594
|
+
if (result?.error)
|
|
1595
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1596
|
+
return interaction.editReply(
|
|
1597
|
+
`🗑️ Giveaway data for \`${id}\` deleted successfully!`
|
|
1598
|
+
);
|
|
1599
|
+
|
|
1600
|
+
// ➕ USER MANAGEMENT LOGIC
|
|
1601
|
+
} else if (command === "adduser") {
|
|
1602
|
+
const user = interaction.options.getUser("user");
|
|
1603
|
+
const result = await GaddUser(id, user.id);
|
|
1604
|
+
if (result?.error)
|
|
1605
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1606
|
+
return interaction.editReply(
|
|
1607
|
+
`➕ User **${user.tag}** added to giveaway \`${id}\`.`
|
|
1608
|
+
);
|
|
1609
|
+
} else if (command === "removeuser") {
|
|
1610
|
+
// ➖ REMOVE USER LOGIC
|
|
1611
|
+
const user = interaction.options.getUser("user");
|
|
1612
|
+
const result = await GremoveUser(id, user.id);
|
|
1613
|
+
if (result?.error)
|
|
1614
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1615
|
+
return interaction.editReply(
|
|
1616
|
+
`➖ User **${user.tag}** removed from giveaway \`${id}\`.`
|
|
1617
|
+
);
|
|
1618
|
+
|
|
1619
|
+
// ⏳ TIME MANAGEMENT LOGIC
|
|
1620
|
+
} else if (command === "addtime") {
|
|
1621
|
+
const timeString = interaction.options.getString("time");
|
|
1622
|
+
const timeMs = ms(timeString);
|
|
1623
|
+
|
|
1624
|
+
if (!timeMs) {
|
|
1625
|
+
return interaction.editReply(
|
|
1626
|
+
"❌ **Error:** Invalid time format provided (e.g., 1h, 30m)."
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
const result = await GaddTime(id, client, timeMs);
|
|
1631
|
+
if (result?.error)
|
|
1632
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1633
|
+
return interaction.editReply(
|
|
1634
|
+
`⏱️ Added ${timeString} to giveaway \`${id}\`.`
|
|
1635
|
+
);
|
|
1636
|
+
} else if (command === "removetime") {
|
|
1637
|
+
// ⏪ REMOVE TIME LOGIC
|
|
1638
|
+
const timeString = interaction.options.getString("time");
|
|
1639
|
+
const timeMs = ms(timeString);
|
|
1640
|
+
|
|
1641
|
+
if (!timeMs) {
|
|
1642
|
+
return interaction.editReply(
|
|
1643
|
+
"❌ **Error:** Invalid time format provided (e.g., 1h, 30m)."
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
const result = await GremoveTime(id, client, timeMs);
|
|
1648
|
+
if (result?.error)
|
|
1649
|
+
return interaction.editReply(`❌ **Error:** ${result.error}`);
|
|
1650
|
+
return interaction.editReply(
|
|
1651
|
+
`⏱️ Removed ${timeString} from giveaway \`${id}\`.`
|
|
1652
|
+
);
|
|
1653
|
+
|
|
1654
|
+
// 📊 DATA LOGIC
|
|
1655
|
+
} else if (command === "data") {
|
|
1656
|
+
const id = interaction.options.getString("id");
|
|
1657
|
+
console.log(id);
|
|
1658
|
+
const data = await Gdata(id);
|
|
1659
|
+
if (data?.error)
|
|
1660
|
+
return interaction.editReply(`❌ **Error:** ${data.error}`);
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
const giveawayEmbed = new EmbedBuilder()
|
|
1664
|
+
.setTitle(`📊 Giveaway Data: ${id}`)
|
|
1665
|
+
.setColor(data.ended ? 0xFF0000 : data.paused ? 0xFFC0CB : 0x00FF00)
|
|
1666
|
+
.setDescription(`**Jump to Message 🔗 ** : ${data.messageId ? `https://discord.com/channels/${data.guildId}/${data.channelId}/${data.messageId}` : 'N/A'}`)
|
|
1667
|
+
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
giveawayEmbed.addFields(
|
|
1671
|
+
{
|
|
1672
|
+
name: "✨ Status",
|
|
1673
|
+
value: data.ended ? "ENDED 🛑" : data.paused ? "PAUSED ⏸️" : "ACTIVE ✅",
|
|
1674
|
+
inline: true
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
name: "🏆 Winners",
|
|
1678
|
+
value: data.winwesNumber.toString(),
|
|
1679
|
+
inline: true
|
|
1680
|
+
},
|
|
1681
|
+
{
|
|
1682
|
+
name: "🙋♂️ Hoster",
|
|
1683
|
+
value: `<@${data.hoster}>`,
|
|
1684
|
+
inline: true
|
|
1685
|
+
},
|
|
1686
|
+
|
|
1687
|
+
{
|
|
1688
|
+
name: "⏳ Ends In",
|
|
1689
|
+
value: `<t:${Math.floor(data.endTime / 1000)}:R>`,
|
|
1690
|
+
inline: true
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
name: "🎟️ Entries",
|
|
1694
|
+
value: data.reaction === "button" ? data.users.length.toString() : "N/A (Reaction Type)",
|
|
1695
|
+
inline: true
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
name: "🖱️ Entry Type",
|
|
1699
|
+
value: data.reaction.toUpperCase(),
|
|
1700
|
+
inline: true
|
|
1701
|
+
}
|
|
1702
|
+
);
|
|
1703
|
+
|
|
1704
|
+
|
|
1705
|
+
if (data.ended && data.winers.length > 0) {
|
|
1706
|
+
giveawayEmbed.addFields({
|
|
1707
|
+
name: "🏅 Past Winners",
|
|
1708
|
+
value: data.winers.map(u => `<@${u}>`).join(', ') || "N/A",
|
|
1709
|
+
inline: false
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
return interaction.editReply({
|
|
1714
|
+
embeds: [giveawayEmbed],
|
|
1715
|
+
content: `Data retrieved for giveaway \`${id}\`.`
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
};
|
|
1720
|
+
|
|
1721
|
+
```
|
|
1722
|
+
|
|
1723
|
+
---
|
|
1724
|
+
|
|
1725
|
+
<summary>📝 Final Improvements and Notes: ✨🚀</summary>
|
|
1726
|
+
|
|
1727
|
+
### 📝 Final Improvements and Notes: ✨🚀
|
|
1728
|
+
|
|
1729
|
+
- **1. Time Parsing via `ms`:** ⏰⏳
|
|
1730
|
+
|
|
1731
|
+
- for time inputs (e.g., `1h`, `30m`) to be correctly converted into milliseconds.
|
|
1732
|
+
|
|
1733
|
+
- **2. List Command Robustness:** 📜🛡️
|
|
1734
|
+
|
|
1735
|
+
- The **`list`** command logic handles large result sets automatically to avoid the Discord 2000-character limit error:
|
|
1736
|
+
- Under 2000 characters: Sent as a direct, formatted message.
|
|
1737
|
+
- Between 2000 and 4000 characters: Sent inside an **EmbedBuilder**.
|
|
1738
|
+
- Over 4000 characters: Sent as a **JSON attachment**, preventing API errors.
|
|
1739
|
+
|
|
1740
|
+
- **3. Function Call Integrity:** ⚙️✅
|
|
1741
|
+
|
|
1742
|
+
- The **`client`** object is correctly passed to **`GaddTime`** and **`GremoveTime`** (`id, client, timeMs`), meeting the module's requirements for management functions.
|
|
1743
|
+
|
|
1744
|
+
- **4. Enhanced User Experience (UX):** 🎯🌟
|
|
1745
|
+
|
|
1746
|
+
- The `channel` option uses **`.addChannelOption()`** for a native Discord selector.
|
|
1747
|
+
- Validation is included for **`winnerCount`** and time formats. 🚫
|
|
1748
|
+
|
|
1749
|
+
- **5. Private and Immediate Feedback:** 🔒💬
|
|
1750
|
+
|
|
1751
|
+
- Uses **`interaction.deferReply({ flags: 64 })`** (ephemeral/private) and **`interaction.editReply()`** for fast, clean, and non-intrusive status updates.
|
|
1752
|
+
|
|
1753
|
+
</details>
|
|
1754
|
+
|
|
1755
|
+
</details>
|
|
1756
|
+
|
|
1757
|
+
---
|
|
1758
|
+
|
|
1759
|
+
## ⚡ Commands & Events
|
|
1760
|
+
|
|
706
1761
|
**💡 Commands & Events made easy!**
|
|
707
1762
|
With `djs-builder`, handling **commands** and **events** is smooth, fast, and fully customizable.
|
|
708
1763
|
You get built-in features like:
|