Se rendre au contenu

La documentation est susceptible d'être modifiée au fil du temps.

📜 Politique de gestion NSL

Chers développeurs,

Le système NSL (Néon Spinellia LuckyScale) a pour objectif de faciliter la gestion collaborative des modules entre bots.

Afin de garantir la cohérence, la compatibilité et l'interopérabilité, nous avons mis en place une liste de modules standardisés.

• ✅ Modules standardisés

  • Permettre un nommage uniforme pour les fonctions communes (ex : logs, tickets, annonces).
  • Éviter les collisions et doublons.
  • Garantir la compatibilité inter-bots.
  • Documenter officiellement les modules disponibles.

• 🛠️ Modules personnalisés

Vous êtes libres de développer des modules propres à votre bot, sans inscription NSL.                                                               
Cependant, toute synchronisation NSL nécessite un mappage avec un module standard.

• 🔒 Accès à la table modules

Par mesure de sécurité et pour garantir l'intégrité du système, la gestion de la table des modules standardisés est réservée aux mainteneurs NSL.

• 📄 Récapitulatif

  • /nsl-core module-list  Liste les modules standards.
  • /nsl-core module-suggest  Proposition d'un module
  • /nsl-core module-add  Ajout d'un module (restreint).
  • /nsl-core module-remove  Suppression d’un module (restreint).

• 📡 Fonctionnement de l'API 

🔧 Règles spécifiques aux routes

RouteSuccès attenduEn cas d'erreur
POST /modules/insertrep { message, code: 200 }rep { message, code: NSL_CODES }
PATCH /modules/updaterep { message, code: 200 }rep { message, code: NSL_CODES }
GET /modules/getrep { data: [], code: 200 }rep { message, code: NSL_CODES }

📢 À retenir

  • rep est toujours présent pour toutes les réponses.
  • GET retourne un tableau data si succès.
  • Les succès ont toujours code: 200, même pour "aucune mise à jour".
  • Les erreurs utilisent le code HTTP correspondant.

• 📝 Exemple d'utilisation - Classe NSLCore

// @ts-nocheck
const { Client, Intents, Collection, ActionRowBuilder, ModalBuilder, EmbedBuilder, StringSelectMenuBuilder, permissionString, WebhookClient, Sweepers, DynamicImageFormat, TextInputComponent, Guild, GuildMember, ButtonBuilder, ChannelSelectMenuBuilder, GuildChannel, Channel, Role, User, Message, AttachmentBuilder, PROFILS, Profil, ActivityType, Embed, EmbedType, ChannelType, OverwriteType, PresenceStatusData, } = require("discord.js");
require("../index");

// ---------------------------------------------------------------------
// Typedefs de réponse (JSDoc) – rend l’autocompletion plus agréable.
// ---------------------------------------------------------------------
/**
 * Structure générique renvoyée par l’API NSL.
 * @typedef {Object} NSL_DATA
 * @property {{message:string, code:NSL_CODES}} rep  Métadonnées de réponse
 * @property {any} [data]                            Charge utile facultative
 */

/**
 * Énumération maison des codes d’état (pseudo‑HTTP).
 * @typedef {Object} NSL_CODES
 * @property {200} VALID          Requête traitée avec succès
 * @property {304} NOT_UPDATED    Rien n’a changé
 * @property {400} SYNTAX_ERROR   Paramètre manquant / mal formé
 * @property {401} UNAUTHENTICATED Token invalide / manquant
 * @property {403} ACCESS_DENIED  Accès refusé (permissions)
 * @property {404} NOT_FOUND      Ressource inexistante
 * @property {500} INTERN_ERROR   Erreur interne serveur
 */

/**
 * Liste concrète des codes (exposée via `module.exports.NSL_CODES`).
 *
 * Pour rester simple, on conserve la valeur numérique identique au code HTTP
 * éponyme même si l’API est privée.
 * @type {NSL_CODES}
 */
const NSL_CODES = {
    VALID: 200,
    NOT_UPDATED: 304,
    SYNTAX_ERROR: 400,
    UNAUTHENTICATED: 401,
    ACCESS_DENIED: 403,
    NOT_FOUND: 404,
    INTERN_ERROR: 500,
};

