1"use strict";(globalThis.webpackChunkmagmastream_docs=globalThis.webpackChunkmagmastream_docs||[]).push([[8606],{5507(e,n,t){t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>a,toc:()=>u});const a=JSON.parse('{"id":"Getting-started/Examples/event-handling","title":"Event handling","description":"Event handling","source":"@site/versioned_docs/version-2.10.3/Getting-started/Examples/event-handling.mdx","sourceDirName":"Getting-started/Examples","slug":"/getting-started/examples/event-handling","permalink":"/docs/2.10.3/getting-started/examples/event-handling","draft":false,"unlisted":false,"editUrl":"https://gitryx.com/MagmaStream/magmastream_documentation/tree/main/versioned_docs/version-2.10.3/Getting-started/Examples/event-handling.mdx","tags":[],"version":"2.10.3","lastUpdatedBy":"Vexify4103","lastUpdatedAt":1791159722000,"sidebarPosition":5,"frontMatter":{"id":"event-handling","title":"Event handling","description":"Event handling","slug":"/getting-started/examples/event-handling","sidebar_position":5,"hide_title":true,"sidebar_label":"\ud83d\udd14 Event handling"},"sidebar":"tutorialSidebar","previous":{"title":"\ud83e\udde0 Command handling","permalink":"/docs/2.10.3/getting-started/examples/command-handling"},"next":{"title":"\ud83d\udcec Registering commands","permalink":"/docs/2.10.3/getting-started/examples/deployment-script"}}');var s=t(74848),r=t(28453),i=t(4865),o=t(19365);const l={id:"event-handling",title:"Event handling",description:"Event handling",slug:"/getting-started/examples/event-handling",sidebar_position:5,hide_title:!0,sidebar_label:"\ud83d\udd14 Event handling"},c=void 0,d={},u=[{value:"Event handling",id:"event-handling",level:2},{value:"Before you continue",id:"before-you-continue",level:2},{value:"Loading event files",id:"loading-event-files",level:2},{value:"Event file structure",id:"event-file-structure",level:2},{value:"Next steps",id:"next-steps",level:2},{value:"Resulting code",id:"resulting-code",level:2}];function m(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",hr:"hr",p:"p",pre:"pre",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.h2,{id:"event-handling",children:"Event handling"}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsxs)(n.p,{children:["This page requires a valid ",(0,s.jsx)(n.a,{href:"/docs/getting-started/examples/main-file",children:"main file"}),", at least one command from ",(0,s.jsx)(n.a,{href:"/docs/getting-started/examples/creating-commands",children:"creating commands"}),", and the ",(0,s.jsx)(n.a,{href:"/docs/getting-started/examples/command-handling",children:"command handler"})," in place. It applies to both slash commands and message commands."]})}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"before-you-continue",children:"Before you continue"}),"\n",(0,s.jsx)(n.p,{children:"Assuming you've followed the guide so far, your project directory should look something like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"discord-bot/\n\u251c\u2500\u2500 node_modules/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 commands/\n\u2502 \u2502 \u2514\u2500\u2500 music/\n\u2502 \u2502 \u2514\u2500\u2500 play.js\n\u2502 \u251c\u2500\u2500 config.js\n\u2502 \u2514\u2500\u2500 index.js\n\u251c\u2500\u2500 package-lock.json\n\u2514\u2500\u2500 package.json\n"})}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"loading-event-files",children:"Loading event files"}),"\n",(0,s.jsxs)(n.p,{children:["Remove any existing ",(0,s.jsx)(n.code,{children:"client.on()"})," calls from ",(0,s.jsx)(n.code,{children:"index.js"}),". Then add the following event loader right after the command-loading section:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const loadEvents = (folderPath) => {\n\tconst eventFiles = fs.readdirSync(folderPath);\n\n\tfor (const file of eventFiles) {\n\t\tconst filePath = path.join(folderPath, file);\n\t\tconst stat = fs.statSync(filePath);\n\n\t\tif (stat.isDirectory()) {\n\t\t\tloadEvents(filePath);\n\t\t} else if (file.endsWith(".js")) {\n\t\t\tconst event = require(filePath);\n\n\t\t\tif (event.once) {\n\t\t\t\tif (filePath.includes(`${path.sep}magmastream${path.sep}
1`)) {\n\t\t\t\t\tclient.manager.on(event.name, (...args) => event.execute(client, ...args));\n\t\t\t\t} else {\n\t\t\t\t\tclient.once(event.name, (...args) => event.execute(client, ...args));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (filePath.includes(`${path.sep}magmastream${path.sep}`)) {\n\t\t\t\t\tclient.manager.on(event.name, (...args) => event.execute(client, ...args));\n\t\t\t\t} else {\n\t\t\t\t\tclient.on(event.name, (...args) => event.execute(client, ...args));\n\t\t\t\t}\n\t\t\t}\n\t\t\tconsole.log(`[INFO] Loaded event: ${event.name}`);\n\t\t}\n\t}\n};\n\nconst eventsPath = path.join(__dirname, "events");\nloadEvents(eventsPath);\n'})}),"\n",(0,s.jsxs)(n.p,{children:["This uses the same recursive pattern as the ",(0,s.jsx)(n.a,{href:"/docs/getting-started/examples/command-handling#loading-command-files",children:"command loader"}),". Files inside an ",(0,s.jsx)(n.code,{children:"events/magmastream/"})," subfolder are registered on ",(0,s.jsx)(n.code,{children:"client.manager"}),"; all others are registered on ",(0,s.jsx)(n.code,{children:"client"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["You can review the complete ",(0,s.jsx)(n.code,{children:"index.js"})," on the GitHub repository ",(0,s.jsxs)(n.a,{href:"https://gitryx.com/MagmaStream/magmastream_basics_bot/tree/js-v2.10.0/src/index.js",children:["here ",(0,s.jsx)("i",{className:"fa-solid fa-arrow-up-right-from-square",style:{fontSize:"0.8em"}})]}),"."]}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"event-file-structure",children:"Event file structure"}),"\n",(0,s.jsxs)(n.p,{children:["Create an ",(0,s.jsx)(n.code,{children:"events/"})," folder inside ",(0,s.jsx)(n.code,{children:"src/"})," with two subfolders: ",(0,s.jsx)(n.code,{children:"discord/"})," and ",(0,s.jsx)(n.code,{children:"magmastream/"}),"."]}),"\n",(0,s.jsx)(n.admonition,{type:"note",children:(0,s.jsx)(n.p,{children:"Select the tab below for the events relevant to your setup."})}),"\n",(0,s.jsxs)(i.A,{children:[(0,s.jsxs)(o.A,{value:"discord-events",label:"Discord events",default:!0,children:[(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"raw.js"}),", ",(0,s.jsx)(n.code,{children:"ready.js"}),", and ",(0,s.jsx)(n.code,{children:"interactionCreate.js"})," inside ",(0,s.jsx)(n.code,{children:"events/discord/"}),". If you are using message commands, also create ",(0,s.jsx)(n.code,{children:"messageCreate.js"}),"."]}),(0,s.jsxs)(i.A,{children:[(0,s.jsx)(o.A,{value:"discord-readyEvent",label:"events/discord/ready.js",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { Events } = require("discord.js");\n\nmodule.exports = {\n name: Events.ClientReady,\n once: true,\n\n async execute(client) {\n await client.manager.init({ clientId: client.user.id });\n console.log(`[INFO] Ready! Logged in as ${client.user.tag}`);\n },\n};\n'})})}),(0,s.jsx)(o.A,{value:"discord-interactionEvent",label:"events/discord/interactionCreate.js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { Events, MessageFlags } = require("discord.js");\n\nmodule.exports = {\n name: Events.InteractionCreate,\n\n async execute(client, interaction) {\n if (!interaction.isChatInputCommand()) return;\n\n const command = client.commands.get(interaction.commandName);\n\n if (!command) {\n console.error(`No command matching ${interaction.commandName} was found.`);\n return;\n }\n\n try {\n await command.execute(client, interaction);\n } catch (error) {\n console.error(error);\n\n if (interaction.replied || interaction.deferred) {\n await interaction.followUp({ content: "There was an error while executing this command!", flags: MessageFlags.Ephemeral });\n } else {\n await interaction.reply({ content: "There was an error while executing this command!", flags: MessageFlags.Ephemeral });\n }\n }\n },\n};\n'})})}),(0,s.jsx)(o.A,{value:"discord-rawEvent",label:"events/discord/raw.js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { Events } = require("discord.js");\n\nmodule.exports = {\n name: Events.Raw,\n\n async execute(client, data) {\n // Required by MagmaStream when not using a provided wrapper\n await client.manager.updateVoiceState(data);\n },\n};\n'})})}),(0,s.jsx)(o.A,{value:"discord-messageCreateEvent",label:"events/discord/message
1Create.js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { Events } = require("discord.js");\nconst config = require("../../config.js");\n\nmodule.exports = {\n\tname: Events.MessageCreate,\n\n\tasync execute(client, message) {\n if (message.author.bot) return;\n\n if (!message.content.startsWith(config.prefix)) return;\n\n const args = message.content.slice(config.prefix.length).trim().split(/ +/);\n\n const commandName = args.shift().toLowerCase();\n\n const command = client.commands.get(commandName);\n\n if (!command) return;\n\n try {\n await command.message(client, message, args);\n } catch (error) {\n console.error(error);\n await message.reply(`An error occurred while executing the command: ${error.message}`);\n }\n },\n};\n'})})})]})]}),(0,s.jsxs)(o.A,{value:"magmastream-events",label:"MagmaStream events",children:[(0,s.jsxs)(n.p,{children:["Create ",(0,s.jsx)(n.code,{children:"nodeConnect.js"}),", ",(0,s.jsx)(n.code,{children:"trackStart.js"}),", and ",(0,s.jsx)(n.code,{children:"queueEnd.js"})," inside ",(0,s.jsx)(n.code,{children:"events/magmastream/"}),"."]}),(0,s.jsxs)(i.A,{children:[(0,s.jsx)(o.A,{value:"magmastream-nodeConnectEvent",label:"events/magmastream/nodeConnect.js",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { ManagerEventTypes } = require("magmastream");\n\nmodule.exports = {\n name: ManagerEventTypes.NodeConnect,\n execute(client, node) {\n console.log(`[NODECONNECT] Connected to node ${node.options.identifier}`);\n },\n};\n'})})}),(0,s.jsx)(o.A,{value:"magmastream-trackStartEvent",label:"events/magmastream/trackStart.js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { ManagerEventTypes } = require("magmastream");\n\nmodule.exports = {\n name: ManagerEventTypes.TrackStart,\n async execute(client, player, track) {\n const channel = client.channels.cache.get(player.textChannelId);\n if (!channel) return;\n\n channel\n .send(`Now playing: \\`${track.title}\\`, requested by \\`${track.requester.username}\\`.`)\n .catch((error) => console.log(`[TRACKSTART] Failed to send message to channel: ${player.textChannelId}`));\n },\n};\n'})})}),(0,s.jsx)(o.A,{value:"magmastream-queueEndEvent",label:"events/magmastream/queueEnd.js",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-javascript",metastring:"showLineNumbers",children:'const { ManagerEventTypes } = require("magmastream");\n\nmodule.exports = {\n name: ManagerEventTypes.QueueEnd,\n async execute(client, player, track) {\n const channel = client.channels.cache.get(player.textChannelId);\n if (!channel) return;\n\n channel\n .send("Queue has ended!")\n .catch((error) => console.log(`[QUEUEEND] Failed to send message to channel: ${player.textChannelId}`));\n },\n};\n'})})})]})]})]}),"\n",(0,s.jsx)(n.p,{children:"Your project directory should now look like this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"discord-bot/\n\u251c\u2500\u2500 node_modules/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 commands/\n\u2502 \u2502 \u2514\u2500\u2500 music/\n\u2502 \u2502 \u2514\u2500\u2500 play.js\n\u2502 \u251c\u2500\u2500 events/\n\u2502 \u2502 \u251c\u2500\u2500 discord/\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 interactionCreate.js\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 messageCreate.js\n\u2502 \u2502 \u2502 \u251c\u2500\u2500 raw.js\n\u2502 \u2502 \u2502 \u2514\u2500\u2500 ready.js\n\u2502 \u2502 \u2514\u2500\u2500 magmastream/\n\u2502 \u2502 \u251c\u2500\u2500 nodeConnect.js\n\u2502 \u2502 \u251c\u2500\u2500 queueEnd.js\n\u2502 \u2502 \u2514\u2500\u2500 trackStart.js\n\u2502 \u251c\u2500\u2500 config.js\n\u2502 \u2514\u2500\u2500 index.js\n\u251c\u2500\u2500 package-lock.json\n\u2514\u2500\u2500 package.json\n"})}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"next-steps",children:"Next steps"}),"\n",(0,s.jsxs)(n.p,{children:["Add more events by creating new files in the relevant subfolder. Next, create a ",(0,s.jsx)(n.a,{href:"/docs/getting-started/examples/deployment-script",children:"deployment script"})," to register your slash commands with Discord."]}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"resulting-code",children:"Resulting code"}),"\n",(0,s.jsxs)(n.p,{children:["You can review the complete example on the GitHub repository ",(0,s.jsxs)(n.a,{href:"https://gitryx.com/MagmaStream/magmastream_basics_bot/tree/js-v2.10.0/src",children:["here ",(0,s.jsx)("i",{className:"fa-solid fa-arrow-up-right-from-square",style:{fontSize:"0.8em"}})]}),"."]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(m,{...e})}):m(e)}},19365(e,n,t){t.d(n,{A:()=>l});t(96540);var a=t(34164),s=t(47751);const r="tabItem_Ymn6";var i=t(74848);function o({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,a.A)(r,n),hidden:t,children:e})}function l({children:e,className:n,value:t}){const{selectedValue:a,lazy:r}=(0,s.uc)(),l=t===a;return!l&&r?null:(0,i.jsx)(o,{className:n,hidden:!l,children:e})}},4865(e,n,t){t.d(n,{A:()=>g});t(96540);var a=t(34164),s=t(17559),r=t(47751),i=t(23104),o=t(92303);const l="tabList__CuJ",c="tabItem_LNqP";var d=t(74848);function u({className:e}){const{selectedValue:n,selectValue:t,tabValues:s,block:o}=(0,r.uc)(),l=[],{blockElementScrollPositionUntilNextRender:u}=(0,i.a_)(),m=e=>{const a=e.currentTarget,r=l.indexOf(a),i=s[r].value;i!==n&&(u(a),t(i))},h=e=>{let n=null;switch(e.key){case"Enter":m(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":o},e),children:s.map(({value:e,label:t,attributes:s})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:h,onClick:m,...s,className:(0,a.A)("tabs__item",c,s?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function m({children:e}){return(0,d.jsx)("div",{className:"margin-top--md",children:e})}function h({className:e,children:n}){return(0,d.jsxs)("div",{className:(0,a.A)(s.G.tabs.container,"tabs-container",l),children:[(0,d.jsx)(u,{className:e}),(0,d.jsx)(m,{children:n})]})}function g(e){const n=(0,o.A)(),t=(0,r.OC)(e);return(0,d.jsx)(r.O_,{value:t,children:(0,d.jsx)(h,{className:e.className,children:(0,r.vT)(e.children)})},String(n))}},47751(e,n,t){t.d(n,{OC:()=>g,O_:()=>x,uc:()=>v,vT:()=>d});var a=t(96540),s=t(56347),r=t(205),i=t(57485),o=t(70679),l=t(31682),c=t(74848);function d(e){return a.Children.toArray(e).filter(e=>"\n"!==e)}function u(e){const{values:n,children:t}=e;return(0,a.useMemo)(()=>{const e=n??function(e){return a.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:a}})=>({value:e,label:n,attributes:t,default:a}))}(t);return function(e){const n=(0,l.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function m({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h({queryString:e=!1,groupId:n}){const t=(0,s.W6)(),r=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(r),(0,a.useCallback)(e=>{if(!r)return;const n=new URLSearchParams(t.location.search);n.set(r,e),t.replace({...t.location,search:n.toString()})},[r,t])]}function g(e){const{defaultValue:n,queryString:t=!1,groupId:s}=e,i=u(e),[l,c]=(0,a.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!m({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i})),[d,g]=h({queryString:t,groupId:s}),[p,v]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,s]=(0,o.Dv)(n);return[t,(0,a.useCallback)(e=>{n&&s.set(e)},[n,s])]}({groupId:s}),x=(()=>{const e=d??p;return m({value:e,tabValues:i})?e:null})();(0,r.A)(()=>{x&&c(x)},[x]);return{selectedValue:l,selectValue:(0,a.useCallback)(e=>{if(!m({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);c(e),g(e),v(e)},[g,v,i]),tabValues:i,lazy:e.lazy??!1,block:e.block??!1}}const p=(0,a.createContext)(null);function v(){const e=a.useContext(p);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function x(e){return(0,c.jsx)(p.Provider,{value:e.value,children:e.children})}},28453(e,n,t){t.d(n,{R:()=>i,x:()=>o});var a=t(96540);const s={},r=a.createContext(s);function i(e){const n=a.useContext(r);return a.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),a.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.