slurpcord
Version:
Simple API wrapper for Discord
316 lines (270 loc) • 11.8 kB
JavaScript
import { getCachedChannel, getCachedGuild, getCachedMember, getCachedUser, userIsIsCache, addGuildToCache, getGuild } from './cache/index.js';
import SlurpcordError from './errors/index.js';
import { commandInteraction, buttonInteraction, RegisterCommands } from './interactions/index.js';
import { guild, user, message as messageType } from "./utils/index.js";
import axios from 'axios';
let sequence = null;
export default class Bot {
#commands = new Map();
#slashCmds = new Map();
#buttons = [];
#guilds = new Map();
#events = new Map();
#connected = false;
constructor(token, prefix = "") {
this.token = token;
this.prefix = prefix;
this.activity = null;
this.#connected = false;
}
command(name, action) {
this.#commands.set(name, action);
}
slashCommand(name, action) {
this.#slashCmds.set(name, action);
}
event(event, action) {
const events = this.#events.get(event);
if (!events) {
this.#events.set(event, [action]);
return;
}
events.push(action);
this.#events.set(event, events);
}
guilds = {
fetch: async (id) => {
if (id) {
const guild = getGuild(id);
if (guild) {
return guild;
}
return null;
} else {
return Array.from(getGuild());
}
},
length: () => {
return Array.from(getGuild()).length;
}
}
users = {
fetch: async (id) => {
if (userIsIsCache(id)) {
return await getCachedUser({ id }, this.token);
} else {
const response = await fetch(`https://discord.com/api/v1/users/${id}`, {
method: 'GET',
headers: {
Authorization: `Bot ${this.token}`,
'Content-Type': 'application/json'
}
})
const data = await response.json();
getCachedUser(data, this.token);
return await user(data, this.token);
}
}
}
channels = {
fetch: async (id) => {
return await getCachedChannel(id, this.token);
}
}
ready(action) {
this.ready = action;
}
async setCommands(commands) {
await RegisterCommands(this.token, this.user.id, commands);
}
setPrefix(prefix) {
this.prefix = prefix;
}
button(action) {
this.#buttons.push(action);
}
setActivity(name, type = 0) {
this.activity = { name, type };
if (this.ws && this.#connected) {
this.ws.send(JSON.stringify({
op: 3,
d: {
since: null,
activities: [this.activity],
status: 'online',
afk: false
}
}));
}
}
async start() {
const WebSocket = await import('ws');
this.ws = new WebSocket.WebSocket('wss://gateway.discord.gg/?v=10&encoding=json');
this.ws.on('close', (code, reason) => {
console.log(`WebSocket closed: ${code} - ${reason}`);
clearInterval(this.heartbeat);
});
this.ws.on('error', (error) => {
throw new SlurpcordError("WebSocket error", error);
});
this.ws.on('message', async (data) => {
const payload = JSON.parse(data);
const { op, t: event, s, d: messageData } = payload;
if (s) sequence = s;
if (op === 10) {
const heartbeatInterval = messageData.heartbeat_interval;
const sendHeartbeat = () => {
this.ws.send(JSON.stringify({
op: 1,
d: sequence
}));
};
sendHeartbeat();
this.heartbeat = setInterval(sendHeartbeat, heartbeatInterval);
this.ws.send(JSON.stringify({
op: 2,
d: {
token: this.token,
intents: 513 | 32768 | 8 | 16 | 2,
properties: {
os: 'linux',
browser: 'bot',
device: 'bot'
},
presence: this.activity ? {
activities: [this.activity],
status: 'online',
afk: false
} : undefined
}
}));
this.#connected = true;
}
if (event === 'MESSAGE_CREATE') {
let message = messageData;
message = await messageType(message, this.token);
if (message.content.startsWith(this.prefix)) {
const [cmd, ...args] = message.content.slice(this.prefix.length).split(/\s+/);
const command = this.#commands.get(cmd);
const input = message.content.split(this.prefix + cmd + " ")[1];
const [guild, userdata, channelData] = await Promise.all([
message.guild_id ? getCachedGuild(message.guild_id, this.token) : null,
getCachedUser(message.author, this.token),
getCachedChannel(message.channel_id, this.token),
]);
message.guild = guild;
message.author = userdata;
message.channel = channelData;
if (command) {
command(message, input);
}
}
if (this.#events.has(event)) {
const [guild, userdata, channelData] = await Promise.all([
message.guild_id ? getCachedGuild(message.guild_id, this.token) : null,
getCachedUser(message.author, this.token),
getCachedChannel(message.channel_id, this.token),
]);
message.guild = guild;
message.author = userdata;
message.channel = channelData;
this.#events.get(event).forEach(func => {
func(message);
})
}
} else if (event === "READY") {
if (this.ready) {
const response = await axios.get("https://discord.com/api/v10/users/@me/guilds", {
headers: {
"Authorization": `Bot ${this.token}`
}
});
if (Array.isArray(response.data)) {
for (let guild of response.data) {
await addGuildToCache(guild.id, guild, this.token);
}
} else console.error("Error fetching guilds:", response);
this.user = messageData.user;
this.user.tag = messageData.user.username + "#" + messageData.user.discriminator;
this.ready();
}
} else if (event === "INTERACTION_CREATE") {
let interaction = messageData;
interaction.isCommand = interaction.type === 2;
interaction.isButton = interaction.type === 3;
if (interaction.type === 2) {
interaction = commandInteraction(interaction);
const commandfunc = this.#slashCmds.get(interaction.data.name);
interaction.options = {};
if (interaction.data.options) {
for (let option of interaction.data.options) {
interaction.options[option.name] = option.value;
}
}
const [channel, userdata, guild, member] = await Promise.all([
getCachedChannel(interaction.channel_id, this.token),
getCachedUser(interaction.guild_id ? interaction.member.user : interaction.user, this.token),
interaction.guild_id ? getCachedGuild(interaction.guild_id, this.token) : null,
getCachedMember(interaction.guild_id, interaction.guild_id ? interaction.member.user : interaction.user, this.token)
]);
interaction.user = userdata;
interaction.channel = channel;
interaction.guild = guild;
interaction.member = member;
if (commandfunc) {
commandfunc(interaction);
}
if (this.#events.has(event)) {
this.#events.get(event).forEach(func => {
func(interaction);
})
}
} else if (interaction.type === 3) {
interaction = buttonInteraction(interaction);
const [channel, userdata, guild, member] = await Promise.all([
getCachedChannel(interaction.channel_id, this.token),
getCachedUser(interaction.member.user, this.token),
getCachedGuild(interaction.guild_id, this.token),
getCachedMember(interaction.guild_id, interaction.member, this.token)
]);
interaction.user = userdata;
interaction.guild = guild;
interaction.member = member;
interaction.channel = channel;
this.#buttons.forEach(func => {
func(interaction);
});
this.#events.get(event).forEach(func => {
func(interaction);
})
}
} else if (event === "GUILD_MEMBER_ADD") {
let member = messageData;
const [guild, user] = await Promise.all([
getCachedGuild(messageData.guild_id, this.token),
getCachedUser(messageData.user, this.token)
]);
const events = this.#events.get(event);
if (events) {
events.forEach(func => {
func(user, guild);
})
}
} else if (event === "GUILD_MEMBER_REMOVE") {
let member = messageData;
const [guild, user] = await Promise.all([
getCachedGuild(member.guild_id, this.token),
getCachedUser(member.user, this.token),
]);
const events = this.#events.get(event);
if (events) {
events.forEach(func => {
func(user, guild);
})
}
} else if (event === "GUILD_CREATE") {
let guild = messageData;
await addGuildToCache(guild.id, guild, this.token);
}});
}
}