-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconfig.py
386 lines (290 loc) · 14 KB
/
config.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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
"""lg-rez / Variables globales
Personnalisation de différents paramètres et accès global
"""
import json
import pkgutil
import typing
import discord
import readycheck
import sqlalchemy
import sqlalchemy.orm
from lgrez.blocs import structure
if typing.TYPE_CHECKING:
from lgrez.bot import LGBot
#: dict[str, Any]: Structure du serveur utilisée par /setup (serveur,
#: rôles, salons, emojis). Voir le fichier ```server_structure.json``
#: (valeur par défaut) pour les possibilités de personnalisation.
server_structure = json.loads(pkgutil.get_data("lgrez", "server_structure.json"))
#: Préfixe des noms des salons de conversation bot.
private_chan_prefix: str = "conv-bot-"
#: Nom de la catégorie des conversations bot, pour l'inscription
#: (sera éventuellement suivi de 2, 3... si plus de 50 joueurs).
#: Devrait correspondre à au nom de la catégorie correspondante dans
#: :attr:`server_structure` ``["categories"]``
private_chan_category_name = None # Deduced from server_structure
#: Nom de la catégorie des boudoirs
#: (sera éventuellement suivi de 2, 3... si plus de 50 boudoirs).
#: Devrait correspondre à au nom de la catégorie correspondante dans
#: :attr:`server_structure` ``["categories"]``
boudoirs_category_name = None # Deduced from server_structure
#: Nom de la catégorie des boudoirs devenus inutiles
#: (sera éventuellement suivi de 2, 3... si plus de 50 boudoirs).
#: Devrait correspondre à au nom de la catégorie correspondante dans
#: :attr:`server_structure` ``["categories"]``
old_boudoirs_category_name = None # Deduced from server_structure
#: Date de début de saison (pour information lors de l'inscription).
debut_saison: str = "32 plopembre"
#: Si ``False``, le processus d'inscription ne demandera pas la
#: chambre des joueurs, qui seront tous inscrits en :attr:`chambre_mj`
#: (et la chambre ne sera pas indiquée dans ``/vivants``).
demande_chambre: bool = True
#: Nom par défaut de la :attr:`~.bdd.Joueur.chambre` des joueurs.
chambre_mj: str = "[chambre MJ]"
async def additional_inscription_step(
journey, # blocs.journey.DiscordJourney
member: discord.Member,
chan: discord.TextChannel,
) -> bool | None:
"""Coroutine permettant d'ajouter des étapes au processus d'inscription.
Cette coroutine est appelée par :func:`.features.inscription.main`
juste avant l'inscription en base. Si elle renvoie `False`,
l'inscription est annulée ; si elle ne renvoie rien ou une autre
valeur, elle continue selon le processus habituel.
Args:
member: Membre en cours d'inscription.
chan: Chan perso créé pour l'inscription.
Returns:
Si ``False``, annule l'inscription.
"""
pass
#: Si ``True``, le bot appellera :meth:`.LGBot.i_am_alive` toutes
#: les 60 secondes. Ce n'est pas activé par défaut.
output_liveness: bool = False
#: :attr:`~.bdd.Role.slug` du rôle par défaut, attribué aux
#: joueurs lors de l'inscription (renvoyé par :meth:`.bdd.Role.default`).
#: Doit correspondre à un rôle existant (défini dans le GSheet *Rôles et
#: actions*).
default_role_slug: str = "nonattr"
#: :attr:`~.bdd.Camp.slug` du camp par défaut, attribué aux
#: joueurs lors de l'inscription (renvoyé par :meth:`.bdd.Camp.default`).
#: Doit correspondre à un camp existant (défini dans le GSheet *Rôles et
#: actions*).
default_camp_slug: str = "nonattr"
#: Nom de la feuille du *Tableau de bord* contenant l'état actuel
#: des joueurs, sur laquelle sont effectuées les modifications.
tdb_main_sheet: str = "Journée en cours"
#: Nom de la feuille du *Tableau de bord* contenant les résultats
#: des votes (après corrections manuelles éventuelles).
tdb_votes_sheet: str = "Journée en cours"
#: Numéro de la ligne de la feuille principale
#: (:attr:`~lgrez.config.tdb_main_sheet`)
#: du *Tableau de bord* contenant les noms des colonnes (commençant de 1).
tdb_header_row: int = 3
#: Nom de la colonne de la feuille principale
#: (:attr:`~lgrez.config.tdb_main_sheet`)
#: du *Tableau de bord* contenant les IDs Discord des joueurs.
tdb_id_column: str = "A"
#: Noms de la première et de la dernière colonne de la zone de
#: la feuille principale (:attr:`~lgrez.config.tdb_main_sheet`) du *Tableau
#: de bord* contenant les informations (colonnes de la BDD) des joueurs.
tdb_main_columns: tuple[str, str] = ("J", "Q")
#: Noms de la première et de la dernière colonne de la zone de
#: la feuille principale (:attr:`~lgrez.config.tdb_main_sheet`) du *Tableau
#: de bord* contenant l'ancien état des informations des joueurs
#: (avant ``/sync``).
tdb_tampon_columns: tuple[str, str] = ("B", "I")
#: Nombre maximal de modèles de ciblages (:class:`.bdd.BaseCiblage`)
#: renseignés pour chaque modèle d'action (:class:`.bdd.BaseAction`), à
#: droite de la feuille ``baseactions`` du GSheet *Rôles et actions*.
max_ciblages_per_action: int = 3
#: :attr:`.bdd.BaseAction.slug` de l'action de base permettant
#: de modifier un vote (rôle de l'*Intrigant* dans le jeu PCéen).
#: Cette baseaction doit avoir deux ciblages de slugs "cible" et "vote".
modif_vote_baseaction: str = "modification-vote"
#: :attr:`.bdd.BaseAction.slug` de l'action de base permettant
#: d'ajouter un/des vote(s) (rôle du *Corbeau* dans le jeu PCéen).
ajout_vote_baseaction: str = "ajout-vote"
ajout_vote__impr_baseaction: str = "dépôt-affiche"
#: Nombre de votes ajoutés par l'action :attr:`ajout_vote_baseaction`.
n_ajouts_votes: int = 2
n_ajouts_votes_impr: int = 1
#: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le condamné du jour.
db_votecond_sheet: str = "votecond_brut"
#: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le nouveau maire.
db_votemaire_sheet: str = "votemaire_brut"
#: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le vote des loups.
db_voteloups_sheet: str = "voteloups_brut"
#: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les actions effectuées.
db_actions_sheet: str = "actions_brut"
#: Mots-clés (en minuscule) utilisables (quelque soit la casse)
#: pour arrêter une commande en cours d'exécution.
stop_keywords: list[str] = ["stop"]
#: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: permettant de recharger une action à son nombre de charges initial.
refills_full: list[str] = ["weekends"]
#: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: permettant de recharger une action de une charge.
refills_one: list[str] = ["forgeron", "rebouteux", "divin"]
#: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: à utiliser par le MJ pour ajouter une charge à une action.
refills_divins: list[str] = ["divin"]
#: Indique si le bot est prêt (:meth:`.LGBot.on_ready` appelé)
#: N'est pas conçu pour être changé manuellement.
is_ready: bool = False
#: bool: Indique si le serveur est construit (``/setup`` appelé)
#: N'est pas conçu pour être changé manuellement.
is_setup = True
class Role(readycheck.ReadyCheck, check_type=discord.Role):
"""Rôles Discord nécessaires au jeu
Cette classe dérive de :class:`readycheck.ReadyCheck` :
accéder aux attributs ci-dessous avant que le bot ne soit connecté
au serveur lève une :exc:`~readycheck.NotReadyError`.
Plus précisément, :meth:`.LGBot.on_ready` remplace le nom du rôle
par l'objet :class:`discord.Role` correspondant : si les noms des
rôles sur Discord ont été modifiés, indiquer leur nom ici
(``lgrez.config.Role.x = "nouveau nom"``) avant de lancer le bot,
sans quoi :meth:`.LGBot.on_ready` lèvera une erreur.
**Ne pas instancier cette classe.**
Rôles utilisés (dans l'ordre hiérarchique conseillé) :
Attributes:
mj: Maître du Jeu.
Nom par défaut : "MJ".
joueur_en_vie: Joueur vivant, pouvant parler publiquement.
Nom par défaut : "Joueur en vie".
joueur_mort: Joueur mort, ne pouvant pas parler publiquement.
Nom par défaut : "Joueur mort".
maire: Joueur élu Maire, mis en avant et pouvant utiliser @everyone.
Nom par défaut : "Maire".
redacteur: Rôle permettant à un joueur d'utiliser les commandes de
gestion d'IA (voir :class:`features.gestion_ia.GestionIA`). Mettre
le même nom que le rôle des MJs si vous voulez supprimer ce rôle.
Nom par défaut : "Rédacteur".
everyone: Rôle de base. Les joueurs dont le rôle le plus élevé
est ce rôle (ou moins) seront ignorés par le bot.
Nom par défaut: "@everyone" (rôle Discord de base)
"""
# Default attributes values will be deduced from server_structure.
mj: discord.Role = None
redacteur: discord.Role = None
joueur_en_vie: discord.Role = None
joueur_mort: discord.Role = None
maire: discord.Role = None
everyone: discord.Role = "@everyone"
class Channel(readycheck.ReadyCheck, check_type=discord.TextChannel):
"""Salons Discord nécessaires au jeu
Cette classe dérive de :class:`readycheck.ReadyCheck` : accéder
aux attributs ci-dessous avant que le bot ne soit connecté au
serveur lève une :exc:`~readycheck.NotReadyError`.
Plus précisément, :meth:`.LGBot.on_ready` remplace le nom du rôle
par l'objet :class:`discord.TextChannel` correspondant : si les noms
des salons sur Discord ont été modifiés, indiquer leur nom ici
(``lgrez.config.Channel.x = "nouveau nom"``) avant de lancer le bot,
sans quoi :meth:`.LGBot.on_ready` lèvera une erreur.
**Ne pas instancier cette classe.**
Salons utilisés (dans l'ordre d'affichage conseillé) :
Attributes:
roles: Salon listant les rôles (catégorie Informations).
Nom par défaut : "roles".
logs: Salon pour les messages techniques (catégorie réservée aux MJs).
Nom par défaut : "logs".
annonces: Salon d'annonces (catégorie Place du village).
Nom par défaut : "annonces".
haros: Salon des haros et candidatures (catégorie Place du village).
Nom par défaut : "haros".
debats: Salon de discussion principal (catégorie Place du village).
Nom par défaut : "débats".
"""
# Default attributes values will be deduced from server_structure.
roles: discord.TextChannel = None
logs: discord.TextChannel = None
annonces: discord.TextChannel = None
haros: discord.TextChannel = None
debats: discord.TextChannel = None
class Emoji(readycheck.ReadyCheck, check_type=discord.Emoji):
"""Emojis Discord nécessaires au jeu
Cette classe dérive de :class:`readycheck.ReadyCheck` : accéder
aux attributs ci-dessous avant que le bot ne soit connecté au
serveur lève une :exc:`~readycheck.NotReadyError`.
Plus précisément, :meth:`.LGBot.on_ready` remplace le nom du rôle
par l'objet :class:`discord.Emoji` correspondant : si les noms
des emojis sur Discord ont été modifiés, indiquer leur nom ici
(``lgrez.config.Emoji.x = "nouveau nom"``) avant de lancer le bot,
sans quoi :meth:`.LGBot.on_ready` lèvera une erreur.
**Ne pas instancier cette classe.**
Emojis utilisés (noms par défaut identiques aux noms des attributs) :
Attributes:
ha
ro: Accolés, forment le mot « haro »
bucher: Représente le vote pour le condamné du jour
maire: Représente le vote pour le nouveau maire
lune: Représente le vote des loups
action: Représente les actions de rôle
void: Image vide, pour séparations verticales et autres filouteries
"""
# Default attributes values will be deduced from server_structure.
ha: discord.Emoji = None
ro: discord.Emoji = None
bucher: discord.Emoji = None
maire: discord.Emoji = None
action: discord.Emoji = None
lune: discord.Emoji = None
void: discord.Emoji = None
def set_config_from_server_structure() -> None:
"""Deduce some configuration values from server structure.
Call this function after customizing :attr:`.server_structure`,
but BEFORE launching the bot! Should never be called at runtime.
"""
global private_chan_category_name
global boudoirs_category_name
global old_boudoirs_category_name
structure.check_server_structure(server_structure, Role, Channel, Emoji)
categories = server_structure["categories"]
private_chan_category_name = categories["private_chan"]["name"]
boudoirs_category_name = categories["boudoirs"]["name"]
old_boudoirs_category_name = categories["old_boudoirs"]["name"]
for role in Role:
if role == "everyone":
continue
setattr(Role, role, server_structure["roles"][role]["name"])
if (base_role := server_structure["base_role"]) == "@everyone":
Role.everyone = "@everyone"
else:
Role.everyone = server_structure["roles"][base_role]["name"]
for channel in Channel:
setattr(
Channel,
channel,
next(chan for categ in categories.values() for slug, chan in categ["channels"].items() if slug == channel)[
"name"
],
)
for emoji in Emoji:
setattr(Emoji, emoji, server_structure["emojis"]["required"][emoji])
set_config_from_server_structure() # First deduction at import time
guild: discord.Guild
bot: "LGBot"
engine: sqlalchemy.engine.Engine
session: sqlalchemy.orm.Session
webhook: discord.Webhook
class _ModuleGlobals(readycheck.ReadyCheck):
"""Module-level attributes with not-None ReadyCheck
(attributes accessed by __getattr__, documented directly in config.rst)
"""
guild = None
bot = None
commands_tree = None
engine = None
session = None
webhook = None
# Variable interne, pour suivi des objets manquants (ne pas changer)
_missing_objects = 0
# Called when module attribute not found: try to look in _ModuleGlobals
def __getattr__(attr):
try:
return getattr(_ModuleGlobals, attr)
except AttributeError:
raise AttributeError(f"module '{__name__}' has no attribute '{attr}'") from None