spacecommands 1.0.0
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/LICENSE +21 -0
- package/README.md +519 -0
- package/dist/Command.js +384 -0
- package/dist/CommandHandler.js +339 -0
- package/dist/FeatureHandler.js +89 -0
- package/dist/SlashCommands.js +220 -0
- package/dist/command-checks/channel-specific.js +30 -0
- package/dist/command-checks/guild-only-check.js +24 -0
- package/dist/command-checks/has-entitlement.js +52 -0
- package/dist/command-checks/has-permissions.js +39 -0
- package/dist/command-checks/has-roles.js +69 -0
- package/dist/command-checks/has-valid-arguments.js +54 -0
- package/dist/command-checks/in-cooldown.js +42 -0
- package/dist/command-checks/is-enabled.js +31 -0
- package/dist/command-checks/is-not-test-only.js +8 -0
- package/dist/command-checks/owner-only-check.js +22 -0
- package/dist/commands/channelonly.js +88 -0
- package/dist/commands/command.js +87 -0
- package/dist/commands/help/!ReactionListener.js +217 -0
- package/dist/commands/help/!get-first-embed.js +57 -0
- package/dist/commands/help/help.js +97 -0
- package/dist/commands/language.js +52 -0
- package/dist/commands/prefix.js +42 -0
- package/dist/commands/requiredrole.js +61 -0
- package/dist/commands/slash.js +102 -0
- package/dist/enums/CommandErrors.js +12 -0
- package/dist/enums/Events.js +9 -0
- package/dist/features/message-upsert.js +15 -0
- package/dist/get-all-files.js +25 -0
- package/dist/handlers/AutoModHandler.js +316 -0
- package/dist/handlers/ComponentHandler.js +110 -0
- package/dist/handlers/ContextMenuHandler.js +113 -0
- package/dist/handlers/EntitlementHandler.js +193 -0
- package/dist/handlers/ModalHandler.js +71 -0
- package/dist/handlers/PollHandler.js +230 -0
- package/dist/index.js +339 -0
- package/dist/message-handler.js +118 -0
- package/dist/models/channel-commands.js +49 -0
- package/dist/models/cooldown.js +51 -0
- package/dist/models/disabled-commands.js +45 -0
- package/dist/models/languages.js +46 -0
- package/dist/models/prefixes.js +46 -0
- package/dist/models/required-roles.js +49 -0
- package/dist/mongo.js +25 -0
- package/dist/permissions.js +39 -0
- package/dist/utils/ComponentBuilder.js +144 -0
- package/dist/utils/InteractionCollector.js +80 -0
- package/messages.json +391 -0
- package/package.json +72 -0
- package/typings.d.ts +276 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Handler for Discord premium features and entitlements
|
|
5
|
+
* Supports Discord's monetization system with SKUs and subscriptions
|
|
6
|
+
*/
|
|
7
|
+
class EntitlementHandler {
|
|
8
|
+
_client;
|
|
9
|
+
_instance;
|
|
10
|
+
_skus = new Map();
|
|
11
|
+
_entitlementCache = new Map();
|
|
12
|
+
_cacheTimeout = 5 * 60 * 1000; // 5 minutes
|
|
13
|
+
constructor(instance) {
|
|
14
|
+
this._instance = instance;
|
|
15
|
+
this._client = instance.client;
|
|
16
|
+
this.setUp();
|
|
17
|
+
}
|
|
18
|
+
setUp() {
|
|
19
|
+
// Listen for new entitlements
|
|
20
|
+
this._client.on('entitlementCreate', (entitlement) => {
|
|
21
|
+
this.clearUserCache(entitlement.userId);
|
|
22
|
+
if (this._instance.debug) {
|
|
23
|
+
console.log(`SpaceCommands > Entitlement created: ${entitlement.id} for user ${entitlement.userId}`);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
// Listen for entitlement updates
|
|
27
|
+
this._client.on('entitlementUpdate', (oldEntitlement, newEntitlement) => {
|
|
28
|
+
this.clearUserCache(newEntitlement.userId);
|
|
29
|
+
if (this._instance.debug) {
|
|
30
|
+
console.log(`SpaceCommands > Entitlement updated: ${newEntitlement.id} for user ${newEntitlement.userId}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// Listen for entitlement deletions
|
|
34
|
+
this._client.on('entitlementDelete', (entitlement) => {
|
|
35
|
+
this.clearUserCache(entitlement.userId);
|
|
36
|
+
if (this._instance.debug) {
|
|
37
|
+
console.log(`SpaceCommands > Entitlement deleted: ${entitlement.id} for user ${entitlement.userId}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Register a SKU for premium features
|
|
43
|
+
*/
|
|
44
|
+
registerSKU(config) {
|
|
45
|
+
this._skus.set(config.skuId, config);
|
|
46
|
+
if (this._instance.debug) {
|
|
47
|
+
console.log(`SpaceCommands > Registered SKU: ${config.skuId} (${config.name || 'Unnamed'})`);
|
|
48
|
+
}
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Register multiple SKUs at once
|
|
53
|
+
*/
|
|
54
|
+
registerSKUs(configs) {
|
|
55
|
+
for (const config of configs) {
|
|
56
|
+
this.registerSKU(config);
|
|
57
|
+
}
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Check if a user has a specific entitlement
|
|
62
|
+
*/
|
|
63
|
+
async hasEntitlement(userId, skuId) {
|
|
64
|
+
const entitlements = await this.getUserEntitlements(userId);
|
|
65
|
+
const entitlement = entitlements.find((e) => e.skuId === skuId);
|
|
66
|
+
return {
|
|
67
|
+
hasEntitlement: !!entitlement,
|
|
68
|
+
entitlement,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a user has any of the specified entitlements
|
|
73
|
+
*/
|
|
74
|
+
async hasAnyEntitlement(userId, skuIds) {
|
|
75
|
+
const entitlements = await this.getUserEntitlements(userId);
|
|
76
|
+
const entitlement = entitlements.find((e) => skuIds.includes(e.skuId));
|
|
77
|
+
return {
|
|
78
|
+
hasEntitlement: !!entitlement,
|
|
79
|
+
entitlement,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check if a user has all of the specified entitlements
|
|
84
|
+
*/
|
|
85
|
+
async hasAllEntitlements(userId, skuIds) {
|
|
86
|
+
const entitlements = await this.getUserEntitlements(userId);
|
|
87
|
+
const entitlementSkus = entitlements.map((e) => e.skuId);
|
|
88
|
+
return skuIds.every((skuId) => entitlementSkus.includes(skuId));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get all active entitlements for a user
|
|
92
|
+
*/
|
|
93
|
+
async getUserEntitlements(userId, useCache = true) {
|
|
94
|
+
// Check cache first
|
|
95
|
+
if (useCache && this._entitlementCache.has(userId)) {
|
|
96
|
+
return this._entitlementCache.get(userId);
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const entitlements = await this._client.application?.entitlements.fetch({
|
|
100
|
+
user: userId,
|
|
101
|
+
});
|
|
102
|
+
if (!entitlements) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const activeEntitlements = entitlements.filter((e) => !e.deleted && e.endsAt && e.endsAt.getTime() > Date.now());
|
|
106
|
+
// Cache the results
|
|
107
|
+
this._entitlementCache.set(userId, Array.from(activeEntitlements.values()));
|
|
108
|
+
// Clear cache after timeout
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
this._entitlementCache.delete(userId);
|
|
111
|
+
}, this._cacheTimeout);
|
|
112
|
+
return Array.from(activeEntitlements.values());
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(`SpaceCommands > Error fetching entitlements for user ${userId}:`, error);
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get all registered SKUs
|
|
121
|
+
*/
|
|
122
|
+
getSKUs() {
|
|
123
|
+
return this._skus;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get a specific SKU configuration
|
|
127
|
+
*/
|
|
128
|
+
getSKU(skuId) {
|
|
129
|
+
return this._skus.get(skuId);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Clear the entitlement cache for a specific user
|
|
133
|
+
*/
|
|
134
|
+
clearUserCache(userId) {
|
|
135
|
+
if (userId) {
|
|
136
|
+
this._entitlementCache.delete(userId);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
this._entitlementCache.clear();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Set the cache timeout duration (in milliseconds)
|
|
144
|
+
*/
|
|
145
|
+
setCacheTimeout(timeout) {
|
|
146
|
+
this._cacheTimeout = timeout;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Check if a user/member has entitlement (works with User or GuildMember)
|
|
151
|
+
*/
|
|
152
|
+
async checkAccess(userOrMember, skuId) {
|
|
153
|
+
const userId = userOrMember.id;
|
|
154
|
+
return this.hasEntitlement(userId, skuId);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Consume a one-time purchase entitlement
|
|
158
|
+
* This marks the entitlement as consumed (for one-time purchases)
|
|
159
|
+
*/
|
|
160
|
+
async consumeEntitlement(entitlementId) {
|
|
161
|
+
try {
|
|
162
|
+
await this._client.application?.entitlements.consume(entitlementId);
|
|
163
|
+
if (this._instance.debug) {
|
|
164
|
+
console.log(`SpaceCommands > Consumed entitlement: ${entitlementId}`);
|
|
165
|
+
}
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.error(`SpaceCommands > Error consuming entitlement ${entitlementId}:`, error);
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get all entitlements for a guild
|
|
175
|
+
*/
|
|
176
|
+
async getGuildEntitlements(guildId, useCache = false) {
|
|
177
|
+
try {
|
|
178
|
+
const entitlements = await this._client.application?.entitlements.fetch({
|
|
179
|
+
guild: guildId,
|
|
180
|
+
});
|
|
181
|
+
if (!entitlements) {
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
const activeEntitlements = entitlements.filter((e) => !e.deleted && e.endsAt && e.endsAt.getTime() > Date.now());
|
|
185
|
+
return Array.from(activeEntitlements.values());
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error(`SpaceCommands > Error fetching entitlements for guild ${guildId}:`, error);
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.default = EntitlementHandler;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const get_all_files_1 = __importDefault(require("../get-all-files"));
|
|
7
|
+
class ModalHandler {
|
|
8
|
+
_client;
|
|
9
|
+
_instance;
|
|
10
|
+
_modalHandlers = new Map();
|
|
11
|
+
constructor(instance, modalsDir, typeScript = false) {
|
|
12
|
+
this._instance = instance;
|
|
13
|
+
this._client = instance.client;
|
|
14
|
+
this.setUp(modalsDir, typeScript);
|
|
15
|
+
}
|
|
16
|
+
async setUp(modalsDir, typeScript = false) {
|
|
17
|
+
// Listen for modal submissions
|
|
18
|
+
this._client.on('interactionCreate', async (interaction) => {
|
|
19
|
+
if (!interaction.isModalSubmit()) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const customId = interaction.customId;
|
|
23
|
+
await this.handleModal(interaction, customId);
|
|
24
|
+
});
|
|
25
|
+
// Load custom modal handlers if directory provided
|
|
26
|
+
if (modalsDir) {
|
|
27
|
+
await this.loadModalHandlers(modalsDir, typeScript);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async loadModalHandlers(dir, typeScript) {
|
|
31
|
+
const files = (0, get_all_files_1.default)(dir, typeScript ? '.ts' : '');
|
|
32
|
+
for (const [file, fileName] of files) {
|
|
33
|
+
const handler = require(file);
|
|
34
|
+
const config = handler.default || handler;
|
|
35
|
+
if (config.type === 'modal') {
|
|
36
|
+
this.registerModalHandler(config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
registerModalHandler(handler) {
|
|
41
|
+
const key = typeof handler.customId === 'string'
|
|
42
|
+
? handler.customId
|
|
43
|
+
: handler.customId.source;
|
|
44
|
+
this._modalHandlers.set(key, handler);
|
|
45
|
+
}
|
|
46
|
+
async handleModal(interaction, customId) {
|
|
47
|
+
for (const [key, handler] of this._modalHandlers.entries()) {
|
|
48
|
+
const regex = handler.customId instanceof RegExp ? handler.customId : null;
|
|
49
|
+
if ((regex && regex.test(customId)) ||
|
|
50
|
+
(!regex && handler.customId === customId)) {
|
|
51
|
+
try {
|
|
52
|
+
await handler.callback(interaction, this._instance);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.error(`SpaceCommands > Error handling modal "${customId}":`, error);
|
|
56
|
+
if (!interaction.replied && !interaction.deferred) {
|
|
57
|
+
await interaction.reply({
|
|
58
|
+
content: 'An error occurred while processing this form.',
|
|
59
|
+
ephemeral: true,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
get modalHandlers() {
|
|
68
|
+
return this._modalHandlers;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.default = ModalHandler;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Handler for Discord Polls
|
|
5
|
+
* Supports creating, managing, and listening to poll events
|
|
6
|
+
*/
|
|
7
|
+
class PollHandler {
|
|
8
|
+
_client;
|
|
9
|
+
_instance;
|
|
10
|
+
_voteHandlers = new Map();
|
|
11
|
+
_endHandlers = [];
|
|
12
|
+
constructor(instance) {
|
|
13
|
+
this._instance = instance;
|
|
14
|
+
this._client = instance.client;
|
|
15
|
+
this.setUp();
|
|
16
|
+
}
|
|
17
|
+
setUp() {
|
|
18
|
+
// Note: Discord.js doesn't have built-in poll events yet
|
|
19
|
+
// Polls are accessed through messages
|
|
20
|
+
// This handler provides utilities for working with polls
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a poll in a message
|
|
24
|
+
* Returns the message containing the poll
|
|
25
|
+
*/
|
|
26
|
+
async createPoll(message, config) {
|
|
27
|
+
try {
|
|
28
|
+
if (config.answers.length < 1 || config.answers.length > 10) {
|
|
29
|
+
throw new Error('Polls must have between 1 and 10 answers');
|
|
30
|
+
}
|
|
31
|
+
const duration = config.duration
|
|
32
|
+
? Math.min(Math.max(config.duration, 1), 168)
|
|
33
|
+
: 24;
|
|
34
|
+
// Poll creation is done through the message content
|
|
35
|
+
// This is a utility method to help construct poll data
|
|
36
|
+
const pollData = {
|
|
37
|
+
question: { text: config.question },
|
|
38
|
+
answers: config.answers.map((answer, index) => ({
|
|
39
|
+
poll_media: {
|
|
40
|
+
text: answer.text,
|
|
41
|
+
emoji: answer.emoji,
|
|
42
|
+
},
|
|
43
|
+
answer_id: index,
|
|
44
|
+
})),
|
|
45
|
+
duration,
|
|
46
|
+
allow_multiselect: config.allowMultiselect || false,
|
|
47
|
+
layout_type: config.layoutType || 1,
|
|
48
|
+
};
|
|
49
|
+
// Fetch the message to get the poll if it was created
|
|
50
|
+
const fetchedMessage = await message.channel.messages.fetch(message.id);
|
|
51
|
+
return fetchedMessage.poll || null;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error('SpaceCommands > Error creating poll:', error);
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get poll results
|
|
60
|
+
*/
|
|
61
|
+
async getPollResults(poll) {
|
|
62
|
+
const results = new Map();
|
|
63
|
+
try {
|
|
64
|
+
for (const answer of poll.answers.values()) {
|
|
65
|
+
const voters = await answer.fetchVoters();
|
|
66
|
+
results.set(answer.id, voters);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('SpaceCommands > Error fetching poll results:', error);
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get the winning answer(s) of a poll
|
|
76
|
+
*/
|
|
77
|
+
getWinningAnswers(poll) {
|
|
78
|
+
const answers = Array.from(poll.answers.values());
|
|
79
|
+
if (answers.length === 0)
|
|
80
|
+
return [];
|
|
81
|
+
const maxVotes = Math.max(...answers.map((a) => a.voteCount));
|
|
82
|
+
return answers.filter((a) => a.voteCount === maxVotes);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* End a poll early
|
|
86
|
+
*/
|
|
87
|
+
async endPoll(poll) {
|
|
88
|
+
try {
|
|
89
|
+
await poll.end();
|
|
90
|
+
if (this._instance.debug) {
|
|
91
|
+
console.log(`SpaceCommands > Ended poll: ${poll.message.id}`);
|
|
92
|
+
}
|
|
93
|
+
// Trigger end handlers
|
|
94
|
+
await this.triggerEndHandlers(poll);
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error('SpaceCommands > Error ending poll:', error);
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Register a handler for when a poll ends
|
|
104
|
+
*/
|
|
105
|
+
registerPollEndHandler(handler) {
|
|
106
|
+
this._endHandlers.push(handler);
|
|
107
|
+
if (this._instance.debug) {
|
|
108
|
+
console.log(`SpaceCommands > Registered poll end handler for: ${handler.pollId}`);
|
|
109
|
+
}
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Trigger all matching end handlers for a poll
|
|
114
|
+
*/
|
|
115
|
+
async triggerEndHandlers(poll) {
|
|
116
|
+
const pollId = poll.message.id;
|
|
117
|
+
for (const handler of this._endHandlers) {
|
|
118
|
+
let matches = false;
|
|
119
|
+
if (typeof handler.pollId === 'string') {
|
|
120
|
+
matches = handler.pollId === pollId;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
matches = handler.pollId.test(pollId);
|
|
124
|
+
}
|
|
125
|
+
if (matches) {
|
|
126
|
+
try {
|
|
127
|
+
await handler.callback(poll, this._instance);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('SpaceCommands > Error executing poll end handler:', error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a user has voted on a poll
|
|
137
|
+
*/
|
|
138
|
+
async hasUserVoted(poll, userId) {
|
|
139
|
+
try {
|
|
140
|
+
for (const answer of poll.answers.values()) {
|
|
141
|
+
const voters = await answer.fetchVoters();
|
|
142
|
+
if (voters.has(userId)) {
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('SpaceCommands > Error checking poll vote:', error);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get user's votes on a poll
|
|
155
|
+
*/
|
|
156
|
+
async getUserVotes(poll, userId) {
|
|
157
|
+
const userVotes = [];
|
|
158
|
+
try {
|
|
159
|
+
for (const answer of poll.answers.values()) {
|
|
160
|
+
const voters = await answer.fetchVoters();
|
|
161
|
+
if (voters.has(userId)) {
|
|
162
|
+
userVotes.push(answer);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error('SpaceCommands > Error getting user votes:', error);
|
|
168
|
+
}
|
|
169
|
+
return userVotes;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get poll statistics
|
|
173
|
+
*/
|
|
174
|
+
getPollStats(poll) {
|
|
175
|
+
const answers = Array.from(poll.answers.values());
|
|
176
|
+
const totalVotes = answers.reduce((sum, answer) => sum + answer.voteCount, 0);
|
|
177
|
+
return {
|
|
178
|
+
totalVotes,
|
|
179
|
+
answerCount: answers.length,
|
|
180
|
+
allowsMultiselect: poll.allowMultiselect,
|
|
181
|
+
expiresAt: poll.expiresAt,
|
|
182
|
+
isExpired: poll.expiresAt ? poll.expiresAt.getTime() < Date.now() : false,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Get a formatted summary of poll results
|
|
187
|
+
*/
|
|
188
|
+
async getFormattedResults(poll) {
|
|
189
|
+
const results = await this.getPollResults(poll);
|
|
190
|
+
const stats = this.getPollStats(poll);
|
|
191
|
+
let output = `**${poll.question.text}**\n\n`;
|
|
192
|
+
for (const answer of poll.answers.values()) {
|
|
193
|
+
const voters = results.get(answer.id);
|
|
194
|
+
const percentage = stats.totalVotes > 0
|
|
195
|
+
? ((answer.voteCount / stats.totalVotes) * 100).toFixed(1)
|
|
196
|
+
: '0.0';
|
|
197
|
+
const answerText = 'text' in answer ? answer.text : answer.id.toString();
|
|
198
|
+
output += `${answerText}: ${answer.voteCount} votes (${percentage}%)\n`;
|
|
199
|
+
}
|
|
200
|
+
output += `\nTotal Votes: ${stats.totalVotes}`;
|
|
201
|
+
return output;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Fetch a poll from a message
|
|
205
|
+
*/
|
|
206
|
+
async fetchPoll(message, channelId) {
|
|
207
|
+
try {
|
|
208
|
+
let msg;
|
|
209
|
+
if (typeof message === 'string') {
|
|
210
|
+
if (!channelId) {
|
|
211
|
+
throw new Error('Channel ID required when fetching by message ID');
|
|
212
|
+
}
|
|
213
|
+
const channel = await this._client.channels.fetch(channelId);
|
|
214
|
+
if (!channel || !channel.isTextBased()) {
|
|
215
|
+
throw new Error('Invalid channel');
|
|
216
|
+
}
|
|
217
|
+
msg = await channel.messages.fetch(message);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
msg = message;
|
|
221
|
+
}
|
|
222
|
+
return msg.poll || null;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
console.error('SpaceCommands > Error fetching poll:', error);
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
exports.default = PollHandler;
|