declare module 'discordplace-sync' { import { EventEmitter } from 'events'; interface CommandOption { name: string; description?: string; type: string; required?: boolean; choices?: Array<{ name: string; value: string | number }>; minLength?: number; maxLength?: number; minValue?: number; maxValue?: number; } interface Command { name: string; description?: string; /** `user_command` / `message_command` : menus contextuels (clic droit). */ type: 'slash_command' | 'prefix_command' | 'user_command' | 'message_command'; options?: CommandOption[]; } /** * Champs acceptés par `updateStats()` / `postStats()`. * Seul `guildCount` est obligatoire ; tout champ absent d'un envoi conserve sa dernière * valeur connue côté serveur. * ⚠️ Les commandes ne passent PAS par ici — elles sont envoyées par `syncBotInfo()`. */ interface BotStats { guildCount: number; userCount?: number; shardCount?: number; latency?: number; uptime?: number; channelCount?: number; commandsUsed?: number; memoryUsage?: number; version?: string; isOnline?: boolean; } /** Bot mis à jour, tel que renvoyé par l'API après une synchronisation. */ interface SyncedBot { id: string; name?: string; guildCount?: number; slashCommandCount?: number; prefixCommandCount?: number; isOnline?: boolean; lastStatsUpdate?: string; } interface SyncOptions { treatAllAsHybrid?: boolean; prefixOnlyCommands?: string[]; slashOnlyCommands?: string[]; /** Noms des commandes à ne JAMAIS publier (réservées au développeur du bot). */ privateCommands?: string[]; /** Rendre `false` pour ne pas publier une commande ; reçoit l'entrée telle que le bot la range. */ commandFilter?: (command: any, name: string) => boolean | void; /** * `false` : ne rien écarter d'office. Par défaut le SDK ne publie ni une commande marquée * `ownerOnly` / `devOnly` / `developerOnly` / `botOwnerOnly` / `testOnly` / `guildIds`, ni une * commande que Discord ne connaît que sur un serveur précis. */ detectPrivateCommands?: boolean; /** Accepté pour compatibilité, sans effet. */ isAutoSync?: boolean; /** Version de VOTRE bot, affichée sur sa fiche. */ version?: string; } /** * L'erreur que rend le SDK (1.9.1) — jamais l'erreur brute d'axios, qui portait la clé API. * `status` : code HTTP, `null` si DiscordPlace n'a pas répondu. `code` : `HTTP_` ou un * code réseau (`ECONNREFUSED`…). `retryable` : réseau, 5xx ou 429. */ class DiscordPlaceError extends Error { name: 'DiscordPlaceError'; status: number | null; code: string | null; endpoint: string | null; retryable: boolean; } interface VoteStatus { voted: boolean; votedAt: string | null; expiresAt: string | null; } interface SyncClientOptions { /** Intervalle entre deux synchronisations, en ms. Défaut : 300000 (5 min). */ interval?: number; /** URL de l'API. Défaut : https://discordplace.com/api */ endpoint?: string; /** * Compte l'usage (commandes, interactions sans réponse, serveurs ajoutés/retirés, * utilisateurs actifs) et l'envoie avec les statistiques — des NOMBRES, jamais d'identifiant. * Défaut : true. */ trackUsage?: boolean; /** Reçoit le webhook de vote sur un petit serveur HTTP ; émet `vote` et `voteTest`. */ webhook?: { port: number; secret: string; path?: string; host?: string }; /** `false` : pas d'avertissement console quand une version plus récente est publiée. Défaut : true. */ updateNotice?: boolean; /** Journal détaillé dans la console. Défaut : false (silencieux). */ debug?: boolean; /** Timeout d'une requête, en ms. Défaut : 10000. */ timeout?: number; /** Nombre TOTAL de tentatives (réseau, 5xx, 429). Défaut : 3. */ retries?: number; /** Version de VOTRE bot, affichée sur sa fiche. */ botVersion?: string; /** Transmis à `syncBotInfo()` à chaque cycle. */ treatAllAsHybrid?: boolean; prefixOnlyCommands?: string[]; slashOnlyCommands?: string[]; /** Commandes privées, jamais publiées — voir `SyncOptions`. */ privateCommands?: string[]; commandFilter?: (command: any, name: string) => boolean | void; detectPrivateCommands?: boolean; } interface DiscordClient { user: { id: string; tag?: string; username?: string; displayAvatarURL?: (options?: { extension?: string; forceStatic?: boolean; format?: string; size?: number }) => string; }; guilds: { cache: Map | { size: number }; }; commands?: Map | Array; slashCommands?: Map | Array; prefixCommands?: Map | Array; application?: { commands?: { cache: Map; fetch: () => Promise; }; }; ws?: { ping?: number; }; uptime?: number; on: (event: string, listener: (...args: any[]) => void) => void; once: (event: string, listener: (...args: any[]) => void) => void; } /** Client bas niveau : contrôle total, aucun automatisme implicite. */ class DiscordPlaceSDK extends EventEmitter { constructor(options: { apiKey: string; botId: string; endpoint?: string; /** Réémet périodiquement les DERNIÈRES statistiques envoyées. Défaut : false. */ autoSync?: boolean; syncInterval?: number; timeout?: number; retries?: number; /** Journal détaillé dans la console (silencieux par défaut). */ debug?: boolean; /** `false` : pas d'avertissement console quand une version plus récente est publiée. */ updateNotice?: boolean; /** Version de VOTRE bot, envoyée par `autoCollectStats()` et `syncBotInfo()`. */ botVersion?: string; }); static DiscordPlaceError: typeof DiscordPlaceError; static version: string; /** * Arme le SDK (`updateStats()` échoue tant qu'il n'a pas été appelé). * ⚠️ N'installe le minuteur de réémission QUE si `autoSync: true`. */ start(): void; stop(): void; updateStats(stats: BotStats): Promise; postStats(stats: BotStats): Promise; /** Statistiques ACTUELLES enregistrées par DiscordPlace. `period` est ignoré (pas d'historique). */ getStats(period?: string): Promise; /** A-t-il voté pour votre bot dans les 12 dernières heures ? (403 si le bot n'est pas approuvé) */ hasVoted(userId: string): Promise; /** Collecte les chiffres depuis un client Discord.js, puis les envoie. */ autoCollectStats(client: DiscordClient): Promise; /** Envoie les informations complètes, commandes incluses. Envoi PONCTUEL. */ syncBotInfo(client: DiscordClient, options?: SyncOptions): Promise; /** Dernière synchronisation reçue par DiscordPlace, et si elle est encore « fraîche ». */ checkSyncStatus(): Promise; /** Envoie un lot d'usage (utilisé par SyncClient ; rarement appelé à la main). */ postUsage(batch: { batchId: string; day: string; usage: Partial; telemetry?: object }): Promise; on(event: 'start', listener: () => void): this; on(event: 'stop', listener: () => void): this; on(event: 'success', listener: (data: any) => void): this; on(event: 'syncSuccess', listener: (data: any) => void): this; on(event: 'updateAvailable', listener: (info: { current: string; latest: string }) => void): this; /** Seulement les échecs de la réémission `autoSync` : une méthode appelée REJETTE, sans émettre. */ on(event: 'error', listener: (error: DiscordPlaceError) => void): this; on(event: string, listener: (...args: any[]) => void): this; } /** * Client haut niveau : s'abonne lui-même à `ready` et synchronise en boucle. * Accepte un client Discord.js ou un `ShardingManager`. */ class SyncClient extends EventEmitter { constructor(apiKey: string, client: DiscordClient | any, options?: SyncClientOptions); /** Force une synchronisation immédiate, hors du cycle. Ne rejette pas : les échecs vont à `error`. */ postNow(): Promise; /** A-t-il voté pour votre bot dans les 12 dernières heures ? Utilisable dès que le client est connecté. */ hasVoted(userId: string): Promise; stop(): void; /** Compte une commande à préfixe (les interactions sont comptées seules). */ trackCommand(commandName: string, userId?: string): void; on(event: 'ready', listener: () => void): this; /** L'écouteur peut être async : sa promesse est attendue, un rejet fait répondre 500 (DiscordPlace réessaie). */ on(event: 'vote', listener: (vote: VoteEvent) => void | Promise): this; on(event: 'voteTest', listener: (vote: VoteEvent) => void | Promise): this; on(event: 'updateAvailable', listener: (info: { current: string; latest: string }) => void): this; on(event: 'usageSynced', listener: (info: { batches: number }) => void): this; on(event: 'synced', listener: (bot: SyncedBot) => void): this; /** Un échec par cycle au plus. Sans écouteur : une ligne `console.warn`, jamais un plantage. */ on(event: 'error', listener: (error: DiscordPlaceError) => void): this; on(event: string, listener: (...args: any[]) => void): this; } interface UsageCounters { interactions: number; commands: number; components: number; modals: number; autocompletes: number; unanswered: number; guildsAdded: number; guildsRemoved: number; activeUsers: number | null; commandCounts: Record; } /** Corps reçu par le webhook de vote. */ interface VoteEvent { type: 'vote' | 'test'; /** Unique par vote : ignorer un id déjà traité. */ id: string; botId: string; userId: string; votedAt: string; nextVoteAt: string | null; } class UsageTracker { constructor(client?: any); attach(client: any): this; detach(): void; track(commandName: string, userId?: string): void; take(): Array<{ day: string; usage: UsageCounters }>; } /** Export principal : raccourci vers `new SyncClient(...)`. */ function createSyncClient( apiKey: string, client: DiscordClient | any, options?: SyncClientOptions ): SyncClient; namespace createSyncClient { export { SyncClient, DiscordPlaceSDK, DiscordPlaceError, VoteStatus, BotStats, SyncedBot, SyncOptions, SyncClientOptions, Command, CommandOption, DiscordClient, UsageTracker, UsageCounters, VoteEvent }; /** Sharding : à appeler dans chaque shard pour que le manager relève son usage. */ export function trackUsage(client: any): UsageTracker; /** Vérifie l'en-tête X-DiscordPlace-Signature sur le corps BRUT. */ export function verifySignature(secret: string, rawBody: string | Buffer, header: string, toleranceSeconds?: number): boolean; /** Gestionnaire (req, res) pour http ou Express (monté avant express.json()). */ export function createWebhookHandler(options: { secret: string; onVote: (vote: VoteEvent) => any }): (req: any, res: any) => Promise; export function startWebhookServer(options: { port: number; secret: string; onVote: (vote: VoteEvent) => any; path?: string; host?: string }): import('http').Server; export const SIGNATURE_HEADER: 'X-DiscordPlace-Signature'; /** Alias historiques de SyncClient. */ export const AutoPoster: typeof SyncClient; export const DJSPoster: typeof SyncClient; export const DJSSharderPoster: typeof SyncClient; } export = createSyncClient; }