Advertisement
WolficekCZ

index.js

Jul 13th, 2025
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 16.10 KB | Source Code | 0 0
  1. const {
  2.   Client,
  3.   GatewayIntentBits,
  4.   EmbedBuilder,
  5.   ButtonBuilder,
  6.   ButtonStyle,
  7.   ActionRowBuilder,
  8.   ChannelType,
  9.   Partials
  10. } = require('discord.js');
  11. const config = require('./config.json');
  12.  
  13. const RECRUITER_ROLE_ID = '1393350418683396198';
  14. const PB_STAFF_ROLE_ID = '1393326980006678669';
  15. const HEAD_OF_RECRUITMENT_ROLE_ID = '1393326540028252210';
  16. const LIVERY_CREATOR_ROLE_ID = '1393552424316305448';
  17. const CLOTHING_CREATOR_ROLE_ID = '1393552487629197378';
  18.  
  19. const claimPermissions = {
  20.   application: [RECRUITER_ROLE_ID, HEAD_OF_RECRUITMENT_ROLE_ID, PB_STAFF_ROLE_ID],
  21.   support_recruiter_app: [HEAD_OF_RECRUITMENT_ROLE_ID, PB_STAFF_ROLE_ID],
  22.   support_report: [PB_STAFF_ROLE_ID],
  23.   support_other: [PB_STAFF_ROLE_ID],
  24.   support_livery: [LIVERY_CREATOR_ROLE_ID, PB_STAFF_ROLE_ID],
  25.   support_clothing: [CLOTHING_CREATOR_ROLE_ID, PB_STAFF_ROLE_ID]
  26. };
  27.  
  28. const closePermissions = {
  29.   application: [HEAD_OF_RECRUITMENT_ROLE_ID, PB_STAFF_ROLE_ID],
  30.   support_recruiter_app: [HEAD_OF_RECRUITMENT_ROLE_ID, PB_STAFF_ROLE_ID],
  31.   support_report: [PB_STAFF_ROLE_ID],
  32.   support_other: [PB_STAFF_ROLE_ID],
  33.   support_livery: [LIVERY_CREATOR_ROLE_ID, PB_STAFF_ROLE_ID],
  34.   support_clothing: [CLOTHING_CREATOR_ROLE_ID, PB_STAFF_ROLE_ID]
  35. };
  36.  
  37. function getThreadType(name) {
  38.   if (name.startsWith('[APP]-')) return 'application';
  39.   if (name.startsWith('[R-APP]-')) return 'support_recruiter_app';
  40.   if (name.startsWith('[REP]-')) return 'support_report';
  41.   if (name.startsWith('[OTH]-')) return 'support_other';
  42.   if (name.startsWith('[LIV]-')) return 'support_livery';
  43.   if (name.startsWith('[CCL]-')) return 'support_clothing';
  44.   return null;
  45. }
  46.  
  47. const client = new Client({
  48.   intents: [
  49.     GatewayIntentBits.Guilds,
  50.     GatewayIntentBits.GuildMessages,
  51.     GatewayIntentBits.MessageContent,
  52.     GatewayIntentBits.GuildMembers
  53.   ],
  54.   partials: [Partials.Channel]
  55. });
  56.  
  57. client.once('ready', async () => {
  58.   console.log(`✅ Logged in as ${client.user.tag}`);
  59.  
  60.   // registrační slash příkazů
  61.   await client.application.commands.set([
  62.     {
  63.       name: 'pb-trial',
  64.       description: 'Send a trial message to the current channel',
  65.       options: [
  66.         {
  67.           name: 'days',
  68.           description: 'Number of trial days',
  69.           type: 4, // INTEGER
  70.           required: true
  71.         }
  72.       ]
  73.     },
  74.     {
  75.       name: 'pb-notrial',
  76.       description: 'Send acceptance message to the current channel'
  77.     }
  78.   ]);
  79.  
  80.   // inicializační embed pro aplikace
  81.   const appChannel = await client.channels.fetch(config.channelId).catch(() => null);
  82.   if (appChannel) {
  83.     const appEmbed = new EmbedBuilder()
  84.       .setTitle('Peaky Blinders Application')
  85.       .setDescription(
  86.         "**Ready to join the ranks of the fiercest crew in town?**\n" +
  87.         "Click **\"Join\"** below to open your private application ticket. 📝\n\n" +
  88.         "*Our Recruiters will be watching closely and will meet you there shortly to guide you through the trials ahead.* 🔥\n\n" +
  89.         "**Remember — this isn’t just a crew, it’s a family.**\n" +
  90.         "*Show us what you’re made of.* 💥\n\n" +
  91.         "**Good luck, and may the razor be sharp.** ✂️"
  92.       )
  93.       .setColor(0xFF0000);
  94.  
  95.     const appRow = new ActionRowBuilder().addComponents(
  96.       new ButtonBuilder().setCustomId('apply_join').setLabel('Join').setStyle(ButtonStyle.Danger)
  97.     );
  98.  
  99.     const msgs = await appChannel.messages.fetch({ limit: 10 });
  100.     const existing = msgs.find(m => m.author.id === client.user.id && m.components.length > 0);
  101.     if (existing) await existing.edit({ embeds: [appEmbed], components: [appRow] });
  102.     else await appChannel.send({ embeds: [appEmbed], components: [appRow] });
  103.   }
  104.  
  105.   // inicializační embed pro support systém
  106.   if (config.channelIdSupport) {
  107.     const supportChannel = await client.channels.fetch(config.channelIdSupport).catch(() => null);
  108.     if (supportChannel) {
  109.       const supportEmbed = new EmbedBuilder()
  110.         .setTitle('✍️ Peaky Blinders Support System')
  111.         .setDescription(
  112.           "**[OTH]** *Other Issues* 🛠️\n" +
  113.           "**[REP]** *Reporting a Member* 📝\n" +
  114.           "**[R-APP]** *Recruiter Application* 🎩\n" +
  115.           "**[LIV]** *Livery Creation & Suggest* 🎨\n" +
  116.           "**[CCL]** *Clothing Creation & Suggest* 👔\n\n" +
  117.           "*Click the button below to open your ticket.*"
  118.         )
  119.         .setColor(0x00f3ff)
  120.         .setFooter({ text: 'Peaky Blinders Support System' })
  121.         .setTimestamp();
  122.  
  123.       const buttons = [
  124.         ['support_other', 'Other 🛠️', ButtonStyle.Success],
  125.         ['support_report', 'Report 📝', ButtonStyle.Danger],
  126.         ['support_recruiter_app', 'Recruiter App 🎩', ButtonStyle.Primary],
  127.         ['support_livery', 'Livery 🎨', ButtonStyle.Secondary],
  128.         ['support_clothing', 'Clothing 👔', ButtonStyle.Secondary]
  129.       ].map(([id, label, style]) => new ButtonBuilder().setCustomId(id).setLabel(label).setStyle(style));
  130.  
  131.       const supportRow = new ActionRowBuilder().addComponents(...buttons);
  132.  
  133.       const msgs = await supportChannel.messages.fetch({ limit: 10 });
  134.       const existing = msgs.find(m => m.author.id === client.user.id && m.components.length > 0);
  135.       if (existing) await existing.edit({ embeds: [supportEmbed], components: [supportRow] });
  136.       else await supportChannel.send({ embeds: [supportEmbed], components: [supportRow] });
  137.     }
  138.   }
  139. });
  140.  
  141. client.on('interactionCreate', async interaction => {
  142.   // 🌟 slash příkazy
  143.   if (interaction.isChatInputCommand()) {
  144.     const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
  145.     if (!member)
  146.       return interaction.reply({ content: '❌ Unable to fetch your member data.', ephemeral: true });
  147.  
  148.     const allowed = [
  149.       RECRUITER_ROLE_ID,
  150.       PB_STAFF_ROLE_ID,
  151.       HEAD_OF_RECRUITMENT_ROLE_ID
  152.     ];
  153.     if (!allowed.some(r => member.roles.cache.has(r)))
  154.       return interaction.reply({ content: '❌ You do not have permission to use this command.', ephemeral: true });
  155.  
  156.     if (interaction.commandName === 'pb-trial') {
  157.       const days = interaction.options.getInteger('days');
  158.       const embed = new EmbedBuilder()
  159.         .setColor(0xffd700)
  160.         .setTitle('📌 Trial Notice')
  161.         .setDescription(
  162.           "*Starting today, your trial period officially begins.*\n" +
  163.           `*This trial will last for **${days} days**, during which we’ll evaluate your activity, behavior, communication, and overall fit with the crew.*\n\n` +
  164.           "*We're not expecting perfection but we do expect presence, respect, and effort.*\n\n" +
  165.           "**🧠 Make sure to read everything written [here](https://discord.com/channels/1206571308456878100/1393329313100595381) carefully.**\n" +
  166.           "*There are no excuses for missing important details during trial.*\n\n" +
  167.           "*Use this time to prove you belong here.*\n" +
  168.           "*Welcome aboard, and good luck.*\n\n" +
  169.           "***By order of the crew.***"
  170.         );
  171.       return interaction.reply({ embeds: [embed] });
  172.     }
  173.  
  174.     if (interaction.commandName === 'pb-notrial') {
  175.       const embed = new EmbedBuilder()
  176.         .setColor(0x3498db)
  177.         .setTitle('🎩 You’ve Been Accepted')
  178.         .setDescription(
  179.           "***By Order of the Crew 🔱***\n\n" +
  180.           "*As of today, you’re no longer just on trial*\n" +
  181.           "*You’re now a** full member** of the crew. 🎩💼*\n" +
  182.           "*You made it through because you showed **loyalty, presence, and discipline** — traits we don’t take lightly.*\n\n" +
  183.           "*But don’t get comfortable. 🥃*\n" +
  184.           "**Wearing the tag means something now.**\n" +
  185.           "*You carry the name, the rep, and the weight that comes with it.*\n" +
  186.           "*Every word, every move, every mistake — it reflects on all of us. 🕶️*\n\n" +
  187.           "***We don’t recruit numbers.***\n" +
  188.           "***We build a unit.***\n" +
  189.           "**We stand for:**\n" +
  190.           "***⚔️ Loyalty***\n" +
  191.           "***🩸 Brotherhood***\n" +
  192.           "***💼 Respect***\n" +
  193.           "***🎯 Precision***\n\n" +
  194.           "*If you're here to **ghost, slack, or chase clout** — this ain’t your place.*\n" +
  195.           "*But if you’re here to ride with us, to earn respect and move like a brotherhood...*\n" +
  196.           "***Then welcome to the real game. 🔥***\n\n" +
  197.           "***Stick with the crew. Protect the name.***\n" +
  198.           "*We win together — or we don’t win at all.*\n\n" +
  199.           "***By Order of the Peaky Blinders 🥃***"
  200.         );
  201.       return interaction.reply({ embeds: [embed] });
  202.     }
  203.   }
  204.  
  205.   // 🚪 tlačítka a thready
  206.   if (!interaction.isButton()) return;
  207.  
  208.   const member = await interaction.guild.members.fetch(interaction.user.id).catch(() => null);
  209.   if (!member) return interaction.reply({ content: '❌ Unable to fetch your member data.', ephemeral: true });
  210.  
  211.   const supportTypes = {
  212.     support_other: {
  213.       namePrefix: '[OTH]-',
  214.       pingRoles: [PB_STAFF_ROLE_ID],
  215.       embedColor: 0x2ECC71,
  216.       embedTitle: '🛠️ Other Issues',
  217.       embedDesc:
  218.         "This is your ticket for other support-related matters.\n" +
  219.         "Describe your issue clearly and our staff will help you shortly."
  220.     },
  221.     support_report: {
  222.       namePrefix: '[REP]-',
  223.       pingRoles: [PB_STAFF_ROLE_ID],
  224.       embedColor: 0xE74C3C,
  225.       embedTitle: '📝 Reporting a Member',
  226.       embedDesc:
  227.         "Use this form to report behavior that violates the rules.\n" +
  228.         "Include evidence if possible (screenshots, video, etc.)."
  229.     },
  230.     support_recruiter_app: {
  231.       namePrefix: '[R-APP]-',
  232.       pingRoles: [HEAD_OF_RECRUITMENT_ROLE_ID, PB_STAFF_ROLE_ID],
  233.       embedColor: 0xE67E22,
  234.       embedTitle: '🎩 Recruiter Application',
  235.       embedDesc:
  236.         "This is your recruiter application support ticket.\n" +
  237.         "Our recruitment team will assist you shortly."
  238.     },
  239.     support_livery: {
  240.       namePrefix: '[LIV]-',
  241.       pingRoles: [LIVERY_CREATOR_ROLE_ID],
  242.       embedColor: 0x3498DB,
  243.       embedTitle: '🎨 Livery Creation',
  244.       embedDesc:
  245.         "Need help with liveries or want to suggest one?\n" +
  246.         "Let our creation team know here."
  247.     },
  248.     support_clothing: {
  249.       namePrefix: '[CCL]-',
  250.       pingRoles: [CLOTHING_CREATOR_ROLE_ID],
  251.       embedColor: 0x9B59B6,
  252.       embedTitle: '👔 Clothing Creation & Suggest',
  253.       embedDesc:
  254.         "Looking to customize clothing or suggest a design?\n" +
  255.         "Our creators are here to help."
  256.     }
  257.   };
  258.  
  259. if (interaction.customId === 'apply_join') {
  260.   const thread = await interaction.channel.threads.create({
  261.     name: `[APP]-${interaction.user.username}`,
  262.     autoArchiveDuration: 1440,
  263.     type: ChannelType.PrivateThread,
  264.     reason: `Application by ${interaction.user.tag}`
  265.   });
  266.  
  267.   const embed1 = new EmbedBuilder()
  268.     .setTitle('📥 Welcome to your Peaky Blinders Crew application!')
  269.     .setDescription(
  270.       "Please fill out the application form below carefully and follow the instructions to proceed."
  271.     )
  272.     .setColor(0x5865F2);
  273.  
  274.   const embed2 = new EmbedBuilder()
  275.     .setTitle('📝 Application Form')
  276.     .setDescription(
  277.       "1️⃣ **In-game Level:**\n" +
  278.       "2️⃣ **In-game Name:**\n" +
  279.       "3️⃣ **Age:**\n" +
  280.       "4️⃣ **Activity Level:** (Active / Semi-Active / Not Much)\n" +
  281.       "5️⃣ **Working Mic?** (Yes/No)\n" +
  282.       "6️⃣ **Understanding of CNR Rules?** (Yes/No)\n" +
  283.       "7️⃣ **Ban History:** (Provide your record from CNR Staff)"
  284.     )
  285.     .setColor(0x3498db);
  286.  
  287.   const embed3 = new EmbedBuilder()
  288.     .setTitle('📛 Ban History & Final Steps')
  289.     .setDescription(
  290.       "**Cops and Robbers Discord:** [Click Here](https://discord.com/invite/6mvRUxF)\n" +
  291.       "**Ban History Request:** [Click Here](https://discord.com/channels/738534756538384499/814776227537747970)\n\n" +
  292.       "Please send us a photo of your Ban History and ping your recruiter inside the thread.\n\n" +
  293.       "🧠 **Easier Way:**\n" +
  294.       "Go to the #support channel → Click on \"Thread\" → Choose \"Other\" → Request your Ban History.\n" +
  295.       "You’ll usually get it within 1–60 minutes.\n\n" +
  296.       "*A recruiter will be with you shortly.*"
  297.     )
  298.     .setImage('https://cdn.discordapp.com/attachments/1393574770720903243/1393574830900777010/Snimek_obrazovky_2025-07-12_144809.png')
  299.     .setColor(0xe74c3c)
  300.     .setFooter({ text: `Requested by ${interaction.user.tag}` })
  301.     .setTimestamp();
  302.  
  303.   const claimButton = new ButtonBuilder().setCustomId('claim_ticket').setLabel('Claim').setStyle(ButtonStyle.Success);
  304.   const closeButton = new ButtonBuilder().setCustomId('close_ticket').setLabel('Close').setStyle(ButtonStyle.Danger);
  305.   const row = new ActionRowBuilder().addComponents(claimButton, closeButton);
  306.  
  307.   const mentions = [`<@${interaction.user.id}>`, `<@&${RECRUITER_ROLE_ID}>`, `<@&${PB_STAFF_ROLE_ID}>`].join(' ');
  308.   await thread.send({ content: `||${mentions}||`, embeds: [embed1, embed2, embed3], components: [row] });
  309.  
  310.   return await interaction.reply({ content: '✅ Your application thread has been created!', ephemeral: true });
  311. }
  312.  
  313.   if (interaction.customId.startsWith('support_')) {
  314.     const type = supportTypes[interaction.customId];
  315.     if (!type) return interaction.reply({ content: '❌ Unknown support type.', ephemeral: true });
  316.  
  317.     const thread = await interaction.channel.threads.create({
  318.       name: `${type.namePrefix}${interaction.user.username}`,
  319.       autoArchiveDuration: 1440,
  320.       type: ChannelType.PrivateThread,
  321.       reason: `Support Ticket by ${interaction.user.tag}`
  322.     });
  323.  
  324.     const embed = new EmbedBuilder()
  325.       .setTitle(type.embedTitle)
  326.       .setDescription(type.embedDesc)
  327.       .setColor(type.embedColor)
  328.       .setFooter({ text: `Requested by ${interaction.user.tag}` })
  329.       .setTimestamp();
  330.  
  331.     const row = new ActionRowBuilder().addComponents(
  332.       new ButtonBuilder().setCustomId('claim_ticket').setLabel('Claim').setStyle(ButtonStyle.Success),
  333.       new ButtonBuilder().setCustomId('close_ticket').setLabel('Close').setStyle(ButtonStyle.Danger)
  334.     );
  335.  
  336.     const mentions = [
  337.       `<@${interaction.user.id}>`,
  338.       ...type.pingRoles.map(r => `<@&${r}>`)
  339.     ].join(' ');
  340.  
  341.     await thread.send({ content: `||${mentions}||`, embeds: [embed], components: [row] });
  342.     await interaction.reply({ content: '✅ Your support ticket thread has been created!', ephemeral: true });
  343.     return;
  344.   }
  345.  
  346.   // Claim ticket
  347.   if (interaction.customId === 'claim_ticket') {
  348.     const thread = interaction.channel;
  349.     const threadType = getThreadType(thread.name || '');
  350.     const allowed = claimPermissions[threadType] || [];
  351.     const hasPerm = allowed.some(r => member.roles.cache.has(r));
  352.  
  353.     if (!hasPerm) return interaction.reply({ content: '❌ You do not have permission to claim this ticket.', ephemeral: true });
  354.  
  355.     const msg = interaction.message;
  356.     const row = msg.components[0];
  357.     const already = row.components.find(c => c.customId === 'claim_ticket')?.data.disabled;
  358.  
  359.     if (already) return interaction.reply({ content: '❌ Ticket already claimed.', ephemeral: true });
  360.  
  361.     const newRow = new ActionRowBuilder();
  362.     for (const comp of row.components) {
  363.       newRow.addComponents(ButtonBuilder.from(comp).setDisabled(comp.customId === 'claim_ticket'));
  364.     }
  365.  
  366.     await interaction.update({
  367.       content: `✅ Claimed by <@${interaction.user.id}>.`,
  368.       components: [newRow]
  369.     });
  370.   }
  371.  
  372.   // Close ticket
  373.   if (interaction.customId === 'close_ticket') {
  374.     const thread = interaction.channel;
  375.     const threadType = getThreadType(thread.name || '');
  376.     const allowed = closePermissions[threadType] || [];
  377.     const hasPerm = allowed.some(r => member.roles.cache.has(r));
  378.  
  379.     if (!hasPerm) return interaction.reply({ content: '❌ You do not have permission to close this ticket.', ephemeral: true });
  380.  
  381.     await interaction.reply({ content: '🔒 Ticket has been closed.' });
  382.     if (thread.isThread()) {
  383.       await thread.setLocked(true);
  384.       await thread.setArchived(true);
  385.     }
  386.   }
  387. });
  388.  
  389. client.login(config.token);
Tags: index
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement