-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathinteractions_discord.py
More file actions
208 lines (155 loc) · 8.14 KB
/
interactions_discord.py
File metadata and controls
208 lines (155 loc) · 8.14 KB
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
import discord
import asyncio
import pe_discord_api
import pe_global_objects as pe_global
import pe_api
import math
from discord.ext.pages import Page, PaginatorButton, Paginator
INTER_ROLES_SLEEP = 0.7
LANGUAGES_ROLES = {
"Assembly": [979775922683129887, "⚒️", None],
"C": [1034588396129095690, "🇨", None],
"C#": [979775187786563584, "🎵", None],
"C++": [979775061877747733, "➕", None],
"Elixir": [1355624521641496816, "💧", None],
"Go": [979776131303616555, "🏁", None],
"Haskell": [979775640876236822, "🍛", None],
"Java": [979775476606304286, "☕", None],
"Julia": [1022102165176729660, "🫐", None],
"Kotlin": [1082412233608409148, "🍵", None],
"Lua": [979776205467316275, "🌕", None],
"Mathematica": [979775980841357342, "🔢", None],
"Matlab": [979776087703822366, "🧪", None],
"Nim": [979775789694341181, "🎲", None],
"OCaml": [1034557184790503424, "🐫", None],
"Python": [979775734233055293, "🐍", None],
"Ruby": [979775693833531462, "♦️", None],
"Rust": [979775594814373901, "⚙️", None],
"Sage": [979776172462325810, "🌿", None],
"Scala": [979776243950030898, "🧲", None],
"Spreadsheets": [979776609378766848, "📃", None]
}
for lang_name in LANGUAGES_ROLES.keys():
if LANGUAGES_ROLES[lang_name][2] is None:
LANGUAGES_ROLES[lang_name][2] = "You like " + lang_name
class Dropdown(discord.ui.Select):
def __init__(self, bot_: discord.Bot, author_: discord.User):
# For example, you can use self.bot to retrieve a user or perform other functions in the callback.
# Alternatively you can use Interaction.client, so you don't need to pass the bot instance.
self.bot = bot_
self.author = author_
self.author_roles = [y.id for y in self.author.roles]
self.bool_roles = {lang_name: LANGUAGES_ROLES[lang_name][0] in self.author_roles for lang_name in LANGUAGES_ROLES.keys()}
# Set the options that will be presented inside the dropdown:
options = [
discord.SelectOption(
label=lang_name,
description=LANGUAGES_ROLES[lang_name][2],
emoji=LANGUAGES_ROLES[lang_name][1],
default=self.bool_roles[lang_name]
) for lang_name in sorted(LANGUAGES_ROLES.keys())
]
# The placeholder is what will be shown when no option is selected.
# The min and max values indicate we can only pick one of the three options.
# The options parameter, contents shown above, define the dropdown options.
super().__init__(
placeholder="Choose your favorite languages:",
min_values=0,
max_values=min(len(LANGUAGES_ROLES.keys()), 25),
options=options,
)
async def callback(self, interaction: discord.Interaction):
# Use the interaction object to send a response message containing
# the user's favourite colour or choice. The self object refers to the
# Select object, and the values attribute gets a list of the user's
# selected options. We only want the first one.
await interaction.response.defer()
new_roles = {lang_name: lang_name in self.values for lang_name in LANGUAGES_ROLES}
for lang_name in sorted(LANGUAGES_ROLES.keys()):
if new_roles[lang_name] == self.bool_roles[lang_name]:
continue
role = discord.utils.get(self.author.guild.roles, id=LANGUAGES_ROLES[lang_name][0])
if new_roles[lang_name] == True and self.bool_roles[lang_name] == False:
await self.author.add_roles(role)
await asyncio.sleep(INTER_ROLES_SLEEP)
if new_roles[lang_name] == False and self.bool_roles[lang_name] == True:
await self.author.remove_roles(role)
await asyncio.sleep(INTER_ROLES_SLEEP)
response = ", ".join(sorted(self.values))
print(f"User {self.author.name} updated roles to {response}")
await interaction.followup.send(
f"Roles updated to {response}",
ephemeral=True
)
# Defines a simple View that allows the user to use the Select menu.
class DropdownView(discord.ui.View):
def __init__(self, bot_: discord.Bot, author: discord.User):
self.bot = bot_
super().__init__()
# Adds the dropdown to our View object
self.add_item(Dropdown(self.bot, author))
# Initializing the view and adding the dropdown can actually be done in a one-liner if preferred:
# super().__init__(Dropdown(self.bot))
def problem_thread_view(problem_number: int):
# Create the button object
button = discord.ui.Button(label="Join thread for #{0} !".format(problem_number), style=discord.ButtonStyle.primary)
# This is the function that will be called back when someone clicks a button
async def button_callback(interaction: discord.Interaction):
await interaction.response.defer()
allowed_members = pe_api.get_all_discord_profiles_who_solved(problem=problem_number)
allowed_discord_ids = list(map(lambda element: int(element[1]), allowed_members))
# If the user did not solve, send an "ephemeral" message that only them will be able to sees
if int(interaction.user.id) not in allowed_discord_ids:
return await interaction.followup.send("Sorry, you did not solve [problem #{0}](<https://projecteuler.net/problem={0}>). If you did solve it, please link your account first.".format(problem_number), ephemeral=True)
# Otherwise, iterate through available threads, and when the name matches, add the user to the list of participants
available_threads = await pe_discord_api.get_available_threads(interaction.guild.id, interaction.channel.id)
for th in available_threads:
if th.name == pe_global.THREAD_DEFAULT_NAME_FORMAT.format(problem_number):
if th.archived:
await th.unarchive()
await th.add_user(pe_global.bot.get_user(interaction.user.id))
break
# Add the method to the button object
button.callback = button_callback
# Timeout none should make the interaction never expire
view = discord.ui.View(timeout=None)
view.add_item(button)
return view
async def leaderboard_page(ctx, leaderboard_data: list, with_emojis: bool, descending: bool, count_per_page: int):
"""
Leaderboard data must be of the kind `[(username1, score1), ...]`
"""
leaderboard_data = list(sorted(leaderboard_data, key=lambda x: x[1], reverse=descending))
pages = (len(leaderboard_data) + (count_per_page - 1)) // count_per_page
my_pages = []
for page in range(1, pages + 1):
embed = discord.Embed(
title=f"Page #{page} of the leaderboard",
color=discord.Colour.blurple()
)
bottom = count_per_page * (page - 1) + 1
top = count_per_page * page
field_value = ""
for index in range(bottom, top + 1):
if index > len(leaderboard_data):
break
ex_aequo_index = index
while ex_aequo_index >= 2 and leaderboard_data[ex_aequo_index - 2][1] == leaderboard_data[index - 1][1]:
ex_aequo_index -= 1
prefix = f"{ex_aequo_index})"
if ex_aequo_index <= 3 and with_emojis:
prefix = ["🥇", "🥈", "🥉"][ex_aequo_index - 1]
username, used_score = leaderboard_data[index - 1]
field_value += f"{prefix} {username} ― (*{used_score}*)\n"
embed.add_field(
name="",
value=field_value
)
my_pages.append(Page(content="", embeds=[embed]) )
buttons = [
PaginatorButton("prev", label="←", style=discord.ButtonStyle.green),
PaginatorButton("page_indicator", style=discord.ButtonStyle.gray, disabled=True),
PaginatorButton("next", label="→", style=discord.ButtonStyle.green)
]
paginator = Paginator(pages=my_pages, use_default_buttons=False, custom_buttons=buttons)
await paginator.respond(interaction=ctx.interaction)