Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions commands/poll/htpoll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
const { Collection } = require('discord.js');
const Command = require('../../structures/Command.js');

const allOptions = Object.values(require('../../assets/json/emoji-alphabet.json'));

const logger = require('pino')({
useLevelLabels: true,
timestamp: () => `,"time":"${new Date()}"`,
});

function removeReaction(sentMsg, user) {
sentMsg.channel.fetchMessage(sentMsg.id).then((message) => {
message.reactions.forEach((r) => r.remove(user.id));
});
}

module.exports = class TimedPollCommand extends Command {
constructor(client) {
super(client, {
name: 'htpoll',
group: 'poll',
memberName: 'htpoll',
aliases: ['hiddentimedpoll'],
description: 'Create a hidden timed poll.',
examples: ['`tpoll 60 \'Which option you choose?\' \'option one\' \'option 2\' \'option N\'`'],
throttling: {
usages: 1,
duration: 30,
},
argsSingleQuotes: true,
argsPromptLimit: 0,
args: [
{
key: 'seconds',
type: 'integer',
prompt: '',
min: 0,
max: 604800, // 604,800 seconds = 1 week
},
{
key: 'question',
type: 'string',
prompt: '',
validate: (question) => {
if (question.length > 0 && question.length < 200) return true;
return 'Invalid question';
},
},
{
key: 'opts',
prompt: '',
type: 'string',
infinite: true,
default: '~NO~OPTS~',
},
],
});
}

async run(msg, {
seconds, question, opts,
}) {
if (opts.length < 2 || opts.length > 10) return msg.reply('The number of options must be greater than 2 and less than 10');

let options = '';
let i;
const pollMsg = [];
const milliseconds = seconds <= 0 ? 0 : seconds * 1000;
const voters = [];
const pollResults = new Collection();

const reactions = allOptions.slice(0, opts.length);

for (i = 0; i < opts.length; i += 1) {
options += `\n${reactions[i]} ${opts[i]}`;

// let's check if there's a repetition in the options
for (let j = i + 1; j < opts.length; j += 1) if (opts[i] === opts[j]) return msg.reply(`**\`poll\` error**: repeated options found: \`${opts[i]}\``);
}

pollMsg.push(`__*${msg.author} started a hidden poll*__:`);
pollMsg.push(`\n:bar_chart: **${question}**\n${options}`);

if (milliseconds) pollMsg.push('\n`Notes:\n- only the first reaction is considered a vote\n- unlisted reactions void the vote`');

const sentMsg = await msg.channel.send(pollMsg);

if (milliseconds) {
const endTime = sentMsg.createdAt;
endTime.setTime(endTime.getTime() + milliseconds);

pollMsg.push(`:stopwatch: *This poll ends at ${endTime}*`);
sentMsg.edit(pollMsg);
}

for (i = 0; i < opts.length; i += 1) await sentMsg.react(reactions[i]);

if (!milliseconds) return;

const filter = async (reaction, user) => {
// ignore bot's reactions
if (this.client.user.id === user.id) {
return false;
}

// do not allow repeated votes
if (voters.indexOf(user.id) < 0) {
voters.push(user.id);
} else {
removeReaction(sentMsg, user);
return false;
}

// do not count invalid reactions
if (!reactions.includes(reaction.emoji.name)) {
return false;
}

// if the logic reaches here, then the reaction is accepted as a vote
let numVotes = pollResults.get(reaction.emoji.name);
numVotes = !numVotes ? 1 : numVotes + 1;

pollResults.set(reaction.emoji.name, numVotes);

removeReaction(sentMsg, user);

return true;
};

sentMsg.awaitReactions(filter, { time: milliseconds })
.then((collected) => {
const pollEndedMsg = [];

pollMsg[0] = `~~${pollMsg[0]}~~\n:no_entry: **THIS POLL IS ALREADY CLOSED** :no_entry:`;
pollMsg.pop(); // removing the message saying when the poll ends
pollMsg.pop(); // removing the note about how to vote
pollMsg.push('\n`This poll is closed.`');
pollMsg.push('__**RESULTS:**__\n');

if (collected.size === 0) {
pollMsg.push('No one voted');
} else {
pollResults.sort((v1, v2) => v2 - v1);
pollResults.forEach((value, key) => pollMsg.push(`${key}: ${value}`));
}

sentMsg.edit(pollMsg);

pollEndedMsg.push('**Your poll has ended.**\n**Click this link to see the results:**');
pollEndedMsg.push(`<${sentMsg.url}>`);
msg.reply(pollEndedMsg);
})
.catch((error) => {
logger.error(error);
msg.reply('**`poll` error**: Something went wrong with your poll.');
});
}
};
15 changes: 10 additions & 5 deletions commands/poll/poll.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
const { Collection } = require('discord.js');
const Command = require('../../structures/Command.js');

const allOptions = Object.values(require('../../assets/json/emoji-alphabet.json'));