/***************************************************************************
 * NSLCore – Néon Spinellia LuckyScale *Core*
 * -------------------------------------------------------------------------
 *  > Fournit une petite sur‑couche pour dialoguer avec l’API REST interne
 *    "NSL" (Néon Spinellia LuckyScale) destinée à centraliser les **modules** de
 *    plusieurs bots Discord.
 *
 *  • Chaque module ("ticket", "economy", « tickets » d’un ami, etc.) peut
 *    être *déclaré*, *mis à jour* ou *activé/désactivé* serveur par serveur.
 *  • Cette classe s’occupe uniquement du transport : construire l’URL, 
 *    sérialiser la charge utile, gérer le *bearer token*, parser la réponse
 *    et logguer des infos compréhensibles en console.
 *  • Les appels retournent systématiquement un objet typé **NSL_DATA** ou
 *    lèvent/propagent une `Error` (que l’appelant peut attraper).
 *
 *  *NB* : ce fichier n’impose aucune dépendance à un logger maison ; il 
 *  utilise simplement `co()` (color‑out) laissé tel quel pour respecter
 *  ton implémentation d’origine.
 ***************************************************************************/
class NSLCore {
    /** Nom de classe exposé pour debug. */
    className = "NSLCore";

    /**
     * Jeton JWT/Bearer attribué par l’API NSL pour authentification.
     * **À ne pas commit** en clair en repo public – garder dans un secret env !
     * @private
     */
    token = ``; // <- inserez votre token ici

    /**
     * Point d’entrée de base de l’API (utile si tu changes d’hôte plus tard).
     * @private
     */
    apiUrl = `http://node1.adky.net:1750/api/nsl`;

    // Rien à faire au constructeur pour l’instant (pas de state interne).
    constructor() { }

    // -----------------------------------------------------------------
    // fetchModules – GET /modules/get
    // -----------------------------------------------------------------
    /**
     * Récupère la/les définition(s) d’un module selon critères.
     *
     * @param {Object} [queryParams]                      Filtres facultatifs
     * @param {?string} [queryParams.bot_id]
     * @param {?string} [queryParams.server_id]
     * @param {?string} [queryParams.module_name]
     * @param {?boolean}[queryParams.coexist]
     * @param {?string} [queryParams.description]
     * @param {?string} [queryParams.color]
     * @param {boolean} [debug=false]                     Log raw response ?
     * @returns {Promise}                 Promesse réponse
     */
    async fetchModules(queryParams = {}, debug = false) {
        // Construit l’URL avec paramètres de requête.
        const url = new URL(`${this.apiUrl}/modules/get`);
        Object.entries(queryParams).forEach(([k, v]) => url.searchParams.append(k, v));

        try {
            const res = await fetch(url.toString(), {
                method: "GET",
                headers: {
                    Authorization: `Bearer ${this.token}`,
                    'nsl-bot-id': client.user.id
                },
            });

            /** @type {NSL_DATA} */
            const data = await res.json();
            if (debug)
                co(
                    `§6[§4NSLCore§6] ℹ️  §l§aGET modules : §6${JSON.stringify(data)}`,
                    "debug"
                );
            return data;
        } catch (err) {
            co(
                `§6[§4NSLCore§6/§cERROR§6] ❌  §l§cErreur fetch GET : §4${err.message}`,
                "error"
            );
            return /** @type {Error} */ (err);
        }
    }

    // -----------------------------------------------------------------
    // fetchModules – GET /modules/get
    // -----------------------------------------------------------------
    /**
     * Récupère la/les définition(s) d’un module selon critères.
     *
     * @param {Object} [queryParams]                      Filtres facultatifs
     * @param {string} [queryParams.module_name]
     * @param {boolean} [debug=false]                     Log raw response ?
     * @returns                 Promesse réponse
     */
    async isStandardized(queryParams = {}, debug = false) {
        // Construit l’URL avec paramètres de requête.
        const url = new URL(`${this.apiUrl}/modules/is-standardized`);
        Object.entries(queryParams).forEach(([k, v]) => url.searchParams.append(k, v));

        try {
            const res = await fetch(url.toString(), {
                method: "GET",
                headers: {
                    Authorization: `Bearer ${this.token}`,
                    'nsl-bot-id': client.user.id
                },
            });

            /** @type {NSL_DATA} */
            const data = await res.json();
            if (debug)
                co(
                    `§6[§4NSLCore§6/§dStandardized§6] ℹ️  §l§aGET modules : §6${JSON.stringify(data)}`,
                    "debug"
                );
            return data.rep.code.ACCESS_DENIED ? false : true
        } catch (err) {
            co(
                `§6[§4NSLCore§6/§dStandardized§6/§cERROR§6] ❌  §l§cErreur fetch GET : §4${err.message}`,
                "error"
            );
            return false
        }
    }

