Este artículo es una traducción de Adrian Chrysanthou, hecha por Gabriella Martinez. Puedes encontrar el artículo original aquí.
Sería genial escucharte en nuestro Discord, puedes contarnos tus ideas, comentarios, sugerencias y dejarnos saber lo que necesitas.
Si prefieres puedes escribirnos a @web3dev_eshttps://twitter.com/web3dev_es en Twitter.
En este tutorial, aprenderás a cómo usar el proveedor StableDiffusion y Discord.js para crear un bot de Discord que genera arte único desde prompts textuales.
Prerrequisitos y Software necesario
- Conocimiento previo de cómo crear un Bot en Discord
- Node.js
- Un editor de código, estaré usando Visual Studio Code
- Una cuenta de Discord y Discord Developer con una aplicación de Discord que ya esté registrada
- Requisitos Avanzados: Stable Diffusion Docker (Imágenes Docker) o RunPod (la nube GPU) para ejecutar tu propio UI de Stable Diffusion Web en tu máquina o nube. Puedes ver los comandos avanzados que usan este método.
Paso 1: Clona y Configura
Primero, clona este repositorio en tu máquina y ábrelo con el editor de códigos que usas. Este repositorio es un template para cualquier Bot de Discord Discord.js. Es similar a la guía de Discord.js para construir tu primer bot.
Paso 2: Establece el Entorno
Para empezar a codear tu bot, abre tu editor de códigos preferido.
Abre tu terminal y ejecuta yarn
para instalar los paquetes desde package.json
Luego, renombra .env-sample
a .env
y dentro de DISCORD_TOKEN=
coloca el token de tu Bot desde la sección de Discord Developer.
Por último, vamos a ver el archivo package.json
para desarrollarlo. Abre package.json
Te darás cuenta de dos comandos debajo de "scripts"
"deploy": "node deploy-commands.js",
"start": "node index.js"
Estos son los comandos para desplegar nuevos comandos para tu bot e iniciarlo, subsecuentemente.
Luego crea una carpeta llamada Commands. Dentro de esa carpeta, haz un archivo generate.js
y un archivo avanzado .js
.
Con todo configurado, ¡Vamos a empezar a desarrollar!
Paso 3: Codeando el Bot
Arranquemos con nuestro bot. Para hacerlo, abre el terminal, ve al directorio de la carpeta de tu proyecto y ejecuta yarn start
Deberías ver un mensaje que diga algo así como “Ready!”. Si no lo ves, por favor, asegúrate que todos los paquetes estén instalados y que no tengas errores de sintaxis en package.json
.
Primero, vamos a abrir generate.js
y comencemos.
Coloca todos los paquetes requeridos en la parte superior del archivo. Asegúrate que dotenv
esté inicializado en primer lugar.
const dotenv = require('dotenv');
dotenv.config();
const WebSocket = require('ws');
const { SlashCommandBuilder, AttachmentBuilder } = require('discord.js');
const createHash = require('hash-generator');
Vamos a crear la función que retorna el hash generado. Este hash es específico al demo de StableDiffusion 1.5, siendo usado por el proveedor aquí
El demo de StableDiffusion 1.5 requiere que el hash sea enviado a través de Web Sockets cuando sea pedido.
Para darte un ejemplo, abajo te darás cuenta que fn_index:2
y session_hash
están dentro de “session_hash”. Luego, vamos a incluir esos parámetros en nuestro archivo generate.js
function generateHash() {
let hash = createHash(12)
return {
session_hash: hash,
fn_index: 2
}
}
Ahora necesitamos construir SlashCommandBuilder
.
Modulizaremos este comando con module.exports
module.exports = {
data: new SlashCommandBuilder()
.setName('generate')
.setDescription('Generates an image from a text prompt using Stable Diffusion 1.5')
.addStringOption(option => option
.setName('prompt')
.setDescription('generate image prompt')
),
};
Pero, ¡espera! No ejecutes el código aún.
Te habrás dado cuenta que no tenemos un execute
al final. Vamos a añadirlo a continuación:
async execute(interaction) {
},
};
Ahora, vamos a juntarlo todo.
Luego, necesitamos obtener la interacción y el string para ejecutar el comando /generate prompt
const prompt = interaction.options.getString('prompt');
console.log('What to generate?', prompt);
Ahora vamos a terminar la siguiente pieza de código en try/catch
y definir unas pocas constantes con una respuesta inmediata a la interacción.
La primera constante es para WebSockets. Se conecta directamente a /queue/join
y nos coloca en la lista de espera del UI de la Web cuando se genere el prompt.
try {
interaction.reply("I'm generating...");
const ws = new WebSocket('wss://runwayml-stable-diffusion-v1-5.hf.space/queue/join');
const hash = generateHash();
} catch (error) {
interaction.reply(error);
console.error(error);
}console.error(error);
Ahora, ¡deberíamos aplicar nuestro Web Socket y generar cosas divertidas!
ws.on('open', () => {});
ws.on('message', async (message) => {});
ws.on('error', async (error) => {
interaction.editReply({
content: 'An error occurred while generating the image',
});
console.error(error);
});
Tu generate.js
debería ser similar a este
module.exports = {
data: new SlashCommandBuilder()
.setName('generate')
.setDescription('Generates an image from a text prompt using Stable Diffusion 1.5')
.addStringOption(option => option
.setName('prompt')
.setDescription('generate image prompt')
),
async execute(interaction) {
const prompt = interaction.options.getString('prompt');
console.log('What to generate?', prompt);
try {
interaction.reply("I'm generating...");
const ws = new WebSocket('wss://runwayml-stable-diffusion-v1-5.hf.space/queue/join');
const hash = generateHash();
ws.on('open', () => {});
ws.on('message', async (message) => {});
ws.on('error', async (error) => {
interaction.editReply({
content: 'An error occurred while generating the image',
});
console.error(error);
});
} catch (error) {
interaction.reply(error);
console.error(error);
}
},
};
Dentro de ws.on(‘message’)
vamos primero a JSON.parse
, el message
regresado y determinar si msg
= ‘send_data’ o si = ‘process_completed’.
Nota: más mensajes se envían, pero estos dos son el foco principal de este tutorial.
Para hacerlo, primero crearemos una constante msg
que contiene un JSON.parse
del mensaje regresado:
const msg = JSON.parse(`${message}`);
if (msg.msg === 'send_data') {
} else if (msg.msg === 'process_completed') {
} else {}
Luego, necesitamos añadir la ‘data’ que queremos enviar. Crearemos una constante de datos que contenga a ambos, los prompts de los usuarios de Discord envueltos en un array, así como el hash constante que hicimos.
const data = {
data: [prompt],
...hash,
};
ws.send(JSON.stringify(data));
Te habrás dado cuenta que también añadí un ws.send
. Esto es para, específicamente, enviar los paquetes de datos con todo lo que se necesita para comenzar la generación del proceso.
Ahora, ve tu código y asegúrate que coincida con este:
const dotenv = require('dotenv');
dotenv.config();
const WebSocket = require('ws');
const {
SlashCommandBuilder,
AttachmentBuilder
} = require('discord.js');
const createHash = require('hash-generator');
function generateHash() {
let hash = createHash(12)
return {
session_hash: hash,
fn_index: 2
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName('generate')
.setDescription('Generates an image from a text prompt using Stable Diffusion 1.5')
.addStringOption(option => option
.setName('prompt')
.setDescription('generate image prompt')
),
async execute(interaction) {
const prompt = interaction.options.getString('prompt');
console.log('What to generate?', prompt);
try {
interaction.reply("I'm generating...");
const ws = new WebSocket('wss://runwayml-stable-diffusion-v1-5.hf.space/queue/join');
const hash = generateHash();
ws.on('open', () => {});
ws.on('message', async (message) => {
const msg = JSON.parse(`${message}`);
if (msg.msg === 'send_data') {
const data = {
data: [prompt],
...hash,
};
ws.send(JSON.stringify(data));
} else if (msg.msg === 'process_completed') {
} else {}
});
ws.on('error', async (error) => {
console.error(error);
interaction.editReply({
content: 'An error occurred while generating the image',
});
});
} catch (error) {
console.error(error);
}
},
};
¡Genial, ya casi estamos terminando!
Ahora vamos a añadir try/catch
y obtener las imágenes enviadas de vuelta luego de msg
= ‘process_completed’
try {
} catch (error) {
console.error(error);
interaction.editReply({
content: 'An error occurred while generating the image',
});
}
Ahora, dentro de try
vamos a obtener el output msg
. Adicionalmente, vamos a crear una constante llamada attachments para el array. Recuerda, vamos a tener varias imágenes enviadas de vuelta.
try {
const results = msg.output.data[0];
const attachments = [];
} catch (error) {
console.error(error);
interaction.editReply({
content: 'An error occurred while generating the image',
});
}
Luego, queremos que se haga un bucle a través de los resultados y dividirlos en nuevas filas para cada ,
encontrada.
Añade los resultados de la constante del buffer y júntalos al AttachmentBuilder de Discord.
Asegúrate de empujar cada attachment al array con attachments.push(attachment)
Tu código debería verse similar a este:
try {
const results = msg.output.data[0];
const attachments = [];
for (let i = 0; i < results.length; i++) {
const data = results[i].split(',')[1];
const buffer = Buffer.from(data, 'base64');
const attachment = new AttachmentBuilder(buffer, {
name: 'generate.png',
});
attachments.push(attachment);
}
} catch (error) {
console.error(error);
interaction.editReply({
content: 'An error occurred while generating the image',
});
}
Por último, ¡debemos enviar los attachments a Discord para que el usuario lo pueda ver!
Nota: estamos usando editReply
ya que originalmente enviamos un reply
cuando comenzamos la generación del proceso.
interaction.editReply({
content: `You asked me for ${prompt}`,
files: attachments,
});
Tu código final para el archivo generate.js
debería verse similar a este:
const dotenv = require('dotenv');
dotenv.config();
const WebSocket = require('ws');
const {
SlashCommandBuilder,
AttachmentBuilder
} = require('discord.js');
const createHash = require('hash-generator');
function generateHash() {
let hash = createHash(12)
return {
session_hash: hash,
fn_index: 2
}
}
module.exports = {
data: new SlashCommandBuilder()
.setName('generate')
.setDescription('Generates an image from a text prompt using Stable Diffusion 1.5')
.addStringOption(option => option
.setName('prompt')
.setDescription('generate image prompt')
),
async execute(interaction) {
const prompt = interaction.options.getString('prompt');
console.log('What to generate?', prompt);
try {
await interaction.reply("I'm generating...");
const ws = new WebSocket('wss://runwayml-stable-diffusion-v1-5.hf.space/queue/join');
const hash = generateHash();
ws.on('open', () => {});
ws.on('message', async (message) => {
const msg = JSON.parse(`${message}`);
if (msg.msg === 'send_hash') {
ws.send(JSON.stringify(hash));
} else if (msg.msg === 'send_data') {
const data = {
data: [prompt],
...hash,
};
ws.send(JSON.stringify(data));
} else if (msg.msg === 'process_completed') {
try {
const results = msg.output.data[0];
const attachments = [];
for (let i = 0; i < results.length; i++) {
const data = results[i].split(',')[1];
const buffer = Buffer.from(data, 'base64');
const attachment = new AttachmentBuilder(buffer, {
name: 'generate.png',
});
attachments.push(attachment);
}
interaction.editReply({
content: `You asked me for ${prompt}`,
files: attachments,
});
} catch (error) {
console.error(error);
await interaction.editReply({
content: 'An error occurred while generating the image',
});
}
}
});
ws.on('error', async (error) => {
console.error(error);
await interaction.editReply({
content: 'An error occurred while generating the image',
});
});
} catch (error) {
console.error(error);
}
},
};
Ahora, ¡vamos a ejecutar el comando y verlo en acción!
¡Eureka! ¡Has generado tu primer par de imágenes usando Stable Diffusion a través de tu propio Bot personalizado de Discord! Este es solo un ejemplo de las cosas posibles que puedes hacer con los comandos de Discord y Web Sockets. Por favor, toma en cuenta que esto es mejor usado para demostraciones y te propongo que alojes tu propia UI Stable Diffusion Web.
Puedes encontrar el código final para el bot de abajo, junto con los Comandos Avanzados que se conectan a la UI de la Web que, tu mismo, alojas en un servicio de la nube GPU o en docker image, usando el paquete request
: https://github.com/f00d4tehg0dz/stable-diffusion-discord-bot-template/tree/final
Gracias por leer. Por favor revisa mi Bot de Discord, Arti e ¡intenta algunos comandos más avanzados!
Discussion (0)