-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpollbot.py
209 lines (158 loc) · 7.28 KB
/
pollbot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import discord
from discord.ext import commands
from discord.ext import tasks
import datetime as dt
DISCORD_TOKEN = "MTA5ODkwMjUzOTIxNjM1NTQ4MA.Gphg9G.A3froWtAcK6gxtXbsDoJPtKuEgTbt_YslZJJ-g"
# Define required intents
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
# Create client object
client = discord.Client(intents=intents)
# slash = SlashCommand(client, sync_commands=True) # Declares slash commands through the client.
POLL_OPTION_EMOJIS = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣"]
SENT_MESSAGE_IDS = []
bot = commands.Bot(command_prefix="!", intents=intents)
@bot.event
async def on_ready():
print(f'Logged in !')
@bot.command(name="ping", description="Check bot's ping")
async def ping(ctx):
await ctx.send(f'Pong! Latency: {round(bot.latency * 1000)} ms')
# bot = interactions.Client(token="your_secret_bot_token")
# @bot.command(
# name="my_first_command",
# description="This is the first command I made!",
# )
# async def my_first_command(ctx: interactions.CommandContext):
# await ctx.send("Hi there!")
@bot.event
async def on_message(message):
global POLL_OPTION_EMOJIS
if message.content.startswith("!create_poll"):
# Extract the parameters from the command
params = message.content.split(";")
name = params[0].replace("!create_poll","").strip()
question = params[1].strip()
options = [x.strip() for x in params[2].strip().split(",")]
orig_options = options
options_count = len(options)
countdown = params[3]
try:
countdown = int(countdown)
except Exception as e:
pass
# validate parameters to check if there are any errors
error = validate_params(name, question, options, countdown)
if error is not None:
# If parameters are not in the expected format, send error message
embed = discord.Embed(title="Error", description=error, color=discord.Color.red())
sent = await message.channel.send(embed=embed)
return
# If there is no error, send the poll message
for i in range(len(options)):
options[i] = f"{POLL_OPTION_EMOJIS[i]} {options[i]}"
options = '\n'.join(options)
embed = discord.Embed(title=f"POLL: {name}", description=f"**{question}\n{options}**", color=0x12ff51)
sent = await message.channel.send(embed=embed)
POLL_OPTION_EMOJIS = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣"]
for i in range(options_count):
# React with the allowed set of emojis
await sent.add_reaction(POLL_OPTION_EMOJIS[i])
# Add sent message id to a global list
SENT_MESSAGE_IDS.append(sent.id)
end_time = dt.datetime.utcnow() + dt.timedelta(seconds=int(countdown)*60)
# define the background task to update the countdown message
@tasks.loop(seconds=1)
async def update_countdown():
# Calculate remaining time in countdown
remaining_time = (end_time - dt.datetime.utcnow()).total_seconds()
if remaining_time > 0:
# If countdown still didn't expire
minutes, seconds = divmod(int(remaining_time), 60)
# Edit the message
description = f"**{question}**\n{options}\n\n*Poll ends in {minutes:02d}:{seconds:02d}*"
embed = discord.Embed(title=f"POLL: {name}", description=description, color=0x12ff51)
await sent.edit(embed=embed)
else:
sent_message = await message.channel.fetch_message(sent.id)
poll_results_count = {}
total_reactions = 0
# If countdown expired
for reaction in sent_message.reactions:
# Enumerate message reactions
for ind, emoji in enumerate(POLL_OPTION_EMOJIS):
# Count number of times an emoji is reacted
if reaction.emoji == emoji:
poll_results_count[ind+1] = reaction.count - 1
if reaction.count>1:
# ALso calculate the total reactions
total_reactions+=1
# Craft the results message
poll_results_message = ""
for ind, count in enumerate(poll_results_count):
# Calculate percentage value of each option
perc = round(poll_results_count[ind+1]/total_reactions * 100)
poll_results_message+=f"{orig_options[ind]} ~ {perc}% ({poll_results_count[ind+1]} votes)\n"
# Send the results message
embed = discord.Embed(title=f"POLL RESULTS: {name}", description=poll_results_message, color=0x13a6f0)
await message.channel.send(embed=embed)
# Delete the original poll message and end tasks.loop function
await sent_message.delete()
update_countdown.cancel()
update_countdown.start()
#@slash.slash(name="Ping", description="Ping command")
#async def ping(ctx): # Defines a new "context" (ctx) command called "ping."
# await ctx.send(f"Pong! ({client.latency*1000}ms)")
@bot.event
async def on_raw_reaction_add(payload):
global SENT_MESSAGE_IDS
# Get the message object
channel = await client.fetch_channel(payload.channel_id)
message = await channel.fetch_message(payload.message_id) # type: ignore
# Get the member object
guild = message.guild
member = await guild.fetch_member(payload.user_id) # type: ignore
if payload.member.bot:
return
sent_by_bot = False
for i in SENT_MESSAGE_IDS:
# Compare message ids
if i==message.id:
sent_by_bot = True
break
if not sent_by_bot:
# IF not sent by bot, ignore
return
# Check if reaction made is allowed
if payload.emoji.name not in POLL_OPTION_EMOJIS:
# Remove reaction
await message.remove_reaction(payload.emoji.name, member)
return
# Remove duplicate votes of the user
user_reaction_count = 0
for r in message.reactions:
async for u in r.users():
if u.id == payload.user_id:
user_reaction_count+=1
if user_reaction_count>1:
await message.remove_reaction(payload.emoji.name, member)
break
def validate_params(name, question, options, countdown):
if name=="":
return "Poll name shouldn't be empty"
if len(name)>=20:
return "Name shouldn't be more than 15 characters"
if question=="":
return "Question shouldn't be empty"
if len(options)<=1:
return "There must be a minimum of 2 options"
if len(options)>5:
return "Maximum options allowed are 5"
if not isinstance(countdown, int):
return "Countdown value must be integer"
return None
if __name__ == "__main__":
# Start the bot
bot.run(DISCORD_TOKEN)
client.run(DISCORD_TOKEN)