require('dotenv').config(); const { Client, GatewayIntentBits, Partials, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder, TextInputBuilder, TextInputStyle, PermissionsBitField, ChannelType } = require('discord.js'); const fs = require('fs'); const TOKEN = process.env.TOKEN; let pixKey = process.env.PIX; const DEFAULT_CHANNEL = process.env.DEFAULT_CHANNEL_ID || ''; const produtosPath = './produtos.json'; const comprasPath = './compras.json'; if (!fs.existsSync(produtosPath)) fs.writeFileSync(produtosPath, '{}'); if (!fs.existsSync(comprasPath)) fs.writeFileSync(comprasPath, '{}'); const readJSON = path => { try { return JSON.parse(fs.readFileSync(path, 'utf8')); } catch { return {}; } } const saveJSON = (path, data) => { fs.writeFileSync(path, JSON.stringify(data, null, 2)); } const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, GatewayIntentBits.DirectMessages ], partials: [Partials.Channel] }); client.once('clientReady', () => { console.log(`🔥 Bot online como ${client.user.tag}`); }); // --- Menu admin --- client.on('messageCreate', async message => { if (message.author.bot) return; const produtos = readJSON(produtosPath); if (!message.guild) { if (Object.keys(produtos).length === 0) return message.reply('🚫 Não há produtos disponíveis no momento.'); const rows = []; for (const name in produtos) { const p = produtos[name]; rows.push(new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId(`buy_${name}`) .setLabel(`${p.name} - R$${p.value}`) .setStyle(ButtonStyle.Primary) )); } return message.reply({ content: '🛒 Produtos disponíveis:', components: rows }); } if (!message.member.permissions.has(PermissionsBitField.Flags.Administrator)) return; if (message.content === '!menu') { const row1 = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId('admin_add_product').setLabel('➕ Adicionar Produto').setStyle(ButtonStyle.Primary), new ButtonBuilder().setCustomId('admin_remove_product').setLabel('🗑️ Remover Produto').setStyle(ButtonStyle.Danger), new ButtonBuilder().setCustomId('admin_update_pix').setLabel('💰 Atualizar Pix').setStyle(ButtonStyle.Secondary), new ButtonBuilder().setCustomId('admin_confirm_purchase').setLabel('✅ Confirmar Compra').setStyle(ButtonStyle.Success), new ButtonBuilder().setCustomId('admin_report').setLabel('📊 Relatório de Vendas').setStyle(ButtonStyle.Secondary) ); await message.channel.send({ content: '**🛠️ Menu do Administrador**', components: [row1] }); } }); // --- Interações --- client.on('interactionCreate', async interaction => { const produtos = readJSON(produtosPath); const compras = readJSON(comprasPath); if (interaction.isButton()) { const id = interaction.customId; // --- Admin --- if (id.startsWith('admin_')) { if (!interaction.member.permissions.has(PermissionsBitField.Flags.Administrator)) return interaction.reply({ content: '❌ Apenas administradores podem usar isso.', ephemeral: true }); // Adicionar Produto if (id === 'admin_add_product') { const modal = new ModalBuilder().setCustomId('modal_add_product').setTitle('Adicionar Produto'); const nameInput = new TextInputBuilder().setCustomId('product_name').setLabel('Nome').setStyle(TextInputStyle.Short); const descInput = new TextInputBuilder().setCustomId('product_desc').setLabel('Descrição').setStyle(TextInputStyle.Paragraph); const valueInput = new TextInputBuilder().setCustomId('product_value').setLabel('Valor (R$)').setStyle(TextInputStyle.Short); const stockInput = new TextInputBuilder().setCustomId('product_stock').setLabel('Estoque').setStyle(TextInputStyle.Short); const channelInput = new TextInputBuilder().setCustomId('product_channel').setLabel('ID do Canal (opcional)').setStyle(TextInputStyle.Short).setRequired(false); modal.addComponents( new ActionRowBuilder().addComponents(nameInput), new ActionRowBuilder().addComponents(descInput), new ActionRowBuilder().addComponents(valueInput), new ActionRowBuilder().addComponents(stockInput), new ActionRowBuilder().addComponents(channelInput) ); return interaction.showModal(modal); } // Remover Produto if (id === 'admin_remove_product') { const nomes = Object.keys(produtos); if (nomes.length === 0) return interaction.reply({ content: '🚫 Não há produtos.', ephemeral: true }); const buttons = nomes.map(n => new ButtonBuilder().setCustomId(`remove_${n}`).setLabel(n).setStyle(ButtonStyle.Danger)); const rows = []; for (let i = 0; i < buttons.length; i += 5) rows.push(new ActionRowBuilder().addComponents(buttons.slice(i, i + 5))); return interaction.reply({ content: '🗑️ Selecione o produto:', components: rows, ephemeral: true }); } // Atualizar Pix if (id === 'admin_update_pix') { const modal = new ModalBuilder().setCustomId('modal_update_pix').setTitle('Atualizar Pix'); const pixInput = new TextInputBuilder().setCustomId('pix_key').setLabel('Chave Pix').setStyle(TextInputStyle.Short); modal.addComponents(new ActionRowBuilder().addComponents(pixInput)); return interaction.showModal(modal); } // Confirmar Compra if (id === 'admin_confirm_purchase') { const pendentes = Object.entries(compras).filter(([_, c]) => c.status === 'pendente'); if (pendentes.length === 0) return interaction.reply({ content: '🚫 Sem compras pendentes.', ephemeral: true }); const buttons = pendentes.map(([key, c]) => new ButtonBuilder().setCustomId(`confirm_${key}`).setLabel(`${c.user} - ${c.product}`).setStyle(ButtonStyle.Success)); const rows = []; for (let i = 0; i < buttons.length; i += 5) rows.push(new ActionRowBuilder().addComponents(buttons.slice(i, i + 5))); return interaction.reply({ content: '📦 Compras pendentes:', components: rows, ephemeral: true }); } // Relatório if (id === 'admin_report') { const guild = await client.guilds.fetch(interaction.guildId); let reportChannel = guild.channels.cache.find(c => c.name === 'relatorio-vendas'); if (!reportChannel) { reportChannel = await guild.channels.create({ name: 'relatorio-vendas', type: ChannelType.GuildText, permissionOverwrites: [ { id: guild.roles.everyone, deny: ['ViewChannel'] }, { id: interaction.member.id, allow: ['ViewChannel', 'SendMessages', 'ReadMessageHistory'] } ] }); } let total = 0; let msg = '**📊 Relatório de Vendas:**\n'; if (Object.keys(compras).length === 0) msg += 'Nenhuma venda ainda.\n'; for (const key in compras) { const c = compras[key]; msg += `\n• ${c.user} comprou ${c.product} - R$${c.value} - Status: ${c.status}`; if (c.status === 'concluída') total += Number(c.value); } msg += `\n\n💰 Total faturado: R$${total}`; await reportChannel.send(msg); return interaction.reply({ content: `✅ Canal de relatório: ${reportChannel}`, ephemeral: true }); } } // --- Comprar Produto --- if (id.startsWith('buy_')) { const name = id.replace('buy_', ''); const produto = produtos[name]; if (!produto) return interaction.reply({ content: '❌ Produto não encontrado!', ephemeral: true }); if (produto.stock <= 0) return interaction.reply({ content: '❌ Produto sem estoque!', ephemeral: true }); produto.stock -= 1; saveJSON(produtosPath, produtos); const key = interaction.user.id + '_' + Date.now(); const guild = await client.guilds.fetch(interaction.guildId); const atendimentoChannel = await guild.channels.create({ name: `atendimento-${interaction.user.username}`, type: ChannelType.GuildText, permissionOverwrites: [ { id: guild.roles.everyone, deny: ['ViewChannel'] }, { id: interaction.user.id, allow: ['ViewChannel', 'SendMessages', 'ReadMessageHistory'] }, { id: interaction.member.id, allow: ['ViewChannel', 'SendMessages', 'ReadMessageHistory'] } ] }); atendimentoChannel.send(`👋 ${interaction.user}, um administrador vai te atender para concluir a compra de **${produto.name}**.\n💰 Pix: \`${pixKey}\``); compras[key] = { userId: interaction.user.id, user: interaction.user.tag, product: produto.name, value: produto.value, status: 'pendente', atendimentoChannelId: atendimentoChannel.id }; saveJSON(comprasPath, compras); return interaction.reply({ content: `✅ Compra iniciada! Canal criado: ${atendimentoChannel}`, ephemeral: true }); } // --- Confirmar Compra --- if (id.startsWith('confirm_')) { const key = id.replace('confirm_', ''); const compra = compras[key]; if (!compra) return interaction.reply({ content: '❌ Compra não encontrada.', ephemeral: true }); compra.status = 'concluída'; saveJSON(comprasPath, compras); try { const user = await client.users.fetch(compra.userId); await user.send(`✅ Sua compra de **${compra.product}** foi confirmada!`); } catch {} const atendimentoChannel = await client.channels.fetch(compra.atendimentoChannelId).catch(() => null); if (atendimentoChannel) atendimentoChannel.delete().catch(() => {}); return interaction.update({ content: `✅ Compra de ${compra.user} confirmada! Canal fechado.`, components: [] }); } // --- Remover Produto --- if (id.startsWith('remove_')) { const name = id.replace('remove_', ''); const produto = produtos[name]; if (!produto) return interaction.reply({ content: '❌ Produto não encontrado.', ephemeral: true }); try { const channel = await client.channels.fetch(produto.channel); const messages = await channel.messages.fetch({ limit: 50 }); for (const m of messages.values()) { if (m.embeds[0]?.title?.includes(produto.name)) await m.delete(); } } catch {} delete produtos[name]; saveJSON(produtosPath, produtos); return interaction.reply({ content: `✅ Produto **${name}** removido e anúncio apagado.`, ephemeral: true }); } } // --- Modais --- if (interaction.isModalSubmit()) { if (interaction.customId === 'modal_add_product') { const name = interaction.fields.getTextInputValue('product_name'); const desc = interaction.fields.getTextInputValue('product_desc'); const value = Number(interaction.fields.getTextInputValue('product_value')); const stock = Number(interaction.fields.getTextInputValue('product_stock')); const channelId = interaction.fields.getTextInputValue('product_channel') || DEFAULT_CHANNEL; if (!channelId) return interaction.reply({ content: '❌ Defina um canal.', ephemeral: true }); if (isNaN(value) || isNaN(stock) || value <= 0 || stock < 0) return interaction.reply({ content: '❌ Valores inválidos.', ephemeral: true }); produtos[name] = { name, description: desc, value, stock, channel: channelId }; saveJSON(produtosPath, produtos); try { const channel = await client.channels.fetch(channelId); const embed = new EmbedBuilder() .setTitle(`🛒 ${name}`) .setDescription(`${desc}\n💰 Preço: R$${value}\n📦 Estoque: ${stock}`) .setColor('#00FF00') .setTimestamp(); const row = new ActionRowBuilder().addComponents( new ButtonBuilder().setCustomId(`buy_${name}`).setLabel('Comprar').setStyle(ButtonStyle.Primary) ); await channel.send({ embeds: [embed], components: [row] }); return interaction.reply({ content: `✅ Produto **${name}** publicado!`, ephemeral: true }); } catch { return interaction.reply({ content: '❌ Canal inválido.', ephemeral: true }); } } if (interaction.customId === 'modal_update_pix') { pixKey = interaction.fields.getTextInputValue('pix_key'); // Notifica todos os compradores pendentes nos canais temporários for (const key in compras) { const c = compras[key]; if (c.status === 'pendente') { try { const channel = await client.channels.fetch(c.atendimentoChannelId); await channel.send(`💰 Olá ${c.user}, o Pix para pagamento de **${c.product}** foi atualizado!\nNova chave Pix: \`${pixKey}\`\nPor favor, envie seu comprovante aqui no canal.`); } catch {} } } return interaction.reply({ content: `✅ Pix atualizado: \`${pixKey}\`. Compradores pendentes foram notificados nos canais de atendimento.`, ephemeral: true }); } } }); client.login(TOKEN);