|
@@ -16,14 +16,11 @@ let activeContests: Dict<ActiveContest> = {};
|
|
|
|
|
|
async function init() {
|
|
async function init() {
|
|
let contestRepo = getRepository(Contest);
|
|
let contestRepo = getRepository(Contest);
|
|
-
|
|
|
|
let contests = await contestRepo.find({
|
|
let contests = await contestRepo.find({
|
|
where: { active: true },
|
|
where: { active: true },
|
|
relations: ["entries"]
|
|
relations: ["entries"]
|
|
});
|
|
});
|
|
|
|
|
|
- let now = new Date();
|
|
|
|
-
|
|
|
|
for (let contest of contests)
|
|
for (let contest of contests)
|
|
await updateContestStatus(contest);
|
|
await updateContestStatus(contest);
|
|
|
|
|
|
@@ -52,73 +49,68 @@ type ContestEntryMessage = {
|
|
|
|
|
|
//TODO: Convert into a transaction
|
|
//TODO: Convert into a transaction
|
|
async function updateContestStatus(contest: Contest) {
|
|
async function updateContestStatus(contest: Contest) {
|
|
- let contestRepo = getRepository(Contest);
|
|
|
|
let voteRepo = getRepository(ContestVote);
|
|
let voteRepo = getRepository(ContestVote);
|
|
let entryRepo = getRepository(ContestEntry);
|
|
let entryRepo = getRepository(ContestEntry);
|
|
|
|
|
|
let channel = client.channels.get(contest.channel) as TextChannel;
|
|
let channel = client.channels.get(contest.channel) as TextChannel;
|
|
-
|
|
|
|
if (!channel) {
|
|
if (!channel) {
|
|
console.log(`Channel ${contest.channel} has been deleted! Removing contest ${contest.id}...`);
|
|
console.log(`Channel ${contest.channel} has been deleted! Removing contest ${contest.id}...`);
|
|
await removeContest(contest.id);
|
|
await removeContest(contest.id);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- let entries = contest.entries.reduce((p, c) => {
|
|
|
|
- p[c.msgId] = c;
|
|
|
|
- return p;
|
|
|
|
- }, <Dict<ContestEntry>>{});
|
|
|
|
-
|
|
|
|
let newestEntry: Date = null;
|
|
let newestEntry: Date = null;
|
|
-
|
|
|
|
|
|
+ let contestEntryMessageIds = new Set<string>(contest.entries.map(e => e.msgId));
|
|
for (let entry of contest.entries) {
|
|
for (let entry of contest.entries) {
|
|
try {
|
|
try {
|
|
let msg = await channel.fetchMessage(entry.msgId);
|
|
let msg = await channel.fetchMessage(entry.msgId);
|
|
- let voteReaction = msg.reactions.get(contest.voteReaction);
|
|
|
|
- let users = await voteReaction.fetchUsers();
|
|
|
|
- let existingVotes = await voteRepo.find({ where: { contest: contest } });
|
|
|
|
|
|
+ let existingVotes = await voteRepo.find({ where: { contest: contest, contestEntry: entry } });
|
|
|
|
+
|
|
|
|
+ let voteReaction = msg.reactions.find(r => r.emoji.toString() == contest.voteReaction);
|
|
|
|
+ if(!voteReaction) {
|
|
|
|
+ await voteRepo.remove(existingVotes);
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ let users = await voteReaction.fetchUsers();
|
|
let [newVotes, removedVotes] = diffEntryVotes(existingVotes, users);
|
|
let [newVotes, removedVotes] = diffEntryVotes(existingVotes, users);
|
|
-
|
|
|
|
voteRepo.remove(existingVotes.filter(v => removedVotes.has(v.userId)));
|
|
voteRepo.remove(existingVotes.filter(v => removedVotes.has(v.userId)));
|
|
-
|
|
|
|
let newVoteEntries = [...newVotes].map(i => voteRepo.create({
|
|
let newVoteEntries = [...newVotes].map(i => voteRepo.create({
|
|
userId: i,
|
|
userId: i,
|
|
contest: contest,
|
|
contest: contest,
|
|
contestEntry: entry
|
|
contestEntry: entry
|
|
}));
|
|
}));
|
|
-
|
|
|
|
await voteRepo.save(newVoteEntries);
|
|
await voteRepo.save(newVoteEntries);
|
|
|
|
|
|
entry.votes = [
|
|
entry.votes = [
|
|
...newVoteEntries,
|
|
...newVoteEntries,
|
|
...existingVotes.filter(v => !removedVotes.has(v.userId))
|
|
...existingVotes.filter(v => !removedVotes.has(v.userId))
|
|
];
|
|
];
|
|
-
|
|
|
|
await entryRepo.save(entry);
|
|
await entryRepo.save(entry);
|
|
|
|
|
|
if (!newestEntry || msg.createdAt > newestEntry)
|
|
if (!newestEntry || msg.createdAt > newestEntry)
|
|
newestEntry = msg.createdAt;
|
|
newestEntry = msg.createdAt;
|
|
} catch (err) {
|
|
} catch (err) {
|
|
console.log(`Failed to update entry ${entry.msgId} for contest ${contest.id} because ${err}!`);
|
|
console.log(`Failed to update entry ${entry.msgId} for contest ${contest.id} because ${err}!`);
|
|
- delete entries[entry.msgId];
|
|
|
|
|
|
+
|
|
|
|
+ await voteRepo.delete({ contestEntry: entry });
|
|
|
|
+ await entryRepo.delete({ msgId: entry.msgId });
|
|
|
|
+ contestEntryMessageIds.delete(entry.msgId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
let newEntries = (await channel.fetchMessages({
|
|
let newEntries = (await channel.fetchMessages({
|
|
after: SnowflakeUtil.generate(newestEntry || contest.startDate)
|
|
after: SnowflakeUtil.generate(newestEntry || contest.startDate)
|
|
- })).filter(m => m.attachments.size != 0);
|
|
|
|
|
|
+ })).filter(m => m.attachments.size != 0 && !contestEntryMessageIds.has(m.id));
|
|
|
|
|
|
for (let [_, msg] of newEntries)
|
|
for (let [_, msg] of newEntries)
|
|
await registerEntry(msg, contest);
|
|
await registerEntry(msg, contest);
|
|
|
|
|
|
if (contest.endDate < new Date()) {
|
|
if (contest.endDate < new Date()) {
|
|
- if (contest.announceWinners)
|
|
|
|
- await printResults(contest, channel);
|
|
|
|
- await contestRepo.update(contest.id, { active: false });
|
|
|
|
|
|
+ await stopContest(contest.id);
|
|
} else {
|
|
} else {
|
|
scheduleJob(contest.endDate, stopContest.bind(null, contest.id));
|
|
scheduleJob(contest.endDate, stopContest.bind(null, contest.id));
|
|
- activeContests[contest.id] = {
|
|
|
|
|
|
+ activeContests[channel.id] = {
|
|
id: contest.id,
|
|
id: contest.id,
|
|
voteReaction: contest.voteReaction
|
|
voteReaction: contest.voteReaction
|
|
};
|
|
};
|
|
@@ -138,6 +130,9 @@ async function registerEntry(msg: Message, contest: Contest) {
|
|
|
|
|
|
let voteReaction = msg.reactions.find(r => r.emoji.toString() == contest.voteReaction);
|
|
let voteReaction = msg.reactions.find(r => r.emoji.toString() == contest.voteReaction);
|
|
|
|
|
|
|
|
+ if(!voteReaction)
|
|
|
|
+ return;
|
|
|
|
+
|
|
let votedUsers = await voteReaction.fetchUsers();
|
|
let votedUsers = await voteReaction.fetchUsers();
|
|
|
|
|
|
await voteRepo.save(votedUsers.map(u => voteRepo.create({
|
|
await voteRepo.save(votedUsers.map(u => voteRepo.create({
|
|
@@ -164,7 +159,7 @@ interface ContestCreationOptions {
|
|
const CONTEST_DEFAULTS: ContestCreationOptions = {
|
|
const CONTEST_DEFAULTS: ContestCreationOptions = {
|
|
duration: "1d",
|
|
duration: "1d",
|
|
announce_winners: false,
|
|
announce_winners: false,
|
|
- vote_reaction: "❤️",
|
|
|
|
|
|
+ vote_reaction: "❤",
|
|
max_winners: 1,
|
|
max_winners: 1,
|
|
unique_winners: true
|
|
unique_winners: true
|
|
};
|
|
};
|
|
@@ -263,16 +258,16 @@ async function printResults(contest: Contest, channel: TextChannel) {
|
|
|
|
|
|
let embed = new RichEmbed({
|
|
let embed = new RichEmbed({
|
|
title: "🎆 Contest results 🎆",
|
|
title: "🎆 Contest results 🎆",
|
|
- color: 0xff3b8dc4,
|
|
|
|
|
|
+ color: 0x3b8dc4,
|
|
timestamp: new Date(),
|
|
timestamp: new Date(),
|
|
description: `The contest has ended!\nCollected ${totalVotes} votes.\nHere are the results:`,
|
|
description: `The contest has ended!\nCollected ${totalVotes} votes.\nHere are the results:`,
|
|
fields: winningEntries.map((e, i) => ({
|
|
fields: winningEntries.map((e, i) => ({
|
|
- name: `${numberToOrdered(i + 1)} place`,
|
|
|
|
- value: `${e.message.toString()} ([View entry](${e.message.url}))`
|
|
|
|
|
|
+ name: `${numberToOrdered(i + 1)} place (${e.votes.length} votes)`,
|
|
|
|
+ value: `${e.message.author.toString()} ([View entry](${e.message.url}))`
|
|
}))
|
|
}))
|
|
});
|
|
});
|
|
|
|
|
|
- await channel.sendEmbed(embed);
|
|
|
|
|
|
+ await channel.send(embed);
|
|
}
|
|
}
|
|
|
|
|
|
async function stopContest(contestId: number) {
|
|
async function stopContest(contestId: number) {
|
|
@@ -292,9 +287,7 @@ async function stopContest(contestId: number) {
|
|
if (contest.announceWinners)
|
|
if (contest.announceWinners)
|
|
await printResults(contest, channel);
|
|
await printResults(contest, channel);
|
|
|
|
|
|
- await repo.update(contestId, {
|
|
|
|
- active: false
|
|
|
|
- });
|
|
|
|
|
|
+ await repo.update(contestId, { active: false });
|
|
}
|
|
}
|
|
|
|
|
|
async function createContest(msg: Message, info: ContestCreationOptions) {
|
|
async function createContest(msg: Message, info: ContestCreationOptions) {
|
|
@@ -357,7 +350,7 @@ async function createContest(msg: Message, info: ContestCreationOptions) {
|
|
await msg.channel.send(`${msg.author.toString()} Started contest (ID: ${contest.id})`);
|
|
await msg.channel.send(`${msg.author.toString()} Started contest (ID: ${contest.id})`);
|
|
|
|
|
|
scheduleJob(contest.endDate, stopContest.bind(null, contest.id));
|
|
scheduleJob(contest.endDate, stopContest.bind(null, contest.id));
|
|
- activeContests[contest.id] = {
|
|
|
|
|
|
+ activeContests[contest.channel] = {
|
|
id: contest.id,
|
|
id: contest.id,
|
|
voteReaction: contest.voteReaction
|
|
voteReaction: contest.voteReaction
|
|
};
|
|
};
|
|
@@ -389,13 +382,12 @@ async function onMessage(actionsDone: boolean, m: Message, content: string) {
|
|
}
|
|
}
|
|
|
|
|
|
async function onReact(reaction: MessageReaction, user: User) {
|
|
async function onReact(reaction: MessageReaction, user: User) {
|
|
- let channel = reaction.message.channel;
|
|
|
|
-
|
|
|
|
- let activeContest = activeContests[channel.id];
|
|
|
|
- if (!activeContest)
|
|
|
|
|
|
+ if(user.bot)
|
|
return;
|
|
return;
|
|
|
|
|
|
- if (reaction.emoji.toString() != activeContest.voteReaction)
|
|
|
|
|
|
+ let channel = reaction.message.channel;
|
|
|
|
+ let activeContest = activeContests[channel.id];
|
|
|
|
+ if (!activeContest || reaction.emoji.toString() != activeContest.voteReaction)
|
|
return;
|
|
return;
|
|
|
|
|
|
let entryRepo = getRepository(ContestEntry);
|
|
let entryRepo = getRepository(ContestEntry);
|
|
@@ -404,26 +396,14 @@ async function onReact(reaction: MessageReaction, user: User) {
|
|
let entry = await entryRepo.findOne({
|
|
let entry = await entryRepo.findOne({
|
|
where: { msgId: reaction.message.id }
|
|
where: { msgId: reaction.message.id }
|
|
});
|
|
});
|
|
-
|
|
|
|
if (!entry)
|
|
if (!entry)
|
|
return;
|
|
return;
|
|
|
|
|
|
let vote = await voteRepo.findOne({
|
|
let vote = await voteRepo.findOne({
|
|
- where: {
|
|
|
|
- userId: user.id,
|
|
|
|
- contest: {
|
|
|
|
- id: activeContest.id
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ where: { userId: user.id, contestId: activeContest.id }
|
|
});
|
|
});
|
|
-
|
|
|
|
if (!vote)
|
|
if (!vote)
|
|
- vote = voteRepo.create({
|
|
|
|
- userId: user.id,
|
|
|
|
- contest: {
|
|
|
|
- id: activeContest.id
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ vote = voteRepo.create({ userId: user.id, contestId: activeContest.id });
|
|
|
|
|
|
vote.contestEntry = entry;
|
|
vote.contestEntry = entry;
|
|
await voteRepo.save(vote);
|
|
await voteRepo.save(vote);
|
|
@@ -447,7 +427,18 @@ export default <ICommand>{
|
|
{
|
|
{
|
|
pattern: "contests",
|
|
pattern: "contests",
|
|
action: async (m) => {
|
|
action: async (m) => {
|
|
- await m.channel.send("Heck");
|
|
|
|
|
|
+ let repo = getRepository(Contest);
|
|
|
|
+ let contests = await repo.find({ where: { active: true } });
|
|
|
|
+
|
|
|
|
+ let contestsData = contests.map(c => ({
|
|
|
|
+ contest: c,
|
|
|
|
+ channel: client.channels.get(c.channel) as TextChannel
|
|
|
|
+ })).filter(c => c.channel);
|
|
|
|
+
|
|
|
|
+ if(contestsData.length == 0)
|
|
|
|
+ await m.channel.send(`${m.author.toString()} There are no currently running contests!`);
|
|
|
|
+ else
|
|
|
|
+ await m.channel.send(`${m.author.toString()} Currently there are contests active in the following channels:\n${contestsData.map((c, i) => `${i + 1}. ${c.channel.toString()}`).join("\n")}`);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
{
|
|
{
|
|
@@ -481,8 +472,25 @@ export default <ICommand>{
|
|
},
|
|
},
|
|
{
|
|
{
|
|
pattern: "announce winners",
|
|
pattern: "announce winners",
|
|
- action: async (m, contents, matches) => {
|
|
|
|
- await m.channel.send("Heck");
|
|
|
|
|
|
+ action: async (m, contents) => {
|
|
|
|
+ let repo = getRepository(Contest);
|
|
|
|
+
|
|
|
|
+ let contest = await repo.findOne({
|
|
|
|
+ where: { channel: m.channel.id },
|
|
|
|
+ order: { endDate: "DESC" }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ if(!contest) {
|
|
|
|
+ await m.channel.send(`${m.author.toString()} There have never been any contests!`);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(contest.active) {
|
|
|
|
+ await stopContest(contest.id);
|
|
|
|
+ if(contest.announceWinners)
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ await printResults(contest, m.channel as TextChannel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
],
|
|
],
|