module.exports = class PollCommand extends Command {
constructor(client) {
super(client, {
name: 'poll',
group: 'poll',
memberName: 'poll',
description: 'Create a (useless) poll.',
description: 'Create a poll.',
examples: ['`poll \'Which option you choose?\' \'option one\' \'option 2\' \'option N\'`'],
argsSingleQuotes: true,
argsPromptLimit: 0,
args: [
{
key: 'hidden',
type: 'string',
prompt: '',
default: 'no',
},
{
key: 'question',
type: 'string',
Expand All @@ -35,6 +38,8 @@ module.exports = class PollCommand extends Command {
}

async run(msg, { question, opts }) {
return this.client.registry.resolveCommand('poll:tpoll').run(msg, { seconds: 0, question, opts });
return this.client.registry.resolveCommand('poll:tpoll').run(msg, {
seconds: 0, question, opts,
});
}
};
28 changes: 14 additions & 14 deletions commands/poll/tpoll.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const { Collection } = require('discord.js');
const Command = require('../../structures/Command.js');

const logger = require('pino')({
useLevelLabels: true,
timestamp: () => `,"time":"${new Date()}"`,
});
const allOptions = Object.values(require('../../assets/json/emoji-alphabet.json'));

module.exports = class TimedPollCommand extends Command {
Expand Down Expand Up @@ -45,7 +48,9 @@ module.exports = class TimedPollCommand extends Command {
});
}

async run(msg, { seconds, question, opts }) {
async run(msg, {
seconds, question, opts,
}) {
if (opts.length < 2 || opts.length > 10) return msg.reply('The number of options must be greater than 2 and less than 10');

let options = '';
Expand All @@ -54,13 +59,14 @@ module.exports = class TimedPollCommand extends Command {
const milliseconds = seconds <= 0 ? 0 : seconds * 1000;
const voters = [];
const pollResults = new Collection();

const reactions = allOptions.slice(0, opts.length);

for (i = 0; i < opts.length; i++) {
for (i = 0; i < opts.length; i += 1) {
options += `\n${reactions[i]} ${opts[i]}`;

// let's check if there's a repetition in the options
for (let j = i + 1; j < opts.length; j++) if (opts[i] === opts[j]) return msg.reply(`**\`poll\` error**: repeated options found: \`${opts[i]}\``);
for (let j = i + 1; j < opts.length; j += 1) if (opts[i] === opts[j]) return msg.reply(`**\`poll\` error**: repeated options found: \`${opts[i]}\``);
}

pollMsg.push(`__*${msg.author} started a poll*__:`);
Expand All @@ -78,11 +84,11 @@ module.exports = class TimedPollCommand extends Command {
sentMsg.edit(pollMsg);
}

for (i = 0; i < opts.length; i++) await sentMsg.react(reactions[i]);
for (i = 0; i < opts.length; i += 1) await sentMsg.react(reactions[i]);

if (!milliseconds) return;

const filter = (reaction, user) => {
const filter = async (reaction, user) => {
// ignore bot's reactions
if (this.client.user.id === user.id) {
return false;
Expand All @@ -92,15 +98,11 @@ module.exports = class TimedPollCommand extends Command {
if (voters.indexOf(user.id) < 0) {
voters.push(user.id);
} else {
// msg.channel.send(`ignoring ${reaction.emoji.name} from \`${user.username}\`: user already voted`)
// .then(m => m.delete(5000));
return false;
}

// do not count invalid reactions
if (!reactions.includes(reaction.emoji.name)) {
// msg.channel.send(`\`${user.username}\` voided the vote with an invalid reaction: ${reaction.emoji.name}`)
// .then(m => m.delete(5000));
return false;
}

Expand All @@ -110,8 +112,6 @@ module.exports = class TimedPollCommand extends Command {

pollResults.set(reaction.emoji.name, numVotes);

// msg.channel.send(`\`${user.username}\`'s vote: ${reaction.emoji.name}`)
// .then(m => m.delete(5000));
return true;
};

Expand All @@ -138,9 +138,9 @@ module.exports = class TimedPollCommand extends Command {
pollEndedMsg.push(`<${sentMsg.url}>`);
msg.reply(pollEndedMsg);
})
.catch((collected) => {
.catch((error) => {
logger.error(error);
msg.reply('**`poll` error**: Something went wrong with your poll.');
console.error(collected);
});
}
};
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ client.registry
})
.registerCommandsIn(path.join(__dirname, 'commands'));

client.once('ready', () => {
client.once('ready', async () => {
client.user.setUsername(BOT_NAME || 'RABot');
logger.info(`[READY] Logged in as ${client.user.tag}! (${client.user.id})`);
client.user.setActivity('if you need help', { type: 'WATCHING' });
getGameList();
await getGameList();
});

client.on('guildMemberAdd', async (member) => {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "retroachievements-discord-bot",
"version": "1.1.0",
"description": "https://github.com/hydrabolt/discord.js/",
"version": "1.1.1",
"description": "Retroachievements.org Discord Bot - www.retroachievements.org",
"homepage": "http://retroachievements.org",
"main": "index.js",
"dependencies": {
"cheerio": "^1.0.0-rc.3",
Expand Down Expand Up @@ -39,6 +40,20 @@
"lint": "eslint --ignore-path .gitignore .",
"lintfix": "eslint --ignore-path .gitignore . --fix"
},
"repository": {
"type": "git",
"url": "https://github.com/RetroAchievements/RABot"
},
"author": "luchaos",
"contributors": [
{
"name": "zapo",
"url": "http://victorz.me"
},
{
"name": "meleu",
"url": "http://meleu.sh/"
}
],
"license": "ISC"
}
Loading