WEB3DEV

Cover image for Criar Arte Alimentada Por IA Usando Prompts De Texto Com Seu Próprio Bot Personalizado Do Discord.Js
Banana Labs
Banana Labs

Posted on

Criar Arte Alimentada Por IA Usando Prompts De Texto Com Seu Próprio Bot Personalizado Do Discord.Js

Neste tutorial, você aprenderá como usar um provedor StableDiffusion e o Discord.js para criar um Bot do Discord que gera uma arte única a partir de prompts de texto.

Ai Image Imagem gerada com o prompt "Casa voadora fotorrealista, muitos detalhes, ultra detalhada, renderizada com o render Octane, por Alexander Jansson".

Pré-Requisitos E Software Necessários.

Passo 1: Clonar e Configurar.

Primeiro, clone este repositório em sua máquina e abra-o com seu editor de código. Este repositório é um modelo básico para qualquer Bot do Discord usando o Discord.js. É similar ao guia Discord.js para construir o seu primeiro bot.

cmd prompt

Passo 2: Configurar o Ambiente

Para começar a programar seu bot, abra o seu editor de código preferido.

vscode

Abra o seu Terminal e execute o comando yarn para instalar os pacotes do arquivo package.json.

terminal

"Em seguida, renomeie o arquivo .env-sample para .env e, dentro dele, no campo DISCORD_TOKEN=, coloque o token do seu bot da seção Bot do desenvolvedor do Discord.

.env

Por último, vamos dar uma olhada no arquivo package.json para desenvolvimento. Abra o arquivo package.json.

Você notará dois comandos dentro de "scripts".

"deploy": "node deploy-commands.js",
"start": "node index.js"
Enter fullscreen mode Exit fullscreen mode

Esses são os comandos para implantar novos comandos para o seu bot e iniciar o bot posteriormente.
Em seguida, crie uma pasta chamada "Commands".
Dentro dessa pasta, crie um arquivo generate.js e um arquivo advanced.js.

diretórios

Com tudo configurado, vamos começar o desenvolvimento!

Passo 3: Codificando o Bot.

Vamos iniciar o nosso bot. Para fazer isso, abra o terminal, vá para o diretório da pasta do seu projeto e execute o comando yarn start.

terminal

Você deverá ver uma mensagem ao longo das linhas de como "Ready!" (pronto). Se essa mensagem não aparecer, verifique se todos os pacotes estão instalados e se não há erros de sintaxe no arquivo package.json.

Vamos abrir o arquivo generate.js primeiro e começar.

Coloque todos os pacotes necessários na parte superior do arquivo. Certifique-se de que o dotenv está inicializado primeiro e antes de tudo.

const dotenv = require('dotenv');
dotenv.config();
const WebSocket = require('ws');
const { SlashCommandBuilder, AttachmentBuilder } = require('discord.js');
const createHash = require('hash-generator');
Enter fullscreen mode Exit fullscreen mode

Vamos criar uma função que retorna um hash gerado. Esse hash é específico para a demonstração StableDiffusion 1.5 usada pelo provedor aqui:

A demonstração Stable Diffusion 1.5 requer um hash que deve ser enviado através de Web Sockets quando solicitado.

Para fornecer um exemplo, abaixo você verá que tanto fn_index:2 quanto session_hash estão dentro de "session_hash". Em seguida, incluiremos esses parâmetros em nosso arquivo generate.js.

stable diffusion

function generateHash() {
  let hash = createHash(12)
  return {
    session_hash: hash,
    fn_index: 2
  }
}
Enter fullscreen mode Exit fullscreen mode

Agora, precisamos construir um SlashCommandBuilder.

Vamos modularizar esse comando com 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')
    ),
}; 
Enter fullscreen mode Exit fullscreen mode

Mas espere! Não execute o código ainda.

Você pode ter percebido que não temos um execute no final. Vamos adicioná-lo a seguir.

async execute(interaction) {
  },
};
Enter fullscreen mode Exit fullscreen mode

Vamos juntar tudo agora.

vscode

Em seguida, queremos buscar a interação e a string a partir da execução do comando /generate prompt.

const prompt = interaction.options.getString('prompt');
console.log('What to generate?', prompt);
Enter fullscreen mode Exit fullscreen mode

Agora vamos envolver o próximo trecho de código em um try/catch e definir algumas constantes com uma resposta imediata à interação.

A primeira constante é para Web Sockets. Isso conecta diretamente ao /queue/join e nos coloca na fila da interface do usuário da Web ao receber um prompt de geração.

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);
Enter fullscreen mode Exit fullscreen mode

Agora devemos aplicar nossos Web Sockets e fazer coisas divertidas!

ws.on('open', () => {});
ws.on('message', async (message) => {});
ws.on('error', async (error) => {
   interaction.editReply({
      content: 'Ocorreu um erro ao gerar a imagem',
   });
   console.error(error);
});  
Enter fullscreen mode Exit fullscreen mode

Seu arquivo generate.js deve estar parecido com isso agora.

module.exports = {
      data: new SlashCommandBuilder()
        .setName('generate')
        .setDescription('Gera uma imagem a partir de um prompt de texto usando o Stable Diffusion 1.5')
        .addStringOption(option => option
          .setName('prompt')
          .setDescription('generate image prompt')
          ),
      async execute(interaction) {
        const prompt = interaction.options.getString('prompt');
        console.log('O que Gerar?', prompt);

        try {
          interaction.reply("Estou gerando...");

          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: 'Ocorreu um erro ao gerar a imagem',
            });
            console.error(error);
          });
        } catch (error) {
            interaction.reply(error);
            console.error(error);
        }
      },
};
Enter fullscreen mode Exit fullscreen mode

Dentro do ws.on('message'), primeiro, faremos um JSON.parse na message (mensagem) retornada e determinaremos se o valor de msg = 'send_data' ou se é ='process_completed'.

Note que mais mensagens são enviadas, mas essas duas são o foco principal deste tutorial.

Para fazer isso, primeiro criamos uma constante chamada msg que contém um JSON.parse da mensagem retornada:

const msg = JSON.parse(`${message}`);
if (msg.msg === 'send_data') {
} else if (msg.msg === 'process_completed') {
} else {}
Enter fullscreen mode Exit fullscreen mode

Em seguida, queremos adicionar os 'dados' que queremos enviar. Criaremos uma constante de dados contendo tanto a solicitação dos usuários do Discord envolvida em uma matriz quanto a constante hash que fizemos.

const data = {
   data: [prompt],
   ...hash,
};
ws.send(JSON.stringify(data));
Enter fullscreen mode Exit fullscreen mode

Você pode ter percebido que adicionei um ws.send também. Isso é especificamente para enviar o pacote de dados com tudo o que é necessário para iniciar o processo de geração.
Agora, dê uma olhada em seu código e verifique se ele corresponde a isso:

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('Gera uma imagem a partir de um prompt de texto usando o Stable Diffusion 1.5')
    .addStringOption(option => option
      .setName('prompt')
      .setDescription('generate image prompt')
    ),
  async execute(interaction) {

    const prompt = interaction.options.getString('prompt');
    console.log('O que Gerar?', prompt);

    try {
      interaction.reply("Estou gerando...");

      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: 'Ocorreu um erro ao gerar a imagem',
        });
      });
    } catch (error) {
      console.error(error);
    }

  },
};
Enter fullscreen mode Exit fullscreen mode

Ótimo! Você está quase lá!
Agora vamos adicionar um try/catch e a buscar as imagens que nos são enviadas depois de msg = ‘process_completed’.

try {

} catch (error) {
       console.error(error);
       interaction.editReply({
          content: 'Ocorreu um erro ao gerar a imagem',
       });
}
Enter fullscreen mode Exit fullscreen mode

A seguir, dentro do bloco try, vamos buscar o resultado da variável msg. Além disso, estamos criando uma constante chamada ‘attachments’ para um array (matriz). Lembre-se de que várias imagens serão enviadas de volta para nós.

try {
     const results = msg.output.data[0];
     const attachments = [];
} catch (error) {
       console.error(error);
       interaction.editReply({
          content: 'Ocorreu um erro ao gerar a imagem',
       });
}
Enter fullscreen mode Exit fullscreen mode

Em seguida, queremos percorrer os resultados e dividi-los em novas linhas para cada vírgula , encontrada.

Adicione ao buffer a constante ‘results’ e anexe-as ao AttachmentBuilder do Discord.

Certifique-se de adicionar cada anexo no array usando attachments.push(attachment).

Seu código deve se parecer com o seguinte:

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: 'Ocorreu um erro ao gerar a imagem',
       });
}
Enter fullscreen mode Exit fullscreen mode

Por fim, devemos enviar os anexos para o Discord para que o usuário possa visualizá-los!

Observação: estamos usando editReply porque originalmente enviamos uma resposta (reply) quando iniciamos o processo de geração.

interaction.editReply({
           content: `You asked me for ${prompt}`,
           files: attachments,
});
Enter fullscreen mode Exit fullscreen mode

O seu código final para o arquivo generate.js deve ser semelhante ao seguinte:

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('Gera uma imagem a partir de um prompt de texto usando o Stable Diffusion 1.5')
    .addStringOption(option => option
      .setName('prompt')
      .setDescription('generate image prompt')
    ),
  async execute(interaction) {

    const prompt = interaction.options.getString('prompt');
    console.log('O que gerar?', 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: 'Ocorreu um erro ao gerar a imagem',
            });
          }
        }
      });

      ws.on('error', async (error) => {
        console.error(error);
        await interaction.editReply({
          content: 'Ocorreu um erro ao gerar a imagem'',
        });
      });
    } catch (error) {
      console.error(error);
    }

  },
};
Enter fullscreen mode Exit fullscreen mode

Agora, vamos executar o comando e vê-lo em ação!

resultados

Sucesso! Você gerou seu primeiro conjunto de imagens usando o Stable Diffusion através do seu próprio Bot personalizado do Discord! Este é apenas um exemplo do que é possível com comandos do Discord e Web Sockets. Por favor, observe que isso é melhor usado para fins de demonstração, e eu recomendo que você hospede sua própria interface do usuário da web do Stable Diffusion.

Você pode encontrar o código final do bot abaixo, juntamente com um comando avançado que se conecta a uma interface do usuário da web que você mesmo hospeda com um serviço de nuvem GPU ou uma imagem do docker, todos utilizando o pacote request: https://github.com/f00d4tehg0dz/stable-diffusion-discord-bot-template/tree/final

Obrigado por ler. Por favor, confira meu bot do Discord, Arti, e experimente alguns comandos mais avançados em ação!



Esse artigo é uma tradução feita por @bananlabs. Você pode encontrar o artigo original aqui.

Top comments (0)