giveaways
Version:
Create easily giveaways with your Discord.js bot by using this framework
430 lines (402 loc) • 21.9 kB
JavaScript
var giveaways = require('./data/giveaways.json');
var ended = require('./data/ended.json');
const ms = require('ms');
const fs = require('fs');
const {EventEmitter} = require('events');
const {ValueSaver} = require('valuesaver');
const path = require('path');
const Discord = require('discord.js');
const getgiveaways = new ValueSaver();
const intervals = new ValueSaver();
const forceend = new ValueSaver();
const endedGiveaways = new ValueSaver();
const globals = {};
const messages = {};
var botclient = null;
const week = 1000 * 60 * 60 * 24 * 7;
var storage = path.join(__dirname, `./data/giveaways.json`);
const oldgiveaways = ended.filter(giveaway => giveaway.timestamp + week < new Date().getTime());
oldgiveaways.forEach(giveaway => {
const index = ended.indexOf(giveaway);
ended.splice(index, 1);
});
if(oldgiveaways.length > 0){
fs.writeFileSync(path.join(__dirname, `./data/ended.json`), JSON.stringify(ended));
}
const wait = (ms) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});
}
const discordversion = Number(Discord.version.split(".")[0]);
if(discordversion < 12) throw new Error(`The giveaways package is not supported for Discord.js version lower than 12`);
(async () => {
do {
await wait(100);
} while (botclient === null);
function openGiveaways(){
giveaways.forEach(giveaway => {
const chan = botclient.channels.cache.get(giveaway.channelid);
if(!chan) return;
chan.messages.fetch(giveaway.messageid).then(msg => {
globals[giveaway.messageid] = new ValueSaver(giveaway.globals);
globals[giveaway.messageid].set(`embed`, new Discord.MessageEmbed(globals[giveaway.messageid].get(`embed`)));
globals[giveaway.messageid].set(`channel`, giveaway.channelid);
if(Boolean(globals[giveaway.messageid].get(`ended`)) === false) updateMessage(giveaway.enddate, msg);
getgiveaways.set(giveaway.messageid, 1);
}).catch(err => {});
});
ended.forEach(end => {
endedGiveaways.set(end.messageid, new ValueSaver(end.globals));
});
}
if(typeof botclient?.isReady === 'function'){
if(botclient.isReady()) openGiveaways();
else botclient.once('ready', () => openGiveaways());
} else openGiveaways();
})();
const updateMessage = (enddate, message) => {
var interval = setInterval(async () => {
if(!intervals.get(message.id)) intervals.set(message.id, interval);
if(Boolean(globals[message.id].get(`ended`)) === true){
clearInterval(interval);
return;
}
var content = ``;
if(globals[message.id].get(`tag`)) content = globals[message.id].get(`tag`);
if(globals[message.id].get(`description`)){
if(content.length > 0){
content += `\n${globals[message.id].get(`description`)}`;
} else content = globals[message.id].get(`description`);
}
const now = new Date().getTime();
const diffTime = enddate - now;
if(diffTime <= 0 || forceend.get(message.id)){
clearInterval(interval);
var winners;
if(globals[message.id].get(`settings`)['bots'] === false){
winners = (await message.reactions.resolve(`🎉`).users.fetch()).filter(m => m.bot === false).random(globals[message.id].get(`settings`)['winners']);
} else {
winners = (await message.reactions.resolve(`🎉`).users.fetch()).random(globals[message.id].get(`settings`)['winners']);
}
globals[message.id].set(`embed`, globals[message.id].get(`embed`).setColor(globals[message.id].get(`endcolor`)));
if(winners.length === 0){
globals[message.id].set(`embed`, globals[message.id].get(`embed`).setDescription(`**Giveaway ended!**\nHosted by: ${globals[message.id].get(`hostedby`)}\n\nWinner(s): There were no winners`));
if(discordversion === 12){
if(globals[message.id].get(`enddescription`)) message.edit(globals[message.id].get(`enddescription`), globals[message.id].get(`embed`)).catch(err => {});
else message.edit(`**Giveaway Ended**`, globals[message.id].get(`embed`)).catch(err => {});
} else if(discordversion === 13){
if(globals[message.id].get(`enddescription`)) message.edit({'content': globals[message.id].get(`enddescription`), embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
else message.edit({'content': `**Giveaway Ended**`, embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
}
return;
} else {
winners = winners.reduce((total, value) => {
message.channel.send(`Congrats, <@!${value.id}>! You've won **${globals[message.id].get(`prize`)}**`);
if(total === ``) return total += `<@!${value.id}>`;
else return total += `\n<@!${value.id}>`;
}, ``);
globals[message.id].set(`embed`, globals[message.id].get(`embed`).setDescription(`**Giveaway ended!**\nHosted by: ${globals[message.id].get(`hostedby`)}\n\nWinner(s): ${winners}`));
if(discordversion === 12){
if(globals[message.id].get(`enddescription`)) message.edit(globals[message.id].get(`enddescription`), globals[message.id].get(`embed`)).catch(err => {});
else message.edit(`**Giveaway Ended**`, globals[message.id].get(`embed`)).catch(err => {});
} else if(discordversion === 13){
if(globals[message.id].get(`enddescription`)) message.edit({'content': globals[message.id].get(`enddescription`), embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
else message.edit({'content': `**Giveaway Ended**`, embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
}
globals[message.id].set(`ended`, 1);
ended.push({
messageid: message.id,
timestamp: new Date().getTime(),
globals: globals[message.id].toReadableArray()
});
fs.writeFileSync(path.join(__dirname, `./data/ended.json`), JSON.stringify(ended));
const filter = giveaways.filter(g => g.messageid === message.id);
if(filter.length > 0){
const index = giveaways.indexOf(filter[0]);
if(index >= 0){
giveaways.splice(index, 1);
fs.writeFileSync(storage, JSON.stringify(giveaways));
}
}
onEnd.emit('end', message.id);
return;
}
} else {
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
var endsin = ``;
var Days = Math.floor(diffTime / (1000 * 60 * 60 * 24));
var Hours = Math.floor((diffTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var Minutes = Math.floor((diffTime % (1000 * 60 * 60)) / (1000 * 60));
var Seconds = Math.floor((diffTime % (1000 * 60)) / 1000);
if(diffDays > 0) endsin = `Ends in ${Days} ${days > 1 ? 'days' : 'day'} and ${Hours} ${Hours > 1 || Hours === 0 ? 'hours' : 'hour'}`;
else {
endsin = `Ends today in ${Hours} ${Hours > 1 || Hours === 0 ? 'hours' : 'hour'} ${Minutes} ${Minutes > 1 || Minutes === 0 ? 'minutes' : 'minute'} and ${Seconds} ${Seconds > 1 || Seconds === 0 ? 'seconds' : 'second'}`;
}
const hostedby = globals[message.id].get(`hostedby`);
globals[message.id].set(`embed`, globals[message.id].get(`embed`).setDescription(`React with ${globals[message.id].get(`reaction`)} to participate!${hostedby ? `\nHosted by: ${hostedby}` : ``}\n${globals[message.id].get(`settings`)['winners']} winner(s)\n\n${endsin}`));
if(discordversion === 12){
if(content.length > 0) message.edit(content, globals[message.id].get(`embed`)).catch(err => {});
else message.edit(globals[message.id].get(`embed`)).catch(err => {});
} else if(discordversion === 13){
if(content.length > 0) message.edit({'content': content, embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
else message.edit({embeds: [globals[message.id].get(`embed`)]}).catch(err => {});
}
}
}, ms(typeof globals[message.id].get(`interval`) === 'string' ? globals[message.id].get(`interval`) : ms(globals[message.id].get(`interval`))));
};
const onEnd = new EventEmitter();
class ClientManager extends EventEmitter{
constructor(client){
if(typeof client === 'undefined' || client === undefined || client === "") throw new Error(`Client is required`);
super();
botclient = client;
setTimeout(() => this.emit(`pass`), 500);
onEnd.on('end', giveaway => {
this.emit(`end`, giveaway);
});
onEnd.on('start', (messageid, channelid) => {
this.emit(`start`, messageid, channelid);
});
}
setCustomStorage(location = path.join(__dirname, `./data/giveaways.json`)){
storage = String(location);
giveaways = require(storage);
this.emit(`update`);
}
reroll(messageid){
return new Promise((resolve, reject) => {
if(!messageid) return reject(new Error(`A message id is required`));
if(!endedGiveaways.get(messageid)) return reject(new Error(`Giveaway doesn't exists or is still going on!`));
const chan = botclient.channels.cache.get(endedGiveaways.get(messageid).get(`channel`));
if(!chan) return reject(new Error(`Channel was not found!`));
chan.messages.fetch(messageid).then(async message => {
var winners;
if(endedGiveaways.get(message.id).get(`settings`)['bots'] === false){
winners = (await message.reactions.resolve(`🎉`).users.fetch()).filter(m => m.bot === false).random(endedGiveaways.get(messageid).get(`settings`)['winners']);
} else {
winners = (await message.reactions.resolve(`🎉`).users.fetch()).random(endedGiveaways.get(messageid).get(`settings`)['winners']);
}
if(winners.length === 0){
reject(new Error(`There are no participants!`));
} else {
winners = winners.reduce((total, value) => {
return total += ` <@!${value.id}>`;
}, ``);
chan.send(`New winner(s):${winners}! Congrats! You've won **${endedGiveaways.get(messageid).get(`prize`)}**!`);
this.emit(`reroll`, messageid);
resolve();
}
}).catch(err => {
return reject(new Error(`Couldn't reroll giveaway due the following reason: ${err}`));
});
});
}
end(messageid){
return new Promise((resolve, reject) => {
if(!messageid) return reject(new Error(`A message id is required`));
if(!globals[String(messageid)]) return reject(new Error(`Giveaway doesn't exists or has already been ended!`));
forceend.set(messageid, 1);
resolve();
});
}
remove(messageid){
return new Promise((resolve, reject) => {
if(!messageid) return reject(new Error(`A message id is required`));
if(!endedGiveaways.get(messageid)) return reject(new Error(`Giveaway doesn't exists or is still going on!`));
if(!intervals.get(String(messageid))) return reject(new Error(`Giveaway couldn't be found!`));
clearInterval(intervals.get(String(messageid)));
const chan = botclient.channels.cache.get(endedGiveaways.get(messageid).get(`channel`));
if(!chan) return resolve();
chan.messages.fetch(messageid).then(msg => {
msg.delete();
const filter = ended.filter(g => g.messageid === messageid);
if(filter.length > 0){
const index = ended.indexOf(filter[0]);
if(index >= 0){
ended.splice(index, 1);
fs.writeFileSync(path.join(__dirname, `./data/ended.json`), JSON.stringify(ended));
this.emit(`remove`, messageid);
}
}
resolve();
}).catch(err => {
reject(new Error(err));
});
});
}
};
class Giveaway extends EventEmitter{
constructor(channel){
if(typeof channel === 'undefined' || channel === undefined || channel === '') throw new Error(`Channel is required`);
if(botclient === null) throw new Error(`The client manager hasn't been created yet`);
super();
this.channel = channel;
if(globals[this.channel.id]) throw new Error(`There has already been made another giveaway for this channel which hasn't been send yet! Please send or remove the giveaway before creating a new one!`);
globals[this.channel.id] = new ValueSaver();
globals[this.channel.id].set(`channel`, this.channel.id);
globals[this.channel.id].set(`embed`, new Discord.MessageEmbed());
globals[this.channel.id].set(`time`, `1 day`);
globals[this.channel.id].set(`settings`, {bots: false, winners: 1});
globals[this.channel.id].set(`interval`, 5000);
globals[this.channel.id].set(`reaction`, `🎉`);
globals[this.channel.id].set(`endcolor`, `#428af5`);
globals[this.channel.id].set(`ended`, 0);
this.emit(`create`);
}
setPrize(prize){
if(!prize || typeof prize !== 'string') throw new Error(`prize is required and must be a string`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setTitle(prize));
globals[this.channel.id].set(`prize`, prize);
return this;
}
setBeginColor(color){
if(!color || typeof color !== 'string') throw new Error(`color is required and must be a string`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setColor(color));
return this;
}
setEndColor(color){
if(!color || typeof color !== 'string') throw new Error(`color is required and must be a string`);
globals[this.channel.id].set(`endcolor`, color);
return this;
}
setReaction(reaction){
if(!reaction || typeof reaction !== 'string') throw new Error(`reaction is required and must be a string`);
globals[this.channel.id].set(`reaction`, reaction);
return this;
}
setHostedBy(userid){
if(!userid || typeof userid !== 'string' && typeof userid !== 'number') throw new Error(`User id for function 'setHostedBy' is not a valid user id`);
globals[this.channel.id].set(`hostedby`, `<@!${userid}>`);
return this;
}
setInterval(time){
if(typeof time !== 'string' && typeof time !== 'number') throw new Error(`time is required and must be a string a string or a number.`);
globals[this.channel.id].set(`interval`, time);
return this;
}
setDescription(description){
if(!description || typeof description !== 'string') throw new Error(`description is required and must be a string`);
globals[this.channel.id].set(`description`, description);
return this;
}
setEndDescription(description){
if(!description || typeof description !== 'string') throw new Error(`description is required and must be a string`);
globals[this.channel.id].set(`enddescription`, description);
return this;
}
setTop(name, imgurl){
if(!name || typeof name !== 'string' || !imgurl || typeof imgurl !== 'string') throw new Error(`name and imgurl are required and must both be a string`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setAuthor(name, imgurl));
return this;
}
setTag(tag){
if(!tag) throw new Error(`tag is required`);
if(this.channel.guild.roles.cache.get(String(tag))) globals[this.channel.id].set(`tag`, `<@&${tag}>`);
else if(this.channel.guild.members.cache.get(String(tag))) globals[this.channel.id].set(`tag`, `<@!${tag}>`);
else globals[this.channel.id].set(`tag`, tag);
return this;
}
setTime(time){
if(typeof time !== `string` && typeof time !== 'number') throw new Error(`time is required and must be a string or a number`);
globals[this.channel.id].set(`time`, time);
return this;
}
setFooter(footer){
if(!footer || typeof footer !== 'string') throw new Error(`footer is required and must be a string`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setFooter(footer));
return this;
}
addSettings(settings = {winners: 1, bots: false}){
if(!settings || typeof settings !== 'object') throw new Error(`settings is required and must be an object`);
for(var key in settings){
globals[this.channel.id].get(`settings`)[key] = settings[key];
}
return this;
}
setThumbnail(img_url){
if(typeof img_url !== 'string') throw new Error(`Please include an image url in order to set the thumbnail`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setThumbnail(img_url));
return this;
}
setImage(img_url){
if(typeof img_url !== 'string') throw new Error(`Please include an image url in order to set the image`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setImage(img_url));
return this;
}
addTimestamp(){
globals[this.channel.id].set(`add_timestamp`, 1);
return this;
}
remove(){
delete globals[this.channel.id];
}
send(){
return new Promise(async (resolve, reject) => {
var content = ``;
if(globals[this.channel.id].get(`tag`)) content = globals[this.channel.id].get(`tag`);
if(globals[this.channel.id].get(`description`)){
if(content.length > 0){
content += `\n${globals[this.channel.id].get(`description`)}`;
} else content = globals[this.channel.id].get(`description`);
}
var sendmessage;
const endstamp = ms(typeof globals[this.channel.id].get(`time`) === 'string' ? globals[this.channel.id].get(`time`) : ms(globals[this.channel.id].get(`time`)));
if(endstamp === undefined) throw new Error(`Invalid time passed!`);
const enddate = new Date(new Date().getTime() + endstamp).getTime();
const now = new Date().getTime();
const diffTime = enddate - now;
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
const diffHours = Math.floor(diffTime / (1000 * 60 * 60));
const diffMinutes = Math.floor(diffTime / (1000 * 60));
const diffSeconds = Math.floor(diffTime / 1000);
var endsin = ``;
const hours = diffHours - (diffDays * 24);
const seconds = diffSeconds - (diffHours * 60 * 60);
const minutes = diffMinutes - (diffHours * 60);
if(diffDays > 0) endsin = `Ends in ${diffDays} ${diffDays > 1 ? 'days' : 'day'} and ${hours} ${hours > 1 || hours === 0 ? 'hours' : 'hour'}`;
else {
endsin = `Ends today in ${diffHours} ${diffHours > 1 || diffHours === 0 ? 'hours' : 'hour'} ${minutes} ${minutes > 1 || minutes === 0 ? 'minutes' : 'minute'} and ${seconds} ${seconds > 1 || seconds === 0 ? 'seconds' : 'second'}`;
}
const hostedby = globals[this.channel.id].get(`hostedby`);
globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setDescription(`React with ${globals[this.channel.id].get(`reaction`) || `🎉`} to participate!${hostedby ? `\nHosted by: ${hostedby}` : ``}\n${globals[this.channel.id].get(`settings`)['winners']} winner(s)\n\n${endsin}`));
if(Boolean(globals[this.channel.id].get(`add_timestamp`)) === true) globals[this.channel.id].set(`embed`, globals[this.channel.id].get(`embed`).setTimestamp(enddate));
try{
if(discordversion === 12){
var message;
if(content.length > 0) sendmessage = await this.channel.send(content, globals[this.channel.id].get(`embed`));
else sendmessage = await this.channel.send(globals[this.channel.id].get(`embed`));
}
else if(discordversion > 12){
var message = {embeds: [globals[this.channel.id].get(`embed`)]};
if(content.length > 0) message['content'] = content;
sendmessage = await this.channel.send(message);
}
else return reject(new Error(`Discord.js versions below 12 are not supported`));
sendmessage.react(globals[this.channel.id].get(`reaction`) || `🎉`);
messages[sendmessage.id] = globals[this.channel.id].get(`embed`);
globals[sendmessage.id] = globals[this.channel.id];
globals[sendmessage.id].set(`endtimestamp`, enddate);
delete globals[this.channel.id];
var json = {
messageid: sendmessage.id,
channelid: sendmessage.channel.id,
enddate: enddate,
globals: globals[sendmessage.id].toReadableArray()
};
giveaways.push(json);
getgiveaways.set(sendmessage.id, 1);
fs.writeFileSync(storage, JSON.stringify(giveaways));
onEnd.emit(`start`, sendmessage.id, this.channel.id);
updateMessage(enddate, sendmessage);
resolve(sendmessage.id);
} catch (error){
return reject(new Error(`Couldn't send giveaway for the following reason: ${error}`));
}
});
}
};
module.exports.Giveaway = Giveaway;
module.exports.ClientManager = ClientManager;