BloumeChat
La classe principale du SDK — le point d'entrée unique pour authentifier un bot, écouter les événements temps réel, et appeler l'API BloumeChat. Étend EventEmitter de Node.js (voir la liste complète des événements dans le guide Événements).
Notation des signatures
Sur cette page, les signatures de méthode (setActivity(activity: ActivityData | null): Promise<void>) sont écrites en notation TypeScript pour préciser exactement les types acceptés — mais tout s'appelle à l'identique en JavaScript, simplement sans les annotations. Voir Utiliser le SDK en JavaScript.
import { BloumeChat } from "bloumechat";
const client = new BloumeChat();const { BloumeChat } = require("bloumechat");
const client = new BloumeChat();Le constructeur ne prend aucun argument : il initialise les managers (users, guilds, channels, members, voice), masque le futur token de manière non énumérable (pour qu'il n'apparaisse jamais dans console.log(client) ou JSON.stringify(client)), et vérifie que baseUrl/socketUrl pointent bien vers un hôte BloumeChat connu — un avertissement est loggé sinon (voir Sécurité de l'hôte).
Propriétés
| Propriété | Type | Description |
|---|---|---|
user | User | null | Le profil du bot lui-même. null avant l'événement ready. |
users | UserManager | Cache de tous les utilisateurs rencontrés (auteurs de messages, membres consultés…). |
guilds | GuildManager | Cache des serveurs où le bot est présent. Pré-rempli automatiquement après login(). |
channels | ChannelManager | Cache des salons connus (texte, vocal…), tous serveurs confondus. |
members | MemberManager | Cache des profils de membre (rôles, permissions par serveur). |
voice | VoiceManager (2.1.0+) | Gère la connexion vocale du bot — client.voice.connection, client.voice.leave(). Préférez channel.join() pour rejoindre un salon. |
voiceStates | VoiceStateManager (4.1.0+) | Cache de l'état vocal courant de chaque utilisateur connu (salon, muet, sourdine, en train de parler…), alimenté par les événements voice:*. Préférez member.voice si vous avez déjà un Member. |
commands | Collection<string, Command> (4.2.0+) | Commandes chargées via loadCommands(), indexées par nom. Vide tant que loadCommands() n'a pas été appelé au moins une fois. |
readyAt | Date | null | Horodatage de la première connexion réussie. null avant ready, ré-initialisé à null par destroy(). |
uptime | number | null (getter) | Millisecondes écoulées depuis readyAt. null si le client n'est pas encore prêt. |
baseUrl | string (readonly) | Base des requêtes REST : https://bloumechat.com/api/v2. |
socketUrl | string (readonly) | Hôte du WebSocket : https://api.bloumechat.com. |
baseUrl / socketUrl sont en lecture seule au sens TypeScript uniquement
readonly est une garantie compile-time, pas runtime — un module malveillant pourrait techniquement réassigner ces champs sur l'instance. Le SDK s'en protège en loggant un avertissement ([BloumeChat SDK] baseUrl/socketUrl points to an unrecognized host...) si l'hôte détecté ne fait pas partie de la liste autorisée (bloumechat.com, api.bloumechat.com, localhost) au moment de la construction du client. Ne redéfinissez jamais ces valeurs vers un hôte tiers : le token du bot y serait envoyé en clair à chaque requête.
uptime
get uptime(): number | nullCalcule Date.now() - readyAt.getTime(). Pratique pour une commande !uptime ou pour exposer un endpoint de health-check.
client.on("messageCreate", (message) => {
if (message.content === "!uptime") {
const seconds = Math.floor((client.uptime ?? 0) / 1000);
message.reply(`En ligne depuis ${seconds}s.`);
}
});client.on("messageCreate", (message) => {
if (message.content === "!uptime") {
const seconds = Math.floor((client.uptime || 0) / 1000);
message.reply(`En ligne depuis ${seconds}s.`);
}
});Connexion
login(token)
login(token: string): Promise<void>| Paramètre | Type | Requis | Défaut | Description |
|---|---|---|---|---|
token | string | Oui | — | Le token de bot, généré depuis le Portail Développeurs. |
Établit la connexion WebSocket authentifiée vers socketUrl, puis :
- Ouvre le socket avec
transports: ["websocket"]et reconnexion automatique activée (reconnectionAttempts: Infinity, délai1s → 30sen backoff). - Dès
connect, appelle en interneGET /auth/mepour chargerclient.user. - Pré-charge tous les serveurs du bot (
client.guilds.fetchAll()), ce qui peuple aussiclient.membersavec le membre du bot lui-même sur chaque serveur (nécessaire pour que ses permissions soient immédiatement disponibles). - Émet
ready, puis résout la promesse.
Erreurs possibles :
- Rejette immédiatement avec une
BloumeChatAuthError("login() requires a non-empty bot token string.") sitokenest vide,null/undefined, ou n'est pas une chaîne. - Rejette si la connexion socket échoue avant la première connexion (
connect_error) — un token invalide ou révoqué déclenche ce cas. - Une fois
readyatteint, les coupures réseau suivantes ne rejettent plus rien : le socket retente indéfiniment en arrière-plan et émetreconnect/disconnect(voir Événements) au lieu de faire échouer la promesse déjà résolue.
import { BloumeChat } from "bloumechat";
const client = new BloumeChat();
client.on("ready", () => {
console.log(`Connecté en tant que ${client.user?.tagString}`);
});
try {
await client.login(process.env.BOT_TOKEN!);
} catch (err) {
console.error("Échec de connexion :", err);
process.exit(1);
}const { BloumeChat } = require("bloumechat");
const client = new BloumeChat();
client.on("ready", () => {
console.log(`Connecté en tant que ${client.user?.tagString}`);
});
client
.login(process.env.BOT_TOKEN)
.catch((err) => {
console.error("Échec de connexion :", err);
process.exit(1);
});Pourquoi await client.login(...) plutôt que client.on("ready", ...) seul ?
La promesse retournée par login() se résout exactement au moment de ready — les deux approches sont équivalentes. Utilisez await en haut de votre script si le reste du code dépend d'un client prêt (par exemple pour enregistrer des commandes) ; utilisez l'écouteur ready si vous préférez une structure événementielle.
destroy()
destroy(): voidFerme proprement la connexion :
- Déconnecte le socket (
socket.disconnect()) et le libère (socket = null) — plus aucune reconnexion automatique n'aura lieu après cet appel. - Réinitialise
readyAtànull(doncuptimeredevientnull). - Efface le token en mémoire.
- Retire tous les écouteurs d'événements enregistrés sur le client (
removeAllListeners()) — y compris les vôtres.
N'efface pas les caches (client.guilds.cache, client.channels.cache, etc.) : ces objets restent accessibles en lecture après destroy(), mais ne seront plus jamais mis à jour. Ne réutilisez pas la même instance après un destroy() — créez un nouveau BloumeChat() pour vous reconnecter.
process.on("SIGINT", () => {
console.log("Arrêt du bot...");
client.destroy();
process.exit(0);
});process.on("SIGINT", () => {
console.log("Arrêt du bot...");
client.destroy();
process.exit(0);
});Requêtes API bas niveau
apiCall(path, options?)
apiCall(path: string, options?: ApiCallOptions): Promise<any>| Paramètre | Type | Requis | Défaut | Description |
|---|---|---|---|---|
path | string | Oui | — | Chemin relatif à baseUrl, ex. "/servers/123/roles". Doit commencer par /. |
options | ApiCallOptions | Non | {} | Étend RequestInit standard (method, body, headers…) avec timeoutMs et maxRetries. |
interface ApiCallOptions extends RequestInit {
headers?: Record<string, string>;
/** Temps max (ms) avant d'annuler la requête. Défaut 15000. */
timeoutMs?: number;
/** Nombre max de tentatives sur erreur réseau / 429 / 502 / 503 / 504. Défaut 3. */
maxRetries?: number;
}Appel REST authentifié brut vers ${baseUrl}${path}, utilisé en interne par toutes les méthodes de haut niveau du SDK (sendMessage, setStatus, tous les managers…). Vous n'en avez besoin directement que pour un endpoint non encore couvert par une méthode dédiée.
Comportement détaillé :
- Ajoute automatiquement
Authorization: Bearer <token>,Content-Type: application/json,X-Bloume-SDK: trueet unUser-Agent— vosheaderspersonnalisés sont fusionnés par-dessus (ils ne peuvent pas écraserAuthorization). - Limite la concurrence côté client à 5 requêtes simultanées maximum : au-delà, les appels supplémentaires sont mis en file d'attente (FIFO) et débloqués un par un dès qu'un slot se libère. Ceci protège votre bot contre un bannissement pour abus si une boucle envoie trop de requêtes d'un coup.
- Sur timeout (
timeoutMsdépassé) ou erreur réseau (TypeErrordefetch), retente automatiquement avec un backoff exponentiel (500ms × 2^tentative, plafonné à 30s), jusqu'àmaxRetriesfois. - Sur réponse
429,502,503ou504, retente également — en respectant le headerRetry-Afterde la réponse s'il est présent (en secondes), sinon le même backoff exponentiel. - Depuis la 1.5.0, un
429enregistre aussi un délai de blocage partagé par bucket (méthode + type de ressource + ID « majeur ») : toute autre requête vers le même bucket patiente automatiquement, sans affecter les requêtes vers des ressources différentes — voir Gestion des erreurs — buckets de rate-limit. - Une réponse
204 No Contentrésout avecnull. - Toute autre réponse non-OK (après épuisement des tentatives) rejette avec une
BloumeChatAPIError(ou sa sous-classeRateLimitErrorpour un429) exposantstatus/path/body. - Un timeout définitivement épuisé rejette avec une
BloumeChatTimeoutError.
Voir aussi
La page Gestion des erreurs détaille toute la hiérarchie (BloumeChatError, BloumeChatAPIError, RateLimitError, BloumeChatAuthError, BloumeChatTimeoutError) avec des exemples instanceof.
// Endpoint non encore couvert par une méthode dédiée du SDK
const stats = await client.apiCall(`/servers/${guildId}/stats`);
console.log(stats.memberCount);
// Avec options personnalisées
const data = await client.apiCall("/servers", {
method: "POST",
body: JSON.stringify({ name: "Mon serveur" }),
timeoutMs: 5000,
maxRetries: 1,
});// Endpoint non encore couvert par une méthode dédiée du SDK
const stats = await client.apiCall(`/servers/${guildId}/stats`);
console.log(stats.memberCount);
// Avec options personnalisées
const data = await client.apiCall("/servers", {
method: "POST",
body: JSON.stringify({ name: "Mon serveur" }),
timeoutMs: 5000,
maxRetries: 1,
});Body toujours en JSON string
apiCall ne sérialise pas options.body pour vous — passez toujours JSON.stringify(...) explicitement, comme dans l'exemple ci-dessus. Le SDK force déjà Content-Type: application/json.
Messagerie
sendMessage(channelId, options)
sendMessage(
channelId: string,
options: string | EmbedBuilder | { content?: string; embeds?: Array<EmbedBuilder | EmbedPayload | Record<string, unknown>>; replyToId?: string }
): Promise<Message>| Paramètre | Type | Requis | Description |
|---|---|---|---|
channelId | string | Oui | Identifiant public du salon cible. |
options | string | EmbedBuilder | object | Oui | Contenu du message — voir les trois formes acceptées ci-dessous. |
Méthode bas niveau utilisée en interne par Channel.send() et Message.reply() — préférez ces raccourcis dans votre code, client.sendMessage() reste utile quand vous n'avez qu'un ID de salon sous la main (par exemple stocké en base de données) sans avoir fetché l'objet Channel.
Trois formes acceptées pour options :
| Forme | Exemple | Résultat |
|---|---|---|
string | "Bonjour !" | Message texte simple. |
EmbedBuilder | new EmbedBuilder().setTitle("Titre") | Message avec un seul embed, sans texte. |
object | { content: "Voici :", embeds: [embed], replyToId: "msg_id" } | Combine texte, embeds multiples, et réponse à un message existant. |
Comportement :
- Envoie via le WebSocket (
message:send) avec unnoncealéatoire, puis attend l'échomessage:newcorrespondant à ce nonce pour résoudre avec l'objetMessageréellement créé côté serveur. - Rejette immédiatement avec une
BloumeChatAuthErrorsi le socket n'est pas connecté (avantlogin()ou aprèsdestroy()). - Rejette immédiatement avec une
BloumeChatAuthError("sendMessage requires non-empty content or at least one embed.") sicontentest vide et qu'aucun embed n'est fourni — évite d'attendre inutilement un accusé de réception qui n'arrivera jamais. - Rejette avec
Error("sendMessage timeout after 10s")si aucun accusé n'est reçu dans les 10 secondes (salon inexistant, permissions insuffisantes côté serveur, etc.).
// Texte simple
await client.sendMessage(channelId, "Bonjour tout le monde !");
// Embed seul
import { EmbedBuilder } from "bloumechat";
const embed = new EmbedBuilder().setTitle("Annonce").setColor("#5e72e4");
await client.sendMessage(channelId, embed);
// Texte + embed + réponse à un message
await client.sendMessage(channelId, {
content: "Voici les détails :",
embeds: [embed],
replyToId: originalMessage.id,
});// Texte simple
await client.sendMessage(channelId, "Bonjour tout le monde !");
// Embed seul
const { EmbedBuilder } = require("bloumechat");
const embed = new EmbedBuilder().setTitle("Annonce").setColor("#5e72e4");
await client.sendMessage(channelId, embed);
// Texte + embed + réponse à un message
await client.sendMessage(channelId, {
content: "Voici les détails :",
embeds: [embed],
replyToId: originalMessage.id,
});Voir aussi : EmbedBuilder, Message, Channel.
Présence (Rich Presence / RPC)
Trois méthodes pour contrôler le statut affiché du bot — voir aussi l'exemple Rich Presence (RPC).
interface ActivityData {
type: "using" | "browsing" | "listening" | "playing";
name: string;
details?: string;
startedAt?: number;
}
interface PresenceData {
status?: "online" | "idle" | "dnd" | "invisible";
activity?: ActivityData | null;
}setActivity(activity)
setActivity(activity: ActivityData | null): Promise<void>| Paramètre | Type | Requis | Description |
|---|---|---|---|
activity | ActivityData | null | Oui | Description de l'activité. Passez null pour l'effacer. |
Émet activity:update sur le socket. name est tronqué côté client à 128 caractères, details à 64 — pas d'erreur levée en cas de dépassement, la chaîne est simplement coupée. Si startedAt n'est pas fourni, il vaut Date.now() (utile pour afficher une durée écoulée côté client BloumeChat). Rejette avec une BloumeChatAuthError si appelé avant login().
// Définir une activité
await client.setActivity({ type: "playing", name: "avec le BloumeChat SDK" });
// Avec des détails
await client.setActivity({
type: "listening",
name: "Radio BloumeChat",
details: "Épisode 12",
});
// Effacer l'activité
await client.setActivity(null);// Définir une activité
await client.setActivity({ type: "playing", name: "avec le BloumeChat SDK" });
// Avec des détails
await client.setActivity({
type: "listening",
name: "Radio BloumeChat",
details: "Épisode 12",
});
// Effacer l'activité
await client.setActivity(null);setStatus(status)
setStatus(status: "online" | "idle" | "dnd" | "invisible"): Promise<void>| Paramètre | Type | Requis | Description |
|---|---|---|---|
status | "online" | "idle" | "dnd" | "invisible" | Oui | Le nouveau statut de présence. |
Émet presence:update sur le socket, met à jour client.user.status localement en optimiste, puis persiste le changement via PATCH /users/settings. Rejette avec une BloumeChatAuthError si le socket n'est pas connecté.
await client.setStatus("dnd");await client.setStatus("dnd");setPresence(data)
setPresence(data: PresenceData): Promise<void>| Paramètre | Type | Requis | Description |
|---|---|---|---|
data | PresenceData | Oui | { status?, activity? } — les deux champs sont optionnels et indépendants. |
Combine setStatus() et setActivity() en un seul appel pratique. N'appelle setStatus() que si data.status est fourni, et setActivity() que si data.activity !== undefined (donc passer explicitement activity: null efface bien l'activité, alors qu'omettre le champ la laisse inchangée).
await client.setPresence({
status: "online",
activity: { type: "playing", name: "avec le BloumeChat SDK" },
});await client.setPresence({
status: "online",
activity: { type: "playing", name: "avec le BloumeChat SDK" },
});Messages privés
createDM(userId)
createDM(userId: string): Promise<DMChannel>| Paramètre | Type | Requis | Description |
|---|---|---|---|
userId | string | Oui | Identifiant public de l'utilisateur destinataire. |
Ouvre (ou récupère, si déjà existant) un salon de messages privés avec cet utilisateur via GET /channels/dm/:userId. Retourne un DMChannel, qui expose sa propre méthode .send().
const dm = await client.createDM("USER_PUBLIC_ID");
await dm.send("Bonjour ! Je suis le bot de modération de ce serveur.");const dm = await client.createDM("USER_PUBLIC_ID");
await dm.send("Bonjour ! Je suis le bot de modération de ce serveur.");Voir aussi : DMChannel.
Invitations
fetchInvite(code)
fetchInvite(code: string): Promise<Invite>| Paramètre | Type | Requis | Description |
|---|---|---|---|
code | string | Oui | Le code d'invitation (partie après bloumechat.com/invite/). |
Résout une invitation en lecture seule — utile pour inspecter le serveur cible avant d'agir, ou pour obtenir un objet Invite que vous pourrez révoquer si le bot dispose de MANAGE_INVITES sur ce serveur.
Un bot ne peut pas rejoindre un serveur seul
Contrairement à un utilisateur humain, un bot ne peut pas "accepter" une invitation par lui-même — il doit être ajouté à un serveur par un utilisateur disposant des permissions nécessaires (via le portail OAuth2, par exemple). fetchInvite() sert uniquement à consulter ou révoquer, jamais à rejoindre.
const invite = await client.fetchInvite("abc123");
console.log(`Invitation vers ${invite.guild?.name}, ${invite.uses} utilisations`);const invite = await client.fetchInvite("abc123");
console.log(`Invitation vers ${invite.guild?.name}, ${invite.uses} utilisations`);Voir aussi : Invite.
Recherche
searchMessages(channelId, query, options?)
searchMessages(channelId: string, query: string, options?: { limit?: number }): Promise<Message[]>| Paramètre | Type | Requis | Défaut | Description |
|---|---|---|---|---|
channelId | string | Oui | — | Identifiant public du salon à fouiller. |
query | string | Oui | — | Terme(s) de recherche en texte libre. |
options.limit | number | Non | (serveur) | Nombre maximum de résultats. Omis de la requête si non fourni. |
Recherche des messages par mot-clé dans un salon via GET /chat/:channelId/search. Retourne des instances Message du SDK (depuis la 1.5.0 — c'était auparavant du JSON brut) — [] si aucun résultat.
const results = await client.searchMessages(channelId, "hello world", { limit: 20 });
console.log(`${results.length} résultat(s) trouvé(s)`);const results = await client.searchMessages(channelId, "hello world", { limit: 20 });
console.log(`${results.length} résultat(s) trouvé(s)`);Commandes
loadCommands(directoryPath) (4.2.0+)
loadCommands(directoryPath: string): Promise<Collection<string, Command>>
interface Command {
name: string;
aliases?: string[];
execute: (...args: any[]) => unknown | Promise<unknown>;
[key: string]: unknown;
}| Paramètre | Type | Requis | Description |
|---|---|---|---|
directoryPath | string | Oui | Dossier contenant les fichiers de commande (non récursif). |
Charge chaque fichier .js/.cjs/.mjs/.ts du dossier et ajoute au client.commands celles dont l'export par défaut (ou nommé command) a un name (string) et un execute (fonction). Remplace le classique fs.readdirSync + new Map() que les bots réécrivaient tous à la main. Un fichier invalide ou en erreur est ignoré avec un console.warn — il n'interrompt pas le chargement des autres.
Utilise import() dynamique (pas require), donc compatible que le bot soit en CommonJS ou en ESM. Charger un fichier .ts directement suppose que le runtime du bot a déjà un loader TypeScript enregistré (tsx, ts-node/esm…) — sans ça, ces fichiers échouent au chargement (avec le même console.warn) plutôt que de planter le processus.
Retour : Promise<Collection<string, Command>> — le client.commands mis à jour (utile pour chaîner). Peut être appelé plusieurs fois (un dossier par plugin, par exemple) : chaque appel ajoute au lieu de remplacer.
Ne dispatche rien automatiquement
loadCommands() charge et stocke — il ne branche pas de préfixe, de cooldown, ni d'appel automatique sur messageCreate. Ces conventions varient trop d'un bot à l'autre pour être imposées par le SDK ; câblez le dispatch vous-même comme dans l'exemple ci-dessous.
// commands/ping.ts
import type { Command } from "bloumechat";
export default {
name: "ping",
execute: (message) => message.reply("Pong !"),
} satisfies Command;
// index.ts
await client.loadCommands(path.join(__dirname, "commands"));
client.on("messageCreate", (message) => {
if (!message.content.startsWith("!")) return;
const name = message.content.slice(1).split(" ")[0];
client.commands.get(name)?.execute(message, client);
});// commands/ping.js
module.exports = {
name: "ping",
execute: (message) => message.reply("Pong !"),
};
// index.js
await client.loadCommands(path.join(__dirname, "commands"));
client.on("messageCreate", (message) => {
if (!message.content.startsWith("!")) return;
const name = message.content.slice(1).split(" ")[0];
client.commands.get(name)?.execute(message, client);
});Voir aussi : Collection.
Sécurité de l'hôte
Le constructeur vérifie que baseUrl et socketUrl pointent vers bloumechat.com, api.bloumechat.com, ou localhost (protocole https: obligatoire, sauf en local). Si ce n'est pas le cas — par exemple une sous-classe qui redéfinit ces champs — un avertissement est loggé sur console.warn à chaque construction d'instance :
[BloumeChat SDK] baseUrl/socketUrl points to an unrecognized host ("evil.example.com"). Your bot token will be sent to this host — make sure this is intentional.Un second avertissement distinct est loggé si le protocole n'est pas https: (hors localhost/127.0.0.1), car le token serait alors transmis en clair. Ces avertissements sont informatifs uniquement — ils ne bloquent jamais l'exécution.
Sérialisation sûre
BloumeChat redéfinit son inspection Node.js (util.inspect) et toJSON() pour toujours remplacer le token interne par "[REDACTED]" (ou null s'il n'est pas encore défini). Concrètement :
console.log(client); // le token n'apparaît jamais, même après login()
JSON.stringify(client); // idemconsole.log(client); // le token n'apparaît jamais, même après login()
JSON.stringify(client); // idemCeci évite qu'un token de bot ne fuite accidentellement dans des logs applicatifs, un rapport de crash, ou une sortie CI.
Exemple complet
import { BloumeChat, EmbedBuilder } from "bloumechat";
const client = new BloumeChat();
client.on("ready", async () => {
console.log(`Connecté en tant que ${client.user?.tagString}`);
await client.setActivity({ type: "playing", name: "avec le BloumeChat SDK" });
});
client.on("messageCreate", (msg) => {
if (msg.author.bot) return;
if (msg.content === "!ping") {
msg.reply("🏓 Pong !");
}
if (msg.content === "!uptime") {
msg.reply(`En ligne depuis ${Math.floor((client.uptime ?? 0) / 1000)}s`);
}
});
client.on("disconnect", (reason) => console.warn("Déconnecté :", reason));
client.on("reconnect", () => console.log("Reconnecté !"));
client.on("error", (err) => console.error("Erreur socket :", err));
await client.login(process.env.BOT_TOKEN!);const { BloumeChat, EmbedBuilder } = require("bloumechat");
const client = new BloumeChat();
client.on("ready", async () => {
console.log(`Connecté en tant que ${client.user?.tagString}`);
await client.setActivity({ type: "playing", name: "avec le BloumeChat SDK" });
});
client.on("messageCreate", (msg) => {
if (msg.author.bot) return;
if (msg.content === "!ping") {
msg.reply("🏓 Pong !");
}
if (msg.content === "!uptime") {
msg.reply(`En ligne depuis ${Math.floor((client.uptime || 0) / 1000)}s`);
}
});
client.on("disconnect", (reason) => console.warn("Déconnecté :", reason));
client.on("reconnect", () => console.log("Reconnecté !"));
client.on("error", (err) => console.error("Erreur socket :", err));
client.login(process.env.BOT_TOKEN);Voir aussi
- Événements — liste complète des événements émis par le client.
- Permissions — guide d'usage du système de permissions.
- Gestion des erreurs —
BloumeChatAuthError,BloumeChatAPIError,RateLimitError,BloumeChatTimeoutError, et les buckets de rate-limit. - EmbedBuilder — construire des embeds riches pour
sendMessage. - Message — l'objet retourné par
sendMessage()et reçu viamessageCreate. - GuildManager, ChannelManager, MemberManager, UserManager — les managers exposés par
client.guilds,client.channels,client.members,client.users.