    // -----------------------------------------------------------------
    // updateModule – PATCH /modules/update
    // -----------------------------------------------------------------
    /**
     * Champ(s) modifiable(s) pour un module existant.
     * @typedef {Object} ModuleField
     * @property {"enabled"|"disabled"} [state]
     * @property {string}                 [description]
     * @property {string}                 [color]
     * @property {boolean}                [coexist]
     */

    /**
     * Met à jour un module existant (partiellement).
     * @param {Object} body                         Corps de requête
     * @param {string} body.bot_id                  ID du bot
     * @param {string} body.server_id               ID du serveur
     * @param {string} body.module_name             Nom unique du module
     * @param {ModuleField|ModuleField[]} body.fields  Attributs à modifier
     * @param {boolean} [debug=false]               Log raw response ?
     * @returns {Promise}           Promesse réponse
     */
    async updateModule(body, debug = false) {
        try {
            const res = await fetch(`${this.apiUrl}/modules/update`, {
                method: "PATCH",
                headers: {
                    Authorization: `Bearer ${this.token}`,
                    "Content-Type": "application/json",
                    'nsl-bot-id': client.user.id
                },
                body: JSON.stringify(body),
            });

            /** @type {NSL_DATA} */
            const data = await res.json();
            if (debug)
                co(
                    `§6[§4NSLCore§6] ℹ️  §l§aPATCH update : §6${JSON.stringify(data)}`,
                    "debug"
                );
            return data;
        } catch (err) {
            co(
                `§6[§4NSLCore§6/§cERROR§6] ❌  §c§lErreur fetch PATCH update : §4${err}`,
                "error"
            );
            return /** @type {Error} */ (err);
        }
    }

    // -----------------------------------------------------------------
    // insertModule – POST /modules/insert
    // -----------------------------------------------------------------
    /**
     * Crée un nouveau module pour un bot/serveur donné.
     * @param {Object} body
     * @param {string} body.bot_id
     * @param {string} body.server_id
     * @param {string} body.module_name
     * @param {?boolean} body.coexist
     * @param {?string}  body.description
     * @param {?string}  body.color
     * @param {boolean} [debug=false]
     * @returns {Promise}
     */
    async insertModule(body, debug = false) {
        try {
            const res = await fetch(`${this.apiUrl}/modules/insert`, {
                method: "POST",
                headers: {
                    Authorization: `Bearer ${this.token}`,
                    "Content-Type": "application/json",
                    'nsl-bot-id': client.user.id
                },
                body: JSON.stringify(body),
            });

            /** @type {NSL_DATA} */
            const data = await res.json();
            if (debug)
                co(
                    `§6[§4NSLCore§6] ℹ️  §l§aPOST insert : §6${JSON.stringify(data)} ` +
                    `§e➔ ${data.rep.code === NSL_CODES.VALID ? "§2" : "§4"}☻`,
                    "debug"
                );
            return data;
        } catch (err) {
            co(
                `§6[§4NSLCore§6/§cERROR§6] ❌  §l§cErreur fetch POST insert : §4${err}`,
                "error"
            );
            return /** @type {Error} */ (err);
        }
    }

