"""lg-rez / Variables globales
Personalisation de différents paramètres et accès global
"""
import json
import pkgutil
import discord
from lgrez.blocs import ready_check, structure
#: 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")
)
#: str: Préfixe des noms des salons de conversation bot.
private_chan_prefix = "conv-bot-"
#: str: 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
#: str: 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
#: str: 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
#: str: Date de début de saison (pour information lors de l'inscription).
debut_saison = "32 plopembre"
#: bool: Si ``False``, le processus d'insciption 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 = True
#: str: Nom par défaut de la :attr:`~.bdd.Joueur.chambre` des joueurs.
chambre_mj = "[chambre MJ]"
[docs]async def additional_inscription_step(member, chan):
"""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 (discord.Member): membre en cours d'inscription.
chan (discord.TextChannel): chan perso créé pour l'inscription.
Returns:
Si ``False``, annule l'inscription.
"""
pass
#: bool: Si ``True``, le bot appellera :meth:`.LGBot.i_am_alive` toutes
#: les 60 secondes. Ce n'est pas activé par défaut.
output_liveness = False
#: str: :attr:`~.bdd.Role.slug` du rôle par défaut, attribué aux
#: joueurs lors de l'inscription (renvoyé par :meth:`.bdd.Role.default`).
#: Doit correspodre à un rôle existant (défini dans le GSheet *Rôles et
#: actions*).
default_role_slug = "nonattr"
#: str: :attr:`~.bdd.Camp.slug` du camp par défaut, attribué aux
#: joueurs lors de l'inscription (renvoyé par :meth:`.bdd.Camp.default`).
#: Doit correspodre à un camp existant (défini dans le GSheet *Rôles et
#: actions*).
default_camp_slug = "nonattr"
#: str: Nom de la feuille du *Tableau de bord* contenant l'état actuel
#: des joueurs, sur laquelle sont effectuées les modifications.
tdb_main_sheet = "Journée en cours"
#: str: Nom de la feuille du *Tableau de bord* contenant les résultats
#: des votes (après corrections manuelles éventuelles).
tdb_votes_sheet = "Journée en cours"
#: int: 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 = 3
#: str: 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 = "A"
#: tuple[str]: 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 = ("J", "Q")
#: tuple[str]: 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 = ("B", "I")
#: int: 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 = 3
#: str: :attr:`.bdd.BaseAction.slug` de l'action de base permettant
#: de modifier un vote (rôle de l'*Intigant* dans le jeu PCéen).
#: Cette baseaction doit avoir deux ciblages de slugs "cible" et "vote".
modif_vote_baseaction = "modification-vote"
#: str: :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 = "ajout-vote"
#: int: Nombre de votes ajoutés par l'action :attr:`ajout_vote_baseaction`.
n_ajouts_votes = 2
#: str: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le condamné du jour.
db_votecond_sheet = "votecond_brut"
#: str: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le nouveau maire.
db_votemaire_sheet = "votemaire_brut"
#: str: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les votes brutes pour le vote des loups.
db_voteloups_sheet = "voteloups_brut"
#: str: Nom de la feuille du GSheet *Données brûtes* où enregistrer
#: les actions effectuées.
db_actions_sheet = "actions_brut"
#: list[str]: Mots-clés (en minuscule) utilisables (quelque soit la casse)
#: pour arrêter une commande en cours d'exécution.
stop_keywords = ["stop", "!stop"]
#: list[str]: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: permettant de recharger une action à son nombre de charges initial.
refills_full = ["weekends"]
#: list[str]: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: permettant de recharger une action de une charge.
refills_one = ["forgeron", "rebouteux", "divin"]
#: list[str]: Mots-clés de rechargement (dans :attr:`.bdd.BaseAction.refill`)
#: à utiliser par le MJ pour ajouter une charge à une action.
refills_divins = ["divin"]
#: bool: Indique si le bot est prêt (:meth:`.LGBot.on_ready` appelé)
#: N'est pas concu pour être changé manuellement.
is_ready = False
#: bool: Indique si le serveur est construit (``!setup`` appelé)
#: N'est pas conçu pour être changé manuellement.
is_setup = True
[docs]class Role(ready_check.ReadyCheck, check_type=discord.Role):
"""Rôles Discord nécessaires au jeu
Cette classe dérive de :class:`.ready_check.ReadyCheck` :
accéder aux attributs ci-dessous avant que le bot ne soit connecté
au serveur lève une :exc:`~.ready_check.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 = None
redacteur = None
joueur_en_vie = None
joueur_mort = None
maire = None
everyone = "@everyone"
[docs]class Channel(ready_check.ReadyCheck, check_type=discord.TextChannel):
"""Salons Discord nécessaires au jeu
Cette classe dérive de :class:`.ready_check.ReadyCheck` : accéder
aux attributs ci-dessous avant que le bot ne soit connecté au
serveur lève une :exc:`~.ready_check.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 = None
logs = None
annonces = None
haros = None
debats = None
[docs]class Emoji(ready_check.ReadyCheck, check_type=discord.Emoji):
"""Emojis Discord nécessaires au jeu
Cette classe dérive de :class:`.ready_check.ReadyCheck` : accéder
aux attributs ci-dessous avant que le bot ne soit connecté au
serveur lève une :exc:`~.ready_check.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 = None
ro = None
bucher = None
maire = None
action = None
lune = None
void = None
[docs]def set_config_from_server_structure():
"""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
class _ModuleGlobals(ready_check.ReadyCheck):
"""Module-level attributes with not-None ReadyCheck
(attributes accessed by __getattr__, documented directly in config.rst)
"""
guild = None
bot = None
loop = 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