-
Notifications
You must be signed in to change notification settings - Fork 0
/
decorators.py
222 lines (192 loc) · 7.49 KB
/
decorators.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
210
211
212
213
214
215
216
217
218
219
220
221
222
import time
import discord
from discord.ext import commands
from functools import wraps
from textwrap import dedent
from sql import database
from sql import crud, schemas
from cogs.utils._logger import timer_logger
TIMEOUT_IN_SECONDS = 60
class ToSAgreementView(discord.ui.View):
def __init__(self, ctx: commands.Context):
super().__init__(timeout=TIMEOUT_IN_SECONDS)
self.value = None
self.ctx: commands.Context = ctx
self.message: discord.Message = None
async def interaction_check(
self, interaction: discord.Interaction[discord.Client]
) -> bool:
if interaction.user.id != self.ctx.author.id:
await interaction.response.send_message(
"You can't interact with this message.", ephemeral=True
)
return False
return True
async def on_timeout(self) -> None:
await self.message.delete()
await self.ctx.reply(
"You took too long to respond. Please run the command again.",
silent=True,
)
self.value = False
self.stop()
@discord.ui.button(label="Yes", style=discord.ButtonStyle.green)
async def confirm(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.defer()
await self.message.delete()
self.value = True
self.stop()
@discord.ui.button(label="No", style=discord.ButtonStyle.red)
async def declined(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.defer()
await self.message.delete()
self.value = False
self.stop()
class ToSChangeView(discord.ui.View):
def __init__(self, ctx: commands.Context):
super().__init__(timeout=TIMEOUT_IN_SECONDS)
self.value = 0
self.ctx: commands.Context = ctx
self.message: discord.Message = None
async def interaction_check(
self, interaction: discord.Interaction[discord.Client]
) -> bool:
if interaction.user.id != self.ctx.author.id:
await interaction.response.send_message(
"You can't interact with this message.", ephemeral=True
)
return False
return True
async def on_timeout(self) -> None:
await self.message.delete()
await self.ctx.reply(
"You took too long to respond. Please run the command again.",
silent=True,
)
self.value = False
self.stop()
@discord.ui.button(label="Yes", style=discord.ButtonStyle.grey)
async def confirm(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.defer()
await self.message.delete()
self.value = 1
self.stop()
@discord.ui.button(label="Yes, and delete my data", style=discord.ButtonStyle.red)
async def delete_data(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.defer()
await self.message.delete()
self.value = 2
self.stop()
@discord.ui.button(label="No", style=discord.ButtonStyle.green)
async def declined(
self, interaction: discord.Interaction, button: discord.ui.Button
):
await interaction.response.defer()
await self.message.delete()
self.value = 0
self.stop()
def requires_tos_acceptance(func):
@wraps(func)
async def wrapper(*args, **kwargs):
ctx: commands.Context = args[1]
user = await crud.get_user(
database.AsyncSessionLocal, schemas.User.Get(user_id=ctx.author.id)
)
if user is None or not user.agreed_to_tos:
invoked_with = (
f"`?{ctx.invoked_with}`"
if ctx.command.name != "tos"
else "additional commands"
)
view = ToSAgreementView(ctx)
view.message = await ctx.send(
dedent(
f"""
{'This command' if ctx.command.name != 'tos' else 'Some commands'} needs to store the following basic data to function:
- Your Discord username and user ID
Furthermore, the following commands store additional data when used:
- `?chat`|`?sharedchat`: Stores the message you send to the bot, the bot's response, and whether the chat is shared with other users.
I do not store normal chat messages. Abuse of any commands will result in a ban from using the bot.
By clicking "Yes", you agree to the above terms and can use {invoked_with}. By clicking "No", you will not be able to use {invoked_with}. You can change your decision at any time by running the `?tos` command or a command that requires ToS acceptance.
"""
),
view=view,
)
await view.wait()
if view.value is True:
if user is None:
await crud.add_user(
database.AsyncSessionLocal,
schemas.User.Add(
user_id=ctx.author.id,
username=ctx.author.name,
agreed_to_tos=True,
),
)
else:
await crud.edit_user_tos(
database.AsyncSessionLocal,
schemas.User.SetTos(user_id=ctx.author.id, agreed_to_tos=True),
)
return await func(*args, **kwargs)
else:
return
elif user.agreed_to_tos and ctx.command.name == "tos":
view = ToSChangeView(ctx)
view.message = await ctx.send(
dedent(
"""
You have already agreed to the Terms of Service. Would you like to change your decision and opt out?
"""
),
view=view,
)
await view.wait()
if view.value == 1:
await crud.edit_user_tos(
database.AsyncSessionLocal,
schemas.User.SetTos(user_id=ctx.author.id, agreed_to_tos=False),
)
await ctx.reply(
"You have opted out of the Terms of Service.", silent=True
)
elif view.value == 2:
await crud.delete_all_user_data(
database.AsyncSessionLocal,
schemas.User.Delete(user_id=ctx.author.id),
)
await ctx.reply(
"You have opted out of the Terms of Service and your data has been deleted.",
silent=True,
)
else:
return
else:
return await func(*args, **kwargs)
return wrapper
def timeit(func):
"""
A decorator that measures the execution time of a function.
Args:
func: The function to be decorated.
Returns:
The decorated function.
"""
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
end = time.perf_counter()
elapsed_time = (end - start) * 1_000_000 # Convert to microseconds
timer_logger.debug(
f"CRUD function {func.__name__} took {round(elapsed_time, 5)} microseconds to execute."
)
return result
return wrapper