    // -----------------------------------------------------------------
    // setModuleState – Helper haute‑niveau (insert ou update selon cas)
    // -----------------------------------------------------------------
    /**
     * Active/désactive un module. Crée l’entrée si elle n’existe pas.
     *
     * @param {Object} params                       Paramètres métier
     * @param {string}  params.bot_id
     * @param {string}  params.server_id
     * @param {string}  params.module_name
     * @param {boolean} [params.coexist=false]
     * @param {?string} [params.description]
     * @param {?string} [params.color]
     * @param {"enabled"|"disabled"} params.newState  Nouvel état désiré
     * @param {Guild}   [params.guild]              Contexte Guild (pour logs)
     * @param {boolean} [debug=false] 
     * @returns {Promise}                  true si sans erreur bloquante
     */
    async setModuleState({
        bot_id,
        server_id,
        module_name,
        coexist = false,
        description,
        color,
        newState,
        guild,
    }, debug = false) {
        try {
            const existingModule = await this.fetchModules({
                bot_id,
                server_id,
                module_name,
            });

            // ----------------------------------------------------------
            // Si le module N’EXISTE PAS encore ➜ insertion.
            // ----------------------------------------------------------
            if (debug) co(`§b${guild ? `${guild.id}:${module_name}` : module_name
                } §e-> §c${existingModule.rep.code} (${existingModule.rep.message}) -> ${existingModule?.data}`)
            if (!existingModule?.data) {
                if (existingModule.rep.code === NSL_CODES.NOT_FOUND && await this.isStandardized({ module_name: module_name }, true)) {
                    co(`§6[§4NSLCore§6] ℹ️  §l§bModule §1§o${guild ? `${guild.id}:${module_name}` : module_name}§r §l§bnon trouvé, insertion...`, "debug");

                    await this.insertModule({
                        bot_id,
                        server_id,
                        module_name,
                        state: newState,
                        coexist,
                        description,
                        color,
                    }).then((e) =>
                        e.rep.code === NSL_CODES.VALID
                            ? co(" §e╚> ✅  §l§aInsertion réussie.", "debug")
                            : co(` §e╚> ❌  §l§cInsertion échoué.§r §e§o(${e.rep.message})`, "debug")
                    );
                } else if (existingModule.rep.code === NSL_CODES.SYNTAX_ERROR) {
                    co(
                        `§6[§4NSLCore§6] ℹ️  §l§bModule §1§o§m${guild ? `${guild.id}:${module_name}` : module_name
                        }§r §l§bnon trouvé...`,
                        "debug"
                    );
                }
            } else {
                // ------------------------------------------------------
                // Le module EXISTE ➜ simple mise à jour de l’état.
                // ------------------------------------------------------
                co(
                    `§6[§4NSLCore§6] ℹ️  §l§bModule §1§o${guild ? `${guild.id}:${module_name}` : module_name
                    }§r §l§btrouvé, mise à jour de l'état...`,
                    "debug"
                );

                await this.updateModule({
                    bot_id,
                    server_id,
                    module_name,
                    fields: { state: newState, coexist, color, description },
                }).then((e) =>
                    e.rep.code === NSL_CODES.VALID
                        ? co(" §e╚> ✅  §l§aMise à jour réussie.", "debug")
                        : co(" §e╚> ❌  §l§cMise à jour échoué.", "debug")
                );
            }
            return true;
        } catch (err) {
            co(
                `§6[§4NSLCore§6/§cERROR§6] ❌  §l§cErreur changement état module : §4${err.message}`,
                "error"
            );
            return false;
        }
    }
}

// ---------------------------------------------------------------------
// Exports – la classe, les codes, et une métadonnée pour ton loader.
// ---------------------------------------------------------------------
module.exports.NSL_CODES = NSL_CODES;
module.exports.NSLCore = NSLCore;
module.exports.class = {
    name: "NSLCore",
    description: "NSL CORE",
};


Contactez notre équipe d'assistance client en envoyant un e-mail à info@yourcompany.example.com, appelez le +1 555-555-5556, ou utilisez le chat en direct sur notre site web. Notre équipe est disponible 24 heures sur 24 et 7 jours sur 7 pour répondre à toutes vos questions.

Nous nous engageons à fournir des solutions rapides et efficaces pour garantir votre satisfaction.

Nous offrons une politique de retour de 30 jours pour tous les produits. Les articles doivent être dans leur état d'origine, ne pas avoir été utilisés et être accompagnés du reçu ou de la preuve d'achat. Les remboursements sont effectués dans les 5 à 7 jours ouvrables suivant la réception de l'article retourné.