diff --git a/404.html b/404.html new file mode 100644 index 0000000..60d1129 --- /dev/null +++ b/404.html @@ -0,0 +1,33 @@ + + + + + + + + + Javacord + + + + +

404

That's a Four-Oh-Four.
Take me home
+ + + diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..9c77ee4 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +javacord.org \ No newline at end of file diff --git a/assets/404.72d4030e.js b/assets/404.72d4030e.js new file mode 100644 index 0000000..9aed62b --- /dev/null +++ b/assets/404.72d4030e.js @@ -0,0 +1 @@ +import{_ as i,f as d,u as p,g as f,r as v,o as k,c as g,a as e,t as c,b as L,w as x,h as l,d as B}from"./app.151ccb98.js";const N={class:"theme-container"},T={class:"page"},b={class:"theme-default-content"},C=e("h1",null,"404",-1),M=d({__name:"404",setup(R){var a,s,n;const _=p(),o=f(),t=(a=o.value.notFound)!=null?a:["Not Found"],r=()=>t[Math.floor(Math.random()*t.length)],u=(s=o.value.home)!=null?s:_.value,m=(n=o.value.backToHome)!=null?n:"Back to home";return(V,w)=>{const h=v("RouterLink");return k(),g("div",N,[e("main",T,[e("div",b,[C,e("blockquote",null,c(r()),1),L(h,{to:l(u)},{default:x(()=>[B(c(l(m)),1)]),_:1},8,["to"])])])])}}});var F=i(M,[["__file","404.vue"]]);export{F as default}; diff --git a/assets/404.html.796e6b78.js b/assets/404.html.796e6b78.js new file mode 100644 index 0000000..519185f --- /dev/null +++ b/assets/404.html.796e6b78.js @@ -0,0 +1 @@ +import{_ as e,o as _,c}from"./app.151ccb98.js";const r={};function t(o,a){return _(),c("div")}var s=e(r,[["render",t],["__file","404.html.vue"]]);export{s as default}; diff --git a/assets/404.html.7d858b3d.js b/assets/404.html.7d858b3d.js new file mode 100644 index 0000000..c4c5770 --- /dev/null +++ b/assets/404.html.7d858b3d.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-3706649a","path":"/404.html","title":"","lang":"en-US","frontmatter":{"layout":"404"},"excerpt":"","headers":[],"git":{},"filePathRelative":null}');export{t as data}; diff --git a/assets/LatestVersion.3ddaef1d.js b/assets/LatestVersion.3ddaef1d.js new file mode 100644 index 0000000..6505664 --- /dev/null +++ b/assets/LatestVersion.3ddaef1d.js @@ -0,0 +1 @@ +import{_ as r,R as c}from"./app.151ccb98.js";const d={__name:"LatestVersion",setup(l){const n="https://shields.io/github/release/Javacord/Javacord";let a=typeof window!="undefined"?window.latestVersion:null;c(async()=>{if(typeof window!="undefined"){if(window.latestVersion===void 0){const s=await(await fetch(n+".json")).json();window.latestVersion=s.value.substring(1),a=s.value.substring(1)}o(document.body,/\$latest-version/g,a),o(document.body,/{{latestVersion}}/g,a);const t=(await(await fetch("https://raw.githubusercontent.com/Javacord/Javacord/master/gradle.properties")).text()).match("=.*-SNAPSHOT")[0].replace("=","").trim();o(document.body,/\$latest-snapshot-version/g,t)}});function o(e,t,s){if(e.nodeType===3&&(e.data=e.data.replace(t,s)),e.nodeType===1&&e.nodeName!=="SCRIPT")for(const i of e.childNodes)o(i,t,s)}return(e,t)=>null}};var u=r(d,[["__file","LatestVersion.vue"]]);export{u as default}; diff --git a/assets/Layout.85f3b300.js b/assets/Layout.85f3b300.js new file mode 100644 index 0000000..206daf5 --- /dev/null +++ b/assets/Layout.85f3b300.js @@ -0,0 +1 @@ +import{_ as w,r as R,o as n,c as i,b as L,f as S,i as P,j as h,k as pe,h as e,F as I,l as A,a as b,t as T,m as y,n as W,p as J,q as C,w as B,s as ve,v as $,d as j,x as Y,y as Be,z as Ne,A as He,B as Q,C as Z,D as E,E as q,G as he,H as me,u as fe,g as H,T as be,I as z,J as ge,K as G,L as X,M as Ie,N as Me,O as ee,P as ke,Q as $e,e as De,R as te,S as Pe,U as K,V as ae,W as Ee,X as Re,Y as Ae,Z as Oe,$ as Fe,a0 as ze,a1 as We}from"./app.151ccb98.js";const Ue={},Ve={class:"theme-default-content"};function Ke(v,a){const t=R("Content");return n(),i("div",Ve,[L(t)])}var je=w(Ue,[["render",Ke],["__file","HomeContent.vue"]]);const qe={key:0,class:"features"},Ge=S({__name:"HomeFeatures",setup(v){const a=P(),t=h(()=>pe(a.value.features)?a.value.features:[]);return(u,o)=>e(t).length?(n(),i("div",qe,[(n(!0),i(I,null,A(e(t),_=>(n(),i("div",{key:_.title,class:"feature"},[b("h2",null,T(_.title),1),b("p",null,T(_.details),1)]))),128))])):y("",!0)}});var Xe=w(Ge,[["__file","HomeFeatures.vue"]]);const Ye=["innerHTML"],Je=["textContent"],Qe=S({__name:"HomeFooter",setup(v){const a=P(),t=h(()=>a.value.footer),u=h(()=>a.value.footerHtml);return(o,_)=>e(t)?(n(),i(I,{key:0},[e(u)?(n(),i("div",{key:0,class:"footer",innerHTML:e(t)},null,8,Ye)):(n(),i("div",{key:1,class:"footer",textContent:T(e(t))},null,8,Je))],64)):y("",!0)}});var Ze=w(Qe,[["__file","HomeFooter.vue"]]);const et=["href","rel","target","aria-label"],tt=S({inheritAttrs:!1}),at=S({...tt,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(v){const a=v,t=W(),u=He(),{item:o}=J(a),_=h(()=>Y(o.value.link)),f=h(()=>Be(o.value.link)||Ne(o.value.link)),c=h(()=>{if(!f.value){if(o.value.target)return o.value.target;if(_.value)return"_blank"}}),r=h(()=>c.value==="_blank"),s=h(()=>!_.value&&!f.value&&!r.value),l=h(()=>{if(!f.value){if(o.value.rel)return o.value.rel;if(r.value)return"noopener noreferrer"}}),p=h(()=>o.value.ariaLabel||o.value.text),d=h(()=>{const k=Object.keys(u.value.locales);return k.length?!k.some(m=>m===o.value.link):o.value.link!=="/"}),g=h(()=>d.value?t.path.startsWith(o.value.link):!1),x=h(()=>s.value?o.value.activeMatch?new RegExp(o.value.activeMatch).test(t.path):g.value:!1);return(k,m)=>{const N=R("RouterLink"),M=R("AutoLinkExternalIcon");return e(s)?(n(),C(N,ve({key:0,class:{"router-link-active":e(x)},to:e(o).link,"aria-label":e(p)},k.$attrs),{default:B(()=>[$(k.$slots,"before"),j(" "+T(e(o).text)+" ",1),$(k.$slots,"after")]),_:3},16,["class","to","aria-label"])):(n(),i("a",ve({key:1,class:"external-link",href:e(o).link,rel:e(l),target:e(c),"aria-label":e(p)},k.$attrs),[$(k.$slots,"before"),j(" "+T(e(o).text)+" ",1),e(r)?(n(),C(M,{key:0})):y("",!0),$(k.$slots,"after")],16,et))}}});var D=w(at,[["__file","AutoLink.vue"]]);const nt={class:"hero"},ot={key:0,id:"main-title"},rt={key:1,class:"description"},st={key:2,class:"actions"},lt=S({__name:"HomeHero",setup(v){const a=P(),t=Q(),u=Z(),o=h(()=>u.value&&a.value.heroImageDark!==void 0?a.value.heroImageDark:a.value.heroImage),_=h(()=>a.value.heroText===null?null:a.value.heroText||t.value.title||"Hello"),f=h(()=>a.value.heroAlt||_.value||"hero"),c=h(()=>a.value.tagline===null?null:a.value.tagline||t.value.description||"Welcome to your VuePress site"),r=h(()=>pe(a.value.actions)?a.value.actions.map(({text:l,link:p,type:d="primary"})=>({text:l,link:p,type:d})):[]),s=()=>{if(!o.value)return null;const l=q("img",{src:he(o.value),alt:f.value});return a.value.heroImageDark===void 0?l:q(me,()=>l)};return(l,p)=>(n(),i("header",nt,[L(s),e(_)?(n(),i("h1",ot,T(e(_)),1)):y("",!0),e(c)?(n(),i("p",rt,T(e(c)),1)):y("",!0),e(r).length?(n(),i("p",st,[(n(!0),i(I,null,A(e(r),d=>(n(),C(D,{key:d.text,class:E(["action-button",[d.type]]),item:d},null,8,["class","item"]))),128))])):y("",!0)]))}});var ut=w(lt,[["__file","HomeHero.vue"]]);const it={class:"home"},ct=S({__name:"Home",setup(v){return(a,t)=>(n(),i("main",it,[L(ut),L(Xe),L(je),L(Ze)]))}});var dt=w(ct,[["__file","Home.vue"]]);const vt=S({__name:"NavbarBrand",setup(v){const a=fe(),t=Q(),u=H(),o=Z(),_=h(()=>u.value.home||a.value),f=h(()=>t.value.title),c=h(()=>o.value&&u.value.logoDark!==void 0?u.value.logoDark:u.value.logo),r=()=>{if(!c.value)return null;const s=q("img",{class:"logo",src:he(c.value),alt:f.value});return u.value.logoDark===void 0?s:q(me,()=>s)};return(s,l)=>{const p=R("RouterLink");return n(),C(p,{to:e(_)},{default:B(()=>[L(r),e(f)?(n(),i("span",{key:0,class:E(["site-name",{"can-hide":e(c)}])},T(e(f)),3)):y("",!0)]),_:1},8,["to"])}}});var _t=w(vt,[["__file","NavbarBrand.vue"]]);const pt=S({__name:"DropdownTransition",setup(v){const a=u=>{u.style.height=u.scrollHeight+"px"},t=u=>{u.style.height=""};return(u,o)=>(n(),C(be,{name:"dropdown",onEnter:a,onAfterEnter:t,onBeforeLeave:a},{default:B(()=>[$(u.$slots,"default")]),_:3}))}});var Le=w(pt,[["__file","DropdownTransition.vue"]]);const ht=["aria-label"],mt={class:"title"},ft=b("span",{class:"arrow down"},null,-1),bt=["aria-label"],gt={class:"title"},kt={class:"navbar-dropdown"},$t={class:"navbar-dropdown-subtitle"},Lt={key:1},yt={class:"navbar-dropdown-subitem-wrapper"},wt=S({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(v){const a=v,{item:t}=J(a),u=h(()=>t.value.ariaLabel||t.value.text),o=z(!1),_=W();ge(()=>_.path,()=>{o.value=!1});const f=r=>{r.detail===0?o.value=!o.value:o.value=!1},c=(r,s)=>s[s.length-1]===r;return(r,s)=>(n(),i("div",{class:E(["navbar-dropdown-wrapper",{open:o.value}])},[b("button",{class:"navbar-dropdown-title",type:"button","aria-label":e(u),onClick:f},[b("span",mt,T(e(t).text),1),ft],8,ht),b("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":e(u),onClick:s[0]||(s[0]=l=>o.value=!o.value)},[b("span",gt,T(e(t).text),1),b("span",{class:E(["arrow",o.value?"down":"right"])},null,2)],8,bt),L(Le,null,{default:B(()=>[G(b("ul",kt,[(n(!0),i(I,null,A(e(t).children,l=>(n(),i("li",{key:l.text,class:"navbar-dropdown-item"},[l.children?(n(),i(I,{key:0},[b("h4",$t,[l.link?(n(),C(D,{key:0,item:l,onFocusout:p=>c(l,e(t).children)&&l.children.length===0&&(o.value=!1)},null,8,["item","onFocusout"])):(n(),i("span",Lt,T(l.text),1))]),b("ul",yt,[(n(!0),i(I,null,A(l.children,p=>(n(),i("li",{key:p.link,class:"navbar-dropdown-subitem"},[L(D,{item:p,onFocusout:d=>c(p,l.children)&&c(l,e(t).children)&&(o.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(n(),C(D,{key:1,item:l,onFocusout:p=>c(l,e(t).children)&&(o.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[X,o.value]])]),_:1})],2))}});var St=w(wt,[["__file","NavbarDropdown.vue"]]);const _e=v=>decodeURI(v).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Ct=(v,a)=>{if(a.hash===v)return!0;const t=_e(a.path),u=_e(v);return t===u},ye=(v,a)=>v.link&&Ct(v.link,a)?!0:v.children?v.children.some(t=>ye(t,a)):!1,we=v=>!Y(v)||/github\.com/.test(v)?"GitHub":/bitbucket\.org/.test(v)?"Bitbucket":/gitlab\.com/.test(v)?"GitLab":/gitee\.com/.test(v)?"Gitee":null,Tt={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},xt=({docsRepo:v,editLinkPattern:a})=>{if(a)return a;const t=we(v);return t!==null?Tt[t]:null},Bt=({docsRepo:v,docsBranch:a,docsDir:t,filePathRelative:u,editLinkPattern:o})=>{if(!u)return null;const _=xt({docsRepo:v,editLinkPattern:o});return _?_.replace(/:repo/,Y(v)?v:`https://github.com/${v}`).replace(/:branch/,a).replace(/:path/,Ie(`${Me(t)}/${u}`)):null},Nt={key:0,class:"navbar-items"},Ht=S({__name:"NavbarItems",setup(v){const a=()=>{const s=ee(),l=fe(),p=Q(),d=H();return h(()=>{var M,O,F;const g=Object.keys(p.value.locales);if(g.length<2)return[];const x=s.currentRoute.value.path,k=s.currentRoute.value.fullPath,m=s.currentRoute.value.hash;return[{text:(M=d.value.selectLanguageText)!=null?M:"unknown language",ariaLabel:(F=(O=d.value.selectLanguageAriaLabel)!=null?O:d.value.selectLanguageText)!=null?F:"unknown language",children:g.map(U=>{var re,se,le,ue,ie,ce;const Ce=(se=(re=p.value.locales)==null?void 0:re[U])!=null?se:{},ne=(ue=(le=d.value.locales)==null?void 0:le[U])!=null?ue:{},oe=`${Ce.lang}`,Te=(ie=ne.selectLanguageName)!=null?ie:oe;let V;if(oe===p.value.lang)V=k;else{const de=x.replace(l.value,U);s.getRoutes().some(xe=>xe.path===de)?V=`${de}${m}`:V=(ce=ne.home)!=null?ce:U}return{text:Te,link:V}})}]})},t=()=>{const s=H(),l=h(()=>s.value.repo),p=h(()=>l.value?we(l.value):null),d=h(()=>l.value&&!Y(l.value)?`https://github.com/${l.value}`:l.value),g=h(()=>d.value?s.value.repoLabel?s.value.repoLabel:p.value===null?"Source":p.value:null);return h(()=>!d.value||!g.value?[]:[{text:g.value,link:d.value}])},u=s=>ke(s)?$e(s):s.children?{...s,children:s.children.map(u)}:s,_=(()=>{const s=H();return h(()=>(s.value.navbar||[]).map(u))})(),f=a(),c=t(),r=h(()=>[..._.value,...f.value,...c.value]);return(s,l)=>e(r).length?(n(),i("nav",Nt,[(n(!0),i(I,null,A(e(r),p=>(n(),i("div",{key:p.text,class:"navbar-item"},[p.children?(n(),C(St,{key:0,item:p},null,8,["item"])):(n(),C(D,{key:1,item:p},null,8,["item"]))]))),128))])):y("",!0)}});var Se=w(Ht,[["__file","NavbarItems.vue"]]);const It=["title"],Mt={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Dt=De('',9),Pt=[Dt],Et={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Rt=b("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),At=[Rt],Ot=S({__name:"ToggleColorModeButton",setup(v){const a=H(),t=Z(),u=()=>{t.value=!t.value};return(o,_)=>(n(),i("button",{class:"toggle-color-mode-button",title:e(a).toggleColorMode,onClick:u},[G((n(),i("svg",Mt,Pt,512)),[[X,!e(t)]]),G((n(),i("svg",Et,At,512)),[[X,e(t)]])],8,It))}});var Ft=w(Ot,[["__file","ToggleColorModeButton.vue"]]);const zt=["title"],Wt=b("div",{class:"icon","aria-hidden":"true"},[b("span"),b("span"),b("span")],-1),Ut=[Wt],Vt=S({__name:"ToggleSidebarButton",emits:["toggle"],setup(v){const a=H();return(t,u)=>(n(),i("div",{class:"toggle-sidebar-button",title:e(a).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:u[0]||(u[0]=o=>t.$emit("toggle"))},Ut,8,zt))}});var Kt=w(Vt,[["__file","ToggleSidebarButton.vue"]]);const jt=S({__name:"Navbar",emits:["toggle-sidebar"],setup(v){const a=H(),t=z(null),u=z(null),o=z(0),_=h(()=>o.value?{maxWidth:o.value+"px"}:{});te(()=>{const r=f(t.value,"paddingLeft")+f(t.value,"paddingRight"),s=()=>{var l;window.innerWidth<=719?o.value=0:o.value=t.value.offsetWidth-r-(((l=u.value)==null?void 0:l.offsetWidth)||0)};s(),window.addEventListener("resize",s,!1),window.addEventListener("orientationchange",s,!1)});function f(c,r){var p,d,g;const s=(g=(d=(p=c==null?void 0:c.ownerDocument)==null?void 0:p.defaultView)==null?void 0:d.getComputedStyle(c,null))==null?void 0:g[r],l=Number.parseInt(s,10);return Number.isNaN(l)?0:l}return(c,r)=>{const s=R("NavbarSearch");return n(),i("header",{ref_key:"navbar",ref:t,class:"navbar"},[L(Kt,{onToggle:r[0]||(r[0]=l=>c.$emit("toggle-sidebar"))}),b("span",{ref_key:"navbarBrand",ref:u},[L(_t)],512),b("div",{class:"navbar-items-wrapper",style:Pe(e(_))},[$(c.$slots,"before"),L(Se,{class:"can-hide"}),$(c.$slots,"after"),e(a).colorModeSwitch?(n(),C(Ft,{key:0})):y("",!0),L(s)],4)],512)}}});var qt=w(jt,[["__file","Navbar.vue"]]);const Gt={class:"page-meta"},Xt={key:0,class:"meta-item edit-link"},Yt={key:1,class:"meta-item last-updated"},Jt={class:"meta-item-label"},Qt={class:"meta-item-info"},Zt={key:2,class:"meta-item contributors"},ea={class:"meta-item-label"},ta={class:"meta-item-info"},aa=["title"],na=j(", "),oa=S({__name:"PageMeta",setup(v){const a=()=>{const r=H(),s=K(),l=P();return h(()=>{var M,O,F;if(!((O=(M=l.value.editLink)!=null?M:r.value.editLink)!=null?O:!0))return null;const{repo:d,docsRepo:g=d,docsBranch:x="main",docsDir:k="",editLinkText:m}=r.value;if(!g)return null;const N=Bt({docsRepo:g,docsBranch:x,docsDir:k,filePathRelative:s.value.filePathRelative,editLinkPattern:(F=l.value.editLinkPattern)!=null?F:r.value.editLinkPattern});return N?{text:m!=null?m:"Edit this page",link:N}:null})},t=()=>{const r=H(),s=K(),l=P();return h(()=>{var g,x,k,m;return!((x=(g=l.value.lastUpdated)!=null?g:r.value.lastUpdated)!=null?x:!0)||!((k=s.value.git)!=null&&k.updatedTime)?null:new Date((m=s.value.git)==null?void 0:m.updatedTime).toLocaleString()})},u=()=>{const r=H(),s=K(),l=P();return h(()=>{var d,g,x,k;return((g=(d=l.value.contributors)!=null?d:r.value.contributors)!=null?g:!0)&&(k=(x=s.value.git)==null?void 0:x.contributors)!=null?k:null})},o=H(),_=a(),f=t(),c=u();return(r,s)=>{const l=R("ClientOnly");return n(),i("footer",Gt,[e(_)?(n(),i("div",Xt,[L(D,{class:"meta-item-label",item:e(_)},null,8,["item"])])):y("",!0),e(f)?(n(),i("div",Yt,[b("span",Jt,T(e(o).lastUpdatedText)+": ",1),L(l,null,{default:B(()=>[b("span",Qt,T(e(f)),1)]),_:1})])):y("",!0),e(c)&&e(c).length?(n(),i("div",Zt,[b("span",ea,T(e(o).contributorsText)+": ",1),b("span",ta,[(n(!0),i(I,null,A(e(c),(p,d)=>(n(),i(I,{key:d},[b("span",{class:"contributor",title:`email: ${p.email}`},T(p.name),9,aa),d!==e(c).length-1?(n(),i(I,{key:0},[na],64)):y("",!0)],64))),128))])])):y("",!0)])}}});var ra=w(oa,[["__file","PageMeta.vue"]]);const sa={key:0,class:"page-nav"},la={class:"inner"},ua={key:0,class:"prev"},ia={key:1,class:"next"},ca=S({__name:"PageNav",setup(v){const a=r=>r===!1?null:ke(r)?$e(r):Ee(r)?r:!1,t=(r,s,l)=>{const p=r.findIndex(d=>d.link===s);if(p!==-1){const d=r[p+l];return d!=null&&d.link?d:null}for(const d of r)if(d.children){const g=t(d.children,s,l);if(g)return g}return null},u=P(),o=ae(),_=W(),f=h(()=>{const r=a(u.value.prev);return r!==!1?r:t(o.value,_.path,-1)}),c=h(()=>{const r=a(u.value.next);return r!==!1?r:t(o.value,_.path,1)});return(r,s)=>e(f)||e(c)?(n(),i("nav",sa,[b("p",la,[e(f)?(n(),i("span",ua,[L(D,{item:e(f)},null,8,["item"])])):y("",!0),e(c)?(n(),i("span",ia,[L(D,{item:e(c)},null,8,["item"])])):y("",!0)])])):y("",!0)}});var da=w(ca,[["__file","PageNav.vue"]]);const va={class:"page"},_a={class:"theme-default-content"},pa=S({__name:"Page",setup(v){return(a,t)=>{const u=R("Content");return n(),i("main",va,[$(a.$slots,"top"),b("div",_a,[$(a.$slots,"content-top"),L(u),$(a.$slots,"content-bottom")]),L(ra),L(da),$(a.$slots,"bottom")])}}});var ha=w(pa,[["__file","Page.vue"]]);const ma=["onKeydown"],fa={class:"sidebar-item-children"},ba=S({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(v){const a=v,{item:t,depth:u}=J(a),o=W(),_=ee(),f=h(()=>ye(t.value,o)),c=h(()=>({"sidebar-item":!0,"sidebar-heading":u.value===0,active:f.value,collapsible:t.value.collapsible})),[r,s]=Re(f.value),l=d=>{t.value.collapsible&&(d.preventDefault(),s())},p=_.afterEach(d=>{Ae(()=>{r.value=t.value.collapsible?f.value:!0})});return Oe(()=>{p()}),(d,g)=>{var k;const x=R("SidebarItem",!0);return n(),i("li",null,[e(t).link?(n(),C(D,{key:0,class:E(e(c)),item:e(t)},null,8,["class","item"])):(n(),i("p",{key:1,tabindex:"0",class:E(e(c)),onClick:l,onKeydown:Fe(l,["enter"])},[j(T(e(t).text)+" ",1),e(t).collapsible?(n(),i("span",{key:0,class:E(["arrow",e(r)?"down":"right"])},null,2)):y("",!0)],42,ma)),(k=e(t).children)!=null&&k.length?(n(),C(Le,{key:2},{default:B(()=>[G(b("ul",fa,[(n(!0),i(I,null,A(e(t).children,m=>(n(),C(x,{key:`${e(u)}${m.text}${m.link}`,item:m,depth:e(u)+1},null,8,["item","depth"]))),128))],512),[[X,e(r)]])]),_:1})):y("",!0)])}}});var ga=w(ba,[["__file","SidebarItem.vue"]]);const ka={key:0,class:"sidebar-items"},$a=S({__name:"SidebarItems",setup(v){const a=W(),t=ae();return te(()=>{ge(()=>a.hash,u=>{const o=document.querySelector(".sidebar");if(!o)return;const _=document.querySelector(`.sidebar a.sidebar-item[href="${a.path}${u}"]`);if(!_)return;const{top:f,height:c}=o.getBoundingClientRect(),{top:r,height:s}=_.getBoundingClientRect();rf+c&&_.scrollIntoView(!1)})}),(u,o)=>e(t).length?(n(),i("ul",ka,[(n(!0),i(I,null,A(e(t),_=>(n(),C(ga,{key:`${_.text}${_.link}`,item:_},null,8,["item"]))),128))])):y("",!0)}});var La=w($a,[["__file","SidebarItems.vue"]]);const ya={class:"sidebar"},wa=S({__name:"Sidebar",setup(v){return(a,t)=>(n(),i("aside",ya,[L(Se),$(a.$slots,"top"),L(La),$(a.$slots,"bottom")]))}});var Sa=w(wa,[["__file","Sidebar.vue"]]);const Ca=S({__name:"Layout",setup(v){const a=K(),t=P(),u=H(),o=h(()=>t.value.navbar!==!1&&u.value.navbar!==!1),_=ae(),f=z(!1),c=m=>{f.value=typeof m=="boolean"?m:!f.value},r={x:0,y:0},s=m=>{r.x=m.changedTouches[0].clientX,r.y=m.changedTouches[0].clientY},l=m=>{const N=m.changedTouches[0].clientX-r.x,M=m.changedTouches[0].clientY-r.y;Math.abs(N)>Math.abs(M)&&Math.abs(N)>40&&(N>0&&r.x<=80?c(!0):c(!1))},p=h(()=>[{"no-navbar":!o.value,"no-sidebar":!_.value.length,"sidebar-open":f.value},t.value.pageClass]);let d;te(()=>{d=ee().afterEach(()=>{c(!1)})}),ze(()=>{d()});const g=We(),x=g.resolve,k=g.pending;return(m,N)=>(n(),i("div",{class:E(["theme-container",e(p)]),onTouchstart:s,onTouchend:l},[$(m.$slots,"navbar",{},()=>[e(o)?(n(),C(qt,{key:0,onToggleSidebar:c},{before:B(()=>[$(m.$slots,"navbar-before")]),after:B(()=>[$(m.$slots,"navbar-after")]),_:3})):y("",!0)]),b("div",{class:"sidebar-mask",onClick:N[0]||(N[0]=M=>c(!1))}),$(m.$slots,"sidebar",{},()=>[L(Sa,null,{top:B(()=>[$(m.$slots,"sidebar-top")]),bottom:B(()=>[$(m.$slots,"sidebar-bottom")]),_:3})]),$(m.$slots,"page",{},()=>[e(t).home?(n(),C(dt,{key:0})):(n(),C(be,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:e(x),onBeforeLeave:e(k)},{default:B(()=>[(n(),C(ha,{key:e(a).path},{top:B(()=>[$(m.$slots,"page-top")]),"content-top":B(()=>[$(m.$slots,"page-content-top")]),"content-bottom":B(()=>[$(m.$slots,"page-content-bottom")]),bottom:B(()=>[$(m.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}});var xa=w(Ca,[["__file","Layout.vue"]]);export{xa as default}; diff --git a/assets/add-bot.55f7988f.png b/assets/add-bot.55f7988f.png new file mode 100644 index 0000000..bb321a8 Binary files /dev/null and b/assets/add-bot.55f7988f.png differ diff --git a/assets/after-finished.b35ece7e.png b/assets/after-finished.b35ece7e.png new file mode 100644 index 0000000..56af8ea Binary files /dev/null and b/assets/after-finished.b35ece7e.png differ diff --git a/assets/app.151ccb98.js b/assets/app.151ccb98.js new file mode 100644 index 0000000..9fc4213 --- /dev/null +++ b/assets/app.151ccb98.js @@ -0,0 +1,10 @@ +const ci={},bl="modulepreload",wr={},yl="/",N=function(t,n){return!n||n.length===0?t():Promise.all(n.map(i=>{if(i=`${yl}${i}`,i in wr)return;wr[i]=!0;const r=i.endsWith(".css"),s=r?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${i}"]${s}`))return;const o=document.createElement("link");if(o.rel=r?"stylesheet":bl,r||(o.as="script",o.crossOrigin=""),o.href=i,document.head.appendChild(o),r)return new Promise((l,a)=>{o.addEventListener("load",l),o.addEventListener("error",()=>a(new Error(`Unable to preload CSS for ${i}`)))})})).then(()=>t())},wl={"v-8daa1a0e":()=>N(()=>import("./index.html.70af15a3.js"),[]).then(({data:e})=>e),"v-92e5302e":()=>N(()=>import("./imprint.html.aa34b88a.js"),[]).then(({data:e})=>e),"v-b106f6ee":()=>N(()=>import("./privacy-policy.html.e6350755.js"),[]).then(({data:e})=>e),"v-348a4efb":()=>N(()=>import("./bot-lifecycle.html.bc8486a5.js"),[]).then(({data:e})=>e),"v-32da090a":()=>N(()=>import("./entity-cache.html.d35078da.js"),[]).then(({data:e})=>e),"v-2037d84f":()=>N(()=>import("./performance-tweaks.html.1ed90e55.js"),[]).then(({data:e})=>e),"v-0b11c848":()=>N(()=>import("./playing-audio.html.15139e1b.js"),[]).then(({data:e})=>e),"v-47a9d05a":()=>N(()=>import("./proxies.html.b2817cee.js"),[]).then(({data:e})=>e),"v-fc3b23ca":()=>N(()=>import("./ratelimits.html.4f60547f.js"),[]).then(({data:e})=>e),"v-595301cf":()=>N(()=>import("./sharding.html.d6f88b19.js"),[]).then(({data:e})=>e),"v-5fd376fa":()=>N(()=>import("./creating-entities.html.d4566e7e.js"),[]).then(({data:e})=>e),"v-37293ae0":()=>N(()=>import("./embeds.html.68dc7509.js"),[]).then(({data:e})=>e),"v-13e99c06":()=>N(()=>import("./emojis-and-reactions.html.7f613dfc.js"),[]).then(({data:e})=>e),"v-e7aba52c":()=>N(()=>import("./gateway-intents.html.96026c66.js"),[]).then(({data:e})=>e),"v-5b369fbc":()=>N(()=>import("./glossary.html.34f5fdb3.js"),[]).then(({data:e})=>e),"v-4256bfd9":()=>N(()=>import("./listeners.html.0ec76197.js"),[]).then(({data:e})=>e),"v-33173f0e":()=>N(()=>import("./logger-config.html.c3000dce.js"),[]).then(({data:e})=>e),"v-29bd20c3":()=>N(()=>import("./message-builder.html.02c7cb1d.js"),[]).then(({data:e})=>e),"v-8012cfce":()=>N(()=>import("./running.html.15e65516.js"),[]).then(({data:e})=>e),"v-c85a18b4":()=>N(()=>import("./completable-futures.html.4bcb806c.js"),[]).then(({data:e})=>e),"v-22528d43":()=>N(()=>import("./lambdas.html.83e4c5bc.js"),[]).then(({data:e})=>e),"v-56bee89c":()=>N(()=>import("./optionals.html.b4abca2e.js"),[]).then(({data:e})=>e),"v-15814726":()=>N(()=>import("./index.html.6c396006.js"),[]).then(({data:e})=>e),"v-5628c715":()=>N(()=>import("./creating-a-bot-account.html.d48fba04.js"),[]).then(({data:e})=>e),"v-7d129412":()=>N(()=>import("./download-installation.html.2a3aecfd.js"),[]).then(({data:e})=>e),"v-6bd28c40":()=>N(()=>import("./faq.html.daf42ee7.js"),[]).then(({data:e})=>e),"v-7bf86adb":()=>N(()=>import("./writing-your-first-bot.html.ab95f1ba.js"),[]).then(({data:e})=>e),"v-36c441c2":()=>N(()=>import("./commands.html.2011a310.js"),[]).then(({data:e})=>e),"v-35e5cc98":()=>N(()=>import("./components.html.604ea486.js"),[]).then(({data:e})=>e),"v-16fe8d71":()=>N(()=>import("./overview.html.d664223d.js"),[]).then(({data:e})=>e),"v-00728006":()=>N(()=>import("./responding.html.3c5c7e21.js"),[]).then(({data:e})=>e),"v-22de0aba":()=>N(()=>import("./eclipse-maven.html.04415d3e.js"),[]).then(({data:e})=>e),"v-6d1d378b":()=>N(()=>import("./intellij-gradle.html.9ec9aa86.js"),[]).then(({data:e})=>e),"v-0ae94875":()=>N(()=>import("./intellij-maven.html.8bb18166.js"),[]).then(({data:e})=>e),"v-3706649a":()=>N(()=>import("./404.html.7d858b3d.js"),[]).then(({data:e})=>e)};function Wi(e,t){const n=Object.create(null),i=e.split(",");for(let r=0;r!!n[r.toLowerCase()]:r=>!!n[r]}const El="itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly",kl=Wi(El);function As(e){return!!e||e===""}function Jn(e){if(Q(e)){const t={};for(let n=0;n{if(n){const i=n.split(xl);i.length>1&&(t[i[0].trim()]=i[1].trim())}}),t}function yn(e){let t="";if(ve(e))t=e;else if(Q(e))for(let n=0;nve(e)?e:e==null?"":Q(e)||Ee(e)&&(e.toString===Ds||!ee(e.toString))?JSON.stringify(e,Ss,2):String(e),Ss=(e,t)=>t&&t.__v_isRef?Ss(e,t.value):zt(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((n,[i,r])=>(n[`${i} =>`]=r,n),{})}:Os(t)?{[`Set(${t.size})`]:[...t.values()]}:Ee(t)&&!Q(t)&&!Ms(t)?String(t):t,ge={},Bt=[],Je=()=>{},Rl=()=>!1,Tl=/^on[^a-z]/,wn=e=>Tl.test(e),qi=e=>e.startsWith("onUpdate:"),Le=Object.assign,Ki=(e,t)=>{const n=e.indexOf(t);n>-1&&e.splice(n,1)},Al=Object.prototype.hasOwnProperty,se=(e,t)=>Al.call(e,t),Q=Array.isArray,zt=e=>Gn(e)==="[object Map]",Os=e=>Gn(e)==="[object Set]",ee=e=>typeof e=="function",ve=e=>typeof e=="string",Ji=e=>typeof e=="symbol",Ee=e=>e!==null&&typeof e=="object",Is=e=>Ee(e)&&ee(e.then)&&ee(e.catch),Ds=Object.prototype.toString,Gn=e=>Ds.call(e),Sl=e=>Gn(e).slice(8,-1),Ms=e=>Gn(e)==="[object Object]",Gi=e=>ve(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,on=Wi(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Qn=e=>{const t=Object.create(null);return n=>t[n]||(t[n]=e(n))},Ol=/-(\w)/g,Xe=Qn(e=>e.replace(Ol,(t,n)=>n?n.toUpperCase():"")),Il=/\B([A-Z])/g,Dt=Qn(e=>e.replace(Il,"-$1").toLowerCase()),Yn=Qn(e=>e.charAt(0).toUpperCase()+e.slice(1)),ui=Qn(e=>e?`on${Yn(e)}`:""),hn=(e,t)=>!Object.is(e,t),fi=(e,t)=>{for(let n=0;n{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,value:n})},Fs=e=>{const t=parseFloat(e);return isNaN(t)?e:t};let Er;const Dl=()=>Er||(Er=typeof globalThis!="undefined"?globalThis:typeof self!="undefined"?self:typeof window!="undefined"?window:typeof global!="undefined"?global:{});let Me;class Ml{constructor(t=!1){this.active=!0,this.effects=[],this.cleanups=[],!t&&Me&&(this.parent=Me,this.index=(Me.scopes||(Me.scopes=[])).push(this)-1)}run(t){if(this.active){const n=Me;try{return Me=this,t()}finally{Me=n}}}on(){Me=this}off(){Me=this.parent}stop(t){if(this.active){let n,i;for(n=0,i=this.effects.length;n{const t=new Set(e);return t.w=0,t.n=0,t},Ns=e=>(e.w&_t)>0,Hs=e=>(e.n&_t)>0,jl=({deps:e})=>{if(e.length)for(let t=0;t{const{deps:t}=e;if(t.length){let n=0;for(let i=0;i{(c==="length"||c>=i)&&l.push(a)});else switch(n!==void 0&&l.push(o.get(n)),t){case"add":Q(e)?Gi(n)&&l.push(o.get("length")):(l.push(o.get(St)),zt(e)&&l.push(o.get(Pi)));break;case"delete":Q(e)||(l.push(o.get(St)),zt(e)&&l.push(o.get(Pi)));break;case"set":zt(e)&&l.push(o.get(St));break}if(l.length===1)l[0]&&Ri(l[0]);else{const a=[];for(const c of l)c&&a.push(...c);Ri(Qi(a))}}function Ri(e,t){const n=Q(e)?e:[...e];for(const i of n)i.computed&&Cr(i);for(const i of n)i.computed||Cr(i)}function Cr(e,t){(e!==qe||e.allowRecurse)&&(e.scheduler?e.scheduler():e.run())}const Bl=Wi("__proto__,__v_isRef,__isVue"),Bs=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Ji)),zl=Zi(),Vl=Zi(!1,!0),Ul=Zi(!0),xr=Wl();function Wl(){const e={};return["includes","indexOf","lastIndexOf"].forEach(t=>{e[t]=function(...n){const i=ce(this);for(let s=0,o=this.length;s{e[t]=function(...n){Gt();const i=ce(this)[t].apply(this,n);return Qt(),i}}),e}function Zi(e=!1,t=!1){return function(i,r,s){if(r==="__v_isReactive")return!e;if(r==="__v_isReadonly")return e;if(r==="__v_isShallow")return t;if(r==="__v_raw"&&s===(e?t?la:qs:t?Ws:Us).get(i))return i;const o=Q(i);if(!e&&o&&se(xr,r))return Reflect.get(xr,r,s);const l=Reflect.get(i,r,s);return(Ji(r)?Bs.has(r):Bl(r))||(e||He(i,"get",r),t)?l:Ce(l)?o&&Gi(r)?l:l.value:Ee(l)?e?tr(l):Yt(l):l}}const ql=zs(),Kl=zs(!0);function zs(e=!1){return function(n,i,r,s){let o=n[i];if(pn(o)&&Ce(o)&&!Ce(r))return!1;if(!e&&!pn(r)&&(Ti(r)||(r=ce(r),o=ce(o)),!Q(n)&&Ce(o)&&!Ce(r)))return o.value=r,!0;const l=Q(n)&&Gi(i)?Number(i)e,Zn=e=>Reflect.getPrototypeOf(e);function Ln(e,t,n=!1,i=!1){e=e.__v_raw;const r=ce(e),s=ce(t);n||(t!==s&&He(r,"get",t),He(r,"get",s));const{has:o}=Zn(r),l=i?Xi:n?ir:mn;if(o.call(r,t))return l(e.get(t));if(o.call(r,s))return l(e.get(s));e!==r&&e.get(t)}function Pn(e,t=!1){const n=this.__v_raw,i=ce(n),r=ce(e);return t||(e!==r&&He(i,"has",e),He(i,"has",r)),e===r?n.has(e):n.has(e)||n.has(r)}function Rn(e,t=!1){return e=e.__v_raw,!t&&He(ce(e),"iterate",St),Reflect.get(e,"size",e)}function Lr(e){e=ce(e);const t=ce(this);return Zn(t).has.call(t,e)||(t.add(e),rt(t,"add",e,e)),this}function Pr(e,t){t=ce(t);const n=ce(this),{has:i,get:r}=Zn(n);let s=i.call(n,e);s||(e=ce(e),s=i.call(n,e));const o=r.call(n,e);return n.set(e,t),s?hn(t,o)&&rt(n,"set",e,t):rt(n,"add",e,t),this}function Rr(e){const t=ce(this),{has:n,get:i}=Zn(t);let r=n.call(t,e);r||(e=ce(e),r=n.call(t,e)),i&&i.call(t,e);const s=t.delete(e);return r&&rt(t,"delete",e,void 0),s}function Tr(){const e=ce(this),t=e.size!==0,n=e.clear();return t&&rt(e,"clear",void 0,void 0),n}function Tn(e,t){return function(i,r){const s=this,o=s.__v_raw,l=ce(o),a=t?Xi:e?ir:mn;return!e&&He(l,"iterate",St),o.forEach((c,u)=>i.call(r,a(c),a(u),s))}}function An(e,t,n){return function(...i){const r=this.__v_raw,s=ce(r),o=zt(s),l=e==="entries"||e===Symbol.iterator&&o,a=e==="keys"&&o,c=r[e](...i),u=n?Xi:t?ir:mn;return!t&&He(s,"iterate",a?Pi:St),{next(){const{value:d,done:p}=c.next();return p?{value:d,done:p}:{value:l?[u(d[0]),u(d[1])]:u(d),done:p}},[Symbol.iterator](){return this}}}}function lt(e){return function(...t){return e==="delete"?!1:this}}function Xl(){const e={get(s){return Ln(this,s)},get size(){return Rn(this)},has:Pn,add:Lr,set:Pr,delete:Rr,clear:Tr,forEach:Tn(!1,!1)},t={get(s){return Ln(this,s,!1,!0)},get size(){return Rn(this)},has:Pn,add:Lr,set:Pr,delete:Rr,clear:Tr,forEach:Tn(!1,!0)},n={get(s){return Ln(this,s,!0)},get size(){return Rn(this,!0)},has(s){return Pn.call(this,s,!0)},add:lt("add"),set:lt("set"),delete:lt("delete"),clear:lt("clear"),forEach:Tn(!0,!1)},i={get(s){return Ln(this,s,!0,!0)},get size(){return Rn(this,!0)},has(s){return Pn.call(this,s,!0)},add:lt("add"),set:lt("set"),delete:lt("delete"),clear:lt("clear"),forEach:Tn(!0,!0)};return["keys","values","entries",Symbol.iterator].forEach(s=>{e[s]=An(s,!1,!1),n[s]=An(s,!0,!1),t[s]=An(s,!1,!0),i[s]=An(s,!0,!0)}),[e,n,t,i]}const[ea,ta,na,ia]=Xl();function er(e,t){const n=t?e?ia:na:e?ta:ea;return(i,r,s)=>r==="__v_isReactive"?!e:r==="__v_isReadonly"?e:r==="__v_raw"?i:Reflect.get(se(n,r)&&r in i?n:i,r,s)}const ra={get:er(!1,!1)},sa={get:er(!1,!0)},oa={get:er(!0,!1)},Us=new WeakMap,Ws=new WeakMap,qs=new WeakMap,la=new WeakMap;function aa(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function ca(e){return e.__v_skip||!Object.isExtensible(e)?0:aa(Sl(e))}function Yt(e){return pn(e)?e:nr(e,!1,Vs,ra,Us)}function ua(e){return nr(e,!1,Zl,sa,Ws)}function tr(e){return nr(e,!0,Yl,oa,qs)}function nr(e,t,n,i,r){if(!Ee(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const s=r.get(e);if(s)return s;const o=ca(e);if(o===0)return e;const l=new Proxy(e,o===2?i:n);return r.set(e,l),l}function Vt(e){return pn(e)?Vt(e.__v_raw):!!(e&&e.__v_isReactive)}function pn(e){return!!(e&&e.__v_isReadonly)}function Ti(e){return!!(e&&e.__v_isShallow)}function Ks(e){return Vt(e)||pn(e)}function ce(e){const t=e&&e.__v_raw;return t?ce(t):e}function Js(e){return Nn(e,"__v_skip",!0),e}const mn=e=>Ee(e)?Yt(e):e,ir=e=>Ee(e)?tr(e):e;function Gs(e){gt&&qe&&(e=ce(e),$s(e.dep||(e.dep=Qi())))}function Qs(e,t){e=ce(e),e.dep&&Ri(e.dep)}function Ce(e){return!!(e&&e.__v_isRef===!0)}function be(e){return Zs(e,!1)}function Ys(e){return Zs(e,!0)}function Zs(e,t){return Ce(e)?e:new fa(e,t)}class fa{constructor(t,n){this.__v_isShallow=n,this.dep=void 0,this.__v_isRef=!0,this._rawValue=n?t:ce(t),this._value=n?t:mn(t)}get value(){return Gs(this),this._value}set value(t){t=this.__v_isShallow?t:ce(t),hn(t,this._rawValue)&&(this._rawValue=t,this._value=this.__v_isShallow?t:mn(t),Qs(this))}}function Ge(e){return Ce(e)?e.value:e}const da={get:(e,t,n)=>Ge(Reflect.get(e,t,n)),set:(e,t,n,i)=>{const r=e[t];return Ce(r)&&!Ce(n)?(r.value=n,!0):Reflect.set(e,t,n,i)}};function Xs(e){return Vt(e)?e:new Proxy(e,da)}function ha(e){const t=Q(e)?new Array(e.length):{};for(const n in e)t[n]=ma(e,n);return t}class pa{constructor(t,n,i){this._object=t,this._key=n,this._defaultValue=i,this.__v_isRef=!0}get value(){const t=this._object[this._key];return t===void 0?this._defaultValue:t}set value(t){this._object[this._key]=t}}function ma(e,t,n){const i=e[t];return Ce(i)?i:new pa(e,t,n)}class ga{constructor(t,n,i,r){this._setter=n,this.dep=void 0,this.__v_isRef=!0,this._dirty=!0,this.effect=new Yi(t,()=>{this._dirty||(this._dirty=!0,Qs(this))}),this.effect.computed=this,this.effect.active=this._cacheable=!r,this.__v_isReadonly=i}get value(){const t=ce(this);return Gs(t),(t._dirty||!t._cacheable)&&(t._dirty=!1,t._value=t.effect.run()),t._value}set value(t){this._setter(t)}}function va(e,t,n=!1){let i,r;const s=ee(e);return s?(i=e,r=Je):(i=e.get,r=e.set),new ga(i,r,s||!r,n)}function vt(e,t,n,i){let r;try{r=i?e(...i):e()}catch(s){En(s,t,n)}return r}function Ve(e,t,n,i){if(ee(e)){const s=vt(e,t,n,i);return s&&Is(s)&&s.catch(o=>{En(o,t,n)}),s}const r=[];for(let s=0;s>>1;gn(Fe[i])tt&&Fe.splice(t,1)}function no(e,t,n,i){Q(e)?n.push(...e):(!t||!t.includes(e,e.allowRecurse?i+1:i))&&n.push(e),to()}function wa(e){no(e,sn,ln,Ht)}function Ea(e){no(e,dt,an,jt)}function Xn(e,t=null){if(ln.length){for(Si=t,sn=[...new Set(ln)],ln.length=0,Ht=0;Htgn(n)-gn(i)),jt=0;jte.id==null?1/0:e.id;function io(e){Ai=!1,Hn=!0,Xn(e),Fe.sort((n,i)=>gn(n)-gn(i));const t=Je;try{for(tt=0;ttg.trim())),d&&(r=n.map(Fs))}let l,a=i[l=ui(t)]||i[l=ui(Xe(t))];!a&&s&&(a=i[l=ui(Dt(t))]),a&&Ve(a,e,6,r);const c=i[l+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[l])return;e.emitted[l]=!0,Ve(c,e,6,r)}}function ro(e,t,n=!1){const i=t.emitsCache,r=i.get(e);if(r!==void 0)return r;const s=e.emits;let o={},l=!1;if(!ee(e)){const a=c=>{const u=ro(c,t,!0);u&&(l=!0,Le(o,u))};!n&&t.mixins.length&&t.mixins.forEach(a),e.extends&&a(e.extends),e.mixins&&e.mixins.forEach(a)}return!s&&!l?(i.set(e,null),null):(Q(s)?s.forEach(a=>o[a]=null):Le(o,s),i.set(e,o),o)}function ei(e,t){return!e||!wn(t)?!1:(t=t.slice(2).replace(/Once$/,""),se(e,t[0].toLowerCase()+t.slice(1))||se(e,Dt(t))||se(e,t))}let Pe=null,so=null;function $n(e){const t=Pe;return Pe=e,so=e&&e.type.__scopeId||null,t}function Ca(e,t=Pe,n){if(!t||e._n)return e;const i=(...r)=>{i._d&&$r(-1);const s=$n(t),o=e(...r);return $n(s),i._d&&$r(1),o};return i._n=!0,i._c=!0,i._d=!0,i}function di(e){const{type:t,vnode:n,proxy:i,withProxy:r,props:s,propsOptions:[o],slots:l,attrs:a,emit:c,render:u,renderCache:d,data:p,setupState:g,ctx:y,inheritAttrs:A}=e;let P,m;const b=$n(e);try{if(n.shapeFlag&4){const M=r||i;P=We(u.call(M,M,d,s,g,p,y)),m=a}else{const M=t;P=We(M.length>1?M(s,{attrs:a,slots:l,emit:c}):M(s,null)),m=t.props?a:xa(a)}}catch(M){un.length=0,En(M,e,1),P=ye(Ne)}let x=P;if(m&&A!==!1){const M=Object.keys(m),{shapeFlag:z}=x;M.length&&z&7&&(o&&M.some(qi)&&(m=La(m,o)),x=bt(x,m))}return n.dirs&&(x=bt(x),x.dirs=x.dirs?x.dirs.concat(n.dirs):n.dirs),n.transition&&(x.transition=n.transition),P=x,$n(b),P}const xa=e=>{let t;for(const n in e)(n==="class"||n==="style"||wn(n))&&((t||(t={}))[n]=e[n]);return t},La=(e,t)=>{const n={};for(const i in e)(!qi(i)||!(i.slice(9)in t))&&(n[i]=e[i]);return n};function Pa(e,t,n){const{props:i,children:r,component:s}=e,{props:o,children:l,patchFlag:a}=t,c=s.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&a>=0){if(a&1024)return!0;if(a&16)return i?Ar(i,o,c):!!o;if(a&8){const u=t.dynamicProps;for(let d=0;de.__isSuspense;function oo(e,t){t&&t.pendingBranch?Q(e)?t.effects.push(...e):t.effects.push(e):Ea(e)}function Ot(e,t){if(ke){let n=ke.provides;const i=ke.parent&&ke.parent.provides;i===n&&(n=ke.provides=Object.create(i)),n[e]=t}}function xe(e,t,n=!1){const i=ke||Pe;if(i){const r=i.parent==null?i.vnode.appContext&&i.vnode.appContext.provides:i.parent.provides;if(r&&e in r)return r[e];if(arguments.length>1)return n&&ee(t)?t.call(i.proxy):t}}const Sr={};function it(e,t,n){return lo(e,t,n)}function lo(e,t,{immediate:n,deep:i,flush:r,onTrack:s,onTrigger:o}=ge){const l=ke;let a,c=!1,u=!1;if(Ce(e)?(a=()=>e.value,c=Ti(e)):Vt(e)?(a=()=>e,i=!0):Q(e)?(u=!0,c=e.some(m=>Vt(m)||Ti(m)),a=()=>e.map(m=>{if(Ce(m))return m.value;if(Vt(m))return At(m);if(ee(m))return vt(m,l,2)})):ee(e)?t?a=()=>vt(e,l,2):a=()=>{if(!(l&&l.isUnmounted))return d&&d(),Ve(e,l,3,[p])}:a=Je,t&&i){const m=a;a=()=>At(m())}let d,p=m=>{d=P.onStop=()=>{vt(m,l,4)}};if(qt)return p=Je,t?n&&Ve(t,l,3,[a(),u?[]:void 0,p]):a(),Je;let g=u?[]:Sr;const y=()=>{if(!!P.active)if(t){const m=P.run();(i||c||(u?m.some((b,x)=>hn(b,g[x])):hn(m,g)))&&(d&&d(),Ve(t,l,3,[m,g===Sr?void 0:g,p]),g=m)}else P.run()};y.allowRecurse=!!t;let A;r==="sync"?A=y:r==="post"?A=()=>Se(y,l&&l.suspense):A=()=>wa(y);const P=new Yi(a,A);return t?n?y():g=P.run():r==="post"?Se(P.run.bind(P),l&&l.suspense):P.run(),()=>{P.stop(),l&&l.scope&&Ki(l.scope.effects,P)}}function Aa(e,t,n){const i=this.proxy,r=ve(e)?e.includes(".")?ao(i,e):()=>i[e]:e.bind(i,i);let s;ee(t)?s=t:(s=t.handler,n=t);const o=ke;Wt(this);const l=lo(r,s.bind(i),n);return o?Wt(o):It(),l}function ao(e,t){const n=t.split(".");return()=>{let i=e;for(let r=0;r{At(n,t)});else if(Ms(e))for(const n in e)At(e[n],t);return e}function Sa(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return ot(()=>{e.isMounted=!0}),ni(()=>{e.isUnmounting=!0}),e}const Be=[Function,Array],Oa={name:"BaseTransition",props:{mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Be,onEnter:Be,onAfterEnter:Be,onEnterCancelled:Be,onBeforeLeave:Be,onLeave:Be,onAfterLeave:Be,onLeaveCancelled:Be,onBeforeAppear:Be,onAppear:Be,onAfterAppear:Be,onAppearCancelled:Be},setup(e,{slots:t}){const n=Oo(),i=Sa();let r;return()=>{const s=t.default&&fo(t.default(),!0);if(!s||!s.length)return;let o=s[0];if(s.length>1){for(const A of s)if(A.type!==Ne){o=A;break}}const l=ce(e),{mode:a}=l;if(i.isLeaving)return hi(o);const c=Or(o);if(!c)return hi(o);const u=Oi(c,l,i,n);Ii(c,u);const d=n.subTree,p=d&&Or(d);let g=!1;const{getTransitionKey:y}=c.type;if(y){const A=y();r===void 0?r=A:A!==r&&(r=A,g=!0)}if(p&&p.type!==Ne&&(!Rt(c,p)||g)){const A=Oi(p,l,i,n);if(Ii(p,A),a==="out-in")return i.isLeaving=!0,A.afterLeave=()=>{i.isLeaving=!1,n.update()},hi(o);a==="in-out"&&c.type!==Ne&&(A.delayLeave=(P,m,b)=>{const x=uo(i,p);x[String(p.key)]=p,P._leaveCb=()=>{m(),P._leaveCb=void 0,delete u.delayedLeave},u.delayedLeave=b})}return o}}},co=Oa;function uo(e,t){const{leavingVNodes:n}=e;let i=n.get(t.type);return i||(i=Object.create(null),n.set(t.type,i)),i}function Oi(e,t,n,i){const{appear:r,mode:s,persisted:o=!1,onBeforeEnter:l,onEnter:a,onAfterEnter:c,onEnterCancelled:u,onBeforeLeave:d,onLeave:p,onAfterLeave:g,onLeaveCancelled:y,onBeforeAppear:A,onAppear:P,onAfterAppear:m,onAppearCancelled:b}=t,x=String(e.key),M=uo(n,e),z=(v,G)=>{v&&Ve(v,i,9,G)},$=(v,G)=>{const B=G[1];z(v,G),Q(v)?v.every(q=>q.length<=1)&&B():v.length<=1&&B()},D={mode:s,persisted:o,beforeEnter(v){let G=l;if(!n.isMounted)if(r)G=A||l;else return;v._leaveCb&&v._leaveCb(!0);const B=M[x];B&&Rt(e,B)&&B.el._leaveCb&&B.el._leaveCb(),z(G,[v])},enter(v){let G=a,B=c,q=u;if(!n.isMounted)if(r)G=P||a,B=m||c,q=b||u;else return;let w=!1;const F=v._enterCb=S=>{w||(w=!0,S?z(q,[v]):z(B,[v]),D.delayedLeave&&D.delayedLeave(),v._enterCb=void 0)};G?$(G,[v,F]):F()},leave(v,G){const B=String(e.key);if(v._enterCb&&v._enterCb(!0),n.isUnmounting)return G();z(d,[v]);let q=!1;const w=v._leaveCb=F=>{q||(q=!0,G(),F?z(y,[v]):z(g,[v]),v._leaveCb=void 0,M[B]===e&&delete M[B])};M[B]=e,p?$(p,[v,w]):w()},clone(v){return Oi(v,t,n,i)}};return D}function hi(e){if(kn(e))return e=bt(e),e.children=null,e}function Or(e){return kn(e)?e.children?e.children[0]:void 0:e}function Ii(e,t){e.shapeFlag&6&&e.component?Ii(e.component.subTree,t):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function fo(e,t=!1,n){let i=[],r=0;for(let s=0;s1)for(let s=0;s!!e.type.__asyncLoader;function re(e){ee(e)&&(e={loader:e});const{loader:t,loadingComponent:n,errorComponent:i,delay:r=200,timeout:s,suspensible:o=!0,onError:l}=e;let a=null,c,u=0;const d=()=>(u++,a=null,p()),p=()=>{let g;return a||(g=a=t().catch(y=>{if(y=y instanceof Error?y:new Error(String(y)),l)return new Promise((A,P)=>{l(y,()=>A(d()),()=>P(y),u+1)});throw y}).then(y=>g!==a&&a?a:(y&&(y.__esModule||y[Symbol.toStringTag]==="Module")&&(y=y.default),c=y,y)))};return je({name:"AsyncComponentWrapper",__asyncLoader:p,get __asyncResolved(){return c},setup(){const g=ke;if(c)return()=>pi(c,g);const y=b=>{a=null,En(b,g,13,!i)};if(o&&g.suspense||qt)return p().then(b=>()=>pi(b,g)).catch(b=>(y(b),()=>i?ye(i,{error:b}):null));const A=be(!1),P=be(),m=be(!!r);return r&&setTimeout(()=>{m.value=!1},r),s!=null&&setTimeout(()=>{if(!A.value&&!P.value){const b=new Error(`Async component timed out after ${s}ms.`);y(b),P.value=b}},s),p().then(()=>{A.value=!0,g.parent&&kn(g.parent.vnode)&&or(g.parent.update)}).catch(b=>{y(b),P.value=b}),()=>{if(A.value&&c)return pi(c,g);if(P.value&&i)return ye(i,{error:P.value});if(n&&!m.value)return ye(n)}}})}function pi(e,{vnode:{ref:t,props:n,children:i,shapeFlag:r},parent:s}){const o=ye(e,n,i);return o.ref=t,o}const kn=e=>e.type.__isKeepAlive;function Ia(e,t){ho(e,"a",t)}function Da(e,t){ho(e,"da",t)}function ho(e,t,n=ke){const i=e.__wdc||(e.__wdc=()=>{let r=n;for(;r;){if(r.isDeactivated)return;r=r.parent}return e()});if(ti(t,i,n),n){let r=n.parent;for(;r&&r.parent;)kn(r.parent.vnode)&&Ma(i,t,n,r),r=r.parent}}function Ma(e,t,n,i){const r=ti(t,e,i,!0);lr(()=>{Ki(i[t],r)},n)}function ti(e,t,n=ke,i=!1){if(n){const r=n[e]||(n[e]=[]),s=t.__weh||(t.__weh=(...o)=>{if(n.isUnmounted)return;Gt(),Wt(n);const l=Ve(t,n,e,o);return It(),Qt(),l});return i?r.unshift(s):r.push(s),s}}const st=e=>(t,n=ke)=>(!qt||e==="sp")&&ti(e,t,n),po=st("bm"),ot=st("m"),Fa=st("bu"),Na=st("u"),ni=st("bum"),lr=st("um"),Ha=st("sp"),ja=st("rtg"),$a=st("rtc");function Ba(e,t=ke){ti("ec",e,t)}function fh(e,t){const n=Pe;if(n===null)return e;const i=si(n)||n.proxy,r=e.dirs||(e.dirs=[]);for(let s=0;st(o,l,void 0,s&&s[l]));else{const o=Object.keys(e);r=new Array(o.length);for(let l=0,a=o.length;lUn(t)?!(t.type===Ne||t.type===Oe&&!vo(t.children)):!0)?e:null}const Di=e=>e?Io(e)?si(e)||e.proxy:Di(e.parent):null,Bn=Le(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Di(e.parent),$root:e=>Di(e.root),$emit:e=>e.emit,$options:e=>bo(e),$forceUpdate:e=>e.f||(e.f=()=>or(e.update)),$nextTick:e=>e.n||(e.n=sr.bind(e.proxy)),$watch:e=>Aa.bind(e)}),Wa={get({_:e},t){const{ctx:n,setupState:i,data:r,props:s,accessCache:o,type:l,appContext:a}=e;let c;if(t[0]!=="$"){const g=o[t];if(g!==void 0)switch(g){case 1:return i[t];case 2:return r[t];case 4:return n[t];case 3:return s[t]}else{if(i!==ge&&se(i,t))return o[t]=1,i[t];if(r!==ge&&se(r,t))return o[t]=2,r[t];if((c=e.propsOptions[0])&&se(c,t))return o[t]=3,s[t];if(n!==ge&&se(n,t))return o[t]=4,n[t];Mi&&(o[t]=0)}}const u=Bn[t];let d,p;if(u)return t==="$attrs"&&He(e,"get",t),u(e);if((d=l.__cssModules)&&(d=d[t]))return d;if(n!==ge&&se(n,t))return o[t]=4,n[t];if(p=a.config.globalProperties,se(p,t))return p[t]},set({_:e},t,n){const{data:i,setupState:r,ctx:s}=e;return r!==ge&&se(r,t)?(r[t]=n,!0):i!==ge&&se(i,t)?(i[t]=n,!0):se(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(s[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:i,appContext:r,propsOptions:s}},o){let l;return!!n[o]||e!==ge&&se(e,o)||t!==ge&&se(t,o)||(l=s[0])&&se(l,o)||se(i,o)||se(Bn,o)||se(r.config.globalProperties,o)},defineProperty(e,t,n){return n.get!=null?e._.accessCache[t]=0:se(n,"value")&&this.set(e,t,n.value,null),Reflect.defineProperty(e,t,n)}};let Mi=!0;function qa(e){const t=bo(e),n=e.proxy,i=e.ctx;Mi=!1,t.beforeCreate&&Dr(t.beforeCreate,e,"bc");const{data:r,computed:s,methods:o,watch:l,provide:a,inject:c,created:u,beforeMount:d,mounted:p,beforeUpdate:g,updated:y,activated:A,deactivated:P,beforeDestroy:m,beforeUnmount:b,destroyed:x,unmounted:M,render:z,renderTracked:$,renderTriggered:D,errorCaptured:v,serverPrefetch:G,expose:B,inheritAttrs:q,components:w,directives:F,filters:S}=t;if(c&&Ka(c,i,null,e.appContext.config.unwrapInjectedRef),o)for(const X in o){const ne=o[X];ee(ne)&&(i[X]=ne.bind(n))}if(r){const X=r.call(n,n);Ee(X)&&(e.data=Yt(X))}if(Mi=!0,s)for(const X in s){const ne=s[X],we=ee(ne)?ne.bind(n,n):ee(ne.get)?ne.get.bind(n,n):Je,Re=!ee(ne)&&ee(ne.set)?ne.set.bind(n):Je,Ie=fe({get:we,set:Re});Object.defineProperty(i,X,{enumerable:!0,configurable:!0,get:()=>Ie.value,set:$e=>Ie.value=$e})}if(l)for(const X in l)_o(l[X],i,n,X);if(a){const X=ee(a)?a.call(n):a;Reflect.ownKeys(X).forEach(ne=>{Ot(ne,X[ne])})}u&&Dr(u,e,"c");function j(X,ne){Q(ne)?ne.forEach(we=>X(we.bind(n))):ne&&X(ne.bind(n))}if(j(po,d),j(ot,p),j(Fa,g),j(Na,y),j(Ia,A),j(Da,P),j(Ba,v),j($a,$),j(ja,D),j(ni,b),j(lr,M),j(Ha,G),Q(B))if(B.length){const X=e.exposed||(e.exposed={});B.forEach(ne=>{Object.defineProperty(X,ne,{get:()=>n[ne],set:we=>n[ne]=we})})}else e.exposed||(e.exposed={});z&&e.render===Je&&(e.render=z),q!=null&&(e.inheritAttrs=q),w&&(e.components=w),F&&(e.directives=F)}function Ka(e,t,n=Je,i=!1){Q(e)&&(e=Fi(e));for(const r in e){const s=e[r];let o;Ee(s)?"default"in s?o=xe(s.from||r,s.default,!0):o=xe(s.from||r):o=xe(s),Ce(o)&&i?Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o.value,set:l=>o.value=l}):t[r]=o}}function Dr(e,t,n){Ve(Q(e)?e.map(i=>i.bind(t.proxy)):e.bind(t.proxy),t,n)}function _o(e,t,n,i){const r=i.includes(".")?ao(n,i):()=>n[i];if(ve(e)){const s=t[e];ee(s)&&it(r,s)}else if(ee(e))it(r,e.bind(n));else if(Ee(e))if(Q(e))e.forEach(s=>_o(s,t,n,i));else{const s=ee(e.handler)?e.handler.bind(n):t[e.handler];ee(s)&&it(r,s,e)}}function bo(e){const t=e.type,{mixins:n,extends:i}=t,{mixins:r,optionsCache:s,config:{optionMergeStrategies:o}}=e.appContext,l=s.get(t);let a;return l?a=l:!r.length&&!n&&!i?a=t:(a={},r.length&&r.forEach(c=>zn(a,c,o,!0)),zn(a,t,o)),s.set(t,a),a}function zn(e,t,n,i=!1){const{mixins:r,extends:s}=t;s&&zn(e,s,n,!0),r&&r.forEach(o=>zn(e,o,n,!0));for(const o in t)if(!(i&&o==="expose")){const l=Ja[o]||n&&n[o];e[o]=l?l(e[o],t[o]):t[o]}return e}const Ja={data:Mr,props:xt,emits:xt,methods:xt,computed:xt,beforeCreate:Te,created:Te,beforeMount:Te,mounted:Te,beforeUpdate:Te,updated:Te,beforeDestroy:Te,beforeUnmount:Te,destroyed:Te,unmounted:Te,activated:Te,deactivated:Te,errorCaptured:Te,serverPrefetch:Te,components:xt,directives:xt,watch:Qa,provide:Mr,inject:Ga};function Mr(e,t){return t?e?function(){return Le(ee(e)?e.call(this,this):e,ee(t)?t.call(this,this):t)}:t:e}function Ga(e,t){return xt(Fi(e),Fi(t))}function Fi(e){if(Q(e)){const t={};for(let n=0;n0)&&!(o&16)){if(o&8){const u=e.vnode.dynamicProps;for(let d=0;d{a=!0;const[p,g]=wo(d,t,!0);Le(o,p),g&&l.push(...g)};!n&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}if(!s&&!a)return i.set(e,Bt),Bt;if(Q(s))for(let u=0;u-1,g[1]=A<0||y-1||se(g,"default"))&&l.push(d)}}}const c=[o,l];return i.set(e,c),c}function Fr(e){return e[0]!=="$"}function Nr(e){const t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:e===null?"null":""}function Hr(e,t){return Nr(e)===Nr(t)}function jr(e,t){return Q(t)?t.findIndex(n=>Hr(n,e)):ee(t)&&Hr(t,e)?0:-1}const Eo=e=>e[0]==="_"||e==="$stable",ar=e=>Q(e)?e.map(We):[We(e)],Xa=(e,t,n)=>{if(t._n)return t;const i=Ca((...r)=>ar(t(...r)),n);return i._c=!1,i},ko=(e,t,n)=>{const i=e._ctx;for(const r in e){if(Eo(r))continue;const s=e[r];if(ee(s))t[r]=Xa(r,s,i);else if(s!=null){const o=ar(s);t[r]=()=>o}}},Co=(e,t)=>{const n=ar(t);e.slots.default=()=>n},ec=(e,t)=>{if(e.vnode.shapeFlag&32){const n=t._;n?(e.slots=ce(t),Nn(t,"_",n)):ko(t,e.slots={})}else e.slots={},t&&Co(e,t);Nn(e.slots,ri,1)},tc=(e,t,n)=>{const{vnode:i,slots:r}=e;let s=!0,o=ge;if(i.shapeFlag&32){const l=t._;l?n&&l===1?s=!1:(Le(r,t),!n&&l===1&&delete r._):(s=!t.$stable,ko(t,r)),o=t}else t&&(Co(e,t),o={default:1});if(s)for(const l in r)!Eo(l)&&!(l in o)&&delete r[l]};function xo(){return{app:null,config:{isNativeTag:Rl,performance:!1,globalProperties:{},optionMergeStrategies:{},errorHandler:void 0,warnHandler:void 0,compilerOptions:{}},mixins:[],components:{},directives:{},provides:Object.create(null),optionsCache:new WeakMap,propsCache:new WeakMap,emitsCache:new WeakMap}}let nc=0;function ic(e,t){return function(i,r=null){ee(i)||(i=Object.assign({},i)),r!=null&&!Ee(r)&&(r=null);const s=xo(),o=new Set;let l=!1;const a=s.app={_uid:nc++,_component:i,_props:r,_container:null,_context:s,_instance:null,version:Ec,get config(){return s.config},set config(c){},use(c,...u){return o.has(c)||(c&&ee(c.install)?(o.add(c),c.install(a,...u)):ee(c)&&(o.add(c),c(a,...u))),a},mixin(c){return s.mixins.includes(c)||s.mixins.push(c),a},component(c,u){return u?(s.components[c]=u,a):s.components[c]},directive(c,u){return u?(s.directives[c]=u,a):s.directives[c]},mount(c,u,d){if(!l){const p=ye(i,r);return p.appContext=s,u&&t?t(p,c):e(p,c,d),l=!0,a._container=c,c.__vue_app__=a,si(p.component)||p.component.proxy}},unmount(){l&&(e(null,a._container),delete a._container.__vue_app__)},provide(c,u){return s.provides[c]=u,a}};return a}}function Vn(e,t,n,i,r=!1){if(Q(e)){e.forEach((p,g)=>Vn(p,t&&(Q(t)?t[g]:t),n,i,r));return}if(Ut(i)&&!r)return;const s=i.shapeFlag&4?si(i.component)||i.component.proxy:i.el,o=r?null:s,{i:l,r:a}=e,c=t&&t.r,u=l.refs===ge?l.refs={}:l.refs,d=l.setupState;if(c!=null&&c!==a&&(ve(c)?(u[c]=null,se(d,c)&&(d[c]=null)):Ce(c)&&(c.value=null)),ee(a))vt(a,l,12,[o,u]);else{const p=ve(a),g=Ce(a);if(p||g){const y=()=>{if(e.f){const A=p?u[a]:a.value;r?Q(A)&&Ki(A,s):Q(A)?A.includes(s)||A.push(s):p?(u[a]=[s],se(d,a)&&(d[a]=u[a])):(a.value=[s],e.k&&(u[e.k]=a.value))}else p?(u[a]=o,se(d,a)&&(d[a]=o)):g&&(a.value=o,e.k&&(u[e.k]=o))};o?(y.id=-1,Se(y,n)):y()}}}let at=!1;const Sn=e=>/svg/.test(e.namespaceURI)&&e.tagName!=="foreignObject",On=e=>e.nodeType===8;function rc(e){const{mt:t,p:n,o:{patchProp:i,createText:r,nextSibling:s,parentNode:o,remove:l,insert:a,createComment:c}}=e,u=(m,b)=>{if(!b.hasChildNodes()){n(null,m,b),jn(),b._vnode=m;return}at=!1,d(b.firstChild,m,null,null,null),jn(),b._vnode=m,at&&console.error("Hydration completed but contains mismatches.")},d=(m,b,x,M,z,$=!1)=>{const D=On(m)&&m.data==="[",v=()=>A(m,b,x,M,z,D),{type:G,ref:B,shapeFlag:q,patchFlag:w}=b,F=m.nodeType;b.el=m,w===-2&&($=!1,b.dynamicChildren=null);let S=null;switch(G){case vn:F!==3?b.children===""?(a(b.el=r(""),o(m),m),S=m):S=v():(m.data!==b.children&&(at=!0,m.data=b.children),S=s(m));break;case Ne:F!==8||D?S=v():S=s(m);break;case cn:if(F!==1&&F!==3)S=v();else{S=m;const ie=!b.children.length;for(let j=0;j{$=$||!!b.dynamicChildren;const{type:D,props:v,patchFlag:G,shapeFlag:B,dirs:q}=b,w=D==="input"&&q||D==="option";if(w||G!==-1){if(q&&Ze(b,null,x,"created"),v)if(w||!$||G&48)for(const S in v)(w&&S.endsWith("value")||wn(S)&&!on(S))&&i(m,S,null,v[S],!1,void 0,x);else v.onClick&&i(m,"onClick",null,v.onClick,!1,void 0,x);let F;if((F=v&&v.onVnodeBeforeMount)&&ze(F,x,b),q&&Ze(b,null,x,"beforeMount"),((F=v&&v.onVnodeMounted)||q)&&oo(()=>{F&&ze(F,x,b),q&&Ze(b,null,x,"mounted")},M),B&16&&!(v&&(v.innerHTML||v.textContent))){let S=g(m.firstChild,b,m,x,M,z,$);for(;S;){at=!0;const ie=S;S=S.nextSibling,l(ie)}}else B&8&&m.textContent!==b.children&&(at=!0,m.textContent=b.children)}return m.nextSibling},g=(m,b,x,M,z,$,D)=>{D=D||!!b.dynamicChildren;const v=b.children,G=v.length;for(let B=0;B{const{slotScopeIds:D}=b;D&&(z=z?z.concat(D):D);const v=o(m),G=g(s(m),b,v,x,M,z,$);return G&&On(G)&&G.data==="]"?s(b.anchor=G):(at=!0,a(b.anchor=c("]"),v,G),G)},A=(m,b,x,M,z,$)=>{if(at=!0,b.el=null,$){const G=P(m);for(;;){const B=s(m);if(B&&B!==G)l(B);else break}}const D=s(m),v=o(m);return l(m),n(null,b,v,D,x,M,Sn(v),z),D},P=m=>{let b=0;for(;m;)if(m=s(m),m&&On(m)&&(m.data==="["&&b++,m.data==="]")){if(b===0)return s(m);b--}return m};return[u,d]}const Se=oo;function sc(e){return oc(e,rc)}function oc(e,t){const n=Dl();n.__VUE__=!0;const{insert:i,remove:r,patchProp:s,createElement:o,createText:l,createComment:a,setText:c,setElementText:u,parentNode:d,nextSibling:p,setScopeId:g=Je,cloneNode:y,insertStaticContent:A}=e,P=(f,h,_,C=null,k=null,R=null,H=!1,T=null,I=!!h.dynamicChildren)=>{if(f===h)return;f&&!Rt(f,h)&&(C=U(f),Ae(f,k,R,!0),f=null),h.patchFlag===-2&&(I=!1,h.dynamicChildren=null);const{type:L,ref:K,shapeFlag:W}=h;switch(L){case vn:m(f,h,_,C);break;case Ne:b(f,h,_,C);break;case cn:f==null&&x(h,_,C,H);break;case Oe:F(f,h,_,C,k,R,H,T,I);break;default:W&1?$(f,h,_,C,k,R,H,T,I):W&6?S(f,h,_,C,k,R,H,T,I):(W&64||W&128)&&L.process(f,h,_,C,k,R,H,T,I,de)}K!=null&&k&&Vn(K,f&&f.ref,R,h||f,!h)},m=(f,h,_,C)=>{if(f==null)i(h.el=l(h.children),_,C);else{const k=h.el=f.el;h.children!==f.children&&c(k,h.children)}},b=(f,h,_,C)=>{f==null?i(h.el=a(h.children||""),_,C):h.el=f.el},x=(f,h,_,C)=>{[f.el,f.anchor]=A(f.children,h,_,C,f.el,f.anchor)},M=({el:f,anchor:h},_,C)=>{let k;for(;f&&f!==h;)k=p(f),i(f,_,C),f=k;i(h,_,C)},z=({el:f,anchor:h})=>{let _;for(;f&&f!==h;)_=p(f),r(f),f=_;r(h)},$=(f,h,_,C,k,R,H,T,I)=>{H=H||h.type==="svg",f==null?D(h,_,C,k,R,H,T,I):B(f,h,k,R,H,T,I)},D=(f,h,_,C,k,R,H,T)=>{let I,L;const{type:K,props:W,shapeFlag:J,transition:Y,patchFlag:oe,dirs:he}=f;if(f.el&&y!==void 0&&oe===-1)I=f.el=y(f.el);else{if(I=f.el=o(f.type,R,W&&W.is,W),J&8?u(I,f.children):J&16&&G(f.children,I,null,C,k,R&&K!=="foreignObject",H,T),he&&Ze(f,null,C,"created"),W){for(const _e in W)_e!=="value"&&!on(_e)&&s(I,_e,null,W[_e],R,f.children,C,k,O);"value"in W&&s(I,"value",null,W.value),(L=W.onVnodeBeforeMount)&&ze(L,C,f)}v(I,f,f.scopeId,H,C)}he&&Ze(f,null,C,"beforeMount");const pe=(!k||k&&!k.pendingBranch)&&Y&&!Y.persisted;pe&&Y.beforeEnter(I),i(I,h,_),((L=W&&W.onVnodeMounted)||pe||he)&&Se(()=>{L&&ze(L,C,f),pe&&Y.enter(I),he&&Ze(f,null,C,"mounted")},k)},v=(f,h,_,C,k)=>{if(_&&g(f,_),C)for(let R=0;R{for(let L=I;L{const T=h.el=f.el;let{patchFlag:I,dynamicChildren:L,dirs:K}=h;I|=f.patchFlag&16;const W=f.props||ge,J=h.props||ge;let Y;_&&Et(_,!1),(Y=J.onVnodeBeforeUpdate)&&ze(Y,_,h,f),K&&Ze(h,f,_,"beforeUpdate"),_&&Et(_,!0);const oe=k&&h.type!=="foreignObject";if(L?q(f.dynamicChildren,L,T,_,C,oe,R):H||we(f,h,T,null,_,C,oe,R,!1),I>0){if(I&16)w(T,h,W,J,_,C,k);else if(I&2&&W.class!==J.class&&s(T,"class",null,J.class,k),I&4&&s(T,"style",W.style,J.style,k),I&8){const he=h.dynamicProps;for(let pe=0;pe{Y&&ze(Y,_,h,f),K&&Ze(h,f,_,"updated")},C)},q=(f,h,_,C,k,R,H)=>{for(let T=0;T{if(_!==C){for(const T in C){if(on(T))continue;const I=C[T],L=_[T];I!==L&&T!=="value"&&s(f,T,L,I,H,h.children,k,R,O)}if(_!==ge)for(const T in _)!on(T)&&!(T in C)&&s(f,T,_[T],null,H,h.children,k,R,O);"value"in C&&s(f,"value",_.value,C.value)}},F=(f,h,_,C,k,R,H,T,I)=>{const L=h.el=f?f.el:l(""),K=h.anchor=f?f.anchor:l("");let{patchFlag:W,dynamicChildren:J,slotScopeIds:Y}=h;Y&&(T=T?T.concat(Y):Y),f==null?(i(L,_,C),i(K,_,C),G(h.children,_,K,k,R,H,T,I)):W>0&&W&64&&J&&f.dynamicChildren?(q(f.dynamicChildren,J,_,k,R,H,T),(h.key!=null||k&&h===k.subTree)&&Lo(f,h,!0)):we(f,h,_,K,k,R,H,T,I)},S=(f,h,_,C,k,R,H,T,I)=>{h.slotScopeIds=T,f==null?h.shapeFlag&512?k.ctx.activate(h,_,C,H,I):ie(h,_,C,k,R,H,I):j(f,h,I)},ie=(f,h,_,C,k,R,H)=>{const T=f.component=mc(f,C,k);if(kn(f)&&(T.ctx.renderer=de),gc(T),T.asyncDep){if(k&&k.registerDep(T,X),!f.el){const I=T.subTree=ye(Ne);b(null,I,h,_)}return}X(T,f,h,_,k,R,H)},j=(f,h,_)=>{const C=h.component=f.component;if(Pa(f,h,_))if(C.asyncDep&&!C.asyncResolved){ne(C,h,_);return}else C.next=h,ya(C.update),C.update();else h.el=f.el,C.vnode=h},X=(f,h,_,C,k,R,H)=>{const T=()=>{if(f.isMounted){let{next:K,bu:W,u:J,parent:Y,vnode:oe}=f,he=K,pe;Et(f,!1),K?(K.el=oe.el,ne(f,K,H)):K=oe,W&&fi(W),(pe=K.props&&K.props.onVnodeBeforeUpdate)&&ze(pe,Y,K,oe),Et(f,!0);const _e=di(f),Ue=f.subTree;f.subTree=_e,P(Ue,_e,d(Ue.el),U(Ue),f,k,R),K.el=_e.el,he===null&&Ra(f,_e.el),J&&Se(J,k),(pe=K.props&&K.props.onVnodeUpdated)&&Se(()=>ze(pe,Y,K,oe),k)}else{let K;const{el:W,props:J}=h,{bm:Y,m:oe,parent:he}=f,pe=Ut(h);if(Et(f,!1),Y&&fi(Y),!pe&&(K=J&&J.onVnodeBeforeMount)&&ze(K,he,h),Et(f,!0),W&&Z){const _e=()=>{f.subTree=di(f),Z(W,f.subTree,f,k,null)};pe?h.type.__asyncLoader().then(()=>!f.isUnmounted&&_e()):_e()}else{const _e=f.subTree=di(f);P(null,_e,_,C,f,k,R),h.el=_e.el}if(oe&&Se(oe,k),!pe&&(K=J&&J.onVnodeMounted)){const _e=h;Se(()=>ze(K,he,_e),k)}(h.shapeFlag&256||he&&Ut(he.vnode)&&he.vnode.shapeFlag&256)&&f.a&&Se(f.a,k),f.isMounted=!0,h=_=C=null}},I=f.effect=new Yi(T,()=>or(L),f.scope),L=f.update=()=>I.run();L.id=f.uid,Et(f,!0),L()},ne=(f,h,_)=>{h.component=f;const C=f.vnode.props;f.vnode=h,f.next=null,Za(f,h.props,C,_),tc(f,h.children,_),Gt(),Xn(void 0,f.update),Qt()},we=(f,h,_,C,k,R,H,T,I=!1)=>{const L=f&&f.children,K=f?f.shapeFlag:0,W=h.children,{patchFlag:J,shapeFlag:Y}=h;if(J>0){if(J&128){Ie(L,W,_,C,k,R,H,T,I);return}else if(J&256){Re(L,W,_,C,k,R,H,T,I);return}}Y&8?(K&16&&O(L,k,R),W!==L&&u(_,W)):K&16?Y&16?Ie(L,W,_,C,k,R,H,T,I):O(L,k,R,!0):(K&8&&u(_,""),Y&16&&G(W,_,C,k,R,H,T,I))},Re=(f,h,_,C,k,R,H,T,I)=>{f=f||Bt,h=h||Bt;const L=f.length,K=h.length,W=Math.min(L,K);let J;for(J=0;JK?O(f,k,R,!0,!1,W):G(h,_,C,k,R,H,T,I,W)},Ie=(f,h,_,C,k,R,H,T,I)=>{let L=0;const K=h.length;let W=f.length-1,J=K-1;for(;L<=W&&L<=J;){const Y=f[L],oe=h[L]=I?ht(h[L]):We(h[L]);if(Rt(Y,oe))P(Y,oe,_,null,k,R,H,T,I);else break;L++}for(;L<=W&&L<=J;){const Y=f[W],oe=h[J]=I?ht(h[J]):We(h[J]);if(Rt(Y,oe))P(Y,oe,_,null,k,R,H,T,I);else break;W--,J--}if(L>W){if(L<=J){const Y=J+1,oe=YJ)for(;L<=W;)Ae(f[L],k,R,!0),L++;else{const Y=L,oe=L,he=new Map;for(L=oe;L<=J;L++){const De=h[L]=I?ht(h[L]):We(h[L]);De.key!=null&&he.set(De.key,L)}let pe,_e=0;const Ue=J-oe+1;let Mt=!1,_r=0;const Zt=new Array(Ue);for(L=0;L=Ue){Ae(De,k,R,!0);continue}let Ye;if(De.key!=null)Ye=he.get(De.key);else for(pe=oe;pe<=J;pe++)if(Zt[pe-oe]===0&&Rt(De,h[pe])){Ye=pe;break}Ye===void 0?Ae(De,k,R,!0):(Zt[Ye-oe]=L+1,Ye>=_r?_r=Ye:Mt=!0,P(De,h[Ye],_,null,k,R,H,T,I),_e++)}const br=Mt?lc(Zt):Bt;for(pe=br.length-1,L=Ue-1;L>=0;L--){const De=oe+L,Ye=h[De],yr=De+1{const{el:R,type:H,transition:T,children:I,shapeFlag:L}=f;if(L&6){$e(f.component.subTree,h,_,C);return}if(L&128){f.suspense.move(h,_,C);return}if(L&64){H.move(f,h,_,de);return}if(H===Oe){i(R,h,_);for(let W=0;WT.enter(R),k);else{const{leave:W,delayLeave:J,afterLeave:Y}=T,oe=()=>i(R,h,_),he=()=>{W(R,()=>{oe(),Y&&Y()})};J?J(R,oe,he):he()}else i(R,h,_)},Ae=(f,h,_,C=!1,k=!1)=>{const{type:R,props:H,ref:T,children:I,dynamicChildren:L,shapeFlag:K,patchFlag:W,dirs:J}=f;if(T!=null&&Vn(T,null,_,f,!0),K&256){h.ctx.deactivate(f);return}const Y=K&1&&J,oe=!Ut(f);let he;if(oe&&(he=H&&H.onVnodeBeforeUnmount)&&ze(he,h,f),K&6)V(f.component,_,C);else{if(K&128){f.suspense.unmount(_,C);return}Y&&Ze(f,null,h,"beforeUnmount"),K&64?f.type.remove(f,h,_,k,de,C):L&&(R!==Oe||W>0&&W&64)?O(L,h,_,!1,!0):(R===Oe&&W&384||!k&&K&16)&&O(I,h,_),C&&wt(f)}(oe&&(he=H&&H.onVnodeUnmounted)||Y)&&Se(()=>{he&&ze(he,h,f),Y&&Ze(f,null,h,"unmounted")},_)},wt=f=>{const{type:h,el:_,anchor:C,transition:k}=f;if(h===Oe){E(_,C);return}if(h===cn){z(f);return}const R=()=>{r(_),k&&!k.persisted&&k.afterLeave&&k.afterLeave()};if(f.shapeFlag&1&&k&&!k.persisted){const{leave:H,delayLeave:T}=k,I=()=>H(_,R);T?T(f.el,R,I):I()}else R()},E=(f,h)=>{let _;for(;f!==h;)_=p(f),r(f),f=_;r(h)},V=(f,h,_)=>{const{bum:C,scope:k,update:R,subTree:H,um:T}=f;C&&fi(C),k.stop(),R&&(R.active=!1,Ae(H,f,h,_)),T&&Se(T,h),Se(()=>{f.isUnmounted=!0},h),h&&h.pendingBranch&&!h.isUnmounted&&f.asyncDep&&!f.asyncResolved&&f.suspenseId===h.pendingId&&(h.deps--,h.deps===0&&h.resolve())},O=(f,h,_,C=!1,k=!1,R=0)=>{for(let H=R;Hf.shapeFlag&6?U(f.component.subTree):f.shapeFlag&128?f.suspense.next():p(f.anchor||f.el),ue=(f,h,_)=>{f==null?h._vnode&&Ae(h._vnode,null,null,!0):P(h._vnode||null,f,h,null,null,null,_),jn(),h._vnode=f},de={p:P,um:Ae,m:$e,r:wt,mt:ie,mc:G,pc:we,pbc:q,n:U,o:e};let te,Z;return t&&([te,Z]=t(de)),{render:ue,hydrate:te,createApp:ic(ue,te)}}function Et({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function Lo(e,t,n=!1){const i=e.children,r=t.children;if(Q(i)&&Q(r))for(let s=0;s>1,e[n[l]]0&&(t[i]=n[s-1]),n[s]=i)}}for(s=n.length,o=n[s-1];s-- >0;)n[s]=o,o=t[o];return n}const ac=e=>e.__isTeleport,Oe=Symbol(void 0),vn=Symbol(void 0),Ne=Symbol(void 0),cn=Symbol(void 0),un=[];let Ke=null;function ii(e=!1){un.push(Ke=e?null:[])}function cc(){un.pop(),Ke=un[un.length-1]||null}let _n=1;function $r(e){_n+=e}function Po(e){return e.dynamicChildren=_n>0?Ke||Bt:null,cc(),_n>0&&Ke&&Ke.push(e),e}function Ro(e,t,n,i,r,s){return Po(So(e,t,n,i,r,s,!0))}function To(e,t,n,i,r){return Po(ye(e,t,n,i,r,!0))}function Un(e){return e?e.__v_isVNode===!0:!1}function Rt(e,t){return e.type===t.type&&e.key===t.key}const ri="__vInternal",Ao=({key:e})=>e!=null?e:null,Mn=({ref:e,ref_key:t,ref_for:n})=>e!=null?ve(e)||Ce(e)||ee(e)?{i:Pe,r:e,k:t,f:!!n}:e:null;function So(e,t=null,n=null,i=0,r=null,s=e===Oe?0:1,o=!1,l=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Ao(t),ref:t&&Mn(t),scopeId:so,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:s,patchFlag:i,dynamicProps:r,dynamicChildren:null,appContext:null};return l?(ur(a,n),s&128&&e.normalize(a)):n&&(a.shapeFlag|=ve(n)?8:16),_n>0&&!o&&Ke&&(a.patchFlag>0||s&6)&&a.patchFlag!==32&&Ke.push(a),a}const ye=uc;function uc(e,t=null,n=null,i=0,r=null,s=!1){if((!e||e===Va)&&(e=Ne),Un(e)){const l=bt(e,t,!0);return n&&ur(l,n),_n>0&&!s&&Ke&&(l.shapeFlag&6?Ke[Ke.indexOf(e)]=l:Ke.push(l)),l.patchFlag|=-2,l}if(wc(e)&&(e=e.__vccOpts),t){t=fc(t);let{class:l,style:a}=t;l&&!ve(l)&&(t.class=yn(l)),Ee(a)&&(Ks(a)&&!Q(a)&&(a=Le({},a)),t.style=Jn(a))}const o=ve(e)?1:Ta(e)?128:ac(e)?64:Ee(e)?4:ee(e)?2:0;return So(e,t,n,i,r,o,s,!0)}function fc(e){return e?Ks(e)||ri in e?Le({},e):e:null}function bt(e,t,n=!1){const{props:i,ref:r,patchFlag:s,children:o}=e,l=t?dc(i||{},t):i;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&Ao(l),ref:t&&t.ref?n&&r?Q(r)?r.concat(Mn(t)):[r,Mn(t)]:Mn(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:o,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Oe?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&bt(e.ssContent),ssFallback:e.ssFallback&&bt(e.ssFallback),el:e.el,anchor:e.anchor}}function cr(e=" ",t=0){return ye(vn,null,e,t)}function hh(e,t){const n=ye(cn,null,e);return n.staticCount=t,n}function ph(e="",t=!1){return t?(ii(),To(Ne,null,e)):ye(Ne,null,e)}function We(e){return e==null||typeof e=="boolean"?ye(Ne):Q(e)?ye(Oe,null,e.slice()):typeof e=="object"?ht(e):ye(vn,null,String(e))}function ht(e){return e.el===null||e.memo?e:bt(e)}function ur(e,t){let n=0;const{shapeFlag:i}=e;if(t==null)t=null;else if(Q(t))n=16;else if(typeof t=="object")if(i&65){const r=t.default;r&&(r._c&&(r._d=!1),ur(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(ri in t)?t._ctx=Pe:r===3&&Pe&&(Pe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else ee(t)?(t={default:t,_ctx:Pe},n=32):(t=String(t),i&64?(n=16,t=[cr(t)]):n=8);e.children=t,e.shapeFlag|=n}function dc(...e){const t={};for(let n=0;nke||Pe,Wt=e=>{ke=e,e.scope.on()},It=()=>{ke&&ke.scope.off(),ke=null};function Io(e){return e.vnode.shapeFlag&4}let qt=!1;function gc(e,t=!1){qt=t;const{props:n,children:i}=e.vnode,r=Io(e);Ya(e,n,r,t),ec(e,i);const s=r?vc(e,t):void 0;return qt=!1,s}function vc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Js(new Proxy(e.ctx,Wa));const{setup:i}=n;if(i){const r=e.setupContext=i.length>1?bc(e):null;Wt(e),Gt();const s=vt(i,e,0,[e.props,r]);if(Qt(),It(),Is(s)){if(s.then(It,It),t)return s.then(o=>{Br(e,o,t)}).catch(o=>{En(o,e,0)});e.asyncDep=s}else Br(e,s,t)}else Do(e,t)}function Br(e,t,n){ee(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Ee(t)&&(e.setupState=Xs(t)),Do(e,n)}let zr;function Do(e,t,n){const i=e.type;if(!e.render){if(!t&&zr&&!i.render){const r=i.template;if(r){const{isCustomElement:s,compilerOptions:o}=e.appContext.config,{delimiters:l,compilerOptions:a}=i,c=Le(Le({isCustomElement:s,delimiters:l},o),a);i.render=zr(r,c)}}e.render=i.render||Je}Wt(e),Gt(),qa(e),Qt(),It()}function _c(e){return new Proxy(e.attrs,{get(t,n){return He(e,"get","$attrs"),t[n]}})}function bc(e){const t=i=>{e.exposed=i||{}};let n;return{get attrs(){return n||(n=_c(e))},slots:e.slots,emit:e.emit,expose:t}}function si(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Xs(Js(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in Bn)return Bn[n](e)}}))}function yc(e,t=!0){return ee(e)?e.displayName||e.name:e.name||t&&e.__name}function wc(e){return ee(e)&&"__vccOpts"in e}const fe=(e,t)=>va(e,t,qt);function ae(e,t,n){const i=arguments.length;return i===2?Ee(t)&&!Q(t)?Un(t)?ye(e,null,[t]):ye(e,t):ye(e,null,t):(i>3?n=Array.prototype.slice.call(arguments,2):i===3&&Un(n)&&(n=[n]),ye(e,t,n))}const Ec="3.2.37",kc="http://www.w3.org/2000/svg",Tt=typeof document!="undefined"?document:null,Vr=Tt&&Tt.createElement("template"),Cc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,i)=>{const r=t?Tt.createElementNS(kc,e):Tt.createElement(e,n?{is:n}:void 0);return e==="select"&&i&&i.multiple!=null&&r.setAttribute("multiple",i.multiple),r},createText:e=>Tt.createTextNode(e),createComment:e=>Tt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Tt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},cloneNode(e){const t=e.cloneNode(!0);return"_value"in e&&(t._value=e._value),t},insertStaticContent(e,t,n,i,r,s){const o=n?n.previousSibling:t.lastChild;if(r&&(r===s||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===s||!(r=r.nextSibling)););else{Vr.innerHTML=i?`${e}`:e;const l=Vr.content;if(i){const a=l.firstChild;for(;a.firstChild;)l.appendChild(a.firstChild);l.removeChild(a)}t.insertBefore(l,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}};function xc(e,t,n){const i=e._vtc;i&&(t=(t?[t,...i]:[...i]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}function Lc(e,t,n){const i=e.style,r=ve(n);if(n&&!r){for(const s in n)Hi(i,s,n[s]);if(t&&!ve(t))for(const s in t)n[s]==null&&Hi(i,s,"")}else{const s=i.display;r?t!==n&&(i.cssText=n):t&&e.removeAttribute("style"),"_vod"in e&&(i.display=s)}}const Ur=/\s*!important$/;function Hi(e,t,n){if(Q(n))n.forEach(i=>Hi(e,t,i));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const i=Pc(e,t);Ur.test(n)?e.setProperty(Dt(i),n.replace(Ur,""),"important"):e[i]=n}}const Wr=["Webkit","Moz","ms"],mi={};function Pc(e,t){const n=mi[t];if(n)return n;let i=Xe(t);if(i!=="filter"&&i in e)return mi[t]=i;i=Yn(i);for(let r=0;r{let e=Date.now,t=!1;if(typeof window!="undefined"){Date.now()>document.createEvent("Event").timeStamp&&(e=performance.now.bind(performance));const n=navigator.userAgent.match(/firefox\/(\d+)/i);t=!!(n&&Number(n[1])<=53)}return[e,t]})();let ji=0;const Sc=Promise.resolve(),Oc=()=>{ji=0},Ic=()=>ji||(Sc.then(Oc),ji=Mo());function Dc(e,t,n,i){e.addEventListener(t,n,i)}function Mc(e,t,n,i){e.removeEventListener(t,n,i)}function Fc(e,t,n,i,r=null){const s=e._vei||(e._vei={}),o=s[t];if(i&&o)o.value=i;else{const[l,a]=Nc(t);if(i){const c=s[t]=Hc(i,r);Dc(e,l,c,a)}else o&&(Mc(e,l,o,a),s[t]=void 0)}}const Kr=/(?:Once|Passive|Capture)$/;function Nc(e){let t;if(Kr.test(e)){t={};let n;for(;n=e.match(Kr);)e=e.slice(0,e.length-n[0].length),t[n[0].toLowerCase()]=!0}return[Dt(e.slice(2)),t]}function Hc(e,t){const n=i=>{const r=i.timeStamp||Mo();(Ac||r>=n.attached-1)&&Ve(jc(i,n.value),t,5,[i])};return n.value=e,n.attached=Ic(),n}function jc(e,t){if(Q(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(i=>r=>!r._stopped&&i&&i(r))}else return t}const Jr=/^on[a-z]/,$c=(e,t,n,i,r=!1,s,o,l,a)=>{t==="class"?xc(e,i,r):t==="style"?Lc(e,n,i):wn(t)?qi(t)||Fc(e,t,n,i,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Bc(e,t,i,r))?Tc(e,t,i,s,o,l,a):(t==="true-value"?e._trueValue=i:t==="false-value"&&(e._falseValue=i),Rc(e,t,i,r))};function Bc(e,t,n,i){return i?!!(t==="innerHTML"||t==="textContent"||t in e&&Jr.test(t)&&ee(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Jr.test(t)&&ve(n)?!1:t in e}const ct="transition",Xt="animation",fr=(e,{slots:t})=>ae(co,zc(e),t);fr.displayName="Transition";const Fo={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};fr.props=Le({},co.props,Fo);const kt=(e,t=[])=>{Q(e)?e.forEach(n=>n(...t)):e&&e(...t)},Gr=e=>e?Q(e)?e.some(t=>t.length>1):e.length>1:!1;function zc(e){const t={};for(const w in e)w in Fo||(t[w]=e[w]);if(e.css===!1)return t;const{name:n="v",type:i,duration:r,enterFromClass:s=`${n}-enter-from`,enterActiveClass:o=`${n}-enter-active`,enterToClass:l=`${n}-enter-to`,appearFromClass:a=s,appearActiveClass:c=o,appearToClass:u=l,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:p=`${n}-leave-active`,leaveToClass:g=`${n}-leave-to`}=e,y=Vc(r),A=y&&y[0],P=y&&y[1],{onBeforeEnter:m,onEnter:b,onEnterCancelled:x,onLeave:M,onLeaveCancelled:z,onBeforeAppear:$=m,onAppear:D=b,onAppearCancelled:v=x}=t,G=(w,F,S)=>{Ct(w,F?u:l),Ct(w,F?c:o),S&&S()},B=(w,F)=>{w._isLeaving=!1,Ct(w,d),Ct(w,g),Ct(w,p),F&&F()},q=w=>(F,S)=>{const ie=w?D:b,j=()=>G(F,w,S);kt(ie,[F,j]),Qr(()=>{Ct(F,w?a:s),ut(F,w?u:l),Gr(ie)||Yr(F,i,A,j)})};return Le(t,{onBeforeEnter(w){kt(m,[w]),ut(w,s),ut(w,o)},onBeforeAppear(w){kt($,[w]),ut(w,a),ut(w,c)},onEnter:q(!1),onAppear:q(!0),onLeave(w,F){w._isLeaving=!0;const S=()=>B(w,F);ut(w,d),qc(),ut(w,p),Qr(()=>{!w._isLeaving||(Ct(w,d),ut(w,g),Gr(M)||Yr(w,i,P,S))}),kt(M,[w,S])},onEnterCancelled(w){G(w,!1),kt(x,[w])},onAppearCancelled(w){G(w,!0),kt(v,[w])},onLeaveCancelled(w){B(w),kt(z,[w])}})}function Vc(e){if(e==null)return null;if(Ee(e))return[gi(e.enter),gi(e.leave)];{const t=gi(e);return[t,t]}}function gi(e){return Fs(e)}function ut(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e._vtc||(e._vtc=new Set)).add(t)}function Ct(e,t){t.split(/\s+/).forEach(i=>i&&e.classList.remove(i));const{_vtc:n}=e;n&&(n.delete(t),n.size||(e._vtc=void 0))}function Qr(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Uc=0;function Yr(e,t,n,i){const r=e._endId=++Uc,s=()=>{r===e._endId&&i()};if(n)return setTimeout(s,n);const{type:o,timeout:l,propCount:a}=Wc(e,t);if(!o)return i();const c=o+"end";let u=0;const d=()=>{e.removeEventListener(c,p),s()},p=g=>{g.target===e&&++u>=a&&d()};setTimeout(()=>{u(n[y]||"").split(", "),r=i(ct+"Delay"),s=i(ct+"Duration"),o=Zr(r,s),l=i(Xt+"Delay"),a=i(Xt+"Duration"),c=Zr(l,a);let u=null,d=0,p=0;t===ct?o>0&&(u=ct,d=o,p=s.length):t===Xt?c>0&&(u=Xt,d=c,p=a.length):(d=Math.max(o,c),u=d>0?o>c?ct:Xt:null,p=u?u===ct?s.length:a.length:0);const g=u===ct&&/\b(transform|all)(,|$)/.test(n[ct+"Property"]);return{type:u,timeout:d,propCount:p,hasTransform:g}}function Zr(e,t){for(;e.lengthXr(n)+Xr(e[i])))}function Xr(e){return Number(e.slice(0,-1).replace(",","."))*1e3}function qc(){return document.body.offsetHeight}const Kc={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},mh=(e,t)=>n=>{if(!("key"in n))return;const i=Dt(n.key);if(t.some(r=>r===i||Kc[r]===i))return e(n)},gh={beforeMount(e,{value:t},{transition:n}){e._vod=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):en(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:i}){!t!=!n&&(i?t?(i.beforeEnter(e),en(e,!0),i.enter(e)):i.leave(e,()=>{en(e,!1)}):en(e,t))},beforeUnmount(e,{value:t}){en(e,t)}};function en(e,t){e.style.display=t?e._vod:"none"}const Jc=Le({patchProp:$c},Cc);let vi,es=!1;function Gc(){return vi=es?vi:sc(Jc),es=!0,vi}const Qc=(...e)=>{const t=Gc().createApp(...e),{mount:n}=t;return t.mount=i=>{const r=Yc(i);if(r)return n(r,!0,r instanceof SVGElement)},t};function Yc(e){return ve(e)?document.querySelector(e):e}const Zc=JSON.parse('{"base":"/","lang":"en-US","title":"Javacord","description":"The Javacord website","head":[["link",{"rel":"icon","href":"/favicon-96x96.png"}]],"locales":{}}');var Xc=([e,t,n])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,n]),eu=e=>{const t=new Set,n=[];return e.forEach(i=>{const r=Xc(i);t.has(r)||(t.add(r),n.push(i))}),n},tu=e=>/^(https?:)?\/\//.test(e),vh=e=>/^mailto:/.test(e),_h=e=>/^tel:/.test(e),No=e=>Object.prototype.toString.call(e)==="[object Object]",nu=e=>e.replace(/\/$/,""),iu=e=>e.replace(/^\//,""),Ho=(e,t)=>{const n=Object.keys(e).sort((i,r)=>{const s=r.split("/").length-i.split("/").length;return s!==0?s:r.length-i.length});for(const i of n)if(t.startsWith(i))return i;return"/"};const jo={"v-8daa1a0e":re(()=>N(()=>import("./index.html.07810de0.js"),[])),"v-92e5302e":re(()=>N(()=>import("./imprint.html.8c064aa1.js"),[])),"v-b106f6ee":re(()=>N(()=>import("./privacy-policy.html.2b93162c.js"),[])),"v-348a4efb":re(()=>N(()=>import("./bot-lifecycle.html.89b6bce4.js"),[])),"v-32da090a":re(()=>N(()=>import("./entity-cache.html.40152af2.js"),[])),"v-2037d84f":re(()=>N(()=>import("./performance-tweaks.html.909e9a4b.js"),[])),"v-0b11c848":re(()=>N(()=>import("./playing-audio.html.1204e1b2.js"),[])),"v-47a9d05a":re(()=>N(()=>import("./proxies.html.b9736f17.js"),[])),"v-fc3b23ca":re(()=>N(()=>import("./ratelimits.html.a990bfd1.js"),[])),"v-595301cf":re(()=>N(()=>import("./sharding.html.592f58f7.js"),[])),"v-5fd376fa":re(()=>N(()=>import("./creating-entities.html.ee81cd24.js"),[])),"v-37293ae0":re(()=>N(()=>import("./embeds.html.f7a6f54c.js"),[])),"v-13e99c06":re(()=>N(()=>import("./emojis-and-reactions.html.ce8f0b41.js"),[])),"v-e7aba52c":re(()=>N(()=>import("./gateway-intents.html.ef5cc4b3.js"),[])),"v-5b369fbc":re(()=>N(()=>import("./glossary.html.28351d5a.js"),[])),"v-4256bfd9":re(()=>N(()=>import("./listeners.html.86bff751.js"),[])),"v-33173f0e":re(()=>N(()=>import("./logger-config.html.f8d1f3ef.js"),[])),"v-29bd20c3":re(()=>N(()=>import("./message-builder.html.2ab31ad9.js"),[])),"v-8012cfce":re(()=>N(()=>import("./running.html.1784634d.js"),[])),"v-c85a18b4":re(()=>N(()=>import("./completable-futures.html.7db4d585.js"),[])),"v-22528d43":re(()=>N(()=>import("./lambdas.html.c0cde88c.js"),[])),"v-56bee89c":re(()=>N(()=>import("./optionals.html.3b46a951.js"),[])),"v-15814726":re(()=>N(()=>import("./index.html.8c8749ec.js"),[])),"v-5628c715":re(()=>N(()=>import("./creating-a-bot-account.html.c9939e16.js"),[])),"v-7d129412":re(()=>N(()=>import("./download-installation.html.de5b4efd.js"),[])),"v-6bd28c40":re(()=>N(()=>import("./faq.html.fe40474c.js"),[])),"v-7bf86adb":re(()=>N(()=>import("./writing-your-first-bot.html.3edd185d.js"),[])),"v-36c441c2":re(()=>N(()=>import("./commands.html.aeac4b05.js"),[])),"v-35e5cc98":re(()=>N(()=>import("./components.html.e89a60b6.js"),[])),"v-16fe8d71":re(()=>N(()=>import("./overview.html.53b5f728.js"),[])),"v-00728006":re(()=>N(()=>import("./responding.html.a1a19125.js"),[])),"v-22de0aba":re(()=>N(()=>import("./eclipse-maven.html.c65d8d94.js"),[])),"v-6d1d378b":re(()=>N(()=>import("./intellij-gradle.html.4d5164dd.js"),[])),"v-0ae94875":re(()=>N(()=>import("./intellij-maven.html.74a49f6d.js"),[])),"v-3706649a":re(()=>N(()=>import("./404.html.796e6b78.js"),[]))},ru={404:re(()=>N(()=>import("./404.72d4030e.js"),[])),Layout:re(()=>N(()=>import("./Layout.85f3b300.js"),[]))};var $o=be(wl),Bo=tr({key:"",path:"",title:"",lang:"",frontmatter:{},excerpt:"",headers:[]}),nt=be(Bo),Cn=()=>nt;ci.webpackHot&&(__VUE_HMR_RUNTIME__.updatePageData=e=>{$o.value[e.key]=()=>Promise.resolve(e),e.key===nt.value.key&&(nt.value=e)});var zo=Symbol(""),su=()=>{const e=xe(zo);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},Vo=Symbol(""),ou=()=>{const e=xe(Vo);if(!e)throw new Error("usePageHead() is called without provider.");return e},lu=Symbol(""),Uo=Symbol(""),au=()=>{const e=xe(Uo);if(!e)throw new Error("usePageLang() is called without provider.");return e},dr=Symbol(""),Wo=()=>{const e=xe(dr);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},mt=be(Zc),cu=()=>mt;ci.webpackHot&&(__VUE_HMR_RUNTIME__.updateSiteData=e=>{mt.value=e});var qo=Symbol(""),bh=()=>{const e=xe(qo);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},uu=Symbol(""),Lt=Yt({resolvePageData:async e=>{const t=$o.value[e],n=await(t==null?void 0:t());return n!=null?n:Bo},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const i=ve(t.description)?t.description:n.description,r=[...Q(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:i}]];return eu(r)},resolvePageHeadTitle:(e,t)=>`${e.title?`${e.title} | `:""}${t.title}`,resolvePageLang:e=>e.lang||"en",resolveRouteLocale:(e,t)=>Ho(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),fu=je({name:"ClientOnly",setup(e,t){const n=be(!1);return ot(()=>{n.value=!0}),()=>{var i,r;return n.value?(r=(i=t.slots).default)==null?void 0:r.call(i):null}}}),du=je({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=Cn(),n=fe(()=>jo[e.pageKey||t.value.key]);return()=>n.value?ae(n.value):ae("div","404 Not Found")}}),ts=je({name:"Vuepress",setup(){const e=Cn(),t=fe(()=>{let n;if(e.value.path){const i=e.value.frontmatter.layout;ve(i)?n=i:n="Layout"}else n="404";return ru[n]||za(n,!1)});return()=>ae(t.value)}}),hu=e=>tu(e)?e:`${cu().value.base}${iu(e)}`,yt=e=>e;function Ko(e,t,n){var i,r,s;t===void 0&&(t=50),n===void 0&&(n={});var o=(i=n.isImmediate)!=null&&i,l=(r=n.callback)!=null&&r,a=n.maxWait,c=Date.now(),u=[];function d(){if(a!==void 0){var g=Date.now()-c;if(g+t>=a)return a-g}return t}var p=function(){var g=[].slice.call(arguments),y=this;return new Promise(function(A,P){var m=o&&s===void 0;if(s!==void 0&&clearTimeout(s),s=setTimeout(function(){if(s=void 0,c=Date.now(),!o){var x=e.apply(y,g);l&&l(x),u.forEach(function(M){return(0,M.resolve)(x)}),u=[]}},d()),m){var b=e.apply(y,g);return l&&l(b),A(b)}u.push({resolve:A,reject:P})})};return p.cancel=function(g){s!==void 0&&clearTimeout(s),u.forEach(function(y){return(0,y.reject)(g)}),u=[]},p}/*! + * vue-router v4.1.2 + * (c) 2022 Eduardo San Martin Morote + * @license MIT + */const $t=typeof window!="undefined";function pu(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const me=Object.assign;function _i(e,t){const n={};for(const i in t){const r=t[i];n[i]=Qe(r)?r.map(e):e(r)}return n}const fn=()=>{},Qe=Array.isArray,mu=/\/$/,gu=e=>e.replace(mu,"");function bi(e,t,n="/"){let i,r={},s="",o="";const l=t.indexOf("#");let a=t.indexOf("?");return l=0&&(a=-1),a>-1&&(i=t.slice(0,a),s=t.slice(a+1,l>-1?l:t.length),r=e(s)),l>-1&&(i=i||t.slice(0,l),o=t.slice(l,t.length)),i=yu(i!=null?i:t,n),{fullPath:i+(s&&"?")+s+o,path:i,query:r,hash:o}}function vu(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function ns(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function _u(e,t,n){const i=t.matched.length-1,r=n.matched.length-1;return i>-1&&i===r&&Kt(t.matched[i],n.matched[r])&&Jo(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Kt(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Jo(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!bu(e[n],t[n]))return!1;return!0}function bu(e,t){return Qe(e)?is(e,t):Qe(t)?is(t,e):e===t}function is(e,t){return Qe(t)?e.length===t.length&&e.every((n,i)=>n===t[i]):e.length===1&&e[0]===t}function yu(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),i=e.split("/");let r=n.length-1,s,o;for(s=0;s1&&r--;else break;return n.slice(0,r).join("/")+"/"+i.slice(s-(s===i.length?1:0)).join("/")}var bn;(function(e){e.pop="pop",e.push="push"})(bn||(bn={}));var dn;(function(e){e.back="back",e.forward="forward",e.unknown=""})(dn||(dn={}));function wu(e){if(!e)if($t){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),gu(e)}const Eu=/^[^#]+#/;function ku(e,t){return e.replace(Eu,"#")+t}function Cu(e,t){const n=document.documentElement.getBoundingClientRect(),i=e.getBoundingClientRect();return{behavior:t.behavior,left:i.left-n.left-(t.left||0),top:i.top-n.top-(t.top||0)}}const oi=()=>({left:window.pageXOffset,top:window.pageYOffset});function xu(e){let t;if("el"in e){const n=e.el,i=typeof n=="string"&&n.startsWith("#"),r=typeof n=="string"?i?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=Cu(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function rs(e,t){return(history.state?history.state.position-t:-1)+e}const $i=new Map;function Lu(e,t){$i.set(e,t)}function Pu(e){const t=$i.get(e);return $i.delete(e),t}let Ru=()=>location.protocol+"//"+location.host;function Go(e,t){const{pathname:n,search:i,hash:r}=t,s=e.indexOf("#");if(s>-1){let l=r.includes(e.slice(s))?e.slice(s).length:1,a=r.slice(l);return a[0]!=="/"&&(a="/"+a),ns(a,"")}return ns(n,e)+i+r}function Tu(e,t,n,i){let r=[],s=[],o=null;const l=({state:p})=>{const g=Go(e,location),y=n.value,A=t.value;let P=0;if(p){if(n.value=g,t.value=p,o&&o===y){o=null;return}P=A?p.position-A.position:0}else i(g);r.forEach(m=>{m(n.value,y,{delta:P,type:bn.pop,direction:P?P>0?dn.forward:dn.back:dn.unknown})})};function a(){o=n.value}function c(p){r.push(p);const g=()=>{const y=r.indexOf(p);y>-1&&r.splice(y,1)};return s.push(g),g}function u(){const{history:p}=window;!p.state||p.replaceState(me({},p.state,{scroll:oi()}),"")}function d(){for(const p of s)p();s=[],window.removeEventListener("popstate",l),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",l),window.addEventListener("beforeunload",u),{pauseListeners:a,listen:c,destroy:d}}function ss(e,t,n,i=!1,r=!1){return{back:e,current:t,forward:n,replaced:i,position:window.history.length,scroll:r?oi():null}}function Au(e){const{history:t,location:n}=window,i={value:Go(e,n)},r={value:t.state};r.value||s(i.value,{back:null,current:i.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(a,c,u){const d=e.indexOf("#"),p=d>-1?(n.host&&document.querySelector("base")?e:e.slice(d))+a:Ru()+e+a;try{t[u?"replaceState":"pushState"](c,"",p),r.value=c}catch(g){console.error(g),n[u?"replace":"assign"](p)}}function o(a,c){const u=me({},t.state,ss(r.value.back,a,r.value.forward,!0),c,{position:r.value.position});s(a,u,!0),i.value=a}function l(a,c){const u=me({},r.value,t.state,{forward:a,scroll:oi()});s(u.current,u,!0);const d=me({},ss(i.value,a,null),{position:u.position+1},c);s(a,d,!1),i.value=a}return{location:i,state:r,push:l,replace:o}}function Su(e){e=wu(e);const t=Au(e),n=Tu(e,t.state,t.location,t.replace);function i(s,o=!0){o||n.pauseListeners(),history.go(s)}const r=me({location:"",base:e,go:i,createHref:ku.bind(null,e)},t,n);return Object.defineProperty(r,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(r,"state",{enumerable:!0,get:()=>t.state.value}),r}function Ou(e){return typeof e=="string"||e&&typeof e=="object"}function Qo(e){return typeof e=="string"||typeof e=="symbol"}const et={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Yo=Symbol("");var os;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(os||(os={}));function Jt(e,t){return me(new Error,{type:e,[Yo]:!0},t)}function ft(e,t){return e instanceof Error&&Yo in e&&(t==null||!!(e.type&t))}const ls="[^/]+?",Iu={sensitive:!1,strict:!1,start:!0,end:!0},Du=/[.+*?^${}()[\]/\\]/g;function Mu(e,t){const n=me({},Iu,t),i=[];let r=n.start?"^":"";const s=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(r+="/");for(let d=0;d1&&(u.endsWith("/")?u=u.slice(0,-1):d=!0);else throw new Error(`Missing required param "${y}"`);u+=b}}return u}return{re:o,score:i,keys:s,parse:l,stringify:a}}function Fu(e,t){let n=0;for(;nt.length?t.length===1&&t[0]===40+40?1:-1:0}function Nu(e,t){let n=0;const i=e.score,r=t.score;for(;n0&&t[t.length-1]<0}const Hu={type:0,value:""},ju=/[a-zA-Z0-9_]/;function $u(e){if(!e)return[[]];if(e==="/")return[[Hu]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(g){throw new Error(`ERR (${n})/"${c}": ${g}`)}let n=0,i=n;const r=[];let s;function o(){s&&r.push(s),s=[]}let l=0,a,c="",u="";function d(){!c||(n===0?s.push({type:0,value:c}):n===1||n===2||n===3?(s.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function p(){c+=a}for(;l{o(b)}:fn}function o(u){if(Qo(u)){const d=i.get(u);d&&(i.delete(u),n.splice(n.indexOf(d),1),d.children.forEach(o),d.alias.forEach(o))}else{const d=n.indexOf(u);d>-1&&(n.splice(d,1),u.record.name&&i.delete(u.record.name),u.children.forEach(o),u.alias.forEach(o))}}function l(){return n}function a(u){let d=0;for(;d=0&&(u.record.path!==n[d].record.path||!Zo(u,n[d]));)d++;n.splice(d,0,u),u.record.name&&!cs(u)&&i.set(u.record.name,u)}function c(u,d){let p,g={},y,A;if("name"in u&&u.name){if(p=i.get(u.name),!p)throw Jt(1,{location:u});A=p.record.name,g=me(Vu(d.params,p.keys.filter(b=>!b.optional).map(b=>b.name)),u.params),y=p.stringify(g)}else if("path"in u)y=u.path,p=n.find(b=>b.re.test(y)),p&&(g=p.parse(y),A=p.record.name);else{if(p=d.name?i.get(d.name):n.find(b=>b.re.test(d.path)),!p)throw Jt(1,{location:u,currentLocation:d});A=p.record.name,g=me({},d.params,u.params),y=p.stringify(g)}const P=[];let m=p;for(;m;)P.unshift(m.record),m=m.parent;return{name:A,path:y,params:g,matched:P,meta:qu(P)}}return e.forEach(u=>s(u)),{addRoute:s,resolve:c,removeRoute:o,getRoutes:l,getRecordMatcher:r}}function Vu(e,t){const n={};for(const i of t)i in e&&(n[i]=e[i]);return n}function Uu(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:Wu(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function Wu(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const i in e.components)t[i]=typeof n=="boolean"?n:n[i];return t}function cs(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function qu(e){return e.reduce((t,n)=>me(t,n.meta),{})}function us(e,t){const n={};for(const i in e)n[i]=i in t?t[i]:e[i];return n}function Zo(e,t){return t.children.some(n=>n===e||Zo(e,n))}const Xo=/#/g,Ku=/&/g,Ju=/\//g,Gu=/=/g,Qu=/\?/g,el=/\+/g,Yu=/%5B/g,Zu=/%5D/g,tl=/%5E/g,Xu=/%60/g,nl=/%7B/g,ef=/%7C/g,il=/%7D/g,tf=/%20/g;function hr(e){return encodeURI(""+e).replace(ef,"|").replace(Yu,"[").replace(Zu,"]")}function nf(e){return hr(e).replace(nl,"{").replace(il,"}").replace(tl,"^")}function Bi(e){return hr(e).replace(el,"%2B").replace(tf,"+").replace(Xo,"%23").replace(Ku,"%26").replace(Xu,"`").replace(nl,"{").replace(il,"}").replace(tl,"^")}function rf(e){return Bi(e).replace(Gu,"%3D")}function sf(e){return hr(e).replace(Xo,"%23").replace(Qu,"%3F")}function of(e){return e==null?"":sf(e).replace(Ju,"%2F")}function Wn(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function lf(e){const t={};if(e===""||e==="?")return t;const i=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;rs&&Bi(s)):[i&&Bi(i)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function af(e){const t={};for(const n in e){const i=e[n];i!==void 0&&(t[n]=Qe(i)?i.map(r=>r==null?null:""+r):i==null?i:""+i)}return t}const cf=Symbol(""),ds=Symbol(""),li=Symbol(""),pr=Symbol(""),zi=Symbol("");function tn(){let e=[];function t(i){return e.push(i),()=>{const r=e.indexOf(i);r>-1&&e.splice(r,1)}}function n(){e=[]}return{add:t,list:()=>e,reset:n}}function pt(e,t,n,i,r){const s=i&&(i.enterCallbacks[r]=i.enterCallbacks[r]||[]);return()=>new Promise((o,l)=>{const a=d=>{d===!1?l(Jt(4,{from:n,to:t})):d instanceof Error?l(d):Ou(d)?l(Jt(2,{from:t,to:d})):(s&&i.enterCallbacks[r]===s&&typeof d=="function"&&s.push(d),o())},c=e.call(i&&i.instances[r],t,n,a);let u=Promise.resolve(c);e.length<3&&(u=u.then(a)),u.catch(d=>l(d))})}function yi(e,t,n,i){const r=[];for(const s of e)for(const o in s.components){let l=s.components[o];if(!(t!=="beforeRouteEnter"&&!s.instances[o]))if(uf(l)){const c=(l.__vccOpts||l)[t];c&&r.push(pt(c,n,i,s,o))}else{let a=l();r.push(()=>a.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${o}" at "${s.path}"`));const u=pu(c)?c.default:c;s.components[o]=u;const p=(u.__vccOpts||u)[t];return p&&pt(p,n,i,s,o)()}))}}return r}function uf(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function hs(e){const t=xe(li),n=xe(pr),i=fe(()=>t.resolve(Ge(e.to))),r=fe(()=>{const{matched:a}=i.value,{length:c}=a,u=a[c-1],d=n.matched;if(!u||!d.length)return-1;const p=d.findIndex(Kt.bind(null,u));if(p>-1)return p;const g=ps(a[c-2]);return c>1&&ps(u)===g&&d[d.length-1].path!==g?d.findIndex(Kt.bind(null,a[c-2])):p}),s=fe(()=>r.value>-1&&pf(n.params,i.value.params)),o=fe(()=>r.value>-1&&r.value===n.matched.length-1&&Jo(n.params,i.value.params));function l(a={}){return hf(a)?t[Ge(e.replace)?"replace":"push"](Ge(e.to)).catch(fn):Promise.resolve()}return{route:i,href:fe(()=>i.value.href),isActive:s,isExactActive:o,navigate:l}}const ff=je({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:hs,setup(e,{slots:t}){const n=Yt(hs(e)),{options:i}=xe(li),r=fe(()=>({[ms(e.activeClass,i.linkActiveClass,"router-link-active")]:n.isActive,[ms(e.exactActiveClass,i.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:ae("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},s)}}}),df=ff;function hf(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function pf(e,t){for(const n in t){const i=t[n],r=e[n];if(typeof i=="string"){if(i!==r)return!1}else if(!Qe(r)||r.length!==i.length||i.some((s,o)=>s!==r[o]))return!1}return!0}function ps(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const ms=(e,t,n)=>e!=null?e:t!=null?t:n,mf=je({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const i=xe(zi),r=fe(()=>e.route||i.value),s=xe(ds,0),o=fe(()=>{let c=Ge(s);const{matched:u}=r.value;let d;for(;(d=u[c])&&!d.components;)c++;return c}),l=fe(()=>r.value.matched[o.value]);Ot(ds,fe(()=>o.value+1)),Ot(cf,l),Ot(zi,r);const a=be();return it(()=>[a.value,l.value,e.name],([c,u,d],[p,g,y])=>{u&&(u.instances[d]=c,g&&g!==u&&c&&c===p&&(u.leaveGuards.size||(u.leaveGuards=g.leaveGuards),u.updateGuards.size||(u.updateGuards=g.updateGuards))),c&&u&&(!g||!Kt(u,g)||!p)&&(u.enterCallbacks[d]||[]).forEach(A=>A(c))},{flush:"post"}),()=>{const c=r.value,u=l.value,d=u&&u.components[e.name],p=e.name;if(!d)return gs(n.default,{Component:d,route:c});const g=u.props[e.name],y=g?g===!0?c.params:typeof g=="function"?g(c):g:null,P=ae(d,me({},y,t,{onVnodeUnmounted:m=>{m.component.isUnmounted&&(u.instances[p]=null)},ref:a}));return gs(n.default,{Component:P,route:c})||P}}});function gs(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const rl=mf;function gf(e){const t=zu(e.routes,e),n=e.parseQuery||lf,i=e.stringifyQuery||fs,r=e.history,s=tn(),o=tn(),l=tn(),a=Ys(et);let c=et;$t&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=_i.bind(null,E=>""+E),d=_i.bind(null,of),p=_i.bind(null,Wn);function g(E,V){let O,U;return Qo(E)?(O=t.getRecordMatcher(E),U=V):U=E,t.addRoute(U,O)}function y(E){const V=t.getRecordMatcher(E);V&&t.removeRoute(V)}function A(){return t.getRoutes().map(E=>E.record)}function P(E){return!!t.getRecordMatcher(E)}function m(E,V){if(V=me({},V||a.value),typeof E=="string"){const Z=bi(n,E,V.path),f=t.resolve({path:Z.path},V),h=r.createHref(Z.fullPath);return me(Z,f,{params:p(f.params),hash:Wn(Z.hash),redirectedFrom:void 0,href:h})}let O;if("path"in E)O=me({},E,{path:bi(n,E.path,V.path).path});else{const Z=me({},E.params);for(const f in Z)Z[f]==null&&delete Z[f];O=me({},E,{params:d(E.params)}),V.params=d(V.params)}const U=t.resolve(O,V),ue=E.hash||"";U.params=u(p(U.params));const de=vu(i,me({},E,{hash:nf(ue),path:U.path})),te=r.createHref(de);return me({fullPath:de,hash:ue,query:i===fs?af(E.query):E.query||{}},U,{redirectedFrom:void 0,href:te})}function b(E){return typeof E=="string"?bi(n,E,a.value.path):me({},E)}function x(E,V){if(c!==E)return Jt(8,{from:V,to:E})}function M(E){return D(E)}function z(E){return M(me(b(E),{replace:!0}))}function $(E){const V=E.matched[E.matched.length-1];if(V&&V.redirect){const{redirect:O}=V;let U=typeof O=="function"?O(E):O;return typeof U=="string"&&(U=U.includes("?")||U.includes("#")?U=b(U):{path:U},U.params={}),me({query:E.query,hash:E.hash,params:"path"in U?{}:E.params},U)}}function D(E,V){const O=c=m(E),U=a.value,ue=E.state,de=E.force,te=E.replace===!0,Z=$(O);if(Z)return D(me(b(Z),{state:ue,force:de,replace:te}),V||O);const f=O;f.redirectedFrom=V;let h;return!de&&_u(i,U,O)&&(h=Jt(16,{to:f,from:U}),Re(U,U,!0,!1)),(h?Promise.resolve(h):G(f,U)).catch(_=>ft(_)?ft(_,2)?_:we(_):X(_,f,U)).then(_=>{if(_){if(ft(_,2))return D(me(b(_.to),{state:ue,force:de,replace:te}),V||f)}else _=q(f,U,!0,te,ue);return B(f,U,_),_})}function v(E,V){const O=x(E,V);return O?Promise.reject(O):Promise.resolve()}function G(E,V){let O;const[U,ue,de]=vf(E,V);O=yi(U.reverse(),"beforeRouteLeave",E,V);for(const Z of U)Z.leaveGuards.forEach(f=>{O.push(pt(f,E,V))});const te=v.bind(null,E,V);return O.push(te),Ft(O).then(()=>{O=[];for(const Z of s.list())O.push(pt(Z,E,V));return O.push(te),Ft(O)}).then(()=>{O=yi(ue,"beforeRouteUpdate",E,V);for(const Z of ue)Z.updateGuards.forEach(f=>{O.push(pt(f,E,V))});return O.push(te),Ft(O)}).then(()=>{O=[];for(const Z of E.matched)if(Z.beforeEnter&&!V.matched.includes(Z))if(Qe(Z.beforeEnter))for(const f of Z.beforeEnter)O.push(pt(f,E,V));else O.push(pt(Z.beforeEnter,E,V));return O.push(te),Ft(O)}).then(()=>(E.matched.forEach(Z=>Z.enterCallbacks={}),O=yi(de,"beforeRouteEnter",E,V),O.push(te),Ft(O))).then(()=>{O=[];for(const Z of o.list())O.push(pt(Z,E,V));return O.push(te),Ft(O)}).catch(Z=>ft(Z,8)?Z:Promise.reject(Z))}function B(E,V,O){for(const U of l.list())U(E,V,O)}function q(E,V,O,U,ue){const de=x(E,V);if(de)return de;const te=V===et,Z=$t?history.state:{};O&&(U||te?r.replace(E.fullPath,me({scroll:te&&Z&&Z.scroll},ue)):r.push(E.fullPath,ue)),a.value=E,Re(E,V,O,te),we()}let w;function F(){w||(w=r.listen((E,V,O)=>{if(!wt.listening)return;const U=m(E),ue=$(U);if(ue){D(me(ue,{replace:!0}),U).catch(fn);return}c=U;const de=a.value;$t&&Lu(rs(de.fullPath,O.delta),oi()),G(U,de).catch(te=>ft(te,12)?te:ft(te,2)?(D(te.to,U).then(Z=>{ft(Z,20)&&!O.delta&&O.type===bn.pop&&r.go(-1,!1)}).catch(fn),Promise.reject()):(O.delta&&r.go(-O.delta,!1),X(te,U,de))).then(te=>{te=te||q(U,de,!1),te&&(O.delta?r.go(-O.delta,!1):O.type===bn.pop&&ft(te,20)&&r.go(-1,!1)),B(U,de,te)}).catch(fn)}))}let S=tn(),ie=tn(),j;function X(E,V,O){we(E);const U=ie.list();return U.length?U.forEach(ue=>ue(E,V,O)):console.error(E),Promise.reject(E)}function ne(){return j&&a.value!==et?Promise.resolve():new Promise((E,V)=>{S.add([E,V])})}function we(E){return j||(j=!E,F(),S.list().forEach(([V,O])=>E?O(E):V()),S.reset()),E}function Re(E,V,O,U){const{scrollBehavior:ue}=e;if(!$t||!ue)return Promise.resolve();const de=!O&&Pu(rs(E.fullPath,0))||(U||!O)&&history.state&&history.state.scroll||null;return sr().then(()=>ue(E,V,de)).then(te=>te&&xu(te)).catch(te=>X(te,E,V))}const Ie=E=>r.go(E);let $e;const Ae=new Set,wt={currentRoute:a,listening:!0,addRoute:g,removeRoute:y,hasRoute:P,getRoutes:A,resolve:m,options:e,push:M,replace:z,go:Ie,back:()=>Ie(-1),forward:()=>Ie(1),beforeEach:s.add,beforeResolve:o.add,afterEach:l.add,onError:ie.add,isReady:ne,install(E){const V=this;E.component("RouterLink",df),E.component("RouterView",rl),E.config.globalProperties.$router=V,Object.defineProperty(E.config.globalProperties,"$route",{enumerable:!0,get:()=>Ge(a)}),$t&&!$e&&a.value===et&&($e=!0,M(r.location).catch(ue=>{}));const O={};for(const ue in et)O[ue]=fe(()=>a.value[ue]);E.provide(li,V),E.provide(pr,Yt(O)),E.provide(zi,a);const U=E.unmount;Ae.add(E),E.unmount=function(){Ae.delete(E),Ae.size<1&&(c=et,w&&w(),w=null,a.value=et,$e=!1,j=!1),U()}}};return wt}function Ft(e){return e.reduce((t,n)=>t.then(()=>n()),Promise.resolve())}function vf(e,t){const n=[],i=[],r=[],s=Math.max(t.matched.length,e.matched.length);for(let o=0;oKt(c,l))?i.push(l):n.push(l));const a=e.matched[o];a&&(t.matched.find(c=>Kt(c,a))||r.push(a))}return[n,i,r]}function ai(){return xe(li)}function mr(){return xe(pr)}const _f=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:i=5})=>{const r=ai(),s=Cn(),l=Ko(()=>{var P,m,b,x;const a=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(a-0)g.some(z=>z.hash===M.hash));for(let M=0;M=((m=(P=z.parentElement)==null?void 0:P.offsetTop)!=null?m:0)-i,v=!$||a<((x=(b=$.parentElement)==null?void 0:b.offsetTop)!=null?x:0)-i;if(!(D&&v))continue;const B=decodeURIComponent(r.currentRoute.value.hash),q=decodeURIComponent(z.hash);if(B===q)return;if(p){for(let w=M+1;w{l(),window.addEventListener("scroll",l)}),ni(()=>{window.removeEventListener("scroll",l)}),it(()=>s.value.path,l)},vs=async(e,...t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace(...t).finally(()=>e.options.scrollBehavior=n)},bf="a.sidebar-item",yf=".header-anchor",wf=300,Ef=5;var kf=yt({setup(){_f({headerLinkSelector:bf,headerAnchorSelector:yf,delay:wf,offset:Ef})}});const _s=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,Cf=()=>window.scrollTo({top:0,behavior:"smooth"});const xf=je({name:"BackToTop",setup(){const e=be(0),t=fe(()=>e.value>300),n=Ko(()=>{e.value=_s()},100);ot(()=>{e.value=_s(),window.addEventListener("scroll",()=>n())});const i=ae("div",{class:"back-to-top",onClick:Cf});return()=>ae(fr,{name:"back-to-top"},()=>t.value?i:null)}});var Lf=yt({rootComponents:[xf]});const Pf=ae("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[ae("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),ae("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Rf=je({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=Wo(),n=fe(()=>{var i;return(i=e.locales[t.value])!=null?i:{openInNewWindow:"open in new window"}});return()=>ae("span",[Pf,ae("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}}),Tf={"/":{openInNewWindow:"open in new window"}};var Af=yt({enhance({app:e}){e.component("ExternalLinkIcon",ae(Rf,{locales:Tf}))}});/*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */var Pt=Object.assign||function(e){for(var t=1;t1&&arguments[1]!==void 0?arguments[1]:{},i=window.Promise||function(w){function F(){}w(F,F)},r=function(w){var F=w.target;if(F===G){y();return}x.indexOf(F)!==-1&&A({target:F})},s=function(){if(!(z||!v.original)){var w=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs($-w)>D.scrollOffset&&setTimeout(y,150)}},o=function(w){var F=w.key||w.keyCode;(F==="Escape"||F==="Esc"||F===27)&&y()},l=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},F=w;if(w.background&&(G.style.background=w.background),w.container&&w.container instanceof Object&&(F.container=Pt({},D.container,w.container)),w.template){var S=Fn(w.template)?w.template:document.querySelector(w.template);F.template=S}return D=Pt({},D,F),x.forEach(function(ie){ie.dispatchEvent(Nt("medium-zoom:update",{detail:{zoom:B}}))}),B},a=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Pt({},D,w))},c=function(){for(var w=arguments.length,F=Array(w),S=0;S0?F.reduce(function(j,X){return[].concat(j,ys(X))},[]):x;return ie.forEach(function(j){j.classList.remove("medium-zoom-image"),j.dispatchEvent(Nt("medium-zoom:detach",{detail:{zoom:B}}))}),x=x.filter(function(j){return ie.indexOf(j)===-1}),B},d=function(w,F){var S=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return x.forEach(function(ie){ie.addEventListener("medium-zoom:"+w,F,S)}),M.push({type:"medium-zoom:"+w,listener:F,options:S}),B},p=function(w,F){var S=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return x.forEach(function(ie){ie.removeEventListener("medium-zoom:"+w,F,S)}),M=M.filter(function(ie){return!(ie.type==="medium-zoom:"+w&&ie.listener.toString()===F.toString())}),B},g=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},F=w.target,S=function(){var j={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},X=void 0,ne=void 0;if(D.container)if(D.container instanceof Object)j=Pt({},j,D.container),X=j.width-j.left-j.right-D.margin*2,ne=j.height-j.top-j.bottom-D.margin*2;else{var we=Fn(D.container)?D.container:document.querySelector(D.container),Re=we.getBoundingClientRect(),Ie=Re.width,$e=Re.height,Ae=Re.left,wt=Re.top;j=Pt({},j,{width:Ie,height:$e,left:Ae,top:wt})}X=X||j.width-D.margin*2,ne=ne||j.height-D.margin*2;var E=v.zoomedHd||v.original,V=bs(E)?X:E.naturalWidth||X,O=bs(E)?ne:E.naturalHeight||ne,U=E.getBoundingClientRect(),ue=U.top,de=U.left,te=U.width,Z=U.height,f=Math.min(V,X)/te,h=Math.min(O,ne)/Z,_=Math.min(f,h),C=(-de+(X-te)/2+D.margin+j.left)/_,k=(-ue+(ne-Z)/2+D.margin+j.top)/_,R="scale("+_+") translate3d("+C+"px, "+k+"px, 0)";v.zoomed.style.transform=R,v.zoomedHd&&(v.zoomedHd.style.transform=R)};return new i(function(ie){if(F&&x.indexOf(F)===-1){ie(B);return}var j=function Ie(){z=!1,v.zoomed.removeEventListener("transitionend",Ie),v.original.dispatchEvent(Nt("medium-zoom:opened",{detail:{zoom:B}})),ie(B)};if(v.zoomed){ie(B);return}if(F)v.original=F;else if(x.length>0){var X=x;v.original=X[0]}else{ie(B);return}if(v.original.dispatchEvent(Nt("medium-zoom:open",{detail:{zoom:B}})),$=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,z=!0,v.zoomed=If(v.original),document.body.appendChild(G),D.template){var ne=Fn(D.template)?D.template:document.querySelector(D.template);v.template=document.createElement("div"),v.template.appendChild(ne.content.cloneNode(!0)),document.body.appendChild(v.template)}if(document.body.appendChild(v.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),v.original.classList.add("medium-zoom-image--hidden"),v.zoomed.classList.add("medium-zoom-image--opened"),v.zoomed.addEventListener("click",y),v.zoomed.addEventListener("transitionend",j),v.original.getAttribute("data-zoom-src")){v.zoomedHd=v.zoomed.cloneNode(),v.zoomedHd.removeAttribute("srcset"),v.zoomedHd.removeAttribute("sizes"),v.zoomedHd.src=v.zoomed.getAttribute("data-zoom-src"),v.zoomedHd.onerror=function(){clearInterval(we),console.warn("Unable to reach the zoom image target "+v.zoomedHd.src),v.zoomedHd=null,S()};var we=setInterval(function(){v.zoomedHd.complete&&(clearInterval(we),v.zoomedHd.classList.add("medium-zoom-image--opened"),v.zoomedHd.addEventListener("click",y),document.body.appendChild(v.zoomedHd),S())},10)}else if(v.original.hasAttribute("srcset")){v.zoomedHd=v.zoomed.cloneNode(),v.zoomedHd.removeAttribute("sizes"),v.zoomedHd.removeAttribute("loading");var Re=v.zoomedHd.addEventListener("load",function(){v.zoomedHd.removeEventListener("load",Re),v.zoomedHd.classList.add("medium-zoom-image--opened"),v.zoomedHd.addEventListener("click",y),document.body.appendChild(v.zoomedHd),S()})}else S()})},y=function(){return new i(function(w){if(z||!v.original){w(B);return}var F=function S(){v.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(v.zoomed),v.zoomedHd&&document.body.removeChild(v.zoomedHd),document.body.removeChild(G),v.zoomed.classList.remove("medium-zoom-image--opened"),v.template&&document.body.removeChild(v.template),z=!1,v.zoomed.removeEventListener("transitionend",S),v.original.dispatchEvent(Nt("medium-zoom:closed",{detail:{zoom:B}})),v.original=null,v.zoomed=null,v.zoomedHd=null,v.template=null,w(B)};z=!0,document.body.classList.remove("medium-zoom--opened"),v.zoomed.style.transform="",v.zoomedHd&&(v.zoomedHd.style.transform=""),v.template&&(v.template.style.transition="opacity 150ms",v.template.style.opacity=0),v.original.dispatchEvent(Nt("medium-zoom:close",{detail:{zoom:B}})),v.zoomed.addEventListener("transitionend",F)})},A=function(){var w=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},F=w.target;return v.original?y():g({target:F})},P=function(){return D},m=function(){return x},b=function(){return v.original},x=[],M=[],z=!1,$=0,D=n,v={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?D=t:(t||typeof t=="string")&&c(t),D=Pt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},D);var G=Of(D.background);document.addEventListener("click",r),document.addEventListener("keyup",o),document.addEventListener("scroll",s),window.addEventListener("resize",y);var B={open:g,close:y,toggle:A,update:l,clone:a,attach:c,detach:u,on:d,off:p,getOptions:P,getImages:m,getZoomedImage:b};return B};function Mf(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||typeof document=="undefined")){var i=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",n==="top"&&i.firstChild?i.insertBefore(r,i.firstChild):i.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}}var Ff=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";Mf(Ff);var Nf=Df;const Hf=Symbol("mediumZoom");const jf=".theme-default-content > img, .theme-default-content :not(a) > img",$f={},Bf=300;var zf=yt({enhance({app:e,router:t}){const n=Nf($f);n.refresh=(i=jf)=>{n.detach(),n.attach(i)},e.provide(Hf,n),t.afterEach(()=>{setTimeout(()=>n.refresh(),Bf)})}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const le={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:'
'},status:null,set:e=>{const t=le.isStarted();e=wi(e,le.settings.minimum,1),le.status=e===1?null:e;const n=le.render(!t),i=n.querySelector(le.settings.barSelector),r=le.settings.speed,s=le.settings.easing;return n.offsetWidth,Vf(o=>{Dn(i,{transform:"translate3d("+ws(e)+"%,0,0)",transition:"all "+r+"ms "+s}),e===1?(Dn(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){Dn(n,{transition:"all "+r+"ms linear",opacity:"0"}),setTimeout(function(){le.remove(),o()},r)},r)):setTimeout(()=>o(),r)}),le},isStarted:()=>typeof le.status=="number",start:()=>{le.status||le.set(0);const e=()=>{setTimeout(()=>{!le.status||(le.trickle(),e())},le.settings.trickleSpeed)};return le.settings.trickle&&e(),le},done:e=>!e&&!le.status?le:le.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=le.status;return t?(typeof e!="number"&&(e=(1-t)*wi(Math.random()*t,.1,.95)),t=wi(t+e,0,.994),le.set(t)):le.start()},trickle:()=>le.inc(Math.random()*le.settings.trickleRate),render:e=>{if(le.isRendered())return document.getElementById("nprogress");Es(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=le.settings.template;const n=t.querySelector(le.settings.barSelector),i=e?"-100":ws(le.status||0),r=document.querySelector(le.settings.parent);return Dn(n,{transition:"all 0 linear",transform:"translate3d("+i+"%,0,0)"}),r!==document.body&&Es(r,"nprogress-custom-parent"),r==null||r.appendChild(t),t},remove:()=>{ks(document.documentElement,"nprogress-busy"),ks(document.querySelector(le.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&Uf(e)},isRendered:()=>!!document.getElementById("nprogress")},wi=(e,t,n)=>en?n:e,ws=e=>(-1+e)*100,Vf=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),Dn=function(){const e=["Webkit","O","Moz","ms"],t={};function n(o){return o.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(l,a){return a.toUpperCase()})}function i(o){const l=document.body.style;if(o in l)return o;let a=e.length;const c=o.charAt(0).toUpperCase()+o.slice(1);let u;for(;a--;)if(u=e[a]+c,u in l)return u;return o}function r(o){return o=n(o),t[o]||(t[o]=i(o))}function s(o,l,a){l=r(l),o.style[l]=a}return function(o,l){for(const a in l){const c=l[a];c!==void 0&&Object.prototype.hasOwnProperty.call(l,a)&&s(o,a,c)}}}(),sl=(e,t)=>(typeof e=="string"?e:gr(e)).indexOf(" "+t+" ")>=0,Es=(e,t)=>{const n=gr(e),i=n+t;sl(n,t)||(e.className=i.substring(1))},ks=(e,t)=>{const n=gr(e);if(!sl(e,t))return;const i=n.replace(" "+t+" "," ");e.className=i.substring(1,i.length-1)},gr=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),Uf=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const Wf=()=>{ot(()=>{const e=ai(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||le.start()}),e.afterEach(n=>{t.add(n.path),le.done()})})};var qf=yt({setup(){Wf()}});const Kf=JSON.parse(`{"colorMode":"dark","colorModeSwitch":true,"navbar":[{"text":"Home","link":"/"},{"text":"Wiki","link":"/wiki/"},{"text":"JavaDocs","link":"https://docs.javacord.org/"},{"text":"Discord Server","link":"https://discord.gg/javacord"},{"text":"GitHub","link":"https://github.com/Javacord/Javacord"},{"text":"Legal","children":[{"text":"Imprint","link":"/imprint"},{"text":"Privacy Policy","link":"/privacy-policy"}]}],"sidebarDepth":1,"sidebar":{"/wiki/":[{"text":"Getting Started","collapsible":false,"children":["/wiki/getting-started/README.md","/wiki/getting-started/download-installation","/wiki/getting-started/creating-a-bot-account","/wiki/getting-started/writing-your-first-bot",{"text":"Beginner IDE Setup","collapsible":true,"children":["/wiki/getting-started/setup/intellij-gradle","/wiki/getting-started/setup/intellij-maven","/wiki/getting-started/setup/eclipse-maven"]},"/wiki/getting-started/faq"]},{"text":"Basic Tutorials","collapsible":false,"children":[{"text":"Interactions","collapsible":true,"children":["/wiki/basic-tutorials/interactions/overview","/wiki/basic-tutorials/interactions/commands","/wiki/basic-tutorials/interactions/components","/wiki/basic-tutorials/interactions/responding"]},"/wiki/basic-tutorials/listeners","/wiki/basic-tutorials/gateway-intents","/wiki/basic-tutorials/embeds","/wiki/basic-tutorials/emojis-and-reactions","/wiki/basic-tutorials/message-builder","/wiki/basic-tutorials/creating-entities","/wiki/basic-tutorials/logger-config","/wiki/basic-tutorials/running","/wiki/basic-tutorials/glossary"]},{"text":"Advanced Topics","collapsible":false,"children":["/wiki/advanced-topics/bot-lifecycle","/wiki/advanced-topics/entity-cache","/wiki/advanced-topics/playing-audio","/wiki/advanced-topics/ratelimits","/wiki/advanced-topics/sharding","/wiki/advanced-topics/performance-tweaks","/wiki/advanced-topics/proxies"]},{"text":"Essential Knowledge","collapsible":false,"children":["/wiki/essential-knowledge/lambdas","/wiki/essential-knowledge/optionals","/wiki/essential-knowledge/completable-futures"]}]},"docsRepo":"Javacord/Website","docsDir":"docs","docsBranch":"master","editLink":true,"editLinkText":"Edit this page on GitHub","locales":{"/":{"selectLanguageName":"English"}},"logo":null,"repo":null,"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),ol=be(Kf),Jf=()=>ol;ci.webpackHot&&(__VUE_HMR_RUNTIME__.updateThemeData=e=>{ol.value=e});const ll=Symbol(""),Gf=()=>{const e=xe(ll);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},Qf=(e,t)=>{var n;return{...e,...(n=e.locales)==null?void 0:n[t]}};var Yf=yt({enhance({app:e}){const t=Jf(),n=e._context.provides[dr],i=fe(()=>Qf(t.value,n.value));e.provide(ll,i),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return i.value}}})}}),al=(e,t)=>{const n=e.__vccOpts||e;for(const[i,r]of t)n[i]=r;return n};const Zf=je({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(ii(),Ro("span",{class:yn(["badge",e.type]),style:Jn({verticalAlign:e.vertical})},[go(t.$slots,"default",{},()=>[cr(Pl(e.text),1)])],6))}});var Xf=al(Zf,[["__file","Badge.vue"]]);const ed=je({name:"CodeGroup",setup(e,{slots:t}){const n=be(-1),i=be([]),r=(l=n.value)=>{l{l>0?n.value=l-1:n.value=i.value.length-1,i.value[n.value].focus()},o=(l,a)=>{l.key===" "||l.key==="Enter"?(l.preventDefault(),n.value=a):l.key==="ArrowRight"?(l.preventDefault(),r(a)):l.key==="ArrowLeft"&&(l.preventDefault(),s(a))};return()=>{var a;const l=(((a=t.default)==null?void 0:a.call(t))||[]).filter(c=>c.type.name==="CodeGroupItem").map(c=>(c.props===null&&(c.props={}),c));return l.length===0?null:(n.value<0||n.value>l.length-1?(n.value=l.findIndex(c=>c.props.active===""||c.props.active===!0),n.value===-1&&(n.value=0)):l.forEach((c,u)=>{c.props.active=u===n.value}),ae("div",{class:"code-group"},[ae("div",{class:"code-group__nav"},ae("ul",{class:"code-group__ul"},l.map((c,u)=>{const d=u===n.value;return ae("li",{class:"code-group__li"},ae("button",{ref:p=>{p&&(i.value[u]=p)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":d},ariaPressed:d,ariaExpanded:d,onClick:()=>n.value=u,onKeydown:p=>o(p,u)},c.props.title))}))),l]))}}}),td=["aria-selected"],nd=je({name:"CodeGroupItem"}),id=je({...nd,props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(ii(),Ro("div",{class:yn(["code-group-item",{"code-group-item__active":e.active}]),"aria-selected":e.active},[go(t.$slots,"default")],10,td))}});var rd=al(id,[["__file","CodeGroupItem.vue"]]),Cs;const xn=typeof window!="undefined",sd=e=>typeof e=="string",Ei=()=>{};xn&&((Cs=window==null?void 0:window.navigator)==null?void 0:Cs.userAgent)&&/iP(ad|hone|od)/.test(window.navigator.userAgent);function od(e,t){function n(...i){e(()=>t.apply(this,i),{fn:t,thisArg:this,args:i})}return n}const cl=e=>e();function ld(e=cl){const t=be(!0);function n(){t.value=!1}function i(){t.value=!0}return{isActive:t,pause:n,resume:i,eventFilter:(...s)=>{t.value&&e(...s)}}}function ul(e){return Nl()?(Hl(e),!0):!1}function ad(e,t=!0){Oo()?po(e):t?e():sr(e)}function yh(e=!1,t={}){const{truthyValue:n=!0,falsyValue:i=!1}=t,r=Ce(e),s=be(e);function o(l){return arguments.length?(s.value=l,s.value):(s.value=s.value===Ge(n)?Ge(i):Ge(n),s.value)}return r?o:[s,o]}var xs=Object.getOwnPropertySymbols,cd=Object.prototype.hasOwnProperty,ud=Object.prototype.propertyIsEnumerable,fd=(e,t)=>{var n={};for(var i in e)cd.call(e,i)&&t.indexOf(i)<0&&(n[i]=e[i]);if(e!=null&&xs)for(var i of xs(e))t.indexOf(i)<0&&ud.call(e,i)&&(n[i]=e[i]);return n};function dd(e,t,n={}){const i=n,{eventFilter:r=cl}=i,s=fd(i,["eventFilter"]);return it(e,od(r,t),s)}var hd=Object.defineProperty,pd=Object.defineProperties,md=Object.getOwnPropertyDescriptors,qn=Object.getOwnPropertySymbols,fl=Object.prototype.hasOwnProperty,dl=Object.prototype.propertyIsEnumerable,Ls=(e,t,n)=>t in e?hd(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,gd=(e,t)=>{for(var n in t||(t={}))fl.call(t,n)&&Ls(e,n,t[n]);if(qn)for(var n of qn(t))dl.call(t,n)&&Ls(e,n,t[n]);return e},vd=(e,t)=>pd(e,md(t)),_d=(e,t)=>{var n={};for(var i in e)fl.call(e,i)&&t.indexOf(i)<0&&(n[i]=e[i]);if(e!=null&&qn)for(var i of qn(e))t.indexOf(i)<0&&dl.call(e,i)&&(n[i]=e[i]);return n};function bd(e,t,n={}){const i=n,{eventFilter:r}=i,s=_d(i,["eventFilter"]),{eventFilter:o,pause:l,resume:a,isActive:c}=ld(r);return{stop:dd(e,t,vd(gd({},s),{eventFilter:o})),pause:l,resume:a,isActive:c}}function yd(e){var t;const n=Ge(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Kn=xn?window:void 0;xn&&window.document;xn&&window.navigator;xn&&window.location;function wd(...e){let t,n,i,r;if(sd(e[0])?([n,i,r]=e,t=Kn):[t,n,i,r]=e,!t)return Ei;let s=Ei;const o=it(()=>yd(t),a=>{s(),a&&(a.addEventListener(n,i,r),s=()=>{a.removeEventListener(n,i,r),s=Ei})},{immediate:!0,flush:"post"}),l=()=>{o(),s()};return ul(l),l}function Ed(e,t={}){const{window:n=Kn}=t,i=Boolean(n&&"matchMedia"in n&&typeof n.matchMedia=="function");let r;const s=be(!1),o=()=>{!i||(r||(r=n.matchMedia(e)),s.value=r.matches)};return ad(()=>{o(),r&&("addEventListener"in r?r.addEventListener("change",o):r.addListener(o),ul(()=>{"removeEventListener"in r?r.removeEventListener("change",o):r.removeListener(o)}))}),s}const Vi=typeof globalThis!="undefined"?globalThis:typeof window!="undefined"?window:typeof global!="undefined"?global:typeof self!="undefined"?self:{},Ui="__vueuse_ssr_handlers__";Vi[Ui]=Vi[Ui]||{};const kd=Vi[Ui];function Cd(e,t){return kd[e]||t}function xd(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"||Array.isArray(e)?"object":Number.isNaN(e)?"any":"number"}const Ld={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}};function Pd(e,t,n,i={}){var r;const{flush:s="pre",deep:o=!0,listenToStorageChanges:l=!0,writeDefaults:a=!0,shallow:c,window:u=Kn,eventFilter:d,onError:p=$=>{console.error($)}}=i,g=(c?Ys:be)(t);if(!n)try{n=Cd("getDefaultStorage",()=>{var $;return($=Kn)==null?void 0:$.localStorage})()}catch($){p($)}if(!n)return g;const y=Ge(t),A=xd(y),P=(r=i.serializer)!=null?r:Ld[A],{pause:m,resume:b}=bd(g,()=>x(g.value),{flush:s,deep:o,eventFilter:d});return u&&l&&wd(u,"storage",z),z(),g;function x($){try{$==null?n.removeItem(e):n.setItem(e,P.write($))}catch(D){p(D)}}function M($){if(!($&&$.key!==e)){m();try{const D=$?$.newValue:n.getItem(e);return D==null?(a&&y!==null&&n.setItem(e,P.write(y)),y):typeof D!="string"?D:P.read(D)}catch(D){p(D)}finally{b()}}}function z($){$&&$.key!==e||(g.value=M($))}}function Rd(e){return Ed("(prefers-color-scheme: dark)",e)}var Ps;(function(e){e.UP="UP",e.RIGHT="RIGHT",e.DOWN="DOWN",e.LEFT="LEFT",e.NONE="NONE"})(Ps||(Ps={}));const hl=Symbol(""),wh=()=>{const e=xe(hl);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Td=()=>{const e=vl(),t=Rd(),n=Pd("vuepress-color-scheme",e.value.colorMode),i=fe({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(r){r===t.value?n.value="auto":n.value=r?"dark":"light"}});Ot(hl,i),Ad(i)},Ad=e=>{const t=(n=e.value)=>{const i=window==null?void 0:window.document.querySelector("html");i==null||i.classList.toggle("dark",n)};ot(()=>{it(e,t,{immediate:!0})}),lr(()=>t())},pl=(...e)=>{const n=ai().resolve(...e),i=n.matched[n.matched.length-1];if(!(i!=null&&i.redirect))return n;const{redirect:r}=i,s=ee(r)?r(n):r,o=ve(s)?{path:s}:s;return pl({hash:n.hash,query:n.query,params:n.params,...o})},Sd=e=>{const t=pl(encodeURI(e));return{text:t.meta.title||e,link:t.name==="404"?e:t.fullPath}};let ki=null,nn=null;const Od={wait:()=>ki,pending:()=>{ki=new Promise(e=>nn=e)},resolve:()=>{nn==null||nn(),ki=null,nn=null}},Id=()=>Od,ml=Symbol("sidebarItems"),Eh=()=>{const e=xe(ml);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},Dd=()=>{const e=vl(),t=su(),n=fe(()=>Md(t.value,e.value));Ot(ml,n)},Md=(e,t)=>{var r,s,o,l;const n=(s=(r=e.sidebar)!=null?r:t.sidebar)!=null?s:"auto",i=(l=(o=e.sidebarDepth)!=null?o:t.sidebarDepth)!=null?l:2;return e.home||n===!1?[]:n==="auto"?Nd(i):Q(n)?gl(n,i):No(n)?Hd(n,i):[]},Fd=(e,t)=>({text:e.title,link:`#${e.slug}`,children:vr(e.children,t)}),vr=(e,t)=>t>0?e.map(n=>Fd(n,t-1)):[],Nd=e=>{const t=Cn();return[{text:t.value.title,children:vr(t.value.headers,e)}]},gl=(e,t)=>{const n=mr(),i=Cn(),r=s=>{var l;let o;if(ve(s)?o=Sd(s):o=s,o.children)return{...o,children:o.children.map(a=>r(a))};if(o.link===n.path){const a=((l=i.value.headers[0])==null?void 0:l.level)===1?i.value.headers[0].children:i.value.headers;return{...o,children:vr(a,t)}}return o};return e.map(s=>r(s))},Hd=(e,t)=>{var s;const n=mr(),i=Ho(e,n.path),r=(s=e[i])!=null?s:[];return gl(r,t)},vl=()=>Gf();var jd=yt({enhance({app:e,router:t}){e.component("Badge",Xf),e.component("CodeGroup",ed),e.component("CodeGroupItem",rd),e.component("AutoLinkExternalIcon",()=>{const i=e.component("ExternalLinkIcon");return i?ae(i):null}),e.component("NavbarSearch",()=>{const i=e.component("Docsearch")||e.component("SearchBox");return i?ae(i):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...i)=>(await Id().wait(),n(...i))},setup(){Td(),Dd()}});const $d=(e,t)=>t.some(n=>{if(ve(n))return n===e.key;const{key:i,ctrl:r=!1,shift:s=!1,alt:o=!1}=n;return i===e.key&&r===e.ctrlKey&&s===e.shiftKey&&o===e.altKey}),Bd=/[^\x00-\x7F]/,zd=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),Rs=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),Ts=(e,t)=>{const n=t.join(" "),i=zd(e);if(Bd.test(e))return i.some(o=>n.toLowerCase().indexOf(o)>-1);const r=e.endsWith(" ");return new RegExp(i.map((o,l)=>i.length===l+1&&!r?`(?=.*\\b${Rs(o)})`:`(?=.*\\b${Rs(o)}\\b)`).join("")+".+","gi").test(n)},Vd=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const n=i=>{!e.value||$d(i,t.value)&&!e.value.contains(i.target)&&(i.preventDefault(),e.value.focus())};ot(()=>{document.addEventListener("keydown",n)}),ni(()=>{document.removeEventListener("keydown",n)})},Ud=[{title:"",headers:[{level:2,title:"\u{1F4A1} About Javacord",slug:"about-javacord",children:[]},{level:2,title:"\u{1F469}\u200D\u{1F3EB} Learn more",slug:"learn-more",children:[]},{level:2,title:"\u{1F91D} Discord Server",slug:"discord-server",children:[]}],path:"/",pathLocale:"/",extraFields:[]},{title:"Imprint",headers:[],path:"/imprint.html",pathLocale:"/",extraFields:[]},{title:"Privacy Policy",headers:[],path:"/privacy-policy.html",pathLocale:"/",extraFields:[]},{title:"Bot Lifecycle",headers:[{level:2,title:"\u{1F4A1} The four states",slug:"the-four-states",children:[{level:3,title:"Connected",slug:"connected",children:[]},{level:3,title:"Disconnected",slug:"disconnected",children:[]},{level:3,title:"Resuming",slug:"resuming",children:[]},{level:3,title:"Reconnecting",slug:"reconnecting",children:[]}]},{level:2,title:"\u{1F48A} How to handle disconnects",slug:"how-to-handle-disconnects",children:[]}],path:"/wiki/advanced-topics/bot-lifecycle.html",pathLocale:"/",extraFields:[]},{title:"Entity Cache",headers:[{level:2,title:"\u{1F52E} What is in the cache?",slug:"what-is-in-the-cache",children:[]},{level:2,title:"\u2753 When are cached entities updated?",slug:"when-are-cached-entities-updated",children:[]},{level:2,title:"\u231A How long are cached entities valid?",slug:"how-long-are-cached-entities-valid",children:[]}],path:"/wiki/advanced-topics/entity-cache.html",pathLocale:"/",extraFields:[]},{title:"Performance Tweaks",headers:[{level:2,title:"\u2702\uFE0F Disabling Startup Wait",slug:"disabling-startup-wait",children:[]},{level:2,title:"\u2699\uFE0F Fine Tuning the Message Cache",slug:"fine-tuning-the-message-cache",children:[]},{level:2,title:"\u{1F48E} Using the Updater classes",slug:"using-the-updater-classes",children:[{level:3,title:"Example",slug:"example",children:[]}]}],path:"/wiki/advanced-topics/performance-tweaks.html",pathLocale:"/",extraFields:[]},{title:"Playing Audio",headers:[{level:2,title:"\u{1F50C} Connect to a voice channel",slug:"connect-to-a-voice-channel",children:[{level:3,title:"Example",slug:"example",children:[]}]},{level:2,title:"\u{1F442} Playing music",slug:"playing-music",children:[]}],path:"/wiki/advanced-topics/playing-audio.html",pathLocale:"/",extraFields:[]},{title:"Proxies",headers:[{level:2,title:"\u{1F468}\u200D\u{1F4BB} Configuring a Proxy ...",slug:"configuring-a-proxy",children:[{level:3,title:"... using System Properties",slug:"using-system-properties",children:[]},{level:3,title:"... using a System Default Proxy Selector",slug:"using-a-system-default-proxy-selector",children:[]},{level:3,title:"... using an Explicitly Set Proxy",slug:"using-an-explicitly-set-proxy",children:[]},{level:3,title:"... using an Explicitly Set Proxy Selector",slug:"using-an-explicitly-set-proxy-selector",children:[]},{level:3,title:"Precedence of the Configuration Options",slug:"precedence-of-the-configuration-options",children:[]}]},{level:2,title:"\u{1F511} Configuring Proxy Authentication ...",slug:"configuring-proxy-authentication",children:[{level:3,title:"... using a System Default Authenticator",slug:"using-a-system-default-authenticator",children:[]},{level:3,title:"... using an Explicitly Set Authenticator",slug:"using-an-explicitly-set-authenticator",children:[]}]},{level:2,title:"\u{1F4A1} Proxy Types",slug:"proxy-types",children:[{level:3,title:"HTTP",slug:"http",children:[]},{level:3,title:"SOCKS 4",slug:"socks-4",children:[]},{level:3,title:"SOCKS 4a",slug:"socks-4a",children:[]},{level:3,title:"SOCKS 5",slug:"socks-5",children:[]}]}],path:"/wiki/advanced-topics/proxies.html",pathLocale:"/",extraFields:[]},{title:"Ratelimits",headers:[{level:2,title:"\u2757 The Most Important Ratelimits",slug:"the-most-important-ratelimits",children:[]},{level:2,title:"\u{1F4AA} Dealing with Ratelimits",slug:"dealing-with-ratelimits",children:[{level:3,title:"Example",slug:"example",children:[]}]},{level:2,title:"\u274C Can I disable ratelimits?",slug:"can-i-disable-ratelimits",children:[]}],path:"/wiki/advanced-topics/ratelimits.html",pathLocale:"/",extraFields:[]},{title:"Sharding",headers:[{level:2,title:"\u{1F469}\u200D\u{1F3ED} Sharding in Javacord",slug:"sharding-in-javacord",children:[{level:3,title:"Logging in with a single shard",slug:"logging-in-with-a-single-shard",children:[]},{level:3,title:"Logging in with a fixed amount of shards",slug:"logging-in-with-a-fixed-amount-of-shards",children:[]},{level:3,title:"Using the recommended shard amount",slug:"using-the-recommended-shard-amount",children:[]}]},{level:2,title:"\u{1F4A1} Behavior of Shards",slug:"behavior-of-shards",children:[{level:3,title:"Managed servers",slug:"managed-servers",children:[]},{level:3,title:"Private messages",slug:"private-messages",children:[]},{level:3,title:"When do I need sharding?",slug:"when-do-i-need-sharding",children:[]}]},{level:2,title:"\u{1F304} Sharding for Very Large Bots",slug:"sharding-for-very-large-bots",children:[]}],path:"/wiki/advanced-topics/sharding.html",pathLocale:"/",extraFields:[]},{title:"Creating Channels, Invites, etc.",headers:[{level:2,title:"\u{1F4D5} Create Channels",slug:"create-channels",children:[]},{level:2,title:"\u{1F4D7} Create Webhooks",slug:"create-webhooks",children:[]},{level:2,title:"\u{1F4D8} Create Invites",slug:"create-invites",children:[]},{level:2,title:"\u{1F4D9} Create Servers",slug:"create-servers",children:[]}],path:"/wiki/basic-tutorials/creating-entities.html",pathLocale:"/",extraFields:[]},{title:"Embeds",headers:[{level:2,title:"\u{1F528} Creating an Embed",slug:"creating-an-embed",children:[]},{level:2,title:"\u{1F4F7} Supported Image Sources",slug:"supported-image-sources",children:[]},{level:2,title:"\u{1F512} Embed Limits",slug:"embed-limits",children:[]},{level:2,title:"\u2753 FAQ",slug:"faq",children:[{level:3,title:"What is the second parameter of setAuthor(...)?",slug:"what-is-the-second-parameter-of-setauthor",children:[]},{level:3,title:"What's the difference between an inline field and a normal one?",slug:"what-s-the-difference-between-an-inline-field-and-a-normal-one",children:[]},{level:3,title:"Can I change the placement of inline fields?",slug:"can-i-change-the-placement-of-inline-fields",children:[]},{level:3,title:"How can I format text in an embed?",slug:"how-can-i-format-text-in-an-embed",children:[]}]}],path:"/wiki/basic-tutorials/embeds.html",pathLocale:"/",extraFields:[]},{title:"Emojis and Reactions",headers:[{level:2,title:"\u{1F6B4}\u200D\u2642\uFE0F Unicode Emojis",slug:"unicode-emojis",children:[{level:3,title:"What are Unicode emojis?",slug:"what-are-unicode-emojis",children:[]},{level:3,title:"How to use them in messages",slug:"how-to-use-them-in-messages",children:[]},{level:3,title:"How to use them for reactions",slug:"how-to-use-them-for-reactions",children:[]}]},{level:2,title:"\u{1F938}\u200D\u2640\uFE0F Custom Emojis",slug:"custom-emojis",children:[{level:3,title:"What are custom emojis?",slug:"what-are-custom-emojis",children:[]},{level:3,title:"How to use them in messages",slug:"how-to-use-them-in-messages-1",children:[]},{level:3,title:"How to use them for reactions",slug:"how-to-use-them-for-reactions-1",children:[]},{level:3,title:"How to get the tag",slug:"how-to-get-the-tag",children:[]}]},{level:2,title:'\u{1F451} Javacord Emoji "Hierarchy"',slug:"javacord-emoji-hierarchy",children:[{level:3,title:"What is a KnownCustomEmoji?",slug:"what-is-a-knowncustomemoji",children:[]}]},{level:2,title:"\u{1F44C} Recommended libraries",slug:"recommended-libraries",children:[]}],path:"/wiki/basic-tutorials/emojis-and-reactions.html",pathLocale:"/",extraFields:[]},{title:"Gateway Intents",headers:[{level:2,title:"\u{1F4CB} List of Intents",slug:"list-of-intents",children:[]},{level:2,title:"\u{1F4A1} What Happens When I Disable Some Intents?",slug:"what-happens-when-i-disable-some-intents",children:[]},{level:2,title:"\u{1F451} Privileged Intents",slug:"privileged-intents",children:[]},{level:2,title:"\u2757 Notable Intents",slug:"notable-intents",children:[{level:3,title:"GUILD_PRESENCES",slug:"guild-presences",children:[]},{level:3,title:"GUILD_MEMBERS",slug:"guild-members",children:[]},{level:3,title:"MESSAGE_CONTENT",slug:"message-content",children:[]}]},{level:2,title:"\u2699\uFE0F Setting Intents",slug:"setting-intents",children:[{level:3,title:"Set All Non-Privileged Intents (Default)",slug:"set-all-non-privileged-intents-default",children:[]},{level:3,title:"Set All Non-Privileged Intents Except",slug:"set-all-non-privileged-intents-except",children:[]},{level:3,title:"Set All Intents",slug:"set-all-intents",children:[]},{level:3,title:"Set All Intents Except",slug:"set-all-intents-except",children:[]},{level:3,title:"Set Intents",slug:"set-intents",children:[]},{level:3,title:"Add Intents",slug:"add-intents",children:[]}]}],path:"/wiki/basic-tutorials/gateway-intents.html",pathLocale:"/",extraFields:[]},{title:"Glossary",headers:[],path:"/wiki/basic-tutorials/glossary.html",pathLocale:"/",extraFields:[]},{title:"Listeners",headers:[{level:2,title:"\u{1F468}\u200D\u{1F527} Creating listeners",slug:"creating-listeners",children:[{level:3,title:"Inline Listeners",slug:"inline-listeners",children:[]},{level:3,title:"In their own class",slug:"in-their-own-class",children:[]},{level:3,title:"Before logging in",slug:"before-logging-in",children:[]},{level:3,title:"Object listeners",slug:"object-listeners",children:[]}]},{level:2,title:"\u{1F4A3} Removing listeners",slug:"removing-listeners",children:[{level:3,title:"Using the returned ListenerManager",slug:"using-the-returned-listenermanager",children:[]},{level:3,title:"Using the removeListener(...) method",slug:"using-the-removelistener-method",children:[]}]}],path:"/wiki/basic-tutorials/listeners.html",pathLocale:"/",extraFields:[]},{title:"Logger Configuration",headers:[{level:2,title:"\u{1F948} Fallback Logger",slug:"fallback-logger",children:[]},{level:2,title:"\u{1F947} Using a Proper Logging Framework",slug:"using-a-proper-logging-framework",children:[{level:3,title:"Adding a Logging Framework",slug:"adding-a-logging-framework",children:[]},{level:3,title:"Configure Your Logging Framework",slug:"configure-your-logging-framework",children:[]},{level:3,title:"Logging the Relevant Shard",slug:"logging-the-relevant-shard",children:[]}]}],path:"/wiki/basic-tutorials/logger-config.html",pathLocale:"/",extraFields:[]},{title:"Using the MessageBuilder",headers:[{level:2,title:"\u{1F575}\uFE0F\u200D\u2640\uFE0F Example",slug:"example",children:[]},{level:2,title:"\u{1F4CD} Allowed Mentions",slug:"allowed-mentions",children:[]}],path:"/wiki/basic-tutorials/message-builder.html",pathLocale:"/",extraFields:[]},{title:"Running and Deploying your Bot",headers:[{level:2,title:"\u{1F477} Running from your IDE",slug:"running-from-your-ide",children:[{level:3,title:"IntelliJ IDEA",slug:"intellij-idea",children:[]},{level:3,title:"Eclipse",slug:"eclipse",children:[]}]},{level:2,title:"\u{1F4E6} Deploying and Running as a Standalone Application",slug:"deploying-and-running-as-a-standalone-application",children:[{level:3,title:"Building a Distribution with Gradle",slug:"building-a-distribution-with-gradle",children:[]},{level:3,title:"Building a Distribution with Maven",slug:"building-a-distribution-with-maven",children:[]},{level:3,title:"Running",slug:"running",children:[]}]},{level:2,title:"\u{1F4A9} Building a Fat Jar",slug:"building-a-fat-jar",children:[{level:3,title:"With Gradle",slug:"with-gradle",children:[]},{level:3,title:"With Maven",slug:"with-maven",children:[]}]}],path:"/wiki/basic-tutorials/running.html",pathLocale:"/",extraFields:[]},{title:"Completable Futures",headers:[{level:2,title:"\u{1F914} What the heck is a future?",slug:"what-the-heck-is-a-future",children:[]},{level:2,title:"\u{1F4D6} Methods",slug:"methods",children:[{level:3,title:"join()",slug:"join",children:[]},{level:3,title:"thenAccept(...)",slug:"thenaccept",children:[]},{level:3,title:"exceptionally(...)",slug:"exceptionally",children:[]},{level:3,title:"thenCompose()",slug:"thencompose",children:[]}]},{level:2,title:"\u{1F4DA} Further Read",slug:"further-read",children:[]}],path:"/wiki/essential-knowledge/completable-futures.html",pathLocale:"/",extraFields:[]},{title:"Lambdas",headers:[{level:2,title:"\u{1F4DA} Further Read",slug:"further-read",children:[]}],path:"/wiki/essential-knowledge/lambdas.html",pathLocale:"/",extraFields:[]},{title:"Optionals",headers:[{level:2,title:"\u{1F4AA} Motivation",slug:"motivation",children:[{level:3,title:"The old way of doing it",slug:"the-old-way-of-doing-it",children:[]},{level:3,title:"The new way of doing it",slug:"the-new-way-of-doing-it",children:[]}]},{level:2,title:"\u{1F4D6} Methods",slug:"methods",children:[{level:3,title:"get()",slug:"get",children:[]},{level:3,title:"isPresent()",slug:"ispresent",children:[]},{level:3,title:"orElse(...)",slug:"orelse",children:[]},{level:3,title:"ifPresent(...)",slug:"ifpresent",children:[]},{level:3,title:"filter(...)",slug:"filter",children:[]},{level:3,title:"map(...)",slug:"map",children:[]},{level:3,title:"flatMap(...)",slug:"flatmap",children:[]}]},{level:2,title:"\u{1F4DA} Further Read",slug:"further-read",children:[]}],path:"/wiki/essential-knowledge/optionals.html",pathLocale:"/",extraFields:[]},{title:"Introduction",headers:[{level:2,title:"\u{1F4DA} Structure of the wiki",slug:"structure-of-the-wiki",children:[]},{level:2,title:"\u{1F91D} Support",slug:"support",children:[]}],path:"/wiki/",pathLocale:"/",extraFields:[]},{title:"Creating a Bot Account",headers:[{level:2,title:"\u{1F4A1} Create a bot and get its token",slug:"create-a-bot-and-get-its-token",children:[]},{level:2,title:"\u2795 How to add a bot to your server",slug:"how-to-add-a-bot-to-your-server",children:[{level:3,title:"Use Javacord to create the invite link",slug:"use-javacord-to-create-the-invite-link",children:[]},{level:3,title:"Create the invite link manually",slug:"create-the-invite-link-manually",children:[]}]},{level:2,title:"\u{1F64B}\u200D\u2642\uFE0F Use the invite link",slug:"use-the-invite-link",children:[]}],path:"/wiki/getting-started/creating-a-bot-account.html",pathLocale:"/",extraFields:[]},{title:"Download / Installation",headers:[{level:2,title:"\u{1F4E6} Javacord Dependency",slug:"javacord-dependency",children:[]},{level:2,title:"\u{1F4DD} Optional Logger Dependency",slug:"optional-logger-dependency",children:[]}],path:"/wiki/getting-started/download-installation.html",pathLocale:"/",extraFields:[]},{title:"Frequently Asked Questions",headers:[{level:2,title:"Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?",slug:"q-why-do-i-receive-empty-no-content-messages-in-i-e-the-messagecreatelistener",children:[]},{level:2,title:"Q: What is ... in the code examples?",slug:"q-what-is-in-the-code-examples",children:[]},{level:2,title:"Q: Why is my code not working?",slug:"q-why-is-my-code-not-working",children:[{level:3,title:"How to properly ask a question to get fast support?",slug:"how-to-properly-ask-a-question-to-get-fast-support",children:[]}]},{level:2,title:"Q: What differs Javacord from JDA and D4J?",slug:"q-what-differs-javacord-from-jda-and-d4j",children:[]}],path:"/wiki/getting-started/faq.html",pathLocale:"/",extraFields:[]},{title:"Writing your first bot",headers:[{level:2,title:"\u2757 Enabling required intents",slug:"enabling-required-intents",children:[]},{level:2,title:"\u{1F511} Log the bot in",slug:"log-the-bot-in",children:[]},{level:2,title:"\u{1F442} Adding a listener",slug:"adding-a-listener",children:[]},{level:2,title:"\u{1F469}\u200D\u{1F527} Putting it all together",slug:"putting-it-all-together",children:[]}],path:"/wiki/getting-started/writing-your-first-bot.html",pathLocale:"/",extraFields:[]},{title:"Interaction Commands aka. Slash Commands",headers:[{level:2,title:"\u{1F4A1} Creating a Command",slug:"creating-a-command",children:[{level:3,title:"\u{1F4D4} Notes on creating commands:",slug:"notes-on-creating-commands",children:[]}]},{level:2,title:"\u2935\uFE0F Get your commands",slug:"get-your-commands",children:[]},{level:2,title:"\u{1F528} Updating Commands",slug:"updating-commands",children:[]},{level:2,title:"\u270D\uFE0F Bulk overwriting commands",slug:"bulk-overwriting-commands",children:[]},{level:2,title:"\u{1F46E}\u200D\u2642\uFE0F Permissions",slug:"permissions",children:[]},{level:2,title:"\u2757 Limits",slug:"limits",children:[{level:3,title:"Registering a command",slug:"registering-a-command",children:[]},{level:3,title:"General",slug:"general",children:[]}]}],path:"/wiki/basic-tutorials/interactions/commands.html",pathLocale:"/",extraFields:[]},{title:"Message Components",headers:[{level:2,title:"\u2754 What are components?",slug:"what-are-components",children:[]},{level:2,title:"\u{1F4A1} Sending a message with a component",slug:"sending-a-message-with-a-component",children:[]}],path:"/wiki/basic-tutorials/interactions/components.html",pathLocale:"/",extraFields:[]},{title:"Interactions",headers:[{level:2,title:"\u{1F4AC} Message Commands",slug:"message-commands",children:[]},{level:2,title:"\u2709\uFE0F Interaction Types",slug:"interaction-types",children:[]},{level:2,title:"\u267B\uFE0F Lifecycle",slug:"lifecycle",children:[]},{level:2,title:"\u{1F4C8} Advantages",slug:"advantages",children:[]},{level:2,title:"\u{1F916} Applications vs. Bots",slug:"applications-vs-bots",children:[]},{level:2,title:"\u{1F50D} See also",slug:"see-also",children:[]}],path:"/wiki/basic-tutorials/interactions/overview.html",pathLocale:"/",extraFields:[]},{title:"Responding to interactions",headers:[{level:2,title:"\u{1F4AC} Responding immediately after receiving an interaction.",slug:"responding-immediately-after-receiving-an-interaction",children:[]},{level:2,title:"\u{1F4AC} Responding after some time when receiving an interaction.",slug:"responding-after-some-time-when-receiving-an-interaction",children:[{level:3,title:"Sending followup messages",slug:"sending-followup-messages",children:[]}]},{level:2,title:"Responding with a Modal",slug:"responding-with-a-modal",children:[]},{level:2,title:"\u{1F4AC} SlashCommand interaction only response methods",slug:"slashcommand-interaction-only-response-methods",children:[{level:3,title:"How to know what slash command was invoked?",slug:"how-to-know-what-slash-command-was-invoked",children:[]},{level:3,title:"Respond to an AutoComplete interaction triggered from a SlashCommand",slug:"respond-to-an-autocomplete-interaction-triggered-from-a-slashcommand",children:[]}]},{level:2,title:"\u{1F4AC} Message Component interaction only response methods",slug:"message-component-interaction-only-response-methods",children:[{level:3,title:"A more complete example of how to respond to Component interactions",slug:"a-more-complete-example-of-how-to-respond-to-component-interactions",children:[]}]}],path:"/wiki/basic-tutorials/interactions/responding.html",pathLocale:"/",extraFields:[]},{title:"Eclipse + Maven",headers:[{level:2,title:"\u{1F527} Setup",slug:"setup",children:[]},{level:2,title:"\u{1F3C3}\u200D\u2640\uFE0F Run the code",slug:"run-the-code",children:[]}],path:"/wiki/getting-started/setup/eclipse-maven.html",pathLocale:"/",extraFields:[]},{title:"IntelliJ + Gradle",headers:[{level:2,title:"\u{1F527} Setup",slug:"setup",children:[]},{level:2,title:"\u{1F3C3}\u200D\u2640\uFE0F Run the code",slug:"run-the-code",children:[]}],path:"/wiki/getting-started/setup/intellij-gradle.html",pathLocale:"/",extraFields:[]},{title:"IntelliJ + Maven",headers:[{level:2,title:"\u{1F527} Setup",slug:"setup",children:[]},{level:2,title:"\u{1F3C3}\u200D\u2640\uFE0F Run the code",slug:"run-the-code",children:[]},{level:2,title:"\u{1F6A7} Possible problems",slug:"possible-problems",children:[]}],path:"/wiki/getting-started/setup/intellij-maven.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]}],_l=be(Ud),Wd=()=>_l;ci.webpackHot&&(__VUE_HMR_RUNTIME__.updateSearchIndex=e=>{_l.value=e});const qd=({searchIndex:e,routeLocale:t,query:n,maxSuggestions:i})=>{const r=fe(()=>e.value.filter(s=>s.pathLocale===t.value));return fe(()=>{const s=n.value.trim().toLowerCase();if(!s)return[];const o=[],l=(a,c)=>{Ts(s,[c.title])&&o.push({link:`${a.path}#${c.slug}`,title:a.title,header:c.title});for(const u of c.children){if(o.length>=i.value)return;l(a,u)}};for(const a of r.value){if(o.length>=i.value)break;if(Ts(s,[a.title,...a.extraFields])){o.push({link:a.path,title:a.title});continue}for(const c of a.headers){if(o.length>=i.value)break;l(a,c)}}return o})},Kd=e=>{const t=be(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},Jd=je({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:n,maxSuggestions:i}=ha(e),r=ai(),s=Wo(),o=Wd(),l=be(null),a=be(!1),c=be(""),u=fe(()=>{var x;return(x=t.value[s.value])!=null?x:{}}),d=qd({searchIndex:o,routeLocale:s,query:c,maxSuggestions:i}),{focusIndex:p,focusNext:g,focusPrev:y}=Kd(d);Vd({input:l,hotKeys:n});const A=fe(()=>a.value&&!!d.value.length),P=()=>{!A.value||y()},m=()=>{!A.value||g()},b=x=>{if(!A.value)return;const M=d.value[x];!M||r.push(M.link).then(()=>{c.value="",p.value=0})};return()=>ae("form",{class:"search-box",role:"search"},[ae("input",{ref:l,type:"search",placeholder:u.value.placeholder,autocomplete:"off",spellcheck:!1,value:c.value,onFocus:()=>a.value=!0,onBlur:()=>a.value=!1,onInput:x=>c.value=x.target.value,onKeydown:x=>{switch(x.key){case"ArrowUp":{P();break}case"ArrowDown":{m();break}case"Enter":{x.preventDefault(),b(p.value);break}}}}),A.value&&ae("ul",{class:"suggestions",onMouseleave:()=>p.value=-1},d.value.map(({link:x,title:M,header:z},$)=>ae("li",{class:["suggestion",{focus:p.value===$}],onMouseenter:()=>p.value=$,onMousedown:()=>b($)},ae("a",{href:x,onClick:D=>D.preventDefault()},[ae("span",{class:"page-title"},M),z&&ae("span",{class:"page-header"},`> ${z}`)]))))])}});const Gd={},Qd=["s","/"],Yd=5;var Zd=yt({enhance({app:e}){e.component("SearchBox",t=>ae(Jd,{locales:Gd,hotKeys:Qd,maxSuggestions:Yd,...t}))}}),Xd={enhance:({app:e})=>{e.component("LatestVersion",re(()=>N(()=>import("./LatestVersion.3ddaef1d.js"),[])))}};const Ci=[kf,Lf,Af,zf,qf,Yf,jd,Zd,Xd],eh=[["v-8daa1a0e","/",{title:""},["/index.html","/README.md"]],["v-92e5302e","/imprint.html",{title:"Imprint"},["/imprint","/imprint.md"]],["v-b106f6ee","/privacy-policy.html",{title:"Privacy Policy"},["/privacy-policy","/privacy-policy.md"]],["v-348a4efb","/wiki/advanced-topics/bot-lifecycle.html",{title:"Bot Lifecycle"},["/wiki/advanced-topics/bot-lifecycle","/wiki/advanced-topics/bot-lifecycle.md"]],["v-32da090a","/wiki/advanced-topics/entity-cache.html",{title:"Entity Cache"},["/wiki/advanced-topics/entity-cache","/wiki/advanced-topics/entity-cache.md"]],["v-2037d84f","/wiki/advanced-topics/performance-tweaks.html",{title:"Performance Tweaks"},["/wiki/advanced-topics/performance-tweaks","/wiki/advanced-topics/performance-tweaks.md"]],["v-0b11c848","/wiki/advanced-topics/playing-audio.html",{title:"Playing Audio"},["/wiki/advanced-topics/playing-audio","/wiki/advanced-topics/playing-audio.md"]],["v-47a9d05a","/wiki/advanced-topics/proxies.html",{title:"Proxies"},["/wiki/advanced-topics/proxies","/wiki/advanced-topics/proxies.md"]],["v-fc3b23ca","/wiki/advanced-topics/ratelimits.html",{title:"Ratelimits"},["/wiki/advanced-topics/ratelimits","/wiki/advanced-topics/ratelimits.md"]],["v-595301cf","/wiki/advanced-topics/sharding.html",{title:"Sharding"},["/wiki/advanced-topics/sharding","/wiki/advanced-topics/sharding.md"]],["v-5fd376fa","/wiki/basic-tutorials/creating-entities.html",{title:"Creating Channels, Invites, etc."},["/wiki/basic-tutorials/creating-entities","/wiki/basic-tutorials/creating-entities.md"]],["v-37293ae0","/wiki/basic-tutorials/embeds.html",{title:"Embeds"},["/wiki/basic-tutorials/embeds","/wiki/basic-tutorials/embeds.md"]],["v-13e99c06","/wiki/basic-tutorials/emojis-and-reactions.html",{title:"Emojis and Reactions"},["/wiki/basic-tutorials/emojis-and-reactions","/wiki/basic-tutorials/emojis-and-reactions.md"]],["v-e7aba52c","/wiki/basic-tutorials/gateway-intents.html",{title:"Gateway Intents"},["/wiki/basic-tutorials/gateway-intents","/wiki/basic-tutorials/gateway-intents.md"]],["v-5b369fbc","/wiki/basic-tutorials/glossary.html",{title:"Glossary"},["/wiki/basic-tutorials/glossary","/wiki/basic-tutorials/glossary.md"]],["v-4256bfd9","/wiki/basic-tutorials/listeners.html",{title:"Listeners"},["/wiki/basic-tutorials/listeners","/wiki/basic-tutorials/listeners.md"]],["v-33173f0e","/wiki/basic-tutorials/logger-config.html",{title:"Logger Configuration"},["/wiki/basic-tutorials/logger-config","/wiki/basic-tutorials/logger-config.md"]],["v-29bd20c3","/wiki/basic-tutorials/message-builder.html",{title:"Using the MessageBuilder"},["/wiki/basic-tutorials/message-builder","/wiki/basic-tutorials/message-builder.md"]],["v-8012cfce","/wiki/basic-tutorials/running.html",{title:"Running and Deploying your Bot"},["/wiki/basic-tutorials/running","/wiki/basic-tutorials/running.md"]],["v-c85a18b4","/wiki/essential-knowledge/completable-futures.html",{title:"Completable Futures"},["/wiki/essential-knowledge/completable-futures","/wiki/essential-knowledge/completable-futures.md"]],["v-22528d43","/wiki/essential-knowledge/lambdas.html",{title:"Lambdas"},["/wiki/essential-knowledge/lambdas","/wiki/essential-knowledge/lambdas.md"]],["v-56bee89c","/wiki/essential-knowledge/optionals.html",{title:"Optionals"},["/wiki/essential-knowledge/optionals","/wiki/essential-knowledge/optionals.md"]],["v-15814726","/wiki/",{title:"Introduction"},["/wiki/index.html","/wiki/getting-started/","/wiki/getting-started/README.md"]],["v-5628c715","/wiki/getting-started/creating-a-bot-account.html",{title:"Creating a Bot Account"},["/wiki/getting-started/creating-a-bot-account","/wiki/getting-started/creating-a-bot-account.md"]],["v-7d129412","/wiki/getting-started/download-installation.html",{title:"Download / Installation"},["/wiki/getting-started/download-installation","/wiki/getting-started/download-installation.md"]],["v-6bd28c40","/wiki/getting-started/faq.html",{title:"Frequently Asked Questions"},["/wiki/getting-started/faq","/wiki/getting-started/faq.md"]],["v-7bf86adb","/wiki/getting-started/writing-your-first-bot.html",{title:"Writing your first bot"},["/wiki/getting-started/writing-your-first-bot","/wiki/getting-started/writing-your-first-bot.md"]],["v-36c441c2","/wiki/basic-tutorials/interactions/commands.html",{title:"Interaction Commands aka. Slash Commands"},["/wiki/basic-tutorials/interactions/commands","/wiki/basic-tutorials/interactions/commands.md"]],["v-35e5cc98","/wiki/basic-tutorials/interactions/components.html",{title:"Message Components"},["/wiki/basic-tutorials/interactions/components","/wiki/basic-tutorials/interactions/components.md"]],["v-16fe8d71","/wiki/basic-tutorials/interactions/overview.html",{title:"Interactions"},["/wiki/basic-tutorials/interactions/overview","/wiki/basic-tutorials/interactions/overview.md"]],["v-00728006","/wiki/basic-tutorials/interactions/responding.html",{title:"Responding to interactions"},["/wiki/basic-tutorials/interactions/responding","/wiki/basic-tutorials/interactions/responding.md"]],["v-22de0aba","/wiki/getting-started/setup/eclipse-maven.html",{title:"Eclipse + Maven"},["/wiki/getting-started/setup/eclipse-maven","/wiki/getting-started/setup/eclipse-maven.md"]],["v-6d1d378b","/wiki/getting-started/setup/intellij-gradle.html",{title:"IntelliJ + Gradle"},["/wiki/getting-started/setup/intellij-gradle","/wiki/getting-started/setup/intellij-gradle.md"]],["v-0ae94875","/wiki/getting-started/setup/intellij-maven.html",{title:"IntelliJ + Maven"},["/wiki/getting-started/setup/intellij-maven","/wiki/getting-started/setup/intellij-maven.md"]],["v-3706649a","/404.html",{title:""},["/404"]]];var th=()=>eh.reduce((e,[t,n,i,r])=>(e.push({name:t,path:n,component:ts,meta:i},...r.map(s=>({path:s,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:ts}]),nh=Su,ih=()=>{const e=gf({history:nh(nu(mt.value.base)),routes:th(),scrollBehavior:(t,n,i)=>i||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var i;(t.path!==n.path||n===et)&&([nt.value]=await Promise.all([Lt.resolvePageData(t.name),(i=jo[t.name])==null?void 0:i.__asyncLoader()]))}),e},rh=e=>{e.component("ClientOnly",fu),e.component("Content",du)},sh=(e,t)=>{const n=fe(()=>Lt.resolveRouteLocale(mt.value.locales,t.currentRoute.value.path)),i=fe(()=>Lt.resolveSiteLocaleData(mt.value,n.value)),r=fe(()=>Lt.resolvePageFrontmatter(nt.value)),s=fe(()=>Lt.resolvePageHeadTitle(nt.value,i.value)),o=fe(()=>Lt.resolvePageHead(s.value,r.value,i.value)),l=fe(()=>Lt.resolvePageLang(nt.value));return e.provide(dr,n),e.provide(qo,i),e.provide(zo,r),e.provide(lu,s),e.provide(Vo,o),e.provide(Uo,l),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>r.value},$head:{get:()=>o.value},$headTitle:{get:()=>s.value},$lang:{get:()=>l.value},$page:{get:()=>nt.value},$routeLocale:{get:()=>n.value},$site:{get:()=>mt.value},$siteLocale:{get:()=>i.value},$withBase:{get:()=>hu}}),{pageData:nt,pageFrontmatter:r,pageHead:o,pageHeadTitle:s,pageLang:l,routeLocale:n,siteData:mt,siteLocaleData:i}},oh=()=>{const e=mr(),t=ou(),n=au(),i=be([]),r=()=>{t.value.forEach(o=>{const l=lh(o);l&&i.value.push(l)})},s=()=>{document.documentElement.lang=n.value,i.value.forEach(o=>{o.parentNode===document.head&&document.head.removeChild(o)}),i.value.splice(0,i.value.length),t.value.forEach(o=>{const l=ah(o);l!==null&&(document.head.appendChild(l),i.value.push(l))})};Ot(uu,s),ot(()=>{r(),s(),it(()=>e.path,()=>s())})},lh=([e,t,n=""])=>{const i=Object.entries(t).map(([l,a])=>ve(a)?`[${l}="${a}"]`:a===!0?`[${l}]`:"").join(""),r=`head > ${e}${i}`;return Array.from(document.querySelectorAll(r)).find(l=>l.innerText===n)||null},ah=([e,t,n])=>{if(!ve(e))return null;const i=document.createElement(e);return No(t)&&Object.entries(t).forEach(([r,s])=>{ve(s)?i.setAttribute(r,s):s===!0&&i.setAttribute(r,"")}),ve(n)&&i.appendChild(document.createTextNode(n)),i},ch=Qc,uh=async()=>{var n;const e=ch({name:"VuepressApp",setup(){var i;oh();for(const r of Ci)(i=r.setup)==null||i.call(r);return()=>[ae(rl),...Ci.flatMap(({rootComponents:r=[]})=>r.map(s=>ae(s)))]}}),t=ih();rh(e),sh(e,t);for(const i of Ci)await((n=i.enhance)==null?void 0:n.call(i,{app:e,router:t,siteData:mt}));return e.use(t),{app:e,router:t}};uh().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{mh as $,cu as A,bh as B,wh as C,yn as D,ae as E,Oe as F,hu as G,fu as H,be as I,it as J,fh as K,gh as L,iu as M,nu as N,ai as O,ve as P,Sd as Q,ot as R,Jn as S,fr as T,Cn as U,Eh as V,No as W,yh as X,sr as Y,ni as Z,al as _,So as a,lr as a0,Id as a1,ye as b,Ro as c,uh as createVueApp,cr as d,hh as e,je as f,vl as g,Ge as h,su as i,fe as j,Q as k,dh as l,ph as m,mr as n,ii as o,ha as p,To as q,za as r,dc as s,Pl as t,Wo as u,go as v,Ca as w,tu as x,vh as y,_h as z}; diff --git a/assets/back-to-top.8efcbe56.svg b/assets/back-to-top.8efcbe56.svg new file mode 100644 index 0000000..8323678 --- /dev/null +++ b/assets/back-to-top.8efcbe56.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/bot-lifecycle.html.89b6bce4.js b/assets/bot-lifecycle.html.89b6bce4.js new file mode 100644 index 0000000..8bad8cd --- /dev/null +++ b/assets/bot-lifecycle.html.89b6bce4.js @@ -0,0 +1,7 @@ +import{_ as a,r as o,o as c,c as i,a as e,b as d,w as l,e as n,d as t}from"./app.151ccb98.js";var r="/assets/lifecycle-state-diagram.5bc421c4.svg";const p={},u=n('

Bot Lifecycle

It's important to know the life-cycle of a discord bot to properly handle disconnects. The following state diagram shows the 4 states a bot can have:

\u{1F4A1} The four states

Connected

The bot is connected to the websocket and receives all events.

Disconnected

The bot is not connected to the websocket and receives no events. It's not uncommon for a bot to occasionally lose connection. This can have various reasons, for example:

  • Your bot lost its internet connection
  • Discord restarted the gateway server you are currently connected to
  • A plane crashed into Discord's data center

The bot will periodically try to resume/reconnect to the websocket. It will start with a small frequency and increase it with every failed reconnect attempt. You can modify the reconnect delay with the DiscordApi#setReconnectDelay(...) method. The following example code would increase the delay linearly. The 1st attempt would be delayed for 2 seconds, the 2nd attempt for 4 seconds, the 3rd attempts for 6 seconds, ...

api.setReconnectDelay(attempt -> attempt * 2);
+

Important: Bots can only reconnect 1000 times in a 24-hour period (every ~90 seconds). This limit is global and across all shards. Upon hitting this limit, all active sessions for the bot will be terminated, the bot's token will be reset, and you will receive an email notification. This is the reason Javacord increases the reconnect delay with every attempt.

By default, the $default_delay$ formula below is used to calculate the reconnect delay

$$ default_delay(a) = \\lfloor a^{1.5} - \\frac{a^{1.5}}{\\frac{1}{(0.1 \\cdot a)} + 1} \\rceil $$

with $a$ being the attempt.

The formula will generate the following reconnect delay:

AttemptDelay
11
22
34
46
57
......
1016
1523
2030
......
5059
10091
150115
......

Resuming

Resuming is only possible for a short time after being disconnected. If the bot can successfully resume the connection, you will not miss any events. Your bot will receive all events you missed while being disconnected. The cache gets updated accordingly.

Reconnecting

If your bot reconnects (not resumes!), the whole cache gets wiped, and you will not receive any missed events.

What does this mean?

`,22),h=t("References to entities (e.g. a "),m=e("code",null,"Server",-1),f=t(", "),b=e("code",null,"User",-1),v=t(", "),g=e("code",null,"Channel",-1),y=t(", ...) will be outdated. This is why you should never store entities, but the id instead. See "),k=t("Entity Cache"),w=t("."),_=e("li",null,"You will miss events. There's no way to receive the missed events.",-1),x=e("li",null,[t("Listeners attached to entities will "),e("strong",null,"not"),t(" be affected, because they are bound to the entity's id, not the object itself.")],-1),T=n(`

\u{1F48A} How to handle disconnects

For most bots, there's nothing you have to do. All registered listeners are reconnect-resistant, which means if your bot is only reacting to events, it will work fine after a restart. For example, the following code will not be affected by a reconnect (besides maybe some missed !ping messages):

api.addMessageCreateListener(event -> {
+    if (event.getMessage().getContent().equalsIgnoreCase("!ping")) {
+        event.getChannel().sendMessage("Pong!");
+    }
+});
+

In case you want to handle reconnects (e.g. fetch the message history to detect missed messages), there are special connection-related listeners which can be used to track the state of the bot:

  • LostConnectionListener
  • ReconnectListener
  • ResumeListener
`,5);function C(L,R){const s=o("RouterLink");return c(),i("div",null,[u,e("ul",null,[e("li",null,[h,m,f,b,v,g,y,d(s,{to:"/wiki/advanced-topics/entity-cache.html#how-long-are-cached-entities-valid"},{default:l(()=>[k]),_:1}),w]),_,x]),T])}var I=a(p,[["render",C],["__file","bot-lifecycle.html.vue"]]);export{I as default}; diff --git a/assets/bot-lifecycle.html.bc8486a5.js b/assets/bot-lifecycle.html.bc8486a5.js new file mode 100644 index 0000000..238e07c --- /dev/null +++ b/assets/bot-lifecycle.html.bc8486a5.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-348a4efb","path":"/wiki/advanced-topics/bot-lifecycle.html","title":"Bot Lifecycle","lang":"en-US","frontmatter":{"keywords":["resume","reconnect","lifecycle","unavailable"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4A1} The four states","slug":"the-four-states","children":[{"level":3,"title":"Connected","slug":"connected","children":[]},{"level":3,"title":"Disconnected","slug":"disconnected","children":[]},{"level":3,"title":"Resuming","slug":"resuming","children":[]},{"level":3,"title":"Reconnecting","slug":"reconnecting","children":[]}]},{"level":2,"title":"\u{1F48A} How to handle disconnects","slug":"how-to-handle-disconnects","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/bot-lifecycle.md"}');export{e as data}; diff --git a/assets/calculate-permissions.31ca902e.png b/assets/calculate-permissions.31ca902e.png new file mode 100644 index 0000000..4abf79f Binary files /dev/null and b/assets/calculate-permissions.31ca902e.png differ diff --git a/assets/click-bot.149192d1.png b/assets/click-bot.149192d1.png new file mode 100644 index 0000000..1be7092 Binary files /dev/null and b/assets/click-bot.149192d1.png differ diff --git a/assets/commands.html.2011a310.js b/assets/commands.html.2011a310.js new file mode 100644 index 0000000..d30405e --- /dev/null +++ b/assets/commands.html.2011a310.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-36c441c2","path":"/wiki/basic-tutorials/interactions/commands.html","title":"Interaction Commands aka. Slash Commands","lang":"en-US","frontmatter":{"keywords":["interaction","slash command"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4A1} Creating a Command","slug":"creating-a-command","children":[{"level":3,"title":"\u{1F4D4} Notes on creating commands:","slug":"notes-on-creating-commands","children":[]}]},{"level":2,"title":"\u2935\uFE0F Get your commands","slug":"get-your-commands","children":[]},{"level":2,"title":"\u{1F528} Updating Commands","slug":"updating-commands","children":[]},{"level":2,"title":"\u270D\uFE0F Bulk overwriting commands","slug":"bulk-overwriting-commands","children":[]},{"level":2,"title":"\u{1F46E}\u200D\u2642\uFE0F Permissions","slug":"permissions","children":[]},{"level":2,"title":"\u2757 Limits","slug":"limits","children":[{"level":3,"title":"Registering a command","slug":"registering-a-command","children":[]},{"level":3,"title":"General","slug":"general","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/interactions/commands.md"}');export{e as data}; diff --git a/assets/commands.html.aeac4b05.js b/assets/commands.html.aeac4b05.js new file mode 100644 index 0000000..5f0bd35 --- /dev/null +++ b/assets/commands.html.aeac4b05.js @@ -0,0 +1,101 @@ +import{_ as n,o as s,c as a,e}from"./app.151ccb98.js";const t={},o=e(`

Interaction Commands aka. Slash Commands

INFO

There are a lot of convenient methods which aim to make your life easier with i.e., not being able to have an invalid configuration of your builder. Therefore, the following examples will only show the usage with the convenient methods.

\u{1F4A1} Creating a Command

INFO

There are 2 different types of Commands:

  • Global | Available for every Server once your Bot gets invited: Created with createGlobal(DiscordApi).
  • Server | Only available on the specific Server: Created with createForServer(Server).

Let's get started with the most basic command, a ping command.

SlashCommand command = SlashCommand.with("ping", "Checks the functionality of this command")
+    .createGlobal(api)
+    .join();
+

That's all you have to do!

Let's have a look at a more complex command which involves nearly all possibilities:

SlashCommand command =
+        SlashCommand.with("channel", "A command dedicated to channels",
+            Arrays.asList(
+                SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND_GROUP, "edit", "Edits a channel",
+                    Arrays.asList(
+                        SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND, "allow", "Allows a permission to a user for a channel",
+                            Arrays.asList(
+                                SlashCommandOption.create(SlashCommandOptionType.CHANNEL, "channel", "The channel to modify", true),
+                                SlashCommandOption.create(SlashCommandOptionType.USER, "user", "The user which permissions should be changed", true),
+                                SlashCommandOption.createWithChoices(SlashCommandOptionType.DECIMAL, "permission", "The permission to allow", true,
+                                    Arrays.asList(
+                                        SlashCommandOptionChoice.create("manage", 0),
+                                        SlashCommandOptionChoice.create("show", 1)))
+        ))))))
+        .createGlobal(api)
+        .join();
+

Let that sink in first!

What are we doing here?

  1. We create a base command called channel.
  2. It has a SUB_COMMAND_GROUP called edit which basically is just a folder where you can put your commands in.
  3. There's a SUB_COMMAND called allow which is our actual command. Therefore, our complete argument looks like channel edit allow.
  4. The SUB_COMMAND has 3 arguments:
    1. The channel which should be edited.
    2. The user which permissions should be changed.
    3. A predefined list of available permissions the command executor can choose of.

\u{1F4D4} Notes on creating commands:

The REQUIRED attribute

You can only mark the last argument as being not required. This means it can be optionally set by the command executor. In the above example you could i.e. set the PERMISSIONS argument to false.

Command structure

Your command has to follow these structures in order to be successfully created:

Command structure
VALID
+
+command
+|
+|__ subcommand
+|
+|__ subcommand
+
+----
+
+command
+|
+|__ subcommand-group
+    |
+    |__ subcommand
+|
+|__ subcommand-group
+    |
+    |__ subcommand
+
+----
+
+VALID
+
+command
+|
+|__ subcommand-group
+    |
+    |__ subcommand
+|
+|__ subcommand
+
+-------
+
+INVALID
+
+
+command
+|
+|__ subcommand-group
+    |
+    |__ subcommand-group
+|
+|__ subcommand-group
+    |
+    |__ subcommand-group
+
+----
+
+INVALID
+
+command
+|
+|__ subcommand
+    |
+    |__ subcommand-group
+|
+|__ subcommand
+    |
+    |__ subcommand-group
+

\u2935\uFE0F Get your commands

All global commands:

Set<SlashCommand> commands = api.getGlobalSlashCommands().join();
+

All commands only available on a single server:

Server server = ...;
+Set<SlashCommand> commands = api.getServerSlashCommands(server).join();
+

WARNING

Getting all commands from a server only contains the commands you have created on this specific server. Therefore, the returned list does not include any global command!

\u{1F528} Updating Commands

When updating your commands you only have to include what you actually want to change. The following updater will change the previous created command and change its base name from channel to channels.

SlashCommand updatedCommand =
+            new SlashCommandUpdater(commandId)
+                .setName("channels")
+                .updateGlobal(api)
+                .join();
+

\u270D\uFE0F Bulk overwriting commands

If you have to update / create multiple commands at once it advised to use the batch updater to only have to do 1 request.

DiscordApi api = ...;
+
+Set<SlashCommandBuilder> builders = new HashSet<>();
+builders.add(new SlashCommandBuilder().setName("server").setDescription("A command for the server"));
+builders.add(new SlashCommandBuilder().setName("permission").setDescription("A command for permissions"));
+                                
+api.bulkOverwriteGlobalApplicationCommands(builders).join();
+

\u{1F46E}\u200D\u2642\uFE0F Permissions

Permissions exist to enable / disable the usage of your commands for certain things. These things may be:

  • Permissions
  • DMs

When you create a command you can specify which permissions are required to use it. In addition to the required permissions, you can also specify whether the command should be available in DMs.

SlashCommand.with("ping","Ping!")
+    .setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR, PermissionType.BAN_MEMBERS)
+    //.setDefaultDisabled() Effectively the same as setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR) but this will lead to the default type by Discord.
+    .setEnabledInDms(false)
+    .createGlobal(api)
+    .join();
+

INFO

Once your bot has been invited to a server, you can not change the permissions afterwards on this server. Then it's up to the server administrators / owner to correctly set up the commands for users / roles / channels.

\u2757 Limits

Registering a command

  • Server commands are specific to the server you specify when making them. Server commands are not available in DMs. Command names are unique per application within each scope (global and server). That means:
  • Your app cannot have two global commands with the same name
  • Your app cannot have two server commands within the same name on the same guild
  • Your app can have a global and guild command with the same name
  • Multiple apps can have commands with the same names

General

  • An app can have up to 100 top-level global commands with unique names
  • An app can have up to an additional 100 server commands per server
  • An app can have up to 25 subcommand groups on a top-level command
  • An app can have up to 25 subcommands within a subcommand group
  • Commands can have up to 25 options
  • Options can have up to 25 choices
  • Maximum of 4000 characters for combined name, description, and value properties for each command and its subcommands and groups
  • Limitations on nesting subcommands and groups
  • Global rate limit of 200 slash command creates per day per server
`,42),i=[o];function c(p,l){return s(),a("div",null,i)}var d=n(t,[["render",c],["__file","commands.html.vue"]]);export{d as default}; diff --git a/assets/completable-futures.html.4bcb806c.js b/assets/completable-futures.html.4bcb806c.js new file mode 100644 index 0000000..2b81daf --- /dev/null +++ b/assets/completable-futures.html.4bcb806c.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-c85a18b4","path":"/wiki/essential-knowledge/completable-futures.html","title":"Completable Futures","lang":"en-US","frontmatter":{"keywords":["CompletableFuture","exceptionally","ExceptionLogger","join","thenAcceptAsync"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F914} What the heck is a future?","slug":"what-the-heck-is-a-future","children":[]},{"level":2,"title":"\u{1F4D6} Methods","slug":"methods","children":[{"level":3,"title":"join()","slug":"join","children":[]},{"level":3,"title":"thenAccept(...)","slug":"thenaccept","children":[]},{"level":3,"title":"exceptionally(...)","slug":"exceptionally","children":[]},{"level":3,"title":"thenCompose()","slug":"thencompose","children":[]}]},{"level":2,"title":"\u{1F4DA} Further Read","slug":"further-read","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/essential-knowledge/completable-futures.md"}');export{e as data}; diff --git a/assets/completable-futures.html.7db4d585.js b/assets/completable-futures.html.7db4d585.js new file mode 100644 index 0000000..3113f84 --- /dev/null +++ b/assets/completable-futures.html.7db4d585.js @@ -0,0 +1,72 @@ +import{_ as c,r as e,o as i,c as u,a as s,b as a,w as l,d as n,e as o}from"./app.151ccb98.js";const r={},k=s("h1",{id:"completable-futures",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#completable-futures","aria-hidden":"true"},"#"),n(" Completable Futures")],-1),d={class:"custom-container warning"},m=s("p",{class:"custom-container-title"},"WARNING",-1),h=n("This tutorial assumes that you are familiar with lambda expressions. Take a look at the "),v=n("lambda introduction"),g=n(" first, if you are not!"),f=n("As Javacord is heavily multithreaded, you must understand the concept of "),b={href:"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html",target:"_blank",rel:"noopener noreferrer"},w=n("Futures"),y=n(" in general, as well as their most common implementation, the "),_={href:"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html",target:"_blank",rel:"noopener noreferrer"},x=n("CompletableFuture"),q=n(". This little introduction gives you a quick overview of the basics you need to know in order to work with Futures."),T=o(`

\u{1F914} What the heck is a future?

A future is basically a wrapper, that will contain a value in the future but might not contain it right now. This is useful, if a method call requires some time and should not block the execution of your current code. You can easily see the difference with a primitive speed comparison:

long currentTime = System.currentTimeMillis();
+channel.sendMessage("Test 1");
+channel.sendMessage("Test 2");
+channel.sendMessage("Test 3");
+channel.sendMessage("Test 4");
+channel.sendMessage("Test 5");
+// Prints "4 ms"
+System.out.println((System.currentTimeMillis() - currentTime) + " ms");
+
long currentTime = System.currentTimeMillis();
+channel.sendMessage("Test 1").join();
+channel.sendMessage("Test 2").join();
+channel.sendMessage("Test 3").join();
+channel.sendMessage("Test 4").join();
+channel.sendMessage("Test 5").join();
+// Prints "894 ms"
+System.out.println((System.currentTimeMillis() - currentTime) + " ms");
+

TIP

join() blocks the current thread until the method finished. This will be explained later.

\u{1F4D6} Methods

join()

The join method blocks the current thread until the method finished. It returns the method's result or throws a CompletionException if anything failed.

The following example would create a new text channel in a given server and sends a message directly afterwards.

// Create the channel
+ServerTextChannel channel = new ServerTextChannelBuilder(server)
+    .setName("new-channel")
+    .create()
+    .join();
+// Send a message in the new channel
+Message message = channel.sendMessage("First!").join();
+// Adds an reaction to the message. Even though this method doesn't return anything,
+// join() ensures, that an exception is thrown in case something went wrong
+message.addReaction("\u{1F44D}").join();
+

DANGER

You should avoid join() for methods which will be called frequently.

TIP

While join() can become a performance issue when you call it very frequently, it is very convenient to use and easy to understand. If you are new to programming and just want to get your first bot working, this is a good method to start with.

Once you gathered more experience, we highly advise against using join as it negatively impacts your bot's performance!

thenAccept(...)

The thenAccept method accepts a Consumer, that consumes the result of the method and is executed asynchronously. It is the method you usually want to use most of the time.

The following example would create a new text channel in a given server and send a message directly afterwards.

new ServerTextChannelBuilder(server)
+    .setName("new-channel")
+    .create()
+    .thenAccept(channel -> {
+        channel.sendMessage("First!").thenAccept(message -> {
+            message.addReaction("\u{1F44D}");
+        });
+    });
+

DANGER

The example code above has a major problem: Any exception that might occur will be completely ignored. This makes it very hard to find bugs.

For example, if your bot doesn't have the permissions to create a new channel, it will just fail silently.

exceptionally(...)

The exceptionally method accepts a Function as parameter, which consumes possible exceptions and returns a fallback value.

The following example would create a new text channel in a given server and send a message directly afterwards. If something fails (e.g., if the bot isn't allowed to create a text channel in the server), it will log an exception.

new ServerTextChannelBuilder(server)
+    .setName("new-channel")
+    .create()
+    .thenAccept(channel -> {
+        channel.sendMessage("First!").thenAccept(message -> {
+            message.addReaction("\u{1F44D}").exceptionally(e -> {
+                e.printStackTrace(); // Adding the reaction failed
+                return null;
+            });
+        }).exceptionally(e -> {
+            e.printStackTrace(); // Message sending failed
+            return null;
+        });
+    }).exceptionally(e -> {
+        e.printStackTrace(); // Channel creation failed    
+        return null;
+    });
+

Wow! This looks ugly \u{1F92E}. But worry not! There are many options to improve this code!

To make things simpler for you, Javacord has the ExceptionLogger class, which can be used here. It logs every exception you didn't catch manually.

new ServerTextChannelBuilder(server)
+    .setName("new-channel")
+    .create()
+    .thenAccept(channel -> {
+        channel.sendMessage("First!").thenAccept(message -> {
+            message.addReaction("\u{1F44D}").exceptionally(ExceptionLogger.get());
+        }).exceptionally(ExceptionLogger.get());
+    }).exceptionally(ExceptionLogger.get());
+

Okay! This is at least a little better, but still not really perfect \u{1F914}.

thenCompose()

`,26),j=n("The "),C=s("code",null,"thenCompose",-1),F=n(" methods allows you to chain futures. It takes a "),M={href:"https://docs.oracle.com/javase/8/docs/api/java/util/function/Function.html",target:"_blank",rel:"noopener noreferrer"},S=n("Function"),A=n(" as parameter, that consumes the future's value and expects a new future to be returned."),N=o(`

The example to create a text channel can now be written like this:

new ServerTextChannelBuilder(server)
+        .setName("new-channel")
+        .create() 
+        .thenCompose(channel -> channel.sendMessage("First!"))
+        .thenCompose(message -> message.addReaction("\u{1F44D}"))
+        .exceptionally(ExceptionLogger.get());
+

Finally \u{1F389}! Now we only need a single exceptionally(...) call at the end. We also got rid of the nested callbacks (usually referred to as "callback hell").

For better understanding, here's the example with comments that tell you the type at each line:

new ServerTextChannelBuilder(server) // ServerTextChannelBuilder
+        .setName("new-channel") // ServerTextChannelBuilder
+        .create() // CompletableFuture<ServerTextChannel>
+        .thenCompose(channel -> channel.sendMessage("First!")) // CompletableFuture<Message>
+        .thenCompose(message -> message.addReaction("\u{1F44D}")) // CompletableFuture<Void>
+        .exceptionally(ExceptionLogger.get()); // CompletableFuture<Void>
+

\u{1F4DA} Further Read

`,6),E=n("This tutorial only focuses on the absolute basics. For a more detailed introduction to CompletableFutures, you can take a look at "),B={href:"https://www.callicoder.com/java-8-completablefuture-tutorial/",target:"_blank",rel:"noopener noreferrer"},R=n("this tutorial"),I=n("."),L=n("You should also take a look at the JavaDoc for a complete list of methods: "),V={href:"https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html",target:"_blank",rel:"noopener noreferrer"},W=n("CompletableFuture JavaDoc"),D=n(".");function J(P,G){const p=e("RouterLink"),t=e("ExternalLinkIcon");return i(),u("div",null,[k,s("div",d,[m,s("p",null,[h,a(p,{to:"/wiki/essential-knowledge/lambdas/"},{default:l(()=>[v]),_:1}),g])]),s("p",null,[f,s("a",b,[w,a(t)]),y,s("a",_,[x,a(t)]),q]),T,s("p",null,[j,C,F,s("a",M,[S,a(t)]),A]),N,s("p",null,[E,s("a",B,[R,a(t)]),I]),s("p",null,[L,s("a",V,[W,a(t)]),D])])}var O=c(r,[["render",J],["__file","completable-futures.html.vue"]]);export{O as default}; diff --git a/assets/components.html.604ea486.js b/assets/components.html.604ea486.js new file mode 100644 index 0000000..14b111c --- /dev/null +++ b/assets/components.html.604ea486.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-35e5cc98","path":"/wiki/basic-tutorials/interactions/components.html","title":"Message Components","lang":"en-US","frontmatter":{"keywords":["interaction","component","button","actionrow","selectmenus"]},"excerpt":"","headers":[{"level":2,"title":"\u2754 What are components?","slug":"what-are-components","children":[]},{"level":2,"title":"\u{1F4A1} Sending a message with a component","slug":"sending-a-message-with-a-component","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/interactions/components.md"}');export{e as data}; diff --git a/assets/components.html.e89a60b6.js b/assets/components.html.e89a60b6.js new file mode 100644 index 0000000..7b128f6 --- /dev/null +++ b/assets/components.html.e89a60b6.js @@ -0,0 +1,20 @@ +import{_ as n,o as s,c as a,e as t}from"./app.151ccb98.js";const e={},p=t(`

Message Components

\u2754 What are components?

Components are interactive elements like buttons or hidden elements like the ActionRow which use is for displaying the visible components. You can add them to a message and interact with users in a very convenient way. Currently, the only interactive components available at the moment are buttons. They differ in style and behaviour(link redirect) seen in the picture below:

\u{1F4A1} Sending a message with a component

Sending a component with your message is a simple as that:

TextChannel channel = ...;
+
+new MessageBuilder()
+    .setContent("Click on one of these Buttons!")
+    .addComponents(
+        ActionRow.of(Button.success("success", "Send a message"),
+            Button.danger("danger", "Delete this message"),
+            Button.secondary("secondary", "Remind me after 5 minutes")))
+    .send(channel);
+

You simply add a High Level component like an ActionRow which is a container for displaying your components. In turn the ActionRow consist of the components you can interact with like Buttons.

This works for Select Menus as well:

TextChannel channel = ...;
+
+new MessageBuilder()
+    .setContent("Select an option of this list!")
+    .addComponents(
+        ActionRow.of(SelectMenu.create("options", "Click here to show the options", 1, 1,
+            Arrays.asList(SelectMenuOption.create("Option One", "You selected Option One!", "Click here to select Option One"),
+                SelectMenuOption.create("Option Two", "You selected Option Two!", "Click here to select Option Two"),
+                SelectMenuOption.create("Option Three", "You selected Option Three!", "Click here to select Option Three")))))
+    .send(channel);
+

`,12),o=[p];function c(i,u){return s(),a("div",null,o)}var k=n(e,[["render",c],["__file","components.html.vue"]]);export{k as default}; diff --git a/assets/confirm.d7539d03.png b/assets/confirm.d7539d03.png new file mode 100644 index 0000000..a26b03c Binary files /dev/null and b/assets/confirm.d7539d03.png differ diff --git a/assets/copy-token.f3b601d6.png b/assets/copy-token.f3b601d6.png new file mode 100644 index 0000000..ae31041 Binary files /dev/null and b/assets/copy-token.f3b601d6.png differ diff --git a/assets/create-application.0e9b1d37.png b/assets/create-application.0e9b1d37.png new file mode 100644 index 0000000..64facbe Binary files /dev/null and b/assets/create-application.0e9b1d37.png differ diff --git a/assets/create-project.f0c107a4.png b/assets/create-project.f0c107a4.png new file mode 100644 index 0000000..f3e7cbe Binary files /dev/null and b/assets/create-project.f0c107a4.png differ diff --git a/assets/creating-a-bot-account.html.c9939e16.js b/assets/creating-a-bot-account.html.c9939e16.js new file mode 100644 index 0000000..82b8f7e --- /dev/null +++ b/assets/creating-a-bot-account.html.c9939e16.js @@ -0,0 +1,3 @@ +import{_ as s,r as i,o as c,c as r,a as e,b as n,d as t,e as o}from"./app.151ccb98.js";var p="/assets/create-application.0e9b1d37.png",d="/assets/click-bot.149192d1.png",l="/assets/add-bot.55f7988f.png",h="/assets/confirm.d7539d03.png",u="/assets/copy-token.f3b601d6.png",k="/assets/get-client-id.1f804477.png",m="/assets/calculate-permissions.31ca902e.png",v="/assets/use-invite-link.6050cdc9.png";const _={},g=e("h1",{id:"creating-a-bot-account",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#creating-a-bot-account","aria-hidden":"true"},"#"),t(" Creating a Bot Account")],-1),b=e("p",null,"After you added Javacord as a dependency with your favorite build manager, you should now create a bot account on the Discord website. This article will guide you through the process.",-1),y=e("h2",{id:"create-a-bot-and-get-its-token",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#create-a-bot-and-get-its-token","aria-hidden":"true"},"#"),t(" \u{1F4A1} Create a bot and get its token")],-1),f={id:"_1-open-https-discord-com-developers-applications-me-and-click-on-create-an-application",tabindex:"-1"},w=e("a",{class:"header-anchor",href:"#_1-open-https-discord-com-developers-applications-me-and-click-on-create-an-application","aria-hidden":"true"},"#",-1),x=t(),I=e("strong",null,"1.",-1),j=t(" Open "),C={href:"https://discordapp.com/developers/applications/me",target:"_blank",rel:"noopener noreferrer"},B=t("https://discord.com/developers/applications/me"),T=t(' and click on "Create an application".'),A=o('

2. Switch to Bot

TIP

If you want to, you can rename your application first

3. Click on Add bot and confirm the popup

4. Copy the bot's token. In this case the token would be NDc[...]pCs. You can just click on Copy.

DANGER

This token is used to login your bot. Keep it secret!

5. If you want to, you can change the bot's name and avatar on this page, too.

\u2795 How to add a bot to your server

Bots cannot join a server on their own like normal Discord users can. Instead, the owner of a server has to invite the bot using a so called Invite Link. There are multiple ways to create the invite link:

The easiest way to obtain an invite link for your bot is by letting Javacord do it for you. Simply execute the following code, and it will print the invite link to your console:

DiscordApi api = new DiscordApiBuilder().setToken("your token").login().join();
+System.out.println(api.createBotInvite());
+

If you don't have Javacord setup yet, you can also create the invite link manually.

Get the client id

In order to add a bot to your server you need its client id.

`,19),D=t("You can get your client id from the "),N={href:"https://discord.com/developers/applications/me",target:"_blank",rel:"noopener noreferrer"},J=t("same page"),E=t(" where you created it."),S=o('

With this id you can create an invite link for your bot.

If you are the owner or admin of the server, you can use this link to add your bot to your server. Otherwise, you have to give the link to the server owner/admins and ask them to add your bot.

TIP

Unlike the token, you don't have to keep your client id secret

Create the url

Just use the following link and replace 123456789 with your own client id.

https://discord.com/api/oauth2/authorize?client_id=123456789&scope=applications.commands%20bot&permissions=0

You can calculate the permissions (in the link above it's the 0) on the page where you created the bot:

You can now open the link and add the bot to your server:

TIP

Only the owner and admins of a server can invite bots. If you do not own a server yet, it is recommended to create one for testing.

',13);function V(Y,L){const a=i("ExternalLinkIcon");return c(),r("div",null,[g,b,y,e("h4",f,[w,x,I,j,e("a",C,[B,n(a)]),T]),A,e("p",null,[D,e("a",N,[J,n(a)]),E]),S])}var P=s(_,[["render",V],["__file","creating-a-bot-account.html.vue"]]);export{P as default}; diff --git a/assets/creating-a-bot-account.html.d48fba04.js b/assets/creating-a-bot-account.html.d48fba04.js new file mode 100644 index 0000000..7f20da4 --- /dev/null +++ b/assets/creating-a-bot-account.html.d48fba04.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5628c715","path":"/wiki/getting-started/creating-a-bot-account.html","title":"Creating a Bot Account","lang":"en-US","frontmatter":{"keywords":["bot creation","get token","add bot","bot invite link"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4A1} Create a bot and get its token","slug":"create-a-bot-and-get-its-token","children":[]},{"level":2,"title":"\u2795 How to add a bot to your server","slug":"how-to-add-a-bot-to-your-server","children":[{"level":3,"title":"Use Javacord to create the invite link","slug":"use-javacord-to-create-the-invite-link","children":[]},{"level":3,"title":"Create the invite link manually","slug":"create-the-invite-link-manually","children":[]}]},{"level":2,"title":"\u{1F64B}\u200D\u2642\uFE0F Use the invite link","slug":"use-the-invite-link","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/creating-a-bot-account.md"}');export{e as data}; diff --git a/assets/creating-entities.html.d4566e7e.js b/assets/creating-entities.html.d4566e7e.js new file mode 100644 index 0000000..ebb43a5 --- /dev/null +++ b/assets/creating-entities.html.d4566e7e.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5fd376fa","path":"/wiki/basic-tutorials/creating-entities.html","title":"Creating Channels, Invites, etc.","lang":"en-US","frontmatter":{"keywords":["creating entities","create entities","entity creation","create channels","channel creation","create webhooks","webhook creation","create invites","invite creation","create server","server creation"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4D5} Create Channels","slug":"create-channels","children":[]},{"level":2,"title":"\u{1F4D7} Create Webhooks","slug":"create-webhooks","children":[]},{"level":2,"title":"\u{1F4D8} Create Invites","slug":"create-invites","children":[]},{"level":2,"title":"\u{1F4D9} Create Servers","slug":"create-servers","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/creating-entities.md"}');export{e as data}; diff --git a/assets/creating-entities.html.ee81cd24.js b/assets/creating-entities.html.ee81cd24.js new file mode 100644 index 0000000..52fa9f9 --- /dev/null +++ b/assets/creating-entities.html.ee81cd24.js @@ -0,0 +1,24 @@ +import{_ as n,o as s,c as a,e}from"./app.151ccb98.js";const t={},p=e(`

Creating Channels, Invites, etc.

Javacord provides XyzBuilder classes to create new Discord entities like channels, webhooks, servers, and many more.

\u{1F4D5} Create Channels

You can get the channel builders for a specific server using the Server#createXyzChannelBuilder or by directly calling the constructor. Creating a ServerVoiceChannel would look like this:

Server server = ...;
+ServerVoiceChannel channel = new ServerVoiceChannelBuilder(server)
+    .setName("example-channel")
+    .setUserlimit(10)
+    .create().join();
+

\u{1F4D7} Create Webhooks

You can get the WebhookBuilder for a specific text channel:

ServerTextChannel channel = ...;
+Webhook webhook = new WebhookBuilder(channel)
+    .setName("Captain Hook")
+    .setAvatar(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
+    .create().join();
+

\u{1F4D8} Create Invites

You can get the InviteBuilder for a specific server channel:

ServerTextChannel channel = ...;
+Invite invite = new InviteBuilder(channel)
+    .setMaxAgeInSeconds(60*60*24)
+    .setMaxUses(42)
+    .create().join();
+

\u{1F4D9} Create Servers

You can get the ServerBuilder from the current api instance:

DiscordApi api = ...;
+long serverId = new ServerBuilder(api)
+    .setName("My Awesome Server")
+    .setIcon(api.getYourself().getAvatar())
+    .setVerificationLevel(VerificationLevel.HIGH)
+    .setDefaultMessageNotificationLevel(DefaultMessageNotificationLevel.ONLY_MENTIONS)
+    .setRegion(Region.EU_CENTRAL)
+    .create().join();
+

WARNING

By default, bots can only create servers if they are in less than 10 servers. You can contact the Discord support to request a higher limit.

`,15),c=[p];function o(i,l){return s(),a("div",null,c)}var r=n(t,[["render",o],["__file","creating-entities.html.vue"]]);export{r as default}; diff --git a/assets/download-installation.html.2a3aecfd.js b/assets/download-installation.html.2a3aecfd.js new file mode 100644 index 0000000..f4a05bd --- /dev/null +++ b/assets/download-installation.html.2a3aecfd.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-7d129412","path":"/wiki/getting-started/download-installation.html","title":"Download / Installation","lang":"en-US","frontmatter":{"keywords":["download and installation","maven","gradle"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4E6} Javacord Dependency","slug":"javacord-dependency","children":[]},{"level":2,"title":"\u{1F4DD} Optional Logger Dependency","slug":"optional-logger-dependency","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/download-installation.md"}');export{e as data}; diff --git a/assets/download-installation.html.de5b4efd.js b/assets/download-installation.html.de5b4efd.js new file mode 100644 index 0000000..306d089 --- /dev/null +++ b/assets/download-installation.html.de5b4efd.js @@ -0,0 +1,37 @@ +import{_ as r,r as o,o as d,c as g,b as a,w as t,a as n,d as s}from"./app.151ccb98.js";const k={},m=n("h1",{id:"download-installation",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#download-installation","aria-hidden":"true"},"#"),s(" Download / Installation")],-1),v=s("The recommended way to get Javacord is to use a build manager, like Gradle or Maven."),_=n("br",null,null,-1),h=s(" If you are not familiar with build managers, you can follow one of the beginner ide setup guides (see navigation) or download Javacord directly from "),b={href:"https://github.com/Javacord/Javacord/releases/latest",target:"_blank",rel:"noopener noreferrer"},f=s("GitHub"),y=s("."),x=n("h2",{id:"javacord-dependency",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#javacord-dependency","aria-hidden":"true"},"#"),s(" \u{1F4E6} Javacord Dependency")],-1),j=n("div",{class:"language-groovy ext-groovy line-numbers-mode"},[n("pre",{class:"language-groovy"},[n("code",null,[s("repositories "),n("span",{class:"token punctuation"},"{"),s(),n("span",{class:"token function"},"mavenCentral"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(),n("span",{class:"token punctuation"},"}"),s(` +dependencies `),n("span",{class:"token punctuation"},"{"),s(" implementation "),n("span",{class:"token string"},"'org.javacord:javacord:$latest-version'"),s(),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),I=n("div",{class:"language-xml ext-xml line-numbers-mode"},[n("pre",{class:"language-xml"},[n("code",null,[n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("dependency")]),n("span",{class:"token punctuation"},">")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("groupId")]),n("span",{class:"token punctuation"},">")]),s("org.javacord"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("artifactId")]),n("span",{class:"token punctuation"},">")]),s("javacord"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("version")]),n("span",{class:"token punctuation"},">")]),s("$latest-version"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("type")]),n("span",{class:"token punctuation"},">")]),s("pom"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),w=n("div",{class:"language-scala ext-scala line-numbers-mode"},[n("pre",{class:"language-scala"},[n("code",null,[s("libraryDependencies "),n("span",{class:"token operator"},"++"),n("span",{class:"token operator"},"="),s(" Seq"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"org.javacord"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"javacord"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"$latest-version"'),n("span",{class:"token punctuation"},")"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"})])],-1),C={class:"custom-container details"},G=n("summary",null,"Click to view snapshot repositories",-1),L=s("Snapshots are automatically deployed from the "),J={href:"https://github.com/Javacord/Javacord/tree/development",target:"_blank",rel:"noopener noreferrer"},D=s("development"),S=s(" branch."),$=n("div",{class:"language-groovy ext-groovy line-numbers-mode"},[n("pre",{class:"language-groovy"},[n("code",null,[s("repositories "),n("span",{class:"token punctuation"},"{"),s(` + maven `),n("span",{class:"token punctuation"},"{"),s(` + url `),n("span",{class:"token interpolation-string"},[n("span",{class:"token string"},'"https://oss.sonatype.org/content/repositories/snapshots/"')]),s(` + `),n("span",{class:"token punctuation"},"}"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +dependencies `),n("span",{class:"token punctuation"},"{"),s(` + implementation `),n("span",{class:"token string"},"'org.javacord:javacord:$latest-snapshot-version'"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),V=n("div",{class:"language-xml ext-xml line-numbers-mode"},[n("pre",{class:"language-xml"},[n("code",null,[n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("repository")]),n("span",{class:"token punctuation"},">")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("id")]),n("span",{class:"token punctuation"},">")]),s("snapshots-repo"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("url")]),n("span",{class:"token punctuation"},">")]),s("https://oss.sonatype.org/content/repositories/snapshots/"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),M=n("div",{class:"language-xml ext-xml line-numbers-mode"},[n("pre",{class:"language-xml"},[n("code",null,[n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("dependency")]),n("span",{class:"token punctuation"},">")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("groupId")]),n("span",{class:"token punctuation"},">")]),s("org.javacord"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("artifactId")]),n("span",{class:"token punctuation"},">")]),s("javacord"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("version")]),n("span",{class:"token punctuation"},">")]),s("$latest-snapshot-version"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("type")]),n("span",{class:"token punctuation"},">")]),s("pom"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),O=n("div",{class:"language-scala ext-scala line-numbers-mode"},[n("pre",{class:"language-scala"},[n("code",null,[s("resolvers "),n("span",{class:"token operator"},"+="),s(),n("span",{class:"token string"},'"snapshots-repo"'),s(" at "),n("span",{class:"token string"},'"https://oss.sonatype.org/content/repositories/snapshots/"'),s(` +libraryDependencies `),n("span",{class:"token operator"},"++"),n("span",{class:"token operator"},"="),s(" Seq"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"org.javacord"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"javacord"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"$latest-snapshot-version"'),n("span",{class:"token punctuation"},")"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),q=n("h2",{id:"optional-logger-dependency",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#optional-logger-dependency","aria-hidden":"true"},"#"),s(" \u{1F4DD} Optional Logger Dependency")],-1),B=n("p",null,"In addition to Javacord, it is also recommended to install a Log4j-2-compatible logging framework. A logging framework can be used to provide a more sophisticated logging experience with being able to configure log format, log targets (console, file, database, Discord direct message, ...), log levels per class, and much more.",-1),E=n("p",null,"For example, Log4j Core:",-1),N=n("div",{class:"language-groovy ext-groovy line-numbers-mode"},[n("pre",{class:"language-groovy"},[n("code",null,[s("dependencies "),n("span",{class:"token punctuation"},"{"),s(" runtimeOnly "),n("span",{class:"token string"},"'org.apache.logging.log4j:log4j-core:2.17.0'"),s(),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"})])],-1),T=n("div",{class:"language-xml ext-xml line-numbers-mode"},[n("pre",{class:"language-xml"},[n("code",null,[n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("dependency")]),n("span",{class:"token punctuation"},">")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("groupId")]),n("span",{class:"token punctuation"},">")]),s("org.apache.logging.log4j"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("artifactId")]),n("span",{class:"token punctuation"},">")]),s("log4j-core"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` + `),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"<"),s("version")]),n("span",{class:"token punctuation"},">")]),s("2.17.0"),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`),n("span",{class:"token tag"},[n("span",{class:"token tag"},[n("span",{class:"token punctuation"},"")]),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),R=n("div",{class:"language-scala ext-scala line-numbers-mode"},[n("pre",{class:"language-scala"},[n("code",null,[s("libraryDependencies "),n("span",{class:"token operator"},"++"),n("span",{class:"token operator"},"="),s(" Seq"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string"},'"org.apache.logging.log4j"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"log4j-core"'),s(),n("span",{class:"token operator"},"%"),s(),n("span",{class:"token string"},'"2.17.0"'),n("span",{class:"token punctuation"},")"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"})])],-1),A=s("Take a look at the "),F=s("logger configuration"),H=s(" wiki article for further information.");function z(K,P){const i=o("LatestVersion"),p=o("ClientOnly"),c=o("ExternalLinkIcon"),e=o("CodeGroupItem"),l=o("CodeGroup"),u=o("RouterLink");return d(),g("div",null,[m,a(p,null,{default:t(()=>[a(i)]),_:1}),n("p",null,[v,_,h,n("a",b,[f,a(c)]),y]),x,a(l,null,{default:t(()=>[a(e,{title:"Gradle",active:""},{default:t(()=>[j]),_:1}),a(e,{title:"Maven"},{default:t(()=>[I]),_:1}),a(e,{title:"Sbt"},{default:t(()=>[w]),_:1})]),_:1}),n("details",C,[G,n("p",null,[L,n("a",J,[D,a(c)]),S]),a(l,null,{default:t(()=>[a(e,{title:"Gradle",active:""},{default:t(()=>[$]),_:1}),a(e,{title:"Maven"},{default:t(()=>[V,M]),_:1}),a(e,{title:"Sbt"},{default:t(()=>[O]),_:1})]),_:1})]),q,B,E,a(l,null,{default:t(()=>[a(e,{title:"Gradle",active:""},{default:t(()=>[N]),_:1}),a(e,{title:"Maven"},{default:t(()=>[T]),_:1}),a(e,{title:"Sbt"},{default:t(()=>[R]),_:1})]),_:1}),n("p",null,[A,a(u,{to:"/wiki/basic-tutorials/logger-config/"},{default:t(()=>[F]),_:1}),H])])}var U=r(k,[["render",z],["__file","download-installation.html.vue"]]);export{U as default}; diff --git a/assets/eclipse-maven.html.04415d3e.js b/assets/eclipse-maven.html.04415d3e.js new file mode 100644 index 0000000..e96389c --- /dev/null +++ b/assets/eclipse-maven.html.04415d3e.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-22de0aba","path":"/wiki/getting-started/setup/eclipse-maven.html","title":"Eclipse + Maven","lang":"en-US","frontmatter":{},"excerpt":"","headers":[{"level":2,"title":"\u{1F527} Setup","slug":"setup","children":[]},{"level":2,"title":"\u{1F3C3}\u200D\u2640\uFE0F Run the code","slug":"run-the-code","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/setup/eclipse-maven.md"}');export{e as data}; diff --git a/assets/eclipse-maven.html.c65d8d94.js b/assets/eclipse-maven.html.c65d8d94.js new file mode 100644 index 0000000..38f477c --- /dev/null +++ b/assets/eclipse-maven.html.c65d8d94.js @@ -0,0 +1,46 @@ +import{_ as i,r as s,o as l,c as u,b as t,w as e,a as n,d as a,e as r}from"./app.151ccb98.js";const d={},k=n("h1",{id:"eclipse-maven",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#eclipse-maven","aria-hidden":"true"},"#"),a(" Eclipse + Maven")],-1),g=a(" This tutorial provides a beginner-friendly click by click guide to set up Javacord with Eclipse and Maven. If you are already familiar with Eclipse and Maven, you can just see the artifact locations at [Download / Installation](/wiki/getting-started/download-installation.md). "),h={class:"custom-container tip"},m=n("p",{class:"custom-container-title"},"Info",-1),v=a("We recommend to use "),b=a("Intellij + Gradle"),f=a(" unless you already have experience with one of the other IDEs or build managers."),_=r(`

\u{1F527} Setup

1. Start Eclipse

2. Create a new project (File -> New -> Project)

3. Select Maven Project

4. Click Next

5. Check Create a simple project

6. Click Next

7. Enter a group id (e.g. com.github.yourname)

8. Enter an artifact id (e.g. myfirstbot)

9. Click Finish

10. Double click on the pom.xml file

11. Select pom.xml

12. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>your.package.name</groupId>
+    <artifactId>myfirstbot</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.javacord</groupId>
+            <artifactId>javacord</artifactId>
+            <version>$latest-version</version>
+            <type>pom</type>
+        </dependency>
+    </dependencies>
+
+</project>
+

13. Create a new package inside the src/main/java folder

14. Create a new class inside this package

15. Save the project (you should do this from time to time)

16. Now you can start coding! Example code:

package com.github.yourname.myfirstbot;
+
+import org.javacord.api.DiscordApi;
+import org.javacord.api.DiscordApiBuilder;
+
+public class Main {
+
+    public static void main(String[] args) {
+        // Insert your bot's token here
+        String token = "your token";
+
+        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
+
+        // Add a listener which answers with "Pong!" if someone writes "!ping"
+        api.addMessageCreateListener(event -> {
+            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
+                event.getChannel().sendMessage("Pong!");
+            }
+        });
+
+        // Print the invite url of your bot
+        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
+    }
+
+}
+

\u{1F3C3}\u200D\u2640\uFE0F Run the code

You can run your code by clicking on the small green arrow

`,30);function y(x,w){const o=s("LatestVersion"),p=s("ClientOnly"),c=s("RouterLink");return l(),u("div",null,[k,t(p,null,{default:e(()=>[t(o)]),_:1}),g,n("div",h,[m,n("p",null,[v,t(c,{to:"/wiki/getting-started/setup/intellij-gradle.html"},{default:e(()=>[b]),_:1}),f])]),_])}var q=i(d,[["render",y],["__file","eclipse-maven.html.vue"]]);export{q as default}; diff --git a/assets/embeds.html.68dc7509.js b/assets/embeds.html.68dc7509.js new file mode 100644 index 0000000..f74b031 --- /dev/null +++ b/assets/embeds.html.68dc7509.js @@ -0,0 +1 @@ +const e=JSON.parse(`{"key":"v-37293ae0","path":"/wiki/basic-tutorials/embeds.html","title":"Embeds","lang":"en-US","frontmatter":{"keywords":["EmbedBuilder","inline field","setTitle","setDescription","setAuthor","addField","addInlineField","setColor","setFooter","setImage","setThumbnail"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F528} Creating an Embed","slug":"creating-an-embed","children":[]},{"level":2,"title":"\u{1F4F7} Supported Image Sources","slug":"supported-image-sources","children":[]},{"level":2,"title":"\u{1F512} Embed Limits","slug":"embed-limits","children":[]},{"level":2,"title":"\u2753 FAQ","slug":"faq","children":[{"level":3,"title":"What is the second parameter of setAuthor(...)?","slug":"what-is-the-second-parameter-of-setauthor","children":[]},{"level":3,"title":"What's the difference between an inline field and a normal one?","slug":"what-s-the-difference-between-an-inline-field-and-a-normal-one","children":[]},{"level":3,"title":"Can I change the placement of inline fields?","slug":"can-i-change-the-placement-of-inline-fields","children":[]},{"level":3,"title":"How can I format text in an embed?","slug":"how-can-i-format-text-in-an-embed","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/embeds.md"}`);export{e as data}; diff --git a/assets/embeds.html.f7a6f54c.js b/assets/embeds.html.f7a6f54c.js new file mode 100644 index 0000000..bc532ef --- /dev/null +++ b/assets/embeds.html.f7a6f54c.js @@ -0,0 +1,16 @@ +import{_ as e,r as t,o,c as i,a,b as c,e as p,d as n}from"./app.151ccb98.js";const l={},d=p(`

Embeds

Embeds are attached to messages and have a special design. The usually look like this:

Embed

\u{1F528} Creating an Embed

Javacord provides an EmbedBuilder which can be used to create embeds:

// Create the embed
+EmbedBuilder embed = new EmbedBuilder()
+    .setTitle("Title")
+    .setDescription("Description")
+    .setAuthor("Author Name", "http://google.com/", "https://cdn.discordapp.com/embed/avatars/0.png")
+    .addField("A field", "Some text inside the field")
+    .addInlineField("An inline field", "More text")
+    .addInlineField("Another inline field", "Even more text")
+    .setColor(Color.BLUE)
+    .setFooter("Footer", "https://cdn.discordapp.com/embed/avatars/1.png")
+    .setImage(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
+    .setThumbnail(new File("C:/Users/Bastian/Pictures/kitten2.png"));
+// Send the embed
+channel.sendMessage(embed);
+

\u{1F4F7} Supported Image Sources

By default, Discord expects embed images to be a link (e.g., the image link used in setFooter(...)), but you can also use attachments for images. If you provide a non-url image source (e.g. the puppy.jpg file used in setImage(...)), Javacord automatically uploads them as an attachment to the message and uses this attachment for the embed.

\u{1F512} Embed Limits

TypeLimit
Title256 characters
Description4096 characters
Field AmountUp to 25 fields
Field Name256 characters
Field Value1024 characters
Footer Text2048 characters
Author Name256 characters

In addition to the limits above, the sum of all characters in an embed structure must not exceed 6000 characters.

\u2753 FAQ

What is the second parameter of setAuthor(...)?

.setAuthor("Author Name", "http://google.com/", "https://cdn.discordapp.com/embed/avatars/0.png")
+
  • First parameter: The name of the author
  • Second parameter: A link for the author (e.g. their homepage). Can be null.
  • Third parameter: The avatar of the author

What's the difference between an inline field and a normal one?

Normal fields always start in a new line, whereas several inline fields can be in the same line.

Can I change the placement of inline fields?

No, Discord does not allow different embed layouts.

How can I format text in an embed?

`,21),r=n("Discord allows for a subset of markdown to be used. See "),u={href:"https://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline-",target:"_blank",rel:"noopener noreferrer"},h=n("their docs"),m=n(" for the specifics.");function k(b,f){const s=t("ExternalLinkIcon");return o(),i("div",null,[d,a("p",null,[r,a("a",u,[h,c(s)]),m])])}var v=e(l,[["render",k],["__file","embeds.html.vue"]]);export{v as default}; diff --git a/assets/emojis-and-reactions.html.7f613dfc.js b/assets/emojis-and-reactions.html.7f613dfc.js new file mode 100644 index 0000000..768f462 --- /dev/null +++ b/assets/emojis-and-reactions.html.7f613dfc.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-13e99c06","path":"/wiki/basic-tutorials/emojis-and-reactions.html","title":"Emojis and Reactions","lang":"en-US","frontmatter":{"keywords":["create emoji","emoji creation","unicode emoji","custom emojis","delete emojis","emoji deletion","send emoji","use emoji","KnownCustomEmoji"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F6B4}\u200D\u2642\uFE0F Unicode Emojis","slug":"unicode-emojis","children":[{"level":3,"title":"What are Unicode emojis?","slug":"what-are-unicode-emojis","children":[]},{"level":3,"title":"How to use them in messages","slug":"how-to-use-them-in-messages","children":[]},{"level":3,"title":"How to use them for reactions","slug":"how-to-use-them-for-reactions","children":[]}]},{"level":2,"title":"\u{1F938}\u200D\u2640\uFE0F Custom Emojis","slug":"custom-emojis","children":[{"level":3,"title":"What are custom emojis?","slug":"what-are-custom-emojis","children":[]},{"level":3,"title":"How to use them in messages","slug":"how-to-use-them-in-messages-1","children":[]},{"level":3,"title":"How to use them for reactions","slug":"how-to-use-them-for-reactions-1","children":[]},{"level":3,"title":"How to get the tag","slug":"how-to-get-the-tag","children":[]}]},{"level":2,"title":"\u{1F451} Javacord Emoji \\"Hierarchy\\"","slug":"javacord-emoji-hierarchy","children":[{"level":3,"title":"What is a KnownCustomEmoji?","slug":"what-is-a-knowncustomemoji","children":[]}]},{"level":2,"title":"\u{1F44C} Recommended libraries","slug":"recommended-libraries","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/emojis-and-reactions.md"}');export{e as data}; diff --git a/assets/emojis-and-reactions.html.ce8f0b41.js b/assets/emojis-and-reactions.html.ce8f0b41.js new file mode 100644 index 0000000..22093b0 --- /dev/null +++ b/assets/emojis-and-reactions.html.ce8f0b41.js @@ -0,0 +1,12 @@ +import{_ as o,r as i,o as c,c as p,a as n,b as t,e as s,d as a}from"./app.151ccb98.js";const u={},r=s('

Emojis and Reactions

There are two different kinds of emojis in Discord: Unicode emojis and custom emojis.

\u{1F6B4}\u200D\u2642\uFE0F Unicode Emojis

What are Unicode emojis?

',4),d=a('Unicode emojis are "normal" text emojis which are supported by (nearly) all chat clients, including Discord. You can find a list with all Unicode emojis here: '),l={href:"https://unicode.org/emoji/charts/full-emoji-list.html",target:"_blank",rel:"noopener noreferrer"},m=a("Full Emoji List"),h=a("."),g=s(`

How to use them in messages

You can either directly add them in your code, e.g.

channel.sendMessage("Hi! \u{1F603}");
+

or use the normal "tag" like you would in the Client:

channel.sendMessage("Hi! :smiley:");
+

How to use them for reactions

Adding unicode reactions is only possible by using the "real" reaction. It doesn't support tags like :smiley:.

message.addReaction("\u{1F603}"); // works
+message.addReaction(":smiley:"); // doesn't work
+

\u{1F938}\u200D\u2640\uFE0F Custom Emojis

What are custom emojis?

Custom emojis are emojis that are created in a server. You can get all custom emojis the bot knows by using DiscordApi#getCustomEmojis().

How to use them in messages

To use custom emojis, you have to know its "tag", which has the format <:name:id>. You can get it by calling CustomEmoji#getMentionTag():

channel.sendMessage("Hi! <:javacord:415465982715494402>");
+
CustomEmoji emoji = ...;
+channel.sendMessage("Hi! " + emoji.getMentionTag());
+

How to use them for reactions

You can either directly use the custom emoji object or use the tag without the <: > if you don't have access a custom emoji object (e.g., because it's from a different shard):

CustomEmoji emoji = ...;
+message.addReaction(emoji);
+
message.addReaction("javacord:415465982715494402");
+

How to get the tag

Just add a \\ in front of the emoji and press Enter

\u{1F451} Javacord Emoji "Hierarchy"

In Javacord, all Emojis are a child of the Emoji interface:

What is a KnownCustomEmoji?

Known custom emojis are emojis that the bot knows because it's a member of the server with this emoji. A custom emoji can be unknown if someone adds a reaction with an unknown emoji for example. A KnownCustomEmoji has additional methods like getServer() or updateName(String).

`,32),k=a("If you are working a lot with Unicode emojis, it's recommended to use a library like "),v={href:"https://github.com/felldo/JEmoji",target:"_blank",rel:"noopener noreferrer"},j=a("JEmoji"),b=a(". It enables you to do things like the following:"),f=s(`
message.addReaction(EmojiManager.getByAlias(":thumbsup:"));
+
`,1);function w(_,x){const e=i("ExternalLinkIcon");return c(),p("div",null,[r,n("p",null,[d,n("a",l,[m,t(e)]),h]),g,n("p",null,[k,n("a",v,[j,t(e)]),b]),f])}var y=o(u,[["render",w],["__file","emojis-and-reactions.html.vue"]]);export{y as default}; diff --git a/assets/enable_privileged_intents.8e8e9669.png b/assets/enable_privileged_intents.8e8e9669.png new file mode 100644 index 0000000..7875483 Binary files /dev/null and b/assets/enable_privileged_intents.8e8e9669.png differ diff --git a/assets/entity-cache.html.40152af2.js b/assets/entity-cache.html.40152af2.js new file mode 100644 index 0000000..60512a1 --- /dev/null +++ b/assets/entity-cache.html.40152af2.js @@ -0,0 +1,37 @@ +import{_ as c,r as t,o as i,c as u,a as n,b as a,w as l,e as o,d as s}from"./app.151ccb98.js";const d={},r=o('

Entity Cache

Javacord keeps an internal cache for entities (e.g. Servers, Channels, Users, ...). It is important to know how the cache behaves to properly use it.

\u{1F52E} What is in the cache?

Nearly every entity known by the bot is guaranteed to be in the cache. There are a few exceptions though:

Users

',5),k=s("Users are only cached when you have the "),h=n("code",null,"GUILD_MEMBERS",-1),m=s(" intent enabled. See "),v=s("Gateway Intents"),g=s(" for more information."),b=n("h4",{id:"messages",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#messages","aria-hidden":"true"},"#"),s(" Messages")],-1),f=s("Not every single message is in the cache, which means you can encounter messages which exist but are not in the cache. This can happen for most message events, e.g. the "),_={href:"https://ci.javacord.org/javadoc/org/javacord/api/event/message/reaction/ReactionAddEvent.html",target:"_blank",rel:"noopener noreferrer"},w=n("code",null,"ReactionAddEvent",-1),y=s(". You can, however, interact with these messages without having them in the cache. Every message event has methods like "),j=n("code",null,"event.deleteMessage()",-1),q=s(", "),E=n("code",null,'event.editMessage("New Content")',-1),x=s(". If you need the message (e.g. to get its content), you can request it using "),M=n("code",null,"event.requestMessage()",-1),S=s("."),I=s("Additionally, you can use the static methods in the "),B={href:"https://ci.javacord.org/javadoc/org/javacord/api/entity/message/Message.html",target:"_blank",rel:"noopener noreferrer"},C=n("code",null,"Message",-1),L=s(" class which only require the channel and message id, e.g. "),R=n("code",null,'Message.edit(api, channelId, messageId, "New content");',-1),N=s(". This is very useful if you want to store them in a database."),W=o(`

Webhooks and Invites

Webhooks and Invites are not kept in the cache at all and won't receive any updates.

Embeds

Embeds from message.getEmbed() won't receive updates. If a message's embed gets edited, getEmbed() will return a completely new embed object.

\u2753 When are cached entities updated?

Javacord's cache exclusively uses websocket events to keep the cache up to date. This means that the content of your objects might be outdated, even though you modified it yourself:

Messages message = ...;
+System.out.println(message.getContent()); // Prints the old content, e.g. "old content"
+message.edit("new content").join(); // Edits the message and waits for success
+System.out.println(message.getContent()); // Still prints "old content"
+Thread.sleep(1000);
+System.out.println(message.getContent()); // Most likely prints "new content" now
+

\u231A How long are cached entities valid?

Even though entities are usually kept in the cache for a very long time, you should not keep references to these objects for a longer period of time, but store the id / use event methods:

// Bad
+Message message = ...;
+message.addReactionAddListener(event -> {
+  if (event.getEmoji().equalsEmoji("\u{1F44E}")) {
+    message.delete(); // Prevents "message" from being garbage collected
+  }
+});
+
+// Good
+Message message = ...;
+message.addReactionAddListener(event -> {
+  if (event.getEmoji().equalsEmoji("\u{1F44E}")) {
+    event.deleteMessage(); // Does not use the message object
+  }
+});
+
// Bad
+Set<User> usersWithBadMood = new HashSet<>();
+api.addReactionAddListener(event -> {
+  if (event.getEmoji().equalsEmoji("\u{1F626}")) {
+    usersWithBadMood.add(event.getUser());
+  }
+});
+
+// Good
+Set<Long> usersWithBadMood = new HashSet<>();
+api.addReactionAddListener(event -> {
+  if (event.getEmoji().equalsEmoji("\u{1F626}")) {
+    usersWithBadMood.add(event.getUser().getId());
+  }
+});
+

Some examples of when cached entities are invalidated:

  • The bot lost its connection to Discord and had to reconnect (not resume)
  • You weren't able to receive updates for an entity, e.g. for Channel, because you left and rejoined a server
`,13);function A(T,U){const p=t("RouterLink"),e=t("ExternalLinkIcon");return i(),u("div",null,[r,n("p",null,[k,h,m,a(p,{to:"/wiki/basic-tutorials/gateway-intents/"},{default:l(()=>[v]),_:1}),g]),b,n("p",null,[f,n("a",_,[w,a(e)]),y,j,q,E,x,M,S]),n("p",null,[I,n("a",B,[C,a(e)]),L,R,N]),W])}var V=c(d,[["render",A],["__file","entity-cache.html.vue"]]);export{V as default}; diff --git a/assets/entity-cache.html.d35078da.js b/assets/entity-cache.html.d35078da.js new file mode 100644 index 0000000..5f7b3d9 --- /dev/null +++ b/assets/entity-cache.html.d35078da.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-32da090a","path":"/wiki/advanced-topics/entity-cache.html","title":"Entity Cache","lang":"en-US","frontmatter":{"keywords":["entity","cache","caching"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F52E} What is in the cache?","slug":"what-is-in-the-cache","children":[]},{"level":2,"title":"\u2753 When are cached entities updated?","slug":"when-are-cached-entities-updated","children":[]},{"level":2,"title":"\u231A How long are cached entities valid?","slug":"how-long-are-cached-entities-valid","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/entity-cache.md"}');export{e as data}; diff --git a/assets/faq.html.daf42ee7.js b/assets/faq.html.daf42ee7.js new file mode 100644 index 0000000..6357df4 --- /dev/null +++ b/assets/faq.html.daf42ee7.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6bd28c40","path":"/wiki/getting-started/faq.html","title":"Frequently Asked Questions","lang":"en-US","frontmatter":{"keywords":["faq","...","deploy","code not working","ask a question","library difference"]},"excerpt":"","headers":[{"level":2,"title":"Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?","slug":"q-why-do-i-receive-empty-no-content-messages-in-i-e-the-messagecreatelistener","children":[]},{"level":2,"title":"Q: What is ... in the code examples?","slug":"q-what-is-in-the-code-examples","children":[]},{"level":2,"title":"Q: Why is my code not working?","slug":"q-why-is-my-code-not-working","children":[{"level":3,"title":"How to properly ask a question to get fast support?","slug":"how-to-properly-ask-a-question-to-get-fast-support","children":[]}]},{"level":2,"title":"Q: What differs Javacord from JDA and D4J?","slug":"q-what-differs-javacord-from-jda-and-d4j","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/faq.md"}');export{e as data}; diff --git a/assets/faq.html.fe40474c.js b/assets/faq.html.fe40474c.js new file mode 100644 index 0000000..d601fc9 --- /dev/null +++ b/assets/faq.html.fe40474c.js @@ -0,0 +1,12 @@ +import{_ as l,r as s,o as d,c,a as e,b as o,w as i,d as t,e as a}from"./app.151ccb98.js";const h={},u=e("h1",{id:"frequently-asked-questions",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#frequently-asked-questions","aria-hidden":"true"},"#"),t(" Frequently Asked Questions")],-1),g=e("p",null,"Here you will find answers to some of the most asked questions.",-1),p=e("h2",{id:"q-why-do-i-receive-empty-no-content-messages-in-i-e-the-messagecreatelistener",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#q-why-do-i-receive-empty-no-content-messages-in-i-e-the-messagecreatelistener","aria-hidden":"true"},"#"),t(" Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?")],-1),_=t("You are missing the privileged "),f=e("code",null,"MESSAGE_CONTENT",-1),m=t(" intent. For more information of how to enable privileged intents and enable them in your code see "),y=t("Gateway Intents"),w=t("."),k=a('

Q: What is ... in the code examples?

You have to replace the ... with an instance that can be assigned to the datatype seen left.

For example, if you see TextChannel channel = ..., you have to replace ... with an instance that is a TextChannel which you can get from the API api.getTextChannelById(CHANNEL_ID) (note this returns an Optional<TextChannel>) or from an event like messageCreateEvent.getChannel().

Q: Why is my code not working?

There are multiple reasons why your code might not work. The most common ones are:

',5),x=e("li",null,"Your code is not being reached. So make sure your code actually gets executed with a print statement or a debugger.",-1),v=t("Add at least "),q=e("code",null,".exceptionally(ExceptionLogger.get())",-1),b=t(" to every "),C=e("a",{href:"../essential-knowledge/completable-futures"},"CompletableFuture",-1),E=t(" (like when sending a message) to show any exceptions that might come from Discord."),A=a('
  • Methods like User#getRoles(Server) do not return the roles of the user. To fix this make sure to add the GUILD_MEMBERS intent.
  • You are getting a NoSuchElementException. Congratulations, you have killed a kitten! You are most likely getting this Exception because you handle Optionals wrong. Read the article on Optionals to learn how to use them correctly.
  • ',2),T=t("If none of these tips will help you, you can ask your question in our "),I={href:"https://discord.gg/javacord",target:"_blank",rel:"noopener noreferrer"},S=t("Discord Server"),M=t("."),N=a(`

    How to properly ask a question to get fast support?

    Don't ask:

    Why is my code not working?
    +//Code
    +
    Why am I getting Exception X?
    +

    To ensure all information is provided that is needed to solve your issue, you should ask your question in a format like:

    I have an issue with:   YOUR_ISSUE
    +I want to do:           WHAT_YOU_WANT_TO_DO
    +Currently this happens: WHAT_HAPPENS_NOW
    +
    +//Code
    +
    +//Exception
    +The exception is thrown in the following line(not the number): CODE_LINE
    +

    Q: What differs Javacord from JDA and D4J?

    While all 3 libraries are Wrappers for the programming language Java, they use different techniques and concepts for their API.

    • Javacord: Uses Java classes for its API like CompletableFuture for async requests and Optional for return types which may be null.
      • Sending a Message: channel.sendMessage("Javacord")
      • Checking if the Author of a message is a user: message.getMessageAuthor().asUser().isPresent()
    • JDA: Has its own wrapper to execute requests and returns null if values are not present.
      • Sending a Message: channel.sendMessage("JDA").queue()
      • Checking if the Author of a message is a user: message.getMember() != null
    • Discord4J: Takes on the reactive approach.
      • Sending a Message: channel.createMessage("Pong!").block();
    `,9);function D(W,O){const n=s("RouterLink"),r=s("ExternalLinkIcon");return d(),c("div",null,[u,g,p,e("p",null,[_,f,m,o(n,{to:"/wiki/basic-tutorials/gateway-intents.html"},{default:i(()=>[y]),_:1}),w]),k,e("ol",null,[x,e("li",null,[v,o(n,{to:"/wiki/essential-knowledge/completable-futures.html#exceptionally"},{default:i(()=>[q]),_:1}),b,C,E]),A]),e("p",null,[T,e("a",I,[S,o(r)]),M]),N])}var L=l(h,[["render",D],["__file","faq.html.vue"]]);export{L as default}; diff --git a/assets/gateway-intents.html.96026c66.js b/assets/gateway-intents.html.96026c66.js new file mode 100644 index 0000000..42e49a5 --- /dev/null +++ b/assets/gateway-intents.html.96026c66.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-e7aba52c","path":"/wiki/basic-tutorials/gateway-intents.html","title":"Gateway Intents","lang":"en-US","frontmatter":{"keywords":["Intents"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4CB} List of Intents","slug":"list-of-intents","children":[]},{"level":2,"title":"\u{1F4A1} What Happens When I Disable Some Intents?","slug":"what-happens-when-i-disable-some-intents","children":[]},{"level":2,"title":"\u{1F451} Privileged Intents","slug":"privileged-intents","children":[]},{"level":2,"title":"\u2757 Notable Intents","slug":"notable-intents","children":[{"level":3,"title":"GUILD_PRESENCES","slug":"guild-presences","children":[]},{"level":3,"title":"GUILD_MEMBERS","slug":"guild-members","children":[]},{"level":3,"title":"MESSAGE_CONTENT","slug":"message-content","children":[]}]},{"level":2,"title":"\u2699\uFE0F Setting Intents","slug":"setting-intents","children":[{"level":3,"title":"Set All Non-Privileged Intents (Default)","slug":"set-all-non-privileged-intents-default","children":[]},{"level":3,"title":"Set All Non-Privileged Intents Except","slug":"set-all-non-privileged-intents-except","children":[]},{"level":3,"title":"Set All Intents","slug":"set-all-intents","children":[]},{"level":3,"title":"Set All Intents Except","slug":"set-all-intents-except","children":[]},{"level":3,"title":"Set Intents","slug":"set-intents","children":[]},{"level":3,"title":"Add Intents","slug":"add-intents","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/gateway-intents.md"}');export{e as data}; diff --git a/assets/gateway-intents.html.ef5cc4b3.js b/assets/gateway-intents.html.ef5cc4b3.js new file mode 100644 index 0000000..bac8ca1 --- /dev/null +++ b/assets/gateway-intents.html.ef5cc4b3.js @@ -0,0 +1,31 @@ +import{_ as c,r as a,o as p,c as l,a as n,b as t,w as d,e as o,d as s}from"./app.151ccb98.js";var r="/assets/enable_privileged_intents.8e8e9669.png";const u={},h=o('

    Gateway Intents

    Discord allows you to "subscribe" to specific groups of events. These "subscriptions" are called intent. Disabling intents that are not required for your bot can significantly increase your bot's performance.

    \u{1F4CB} List of Intents

    Below you can find a table with all intents supported by Discord.

    IntentSafe to DisablePrivileged
    GUILDS\u274C\u274C
    GUILD_MEMBERS\u2714\uFE0F\u2714\uFE0F
    GUILD_BANS\u26A0\uFE0F*\u274C
    GUILD_EMOJIS\u26A0\uFE0F*\u274C
    GUILD_INTEGRATIONS\u2714\uFE0F\u274C
    GUILD_WEBHOOKS\u2714\uFE0F\u274C
    GUILD_INVITES\u2714\uFE0F\u274C
    GUILD_VOICE_STATES\u26A0\uFE0F*\u274C
    GUILD_PRESENCES\u2714\uFE0F\u2714\uFE0F
    GUILD_MESSAGES\u2714\uFE0F\u274C
    GUILD_MESSAGE_REACTIONS\u2714\uFE0F\u274C
    GUILD_MESSAGE_TYPING\u2714\uFE0F\u274C
    DIRECT_MESSAGES\u2714\uFE0F\u274C
    DIRECT_MESSAGE_REACTIONS\u2714\uFE0F\u274C
    DIRECT_MESSAGE_TYPING\u2714\uFE0F\u274C
    MESSAGE_CONTENT\u2714\uFE0F\u2714\uFE0F
    AUTO_MODERATION_CONFIGURATION\u2714\uFE0F\u274C
    AUTO_MODERATION_EXECUTION\u2714\uFE0F\u274C

    * Will most likely work, but needs further testing

    ',6),k={class:"custom-container tip"},v=n("p",{class:"custom-container-title"},"Good to know!",-1),m=n("em",null,"Guild",-1),b=s(" is a synonym for servers, commonly used in Discord's API. See "),g=s("Glossary"),_=s("."),f=n("h2",{id:"what-happens-when-i-disable-some-intents",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#what-happens-when-i-disable-some-intents","aria-hidden":"true"},"#"),s(" \u{1F4A1} What Happens When I Disable Some Intents?")],-1),E=n("p",null,"When you disable some of the listed intents, Javacord will not fire events that belong to the intents and will not update these specific parts of the cache.",-1),I=n("p",null,[s("At the moment, we don't have a list which events are affected by which intents (but it will come soon\u2122\uFE0F). However, most intents should be self-explanatory. E.g. when you disable the "),n("code",null,"DIRECT_MESSAGES"),s(" intent, your bot will not receive any private messages.")],-1),S=n("h2",{id:"privileged-intents",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#privileged-intents","aria-hidden":"true"},"#"),s(" \u{1F451} Privileged Intents")],-1),y=s('Some intents are defined as "privileged" due to the sensitive nature of the data. To use these intents, you have to go to your bot in the '),w={href:"https://discord.com/developers/applications",target:"_blank",rel:"noopener noreferrer"},T=s("Developer Portal"),D=s(" (where you created bot) and manually enable the intents:"),A=n("p",null,[n("img",{src:r,alt:""})],-1),x=n("p",null,"There are some additionally restrictions for bots that are in over 100 servers:",-1),G=n("ul",null,[n("li",null,"Your bot must be verified"),n("li",null,"Your bot must be whitelisted to use this intents")],-1),N=s("Take a look at the official article from Discord about this topic and how to verify your bot: "),U={href:"https://support.discord.com/hc/en-us/articles/360040720412",target:"_blank",rel:"noopener noreferrer"},j=s("Bot Verification and Data Whitelisting"),O=s("."),L=o(`

    \u2757 Notable Intents

    The following two intents are especially noteworthy: GUILD_MEMBERS and GUILD_PRESENCES. Besides being privileged, they have some special implications for Javacord:

    GUILD_PRESENCES

    This intent is required to get updates about a user's status (i.e., if they are online, what game they are playing, ...). Additionally, without this intent it might take considerably longer to cache all users because of ratelimits (up to 10 minutes for shards with 1000 servers). It is advised against setting DiscordApiBuilder#setWaitForAllUsersOnStartup(true) without this intent, unless absolutely necessary.

    GUILD_MEMBERS

    This intent is required to keep all users in Javacord's cache. Without this intent, methods like Server#getMembers() or DiscordApi#getCachedUsers() will return empty collections. However, you will still be able to access users from objects like messages, e.g. Message#getUserAuthor() will still work.

    MESSAGE_CONTENT

    This intent is a bit different to the other as it does not act as a toggle to receive any events. It's sole purpose is to receive the message content, attachments, components, and embeds. Otherwise, these fields will be empty when you receive a Message object.

    \u2699\uFE0F Setting Intents

    Javacord allows you to specify intents in the DiscordApiBuilder prior to login. There are many options to set intents. The following example code shows the most common ones:

    Set All Non-Privileged Intents (Default)

    This method enables all non-privileged intents. This is the default setting in Javacord.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllNonPrivilegedIntents()
    +    .login()
    +    .join();
    +

    Set All Non-Privileged Intents Except

    This method enabled all non-privileged intents, except the given ones.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllNonPrivilegedIntentsExcept(Intent.GUILD_WEBHOOKS)
    +    .login()
    +    .join();
    +

    Set All Intents

    This method enabled all intents.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllIntents()
    +    .login()
    +    .join();
    +

    Set All Intents Except

    This method enabled all intents, except the given ones.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllIntentsExcept(Intent.GUILD_PRESENCES, Intent.GUILD_WEBHOOKS)
    +    .login()
    +    .join();
    +

    Set Intents

    This method only enables the given intents.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setIntents(Intent.GUILDS, Intent.DIRECT_MESSAGES)
    +    .login()
    +    .join();
    +

    Add Intents

    This method adds the intents to the currently enabled ones(by default all non-privileged). This is useful i.e. if you only want to enable 1 privileged intent like the MESSAGE_CONTENT

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .addIntents(Intent.MESSAGE_CONTENT)
    +    .login()
    +    .join();
    +
    `,28);function M(B,C){const i=a("RouterLink"),e=a("ExternalLinkIcon");return p(),l("div",null,[h,n("div",k,[v,n("p",null,[m,b,t(i,{to:"/wiki/basic-tutorials/glossary/"},{default:d(()=>[g]),_:1}),_])]),f,E,I,S,n("p",null,[y,n("a",w,[T,t(e)]),D]),A,x,G,n("p",null,[N,n("a",U,[j,t(e)]),O]),L])}var q=c(u,[["render",M],["__file","gateway-intents.html.vue"]]);export{q as default}; diff --git a/assets/get-client-id.1f804477.png b/assets/get-client-id.1f804477.png new file mode 100644 index 0000000..676c256 Binary files /dev/null and b/assets/get-client-id.1f804477.png differ diff --git a/assets/glossary.html.28351d5a.js b/assets/glossary.html.28351d5a.js new file mode 100644 index 0000000..eb7a39d --- /dev/null +++ b/assets/glossary.html.28351d5a.js @@ -0,0 +1 @@ +import{_ as r,r as i,o as c,c as d,a as e,b as o,w as l,d as t}from"./app.151ccb98.js";const a={},_=e("h1",{id:"glossary",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#glossary","aria-hidden":"true"},"#"),t(" Glossary")],-1),u=e("p",null,"This is a list with the most common Discord-related terms:",-1),h=e("li",null,[e("strong",null,[e("code",null,"Guild")]),t(" - A synonym for "),e("code",null,"server")],-1),g=e("li",null,[e("strong",null,[e("code",null,"Selfbot")]),t(" - A client account bot, usually logged in to a user's own account")],-1),m=e("strong",null,[e("code",null,"Sharding")],-1),p=t(" - Splitting a bot into several independent "),f=e("code",null,"shards",-1),k=t(", see "),b=t("Sharding"),w=e("li",null,[e("strong",null,[e("code",null,"Token")]),t(" - Used to login instead of requiring a username + password")],-1),v=e("strong",null,[e("code",null,"Embed")],-1),y=t(' - A "fancy" message, see '),R=t("Embed FAQ"),x=e("strong",null,[e("code",null,"Ratelimit")],-1),A=t(" - Prevents you from spamming actions, see "),T=t("Ratelimit FAQ"),E=e("strong",null,[e("code",null,"Websocket")],-1),S=t(" - A "),P={href:"https://en.wikipedia.org/wiki/Transmission_Control_Protocol",target:"_blank",rel:"noopener noreferrer"},q=t("TCP"),C=t(' "connection" to Discord that receives events, see '),D={href:"https://en.wikipedia.org/wiki/WebSocket",target:"_blank",rel:"noopener noreferrer"},L=t("Wikipedia"),B=e("li",null,[e("strong",null,[e("code",null,"Gateway")]),t(" - The address for the "),e("code",null,"websocket")],-1),G=e("strong",null,[e("code",null,"Rest"),t(" / "),e("code",null,"Rest Request")],-1),N=t(" - "),V={href:"https://en.wikipedia.org/wiki/Representational_state_transfer",target:"_blank",rel:"noopener noreferrer"},W=t("REST"),F=t(" is used to perform actions like sending messages. Rest Requests do not require an active websocket connection."),I=e("li",null,[e("strong",null,[e("code",null,"Activity")]),t(" - The text underneath the username, usually "),e("code",null,"Playing Xyz")],-1),Q=e("strong",null,[e("code",null,"Rich Presence")],-1),z=t(" - A more detailed activity, see "),U={href:"https://discordapp.com/developers/docs/rich-presence/getting-approved",target:"_blank",rel:"noopener noreferrer"},X=t("Discord Docs");function j(H,J){const s=i("RouterLink"),n=i("ExternalLinkIcon");return c(),d("div",null,[_,u,e("ul",null,[h,g,e("li",null,[m,p,f,k,o(s,{to:"/wiki/advanced-topics/sharding.html"},{default:l(()=>[b]),_:1})]),w,e("li",null,[v,y,o(s,{to:"/wiki/basic-tutorials/embeds.html"},{default:l(()=>[R]),_:1})]),e("li",null,[x,A,o(s,{to:"/wiki/advanced-topics/ratelimits.html"},{default:l(()=>[T]),_:1})]),e("li",null,[E,S,e("a",P,[q,o(n)]),C,e("a",D,[L,o(n)])]),B,e("li",null,[G,N,e("a",V,[W,o(n)]),F]),I,e("li",null,[Q,z,e("a",U,[X,o(n)])])])])}var M=r(a,[["render",j],["__file","glossary.html.vue"]]);export{M as default}; diff --git a/assets/glossary.html.34f5fdb3.js b/assets/glossary.html.34f5fdb3.js new file mode 100644 index 0000000..4dcbf8e --- /dev/null +++ b/assets/glossary.html.34f5fdb3.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-5b369fbc","path":"/wiki/basic-tutorials/glossary.html","title":"Glossary","lang":"en-US","frontmatter":{"keywords":["Guild","Selfbot","Sharding","Token","Embed","Ratelimit","Websocket","Gateway","Rest Request","Activity","Rich Presence"]},"excerpt":"","headers":[],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/glossary.md"}');export{e as data}; diff --git a/assets/imprint.html.8c064aa1.js b/assets/imprint.html.8c064aa1.js new file mode 100644 index 0000000..3c5490c --- /dev/null +++ b/assets/imprint.html.8c064aa1.js @@ -0,0 +1 @@ +import{_ as t,o as r,c as i,a as e,d as a}from"./app.151ccb98.js";const c={},n=e("h1",{id:"imprint",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#imprint","aria-hidden":"true"},"#"),a(" Imprint")],-1),s=e("iframe",{src:"/im.html",style:{height:"calc(100vh - 250px)",width:"100%"}},null,-1),o=[n,s];function _(d,h){return r(),i("div",null,o)}var m=t(c,[["render",_],["__file","imprint.html.vue"]]);export{m as default}; diff --git a/assets/imprint.html.aa34b88a.js b/assets/imprint.html.aa34b88a.js new file mode 100644 index 0000000..21b54e1 --- /dev/null +++ b/assets/imprint.html.aa34b88a.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-92e5302e","path":"/imprint.html","title":"Imprint","lang":"en-US","frontmatter":{"search":false},"excerpt":"","headers":[],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"imprint.md"}');export{e as data}; diff --git a/assets/index.html.07810de0.js b/assets/index.html.07810de0.js new file mode 100644 index 0000000..994f00a --- /dev/null +++ b/assets/index.html.07810de0.js @@ -0,0 +1 @@ +import{_ as i,r as n,o as d,c,a as e,b as t,w as r,d as o}from"./app.151ccb98.js";const l={},h=e("h2",{id:"about-javacord",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#about-javacord","aria-hidden":"true"},"#"),o(" \u{1F4A1} About Javacord")],-1),_=o("Javacord is a modern library that focuses on simplicity and speed \u{1F680}. By reducing itself to standard Java classes and features like "),u=e("code",null,"Optional",-1),m=o("s and "),f=e("code",null,"CompletableFuture",-1),p=o("s, it is extremely easy to use for every Java developer, as it does not require you to learn any new frameworks or complex abstractions. It has rich documentation and an "),v=e("strong",null,"awesome community",-1),b=o(" \u{1F4AA} on Discord that loves to help with any specific problems and questions."),y=e("h2",{id:"learn-more",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#learn-more","aria-hidden":"true"},"#"),o(" \u{1F469}\u200D\u{1F3EB} Learn more")],-1),k=o("This website is the home to our wiki for everyone who want to learn Javacord. If you haven't already, you should first take a look at our "),w={href:"https://github.com/Javacord/Javacord",target:"_blank",rel:"noopener noreferrer"},x=o("GitHub page"),g=o(" before you continue with the wiki."),J=e("h2",{id:"discord-server",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#discord-server","aria-hidden":"true"},"#"),o(" \u{1F91D} Discord Server")],-1),D=e("p",null,"Did you know that Javacord has a Discord server? No? Well, now you do! \u{1F609}",-1),L=o("Javacord's Discord community is an excellent resource if you have questions about the library. You can join it by clicking \u{1F449} "),j={href:"https://discord.gg/javacord",target:"_blank",rel:"noopener noreferrer"},B=e("strong",null,"here",-1),I=o(" \u{1F448}.");function N(q,C){const a=n("RouterLink"),s=n("ExternalLinkIcon");return d(),c("div",null,[h,e("p",null,[_,t(a,{to:"/wiki/essential-knowledge/optionals.html"},{default:r(()=>[u]),_:1}),m,t(a,{to:"/wiki/essential-knowledge/completable-futures.html"},{default:r(()=>[f]),_:1}),p,v,b]),y,e("p",null,[k,e("a",w,[x,t(s)]),g]),J,D,e("p",null,[L,e("a",j,[B,t(s)]),I])])}var V=i(l,[["render",N],["__file","index.html.vue"]]);export{V as default}; diff --git a/assets/index.html.6c396006.js b/assets/index.html.6c396006.js new file mode 100644 index 0000000..64635a2 --- /dev/null +++ b/assets/index.html.6c396006.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-15814726","path":"/wiki/","title":"Introduction","lang":"en-US","frontmatter":{"permalink":"/wiki"},"excerpt":"","headers":[{"level":2,"title":"\u{1F4DA} Structure of the wiki","slug":"structure-of-the-wiki","children":[]},{"level":2,"title":"\u{1F91D} Support","slug":"support","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/README.md"}');export{t as data}; diff --git a/assets/index.html.70af15a3.js b/assets/index.html.70af15a3.js new file mode 100644 index 0000000..88694ac --- /dev/null +++ b/assets/index.html.70af15a3.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"","lang":"en-US","frontmatter":{"home":true,"heroImage":"/img/javacord3_banner.png","heroText":null,"tagline":"An easy to use multithreaded library for creating Discord bots in Java.","actions":[{"text":"View Wiki \u2192","link":"/wiki/","type":"primary"}]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4A1} About Javacord","slug":"about-javacord","children":[]},{"level":2,"title":"\u{1F469}\u200D\u{1F3EB} Learn more","slug":"learn-more","children":[]},{"level":2,"title":"\u{1F91D} Discord Server","slug":"discord-server","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"README.md"}');export{e as data}; diff --git a/assets/index.html.8c8749ec.js b/assets/index.html.8c8749ec.js new file mode 100644 index 0000000..519cf18 --- /dev/null +++ b/assets/index.html.8c8749ec.js @@ -0,0 +1 @@ +import{_ as a,r,o as s,c as i,a as e,b as n,e as c,d as t}from"./app.151ccb98.js";const d={},l=c('

    Introduction

    Welcome to the Javacord wiki! \u{1F44B}

    This wiki will help you to get started with your first Discord bot as fast as possible.

    \u{1F4DA} Structure of the wiki

    The wiki is divided into four groups:

    • Getting Started focuses on teaching you how to setup up everything to get the most basic bot working.
    • Basic tutorials contains articles about various concepts and classes of Javacord. Take a look at the headlines of each article and decide yourself, if it is relevant for you.
    • Advanced Topics focuses on some more advanced topics that are not strictly necessary to start working with Javacord, but might become handy later on.
    • Essential Knowledge teaches you the most important Java features/classes that you should know to comfortably work with Javacord. If you already have decent Java knowledge, you can skip this completely.

    \u{1F91D} Support

    While the wiki is great and covers many aspects of Javacord, we highly recommended you to join our Discord server if you have any questions:

    ',8),h=t("Join the "),u={href:"https://discord.gg/javacord",target:"_blank",rel:"noopener noreferrer"},p=t("Javacord server");function f(g,v){const o=r("ExternalLinkIcon");return s(),i("div",null,[l,e("ul",null,[e("li",null,[h,e("strong",null,[e("a",u,[p,n(o)])])])])])}var m=a(d,[["render",f],["__file","index.html.vue"]]);export{m as default}; diff --git a/assets/intellij-gradle.html.4d5164dd.js b/assets/intellij-gradle.html.4d5164dd.js new file mode 100644 index 0000000..49f275b --- /dev/null +++ b/assets/intellij-gradle.html.4d5164dd.js @@ -0,0 +1,42 @@ +import{_ as r,r as a,o as p,c as l,b as s,w as t,a as e,d as n,e as d}from"./app.151ccb98.js";var u="/assets/create-project.f0c107a4.png",k="/assets/select-gradle.959beb91.png",g="/assets/new-project.7658c402.png",h="/assets/new-project-2.3dadb90c.png",v="/assets/new-project-3.bd7a7df1.png",A="/assets/after-finished.b35ece7e.png",b="/assets/new-package.fc4668da.png",m="/assets/new-package-2.9c890d4d.png",f="/assets/new-class.388b2e2e.png",y="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVsAAACXCAYAAAC7kviWAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAA8zSURBVHhe7Z3LkxRFHseJMDbYgxf34p68eNAwXIONxV1tmHF4uQwg8hBQdGcwgIlAcGVZBBGXGXksAkoMzsDIuu7jwMxghKFeOPRc/AO8e+BmePLgaQ/ecuuX1VmVXZ1VU/3Kru76fCM+MV2ZWdVTSfRnfpU1Q6349YXvFAAAdBctW0IIId0LsiWEEA9BtoQQ4iHIlhBCPATZEkKIhyBbQgjxkNyy/dWZ/7UFIYSUOU3JdmhoqCWQLSGk7GlKtmvWrGkJZEsIKXuaku0zzzyj+fnnnzXffPNN1Ca89957ut1uE5AtIaTfsmLFCvXtt9/WthojfTImb5qS7erVqzUzMzNaqjt27IjahDNnzuh2u01YXrb31XRlhf7GNZXpoKVXuacm5HuYuFfbNpH2ipruyjeWdv7dfE9CSFZEpr9Y+UuncLP60tKUbFetWqU5ffq0lqrZXq49U7b3p1UlEEyd24K26aTrWkorsgr3qQTyq/dtl8SXef7IlpBexiXVVkQraUq2Tz75pObkyZNaqmbbcOrUKWd7umzDiq6hiOxYWpft9L1AgnUVdjfEt9z5I1tCeh1brq2KVtKUbB977DHNzZs3tVS3b9+uHn/8cfXxxx+rw4cPq6+//lr98MMP0ThDqmx1VTcRKCUtoWwmJirB5XVtXK0SDC+5433vTViX4dpesm+yLUjK/nFiwckxK5HpEuJzHEfGx+J0jE9aNef5m2M0nqOkfgkibHa1EUJajZFsq6KVNCXbRx99VPPjjz9qvvzyS/XEE0+oxcVF9dNPP6nvv/9eHTlyJBpnyJRt5vpsKMxU4d2bsPpM7DGJ8W3tn3Fcc5zgayRBaatUouPfn45fR8l1/tb7RLHa7fc0cbURQlqOd9k+8sgjLdFuZRvJRo+PKzaNJbe4PUWKWftHaRRpOMZqTzuOJc97QTUeL0VIpWkd06TZ83edY+17qRO5q40Q0lKMaOWr/brZNCXbhx9+uCVSZasl1MSaZZqc6tptseXcvy6JfaLvMSlb13HMewdjI8kG42S8s4Jt4vxTzzHM/aByFgnbx3K1EULyxyVXV1ueNCXbhx56qCXSZRukVq3VCSEQi/tuvGw7KjY5hpGZlpLZJ+f+dUnuE0Qf06omM44jgksuH0xMuJYrasl7/qnnGEe/d6LR1UYIWT4i0zSpZvWlpSnZPvjggxq5OebC9CfJlK0kklmNqArMEl8NbamwQgz3nQiqynif6KaSsZlzfzuO9wwi0oplqxvcx0mKMLntSvJYzvNPOce6pYVa5etqI4Q0Ffn8ZMlU+mRM3jQl25UrV7bEsrIlhJABT1OyfeCBB1oC2RJCyp6mZNsOhBBS5uSWLSGEkNaDbAkhxEOQLSGEeAiyJYQQD0G2hBDiIciWEEI8BNkSQoiHIFtCCPGQSLZrn1sHAABdAtkCAHgA2QIAeADZAgB4ANkCAHgA2QIAeADZAgB4ANkCAHgA2QIAeKB0sh164U9qaPx9NfT6JQAYFOQzHXy2XZ/5olA+2SJagMEk+Gy7PvNFoXyyrf3DPP/mLAAMCOZz7frMF4VSy3bT6BYA6HOQbUFBtgCDBbItKMgWYLBAtgUF2QIMFsi2oCBbgMEC2RYUZAswWLQr2/Wb/qiuXL2mNm3e4uw3fPHFF872vCBbAOhr2pHt0Mh69dH161qk09M31HPrNzjHCT2TbWX4mJqpVlV19pijfV6d2zNS114UsmS7cfNxdUvOae54Q5+w/+KiqlYX1fmxUWe/sHHsolpYZgwAdI52ZPu3c5NaoobJqfQ/jOixbOfVnfmqmjkai7X/ZbuoFhYaZRmJGJECFIp2ZGtYWFhwttv0XLbnjk4Fwp1Su4dDuQ6CbG/NBcK9OFbfd3wuqHjndD+yBSgO5ZFtINUjs1V1Z3JfQ7tsS9/S0pLGLDnUiTqoFqVPquPdk/PRWHM8PX5PPK5anVVHArFXjs6qqiX5vOSR7fkx+Tqn3tocSnXj5jF1Xle7pj9sf2vOOrfa0kN8jNH49cW5aFxS4gDQHqWSbdrr7PHBJXlNliJOEVEkbBGpkWrieNIn47or21Et0lvHa7KVddiFi2q/1Z+2X+PreA1YV8eWxAGgfUolW70t8gsqV1d7VP3VydYak7FtV7XRcRI35Zohr2xjwcbitfv1+ECe8bmlydYan9gGgPYpn2yH96lz+mZZUpSmQpX+WnvTsg2PYd67HXLL1iwdHI+lW9evf+sgrFLjZYbEGGQL0HVKJ1vdVqtCowrWutQP+2rtzchWv47XhKP36vIygt6uVa5mnbVOpLIkYCRs/boXsgXwSzdlK793K79/K6I1XPvwo8zfx02jo7IV5CZXvFwQVrv6Mnt+Vs20UNnq7cRSgl6u8CFbXbHaN8pskUpf7dyCMbeobAF6QrcrW/kLs9u3b2vR3rx1S63buMk5bjlalm2/kiVbAOg/ui1bYfPWF9SHH11f9k96s2hZtqbKzMK1X69BtgCDRSdk6wMqWwDoa5BtQUG2AIMFsi0oyBZgsEC2BQXZAgwWyLag2LIFgMEA2RaQofH3o38YABgggs+26zNfFMon2xf+hHABBg35TAefbddnviiUTrYAAL0A2QIAeADZAgB4ANkCAHgA2QIAeADZAgB4ANkCAHgA2QIAeADZAgB4oLSylUdb7H/1VXXw4CF16BAAgBtxhLii1cfhGEor21f271cvv7JfrdvQ3gQCwKCzXm3bvkM7w92fj9LK9uDBg2oE0QJATsQZrva8lFa2cnngagcAcNGuM5AtAEAOkG2L5J64jaNq+NAJNXzmunruwj818lrapM+5DwAMHMi2RfJM3NDeA2r4/Kdq+O//cRP0yRjXvgAwWCDbFllu4rRoLzkEmyQYg3ABBp+eybYyfEzNVOfVuT0jte196tx8VVVnj6nKnil1x+rLS/KY3SRz4jaNZle0SYKxso/zWDXM/CwtLWmq81Nq9/CI13MGgNYpjGyPzFbVncl9DeOaoSiy1Wu0dUL9t1r9+gn11Ja9atW2/WrVzvFEf4Cs4TqOJYQ/fKpq5mh8XtJ2LthGtgD9QSFku3tyXle0rnHNUBjZyg0wS6SrX/+r+u2ucTV0IaPaDfZxHctUtLZo6/uRLYAvjp94W/3js/+mIv2u/YTey3ZyNrokbugLBGKPM5fQdgVsqr6wfSre72jjcTtJpmwTSwi/2bRTPXvqal1bA7KU4DhWeH6z6kjKeSRlK1cI0VJD7QdYcglCxO1qs48LAI0MjaxX75yddIpW2qXftZ/QY9mGH/bkB71RtuFaru4TidbkY/rM/rpCLqRsd6hn375S19ZAlmwzziMpW1e7novElYOrDQCWZ3jdBjV14XKdaGVb2l3jDb2vbI823gyrE0VCJnV9CRElx3aTTNkmlhF+d+AttWrnWGvLCE1WtiJRU61GP3hq1b/riqDddXKAMjKy4Xl1+dp1LVr5KtuucTa9l63IQKosSyh1fUmZ2H1FlW3DDbJ/qdUH/qKeGt2tntq6V63afSDRH5Byg6yZNVtbzOF+9XMhlX/ySsLVBgDLs3HzqK5o5aurP0khZCvbegmgJs46gSTGNfYVbxmh07/6ZapVW4gi1uRvI9jnHIo3njeDzFGymnW1AUBnKYxsBbm5I7LYNXQ0IdR4XMO2ddlclBtkQqf/qMFc9ptzdf9gim96Vedn1UytsrXnKFrvdrS53hcAOkPPZNvv5Jk4LdysCjfo46/HAMpBz2RrqioXrvFFI/fE8R/RAEAAlW2LtDtxAFAukG2LIFsAaAZk2yI8FgcAmoHH4rQID3wEgHzwwMe24FHmAJAHHmUOANBHIFsAAA8gWwAADyBbAAAPIFsAAA8gWwAADyBbAAAPIFsAAA8gWwAADyBbAAAPIFsAAA8gWwAADyBbAAAPIFsAAA8gW+hrhtZtUBvGz6gtb15V245/CAEyFzInMjeuOctieGSd2rVnr3ptfFyNHTgAATIXMicyN645ywuyhb5mYyCV0TevqbVjF9QfXplSv3+53MgcyFzInMjcuOYsCy3asXG146W9atuOXRAgcyFzInPjmrO8IFvoa7b8+apaO37BKZ4yI3Oi58YxZ1m8NjamdgZScUmnzMicyNy45iwvyBb6GrlspqJtROZE5sY1Z1nIZbNLNrBLz41rzvKCbKGvEaG4ZAPIttMgWyg1yDYdZNtZkC2UGmSbDrLtLMgWSg2yTQfZdhZkC6WmV7LdfvUrtbS0FHHn6pWo7+l9M+pGtaqq1QV1eN+kbjv8aVWPq37+mdpea+s2RZDt1hcPq0t3w3PX53/3A3XgxZ3OsZ1m64un1Fz1rrp0sDPvh2yh1PRCtkacN94Npfn0uwuhSD6dCbcTso36PYpW6LVstx78QC0G8zB3MpadtF2ytrsJsgXoIL5l+/Qbn6k7ItKaWA0i4Gr1K3X2jUCudbI1r8M+e59u00vZmorWFq1vkC1AB/EtW7N8YKraZLssJ9iyvVGrgu1lBl/0VLa6qr2tTmQsGZz4xFpe+OSUbosEefl21Ld4+XC0T3JZwsjcVNH6WLX3RbYAHaQosjVLBbZsjRDstVuf9Fy2OddnbSmGr4MfVEa+J29b8gxFa8s3ub/ZR8YgW4AOUrTKVtrtyvZsrd33eq1Q9MpWpBj/QLJla4nTFnHKMe2qNjpeIGtkC9BBfMs2eTPMkL5me0Wd/TwUge+lhJ7Kdpk1W1uc4dh2ZetoR7YAncO3bAVbrLJtLyHo7TrZBvI1N9U83yTrpWwFU7m6fhtBLw/UlhlCWeaQbeYywvLLC+2CbKHU9EK2glk2MNhVa1K20hb9nq3H9dtey1YIRWqtXxvBWje6qndvq7kclW28HR/PdYNMH5NlBIDO0ivZ9gNFkO0ggWyh1CDbdJBtZ0G2UGqQbTrItrMgWyg1IhT+8/BG+M/DOw+yhVLDY3Hc8FiczsJjcaD0bBh/hwc+WsgcmAc+yty45iyLXS/t0Q835IGPMdEDH4O5cc1ZXpAt9DWV4RG1fuw0jzK3kLmQOZG5cc1ZFmuGhtWu3S/xKHML/SjzYE5kblxzlhdkC31NZe2werayBhzI3LjmLAvmM51W5tMG2ULfowWxZq3zA1JKgrloRwzMZ4I259OAbAEAPIBsAQA8gGwBADyAbAEAPIBsAQA8gGwBADyAbAEAPIBsAQA8gGwBADwQyRYAALrJd+r/zHVjD33sbMEAAAAASUVORK5CYII=",w="/assets/run-the-bot.f048bed2.png";const j={},V=e("h1",{id:"intellij-gradle",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#intellij-gradle","aria-hidden":"true"},"#"),n(" IntelliJ + Gradle")],-1),q=n("This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Gradle. If you are already familiar with IntelliJ and Gradle, you can just see the artifact locations at "),Z=n("Download / Installation"),B=n("."),C=d('

    \u{1F527} Setup

    1. Start IntelliJ

    2. Create a new project (File -> New -> Project)

    3. Select Gradle

    4. Make sure to select an SDK which is 1.8 (or greater)

    5. Click Next

    6. Enter a group id (e.g. com.github.yourname)

    You can choose whatever you want

    7. Enter an artifact id (e.g. myfirstbot)

    You can choose whatever you want

    8. Click Next

    9. Check Use auto-import

    10. Click Next

    11. Click Finish

    12. Locate the build.gradle file and open it

    12. Add the Javacord dependency. Your build.gradle file should now look like this

    plugins {
    +    id 'java'
    +}
    +
    +group 'com.github.yourname'
    +version '1.0-SNAPSHOT'
    +
    +sourceCompatibility = 1.8
    +
    +repositories {
    +    mavenCentral()
    +}
    +
    +dependencies {
    +    implementation 'org.javacord:javacord:$latest-version'
    +}
    +

    13. Create a new package in the src/main/java folder

    14. Create a new class inside this package

    15. You can now start coding!

    Example code:

    package com.github.yourname;
    +
    +import org.javacord.api.DiscordApi;
    +import org.javacord.api.DiscordApiBuilder;
    +
    +public class Main {
    +
    +    public static void main(String[] args) {
    +        // Insert your bot's token here
    +        String token = "your token";
    +
    +        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +
    +        // Print the invite url of your bot
    +        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
    +    }
    +
    +}
    +

    \u{1F3C3}\u200D\u2640\uFE0F Run the code

    You can run your code by clicking on the small green arrow

    ',32);function I(x,W){const o=a("LatestVersion"),i=a("ClientOnly"),c=a("RouterLink");return p(),l("div",null,[V,s(i,null,{default:t(()=>[s(o)]),_:1}),e("p",null,[q,s(c,{to:"/wiki/getting-started/download-installation.html"},{default:t(()=>[Z]),_:1}),B]),C])}var N=r(j,[["render",I],["__file","intellij-gradle.html.vue"]]);export{N as default}; diff --git a/assets/intellij-gradle.html.9ec9aa86.js b/assets/intellij-gradle.html.9ec9aa86.js new file mode 100644 index 0000000..cdd7a73 --- /dev/null +++ b/assets/intellij-gradle.html.9ec9aa86.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-6d1d378b","path":"/wiki/getting-started/setup/intellij-gradle.html","title":"IntelliJ + Gradle","lang":"en-US","frontmatter":{},"excerpt":"","headers":[{"level":2,"title":"\u{1F527} Setup","slug":"setup","children":[]},{"level":2,"title":"\u{1F3C3}\u200D\u2640\uFE0F Run the code","slug":"run-the-code","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/setup/intellij-gradle.md"}');export{e as data}; diff --git a/assets/intellij-maven.html.74a49f6d.js b/assets/intellij-maven.html.74a49f6d.js new file mode 100644 index 0000000..1579679 --- /dev/null +++ b/assets/intellij-maven.html.74a49f6d.js @@ -0,0 +1,46 @@ +import{_ as i,r as t,o as l,c as u,b as s,w as e,a,d as n,e as r}from"./app.151ccb98.js";const d={},k=a("h1",{id:"intellij-maven",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#intellij-maven","aria-hidden":"true"},"#"),n(" IntelliJ + Maven")],-1),g=n("This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Maven. If you are already familiar with IntelliJ and Maven, you can just see the artifact locations at "),h=n("Download / Installation"),m=n("."),v={class:"custom-container tip"},b=a("p",{class:"custom-container-title"},"Info",-1),f=n("We recommend to use "),_=n("Intellij + Gradle"),w=n(" unless you already have experience with one of the other IDEs or build managers."),y=r(`

    \u{1F527} Setup

    1. Start IntelliJ

    2. Create a new project (File -> New -> Project)

    3. Select Maven

    4. Make sure to select an SDK which is 1.8 (or greater)

    5.* Click Next

    6. Enter a group id (e.g. com.github.yourname)

    7. Enter an artifact id (e.g. myfirstbot)

    8. Click Next

    9. Click on Finish

    10. Your project should now look like this. First click on Enable Auto-Import

    11. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    +<project xmlns="http://maven.apache.org/POM/4.0.0"
    +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    +    <modelVersion>4.0.0</modelVersion>
    +
    +    <groupId>your.package.name</groupId>
    +    <artifactId>myfirstbot</artifactId>
    +    <version>1.0-SNAPSHOT</version>
    +
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.javacord</groupId>
    +            <artifactId>javacord</artifactId>
    +            <version>$latest-version</version>
    +            <type>pom</type>
    +        </dependency>
    +    </dependencies>
    +
    +</project>
    +

    12. Create a new package

    13. Create a new class inside this package

    14. You can now start coding! Example code:

    package com.github.yourname;
    +
    +import org.javacord.api.DiscordApi;
    +import org.javacord.api.DiscordApiBuilder;
    +
    +public class Main {
    +
    +    public static void main(String[] args) {
    +        // Insert your bot's token here
    +        String token = "your token";
    +
    +        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +
    +        // Print the invite url of your bot
    +        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
    +    }
    +    
    +}
    +

    \u{1F3C3}\u200D\u2640\uFE0F Run the code

    You can run your code by clicking on the small green arrow

    \u{1F6A7} Possible problems

    Note: If you get the following error:

    you have to change your language level to 1.8

    `,30);function x(j,q){const p=t("LatestVersion"),c=t("ClientOnly"),o=t("RouterLink");return l(),u("div",null,[k,s(c,null,{default:e(()=>[s(p)]),_:1}),a("p",null,[g,s(o,{to:"/wiki/getting-started/download-installation.html"},{default:e(()=>[h]),_:1}),m]),a("div",v,[b,a("p",null,[f,s(o,{to:"/wiki/getting-started/setup/intellij-gradle.html"},{default:e(()=>[_]),_:1}),w])]),y])}var C=i(d,[["render",x],["__file","intellij-maven.html.vue"]]);export{C as default}; diff --git a/assets/intellij-maven.html.8bb18166.js b/assets/intellij-maven.html.8bb18166.js new file mode 100644 index 0000000..20bdefb --- /dev/null +++ b/assets/intellij-maven.html.8bb18166.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-0ae94875","path":"/wiki/getting-started/setup/intellij-maven.html","title":"IntelliJ + Maven","lang":"en-US","frontmatter":{},"excerpt":"","headers":[{"level":2,"title":"\u{1F527} Setup","slug":"setup","children":[]},{"level":2,"title":"\u{1F3C3}\u200D\u2640\uFE0F Run the code","slug":"run-the-code","children":[]},{"level":2,"title":"\u{1F6A7} Possible problems","slug":"possible-problems","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/setup/intellij-maven.md"}');export{e as data}; diff --git a/assets/lambdas.html.83e4c5bc.js b/assets/lambdas.html.83e4c5bc.js new file mode 100644 index 0000000..2a888d1 --- /dev/null +++ b/assets/lambdas.html.83e4c5bc.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-22528d43","path":"/wiki/essential-knowledge/lambdas.html","title":"Lambdas","lang":"en-US","frontmatter":{"keywords":["lambdas"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4DA} Further Read","slug":"further-read","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/essential-knowledge/lambdas.md"}');export{e as data}; diff --git a/assets/lambdas.html.c0cde88c.js b/assets/lambdas.html.c0cde88c.js new file mode 100644 index 0000000..ed54660 --- /dev/null +++ b/assets/lambdas.html.c0cde88c.js @@ -0,0 +1,19 @@ +import{_ as i,r as o,o as p,c as l,a,b as s,w as r,d as n,e as t}from"./app.151ccb98.js";const u={},d=a("h1",{id:"lambdas",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#lambdas","aria-hidden":"true"},"#"),n(" Lambdas")],-1),v=n("Lambdas are used to implement "),k={href:"https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html",target:"_blank",rel:"noopener noreferrer"},h=n("functional interfaces"),m=n(". Simply said, functional interfaces are interfaces with a single method definition. All listeners in Javacord are functional interfaces and look like this internally (simplified):"),_=t(`
    @FunctionalInterface
    +public interface MessageCreateListener {
    +    void onMessageCreate(MessageCreateEvent event);
    +}
    +
    `,1),b=n("Before Java 8, you would have implemented this kind of listener as an "),f={href:"https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html",target:"_blank",rel:"noopener noreferrer"},g=n("anonymous class"),w=n(", which would look like this:"),j=t(`
    api.addMessageCreateListener(new MessageCreateListener() {
    +    @Override
    +    public void onMessageCreate(MessageCreateEvent event) {
    +        // Do stuff
    +        event.pinMessage();
    +    }
    +});
    +

    In Java 8, this can be replaced with a lambda expression, which does exactly the same thing, but in a more readable fashion. The method parameter (in this case event) is written in front of the -> arrow, and the method body is written after it.

    api.addMessageCreateListener(event -> {
    +    // Do stuff
    +    event.pinMessage();
    +});
    +

    TIP

    If the method has more than one parameter, it would look like this:

    (param1, param2) -> { ... }
    +

    There's even a shorter version: If you are only executing one statement, you can get rid of the { } brackets as well:

    api.addMessageCreateListener(event -> event.pinMessage());
    +
    `,6),x=n('However, the above method can be shortened even more, by replacing the lambda expression with a so called "'),y={href:"https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html",target:"_blank",rel:"noopener noreferrer"},M=n("method reference"),C=n('".'),L=t(`
    api.addMessageCreateListener(MessageEvent::pinMessage);
    +
    `,1),I=n("There are also plenty classes in Java 8, that make use of lambda expressions. One example would be the Optional class, which is explained "),O=n("here"),E=n("."),T=a("h2",{id:"further-read",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#further-read","aria-hidden":"true"},"#"),n(" \u{1F4DA} Further Read")],-1),B=n("This tutorial only focuses on the absolute basics. For an in-depth introduction to lambda expressions, you can take a look at "),F={href:"https://www.oracle.com/technical-resources/articles/java/architect-lambdas-part1.html",target:"_blank",rel:"noopener noreferrer"},J=n("Oracle's article about lambda expressions"),N=n(".");function V(R,D){const e=o("ExternalLinkIcon"),c=o("RouterLink");return p(),l("div",null,[d,a("p",null,[v,a("a",k,[h,s(e)]),m]),_,a("p",null,[b,a("a",f,[g,s(e)]),w]),j,a("p",null,[x,a("a",y,[M,s(e)]),C]),L,a("p",null,[I,s(c,{to:"/wiki/essential-knowledge/optionals/"},{default:r(()=>[O]),_:1}),E]),T,a("p",null,[B,a("a",F,[J,s(e)]),N])])}var A=i(u,[["render",V],["__file","lambdas.html.vue"]]);export{A as default}; diff --git a/assets/lifecycle-state-diagram.5bc421c4.svg b/assets/lifecycle-state-diagram.5bc421c4.svg new file mode 100644 index 0000000..c85d10e --- /dev/null +++ b/assets/lifecycle-state-diagram.5bc421c4.svg @@ -0,0 +1,2 @@ + +
    Logged in
    Logged in
    Connection Lost
    Connection Lost
    Demanded Shutdown
    Demanded Shutdown
    Connected
    Connected
    Resuming possible
    Resuming possible
    Resuming not possible
    Resuming not possible
    Demanded Shutdown
    Demanded Shutdown
    Disconnected
    Disconnected
    Reconnect failed
    Reconnect failed
    Reconnected
    Reconnected
    Reconnecting
    Reconnecting
    Resumed
    Resumed
    Resuming failed
    Resuming failed
    Resuming
    Resuming
    \ No newline at end of file diff --git a/assets/lifecycle_command.b2e278fd.png b/assets/lifecycle_command.b2e278fd.png new file mode 100644 index 0000000..1435a25 Binary files /dev/null and b/assets/lifecycle_command.b2e278fd.png differ diff --git a/assets/lifecycle_interaction.38162b05.png b/assets/lifecycle_interaction.38162b05.png new file mode 100644 index 0000000..e1bbc25 Binary files /dev/null and b/assets/lifecycle_interaction.38162b05.png differ diff --git a/assets/listeners.html.0ec76197.js b/assets/listeners.html.0ec76197.js new file mode 100644 index 0000000..a2787fd --- /dev/null +++ b/assets/listeners.html.0ec76197.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-4256bfd9","path":"/wiki/basic-tutorials/listeners.html","title":"Listeners","lang":"en-US","frontmatter":{"keywords":["creating listeners","listener creation","ListenerManager","removeListener","remove listener"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F468}\u200D\u{1F527} Creating listeners","slug":"creating-listeners","children":[{"level":3,"title":"Inline Listeners","slug":"inline-listeners","children":[]},{"level":3,"title":"In their own class","slug":"in-their-own-class","children":[]},{"level":3,"title":"Before logging in","slug":"before-logging-in","children":[]},{"level":3,"title":"Object listeners","slug":"object-listeners","children":[]}]},{"level":2,"title":"\u{1F4A3} Removing listeners","slug":"removing-listeners","children":[{"level":3,"title":"Using the returned ListenerManager","slug":"using-the-returned-listenermanager","children":[]},{"level":3,"title":"Using the removeListener(...) method","slug":"using-the-removelistener-method","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/listeners.md"}');export{e as data}; diff --git a/assets/listeners.html.86bff751.js b/assets/listeners.html.86bff751.js new file mode 100644 index 0000000..782ce1e --- /dev/null +++ b/assets/listeners.html.86bff751.js @@ -0,0 +1,53 @@ +import{_ as n,o as s,c as a,e}from"./app.151ccb98.js";const t={},p=e(`

    Listeners

    \u{1F468}\u200D\u{1F527} Creating listeners

    Creating listeners is extremely easy in Javacord. You can either use Java 8's lambda expressions to register listeners inline or just create a new class for them, if an inline listener would get too messy.

    Inline Listeners

    api.addMessageCreateListener(event -> {
    +    if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +        event.getChannel().sendMessage("Pong!");
    +    }
    +});
    +

    In their own class

    api.addListener(new MyListener());
    +

    and

    public class MyListener implements MessageCreateListener {
    +
    +    @Override
    +    public void onMessageCreate(MessageCreateEvent event) {
    +        if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +            event.getChannel().sendMessage("Pong!");
    +        }
    +    }
    +
    +}
    +

    Before logging in

    Sometimes it might be useful to add listeners before calling the DiscordApiBuilder#login() method.

    DiscordApi api = new DiscordApiBuilder()
    +        // An inline listener
    +        .addMessageCreateListener(event -> {
    +            Message message = event.getMessage();
    +            if (message.getContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        })
    +        .addServerBecomesAvailableListener(event -> {
    +            System.out.println("Loaded " + event.getServer().getName());
    +        })
    +        // A listener in their own class
    +        .addListener(new MyListener())
    +         // Alternative syntax that can be used for classes that require a DiscordApi parameter in their constructor
    +        .addListener(MyListener::new)
    +        .setToken("top secret")
    +        .setWaitForServersOnStartup(false)
    +        .login()
    +        .join();
    +

    Note: In most cases, it's enough to add listeners after logging in

    Object listeners

    Another cool feature is the ability to attach listeners directly to objects. An example where this can be useful is, for example, reacting to reactions. The following code would delete the message if someone adds a \u{1F44E} reaction.

    message.addReactionAddListener(event -> {
    +    if (event.getEmoji().equalsEmoji("\u{1F44E}")) {
    +        event.deleteMessage();
    +    }
    +}).removeAfter(30, TimeUnit.MINUTES);
    +

    Seems like the bot is very sensitive to criticism.

    \u{1F4A3} Removing listeners

    There are two ways to remove a listener:

    Using the returned ListenerManager

    Every time you register a listener, a ListenerManager is returned which can be used to unregister the listener:

    ListenerManager<MessageCreateListener> listenerManager = api.addMessageCreateListener(event -> {
    +    // Do stuff
    +});
    +
    +listenerManager.remove();
    +

    This manager also has some utility methods. You can, for example, remove a listener after a given time, which can be useful for object listeners:

    message.addReactionAddListener(event -> {
    +  // Do stuff
    +}).removeAfter(30, TimeUnit.MINUTES);
    +

    Using the removeListener(...) method

    You can remove any listener using the removeListener(...) method:

    MyListener listener = new MyListener();
    +api.addListener(listener);
    +// ...
    +api.removeListener(listener);
    +
    `,27),o=[p];function c(i,l){return s(),a("div",null,o)}var r=n(t,[["render",c],["__file","listeners.html.vue"]]);export{r as default}; diff --git a/assets/logger-config.html.c3000dce.js b/assets/logger-config.html.c3000dce.js new file mode 100644 index 0000000..fdf7a5c --- /dev/null +++ b/assets/logger-config.html.c3000dce.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-33173f0e","path":"/wiki/basic-tutorials/logger-config.html","title":"Logger Configuration","lang":"en-US","frontmatter":{"keywords":["log4j","log4j2","slf4j","logback","logging","logger"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F948} Fallback Logger","slug":"fallback-logger","children":[]},{"level":2,"title":"\u{1F947} Using a Proper Logging Framework","slug":"using-a-proper-logging-framework","children":[{"level":3,"title":"Adding a Logging Framework","slug":"adding-a-logging-framework","children":[]},{"level":3,"title":"Configure Your Logging Framework","slug":"configure-your-logging-framework","children":[]},{"level":3,"title":"Logging the Relevant Shard","slug":"logging-the-relevant-shard","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/logger-config.md"}');export{e as data}; diff --git a/assets/logger-config.html.f8d1f3ef.js b/assets/logger-config.html.f8d1f3ef.js new file mode 100644 index 0000000..efec042 --- /dev/null +++ b/assets/logger-config.html.f8d1f3ef.js @@ -0,0 +1,13 @@ +import{_ as s,r as l,o as t,c as r,a,b as o,d as e,e as i}from"./app.151ccb98.js";const c={},g=a("h1",{id:"logger-configuration",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#logger-configuration","aria-hidden":"true"},"#"),e(" Logger Configuration")],-1),d=e("Logging is an important tool to keep track of what is going on in your application. Javacord uses the "),p={href:"https://logging.apache.org/log4j/2.x/manual/api.html",target:"_blank",rel:"noopener noreferrer"},u=e("Log4j 2 API"),h=e(", which allows you to use your favorite logging framework to log messages in your own code and have all logging messages end up in the same destination. In case you do not add your own logging framework, a fallback logger is used that logs to the console."),m=a("br",null,null,-1),k=e(" If you want more control, add a proper logging framework that supports your needs and configure it accordingly. You can for example configure log messages on a per-class level, change log levels during runtime, or log to a file or database."),f=i(`

    \u{1F948} Fallback Logger

    Javacord's fallback logger is a simple Log4j logger which always logs INFO level and higher. It allows you to enable DEBUG and TRACE logging manually. As log levels are hierarchical, enabling TRACE will also implicitly enable DEBUG, and disabling DEBUG will also implicitly disable TRACE.

    // Enable debug logging
    +FallbackLoggerConfiguration.setDebug(true);
    +
    +// Enable trace logging
    +FallbackLoggerConfiguration.setTrace(true);
    +

    Changing the log level of the fallback logger only affects newly created loggers. Pre-existing loggers will not have their log level changed. So if you want to configure the fallback logger, you should do this as one of the first actions in your bot code. If you want to change log levels during runtime, you should use a proper logging framework like Log4j 2 Core or another library that supports this.

    All fallback logger messages are printed to the standard output stream (System.out) and thus usually to your console. If you want to log to a file, database, or anything else, you should consider using a proper logging framework which allows you to configure this behavior.

    This is how a log line from the fallback logger will look like:

    <time with date            ><level><logger name, usually the logging class              > <message            > <the thread context, here the shard number>
    +2018-08-03 20:00:06.080+0200 DEBUG org.javacord.core.util.gateway.DiscordWebSocketAdapter Received HELLO packet {shard=0}
    +

    \u{1F947} Using a Proper Logging Framework

    Adding a Logging Framework

    Adding a logging framework of your choice is very straightforward. You can just add it as a dependency, and it will be detected by Log4j automatically. The following example adds Log4j 2 using Gradle:

    dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-core:2.17.0' }
    +

    You can also use an SLF4J compatible logging framework using log4j-to-slf4j. The following example adds Logback Classic using Gradle:

    dependencies {
    +    runtimeOnly 'org.apache.logging.log4j:log4j-to-slf4j:2.17.0'
    +    runtimeOnly 'ch.qos.logback:logback-classic:1.2.3'
    +}
    +

    Configure Your Logging Framework

    `,14),b=a("strong",null,"Log4j 2",-1),v=e(": "),y={href:"https://logging.apache.org/log4j/2.x/manual/configuration.html",target:"_blank",rel:"noopener noreferrer"},_=e("Log4j configuration"),w=a("strong",null,"Logback Classic",-1),L=e(": "),x={href:"https://logback.qos.ch/manual/configuration.html",target:"_blank",rel:"noopener noreferrer"},j=e("Logback configuration"),C=a("h3",{id:"logging-the-relevant-shard",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#logging-the-relevant-shard","aria-hidden":"true"},"#"),e(" Logging the Relevant Shard")],-1),E=a("p",null,[e("Javacord adds the relevant shard to each log message. The facility that stores this information has a different name depending on which logging framework you use. For Log4j 2, this is called Thread Context Map and can be added in a pattern layout with "),a("code",null,"%X{shard}"),e(", or you can add the whole thread context map by using "),a("code",null,"%X"),e(". For Logback Classic, it is called MDC and can be added with the same pattern expressions as for Log4j.")],-1);function F(T,A){const n=l("ExternalLinkIcon");return t(),r("div",null,[g,a("p",null,[d,a("a",p,[u,o(n)]),h,m,k]),f,a("ul",null,[a("li",null,[b,v,a("a",y,[_,o(n)])]),a("li",null,[w,L,a("a",x,[j,o(n)])])]),C,E])}var B=s(c,[["render",F],["__file","logger-config.html.vue"]]);export{B as default}; diff --git a/assets/message-builder.html.02c7cb1d.js b/assets/message-builder.html.02c7cb1d.js new file mode 100644 index 0000000..51402f7 --- /dev/null +++ b/assets/message-builder.html.02c7cb1d.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-29bd20c3","path":"/wiki/basic-tutorials/message-builder.html","title":"Using the MessageBuilder","lang":"en-US","frontmatter":{"keywords":["create messages","message creation","sendMessage"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F575}\uFE0F\u200D\u2640\uFE0F Example","slug":"example","children":[]},{"level":2,"title":"\u{1F4CD} Allowed Mentions","slug":"allowed-mentions","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/message-builder.md"}');export{e as data}; diff --git a/assets/message-builder.html.2ab31ad9.js b/assets/message-builder.html.2ab31ad9.js new file mode 100644 index 0000000..44061a5 --- /dev/null +++ b/assets/message-builder.html.2ab31ad9.js @@ -0,0 +1,27 @@ +import{_ as n,o as s,c as a,e as t}from"./app.151ccb98.js";const e={},p=t(`

    Using the MessageBuilder

    The MessageBuilder class is a more powerful alternative to the TextChannel#sendMessage(...) method.

    It can be used to construct more complex messages and supports some additional features that are not possible with a simple TextChannel#sendMessage(...) call.

    \u{1F575}\uFE0F\u200D\u2640\uFE0F Example

    The following code

    new MessageBuilder()
    +    .append("Look at these ")
    +    .append("awesome", MessageDecoration.BOLD, MessageDecoration.UNDERLINE)
    +    .append(" animal pictures! \u{1F603}")
    +    .appendCode("java", "System.out.println(\\"Sweet!\\");")
    +    .addAttachment(new File("C:/Users/Bastian/Pictures/kitten.jpg"))
    +    .addAttachment(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
    +    .setEmbed(new EmbedBuilder()
    +            .setTitle("WOW")
    +            .setDescription("Really cool pictures!")
    +            .setColor(Color.ORANGE))
    +    .send(channel);
    +

    will be displayed like this:

    \u{1F4CD} Allowed Mentions

    The allowed mentions object lets you control what should be mentioned (pinged) in a message if it contains mentions.

    The following code will ping:

    • The user0
    • All mentioned roles in the message

    And will not ping:

    • @everyone and @here
    • The user1
    AllowedMentions allowedMentions = new AllowedMentionsBuilder()
    +                .addUser(user0.getId())
    +                .setMentionRoles(true)
    +                .setMentionEveryoneAndHere(false)
    +                .build();
    +
    +        new MessageBuilder()
    +                .setAllowedMentions(allowedMentions)
    +                .append(user0.getMentionTag())
    +                .append(user1.getMentionTag())
    +                .append(role.getMentionTag())
    +                .append(role2.getMentionTag())
    +                .append("@everyone")
    +                .send(channel);
    +

    If you add a user to the mentions object and set setMentionUsers(true) it will ping every mentioned user. The same applies for setMentionRoles(true)

    `,16),o=[p];function c(i,l){return s(),a("div",null,o)}var d=n(e,[["render",c],["__file","message-builder.html.vue"]]);export{d as default}; diff --git a/assets/new-class.388b2e2e.png b/assets/new-class.388b2e2e.png new file mode 100644 index 0000000..986c828 Binary files /dev/null and b/assets/new-class.388b2e2e.png differ diff --git a/assets/new-package-2.9c890d4d.png b/assets/new-package-2.9c890d4d.png new file mode 100644 index 0000000..4f265ad Binary files /dev/null and b/assets/new-package-2.9c890d4d.png differ diff --git a/assets/new-package.fc4668da.png b/assets/new-package.fc4668da.png new file mode 100644 index 0000000..934ebaf Binary files /dev/null and b/assets/new-package.fc4668da.png differ diff --git a/assets/new-project-2.3dadb90c.png b/assets/new-project-2.3dadb90c.png new file mode 100644 index 0000000..7bcf819 Binary files /dev/null and b/assets/new-project-2.3dadb90c.png differ diff --git a/assets/new-project-3.bd7a7df1.png b/assets/new-project-3.bd7a7df1.png new file mode 100644 index 0000000..290b22f Binary files /dev/null and b/assets/new-project-3.bd7a7df1.png differ diff --git a/assets/new-project.7658c402.png b/assets/new-project.7658c402.png new file mode 100644 index 0000000..4e3fd69 Binary files /dev/null and b/assets/new-project.7658c402.png differ diff --git a/assets/optionals.html.3b46a951.js b/assets/optionals.html.3b46a951.js new file mode 100644 index 0000000..41f0651 --- /dev/null +++ b/assets/optionals.html.3b46a951.js @@ -0,0 +1,55 @@ +import{_ as c,r as o,o as i,c as l,a as s,b as a,w as u,d as n,e}from"./app.151ccb98.js";const r={},d=s("h1",{id:"optionals",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#optionals","aria-hidden":"true"},"#"),n(" Optionals")],-1),k={class:"custom-container warning"},m=s("p",{class:"custom-container-title"},"WARNING",-1),v=n("This tutorial assumes that you are familiar with lambda expressions. Take a look at the "),h=n("lambda introduction"),g=n(" first, if you are not!"),f=e(`

    \u{1F4AA} Motivation

    The Optional class is widely used in Javacord. Basically, every method that might return a null value will return an Optional in Javacord instead. Optionals help you to avoid NullPointerExceptions and make it very clear if a method may not have a result. Here's a small example:

    The old way of doing it

    User user = api.getCachedUserById(123L);
    +if (user != null) {
    +  user.sendMessage("Hi!");
    +}
    +

    The new way of doing it

    api.getCachedUserById(123L).ifPresent(user -> 
    +  user.sendMessage("Hi!")
    +);
    +

    You can imagine an Optional like a box \u{1F4E6} that may or may not contain a value. Before accessing this value, you have to "unpack" this box first.

    \u{1F4D6} Methods

    `,8),b=n("The Optional class has many useful methods which can all be found in the "),y={href:"https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html",target:"_blank",rel:"noopener noreferrer"},_=n("JavaDocs"),x=n(". This tutorial gives a short introduction to the most common ones."),j=e(`

    get()

    The get method returns the value of the Optional or throws a NoSuchElementException if it does not contain a value.

    TextChannel channel = api.getTextChannelById(123L).get();
    +channel.sendMessage("Hi");
    +

    DANGER

    You should never use this method blindly but only if you are 100% sure the optional contains a value.

    Every time you use this method carelessly, a kitten dies \u{1F640}! True story.

    isPresent()

    The isPresent methods checks, if the Optional contains a value.

    Optional<TextChannel> channel = api.getTextChannelById(123L);
    +if (channel.isPresent()) {
    +  // A text channel with the id 123 exists. It's safe to call #get() now
    +  channel.get().sendMessage("Hi");
    +}
    +

    orElse(...)

    The orElse methods returns the value of the Optional if it is present. Otherwise, it returns the given default value.

    // The user may not have a nickname on the given server. 
    +// In this case, we use the user's "regular" name.
    +String displayName = user.getNickname(server).orElse(user.getName());
    +

    The example above is (mostly) equivalent to the example below but much more concise.

    String displayName = "";
    +Optional<String> nickname = user.getNickname(server);
    +if (nickname.isPresent()) {
    +  displayName = nickname.get();
    +} else {
    +  displayName = user.getName();
    +}
    +

    TIP

    In this case you can just use user.getDisplayName(server) instead.

    ifPresent(...)

    `,14),w=n("The "),O=s("code",null,"ifPresent",-1),q=n(" method is very similar to an "),T=s("code",null,"if (value != null) { ... }",-1),N=n(" check. It takes a "),U={href:"https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html",target:"_blank",rel:"noopener noreferrer"},I=n("Consumer"),B=n(" as it's argument. This consumer is called if the Optional contains a value. Together with lambda expressions this can be a very handy method."),C=e(`
    api.getTextChannelById(123L).ifPresent(channel -> {
    +  channel.sendMessage("Hi!");
    +});
    +

    The example above is (mostly) equivalent to the example below but more concise.

    Optional<TextChannel> channel = api.getTextChannelById(123L);
    +if (channel.isPresent()) {
    +  channel.get().sendMessage("Hi!");
    +}
    +

    filter(...)

    The filter method filters the Optional for a given criteria.

    Optional<User> botUser = api.getCachedUserById(123L).filter(User::isBot);
    +

    The example above is equivalent to the example below but more concise.

    Optional<User> user = api.getCachedUserById(123L);
    +Optional<User> botUser;
    +if (user.isPresent() && user.get().isBot()) {
    +  botUser = user;
    +} else {
    +  botUser = Optional.empty();
    +}
    +

    map(...)

    The map method "converts" the type of an Optional. This is useful, if the type of an Optional does not contain the final value you need.

    The following example gets the name of the bots current activity (the "Playing xyz" status) or "None" if the bot has no current activity.

    String activityName = api.getYourself().getActivity().map(Activity::getName).orElse("None");
    +

    For better understanding, here's the exact same code but with the types as comments:

    String activityName =  api.getYourself() // User
    +        .getActivity() // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    flatMap(...)

    The flatMap method if very similar to the map methods. It is used to map values that itself are Optionals to prevent Optional nesting (a "box in a box").

    String activityName = api.getCachedUserById(123L) // Optional<User>
    +        .flatMap(User::getActivity) // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    Without flatMap, the code would look like this:

    String activityName = api.getCachedUserById(123L) // Optional<User>
    +        .map(User::getActivity) // Optional<Optional<Activity>>
    +        .filter(Optional::isPresent) // Optional<Optional<Activity>>
    +        .map(Optional::get) // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    \u{1F4DA} Further Read

    `,20),A=n("This tutorial only focuses on the absolute basics. For an in-depth introduction to Optionals, you can take a look at "),E={href:"https://www.oracle.com/technical-resources/articles/java/java8-optional.html",target:"_blank",rel:"noopener noreferrer"},S=n("Oracle's article about optionals"),L=n(".");function P(M,H){const p=o("RouterLink"),t=o("ExternalLinkIcon");return i(),l("div",null,[d,s("div",k,[m,s("p",null,[v,a(p,{to:"/wiki/essential-knowledge/lambdas/"},{default:u(()=>[h]),_:1}),g])]),f,s("p",null,[b,s("a",y,[_,a(t)]),x]),j,s("p",null,[w,O,q,T,N,s("a",U,[I,a(t)]),B]),C,s("p",null,[A,s("a",E,[S,a(t)]),L])])}var V=c(r,[["render",P],["__file","optionals.html.vue"]]);export{V as default}; diff --git a/assets/optionals.html.b4abca2e.js b/assets/optionals.html.b4abca2e.js new file mode 100644 index 0000000..1b9637a --- /dev/null +++ b/assets/optionals.html.b4abca2e.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-56bee89c","path":"/wiki/essential-knowledge/optionals.html","title":"Optionals","lang":"en-US","frontmatter":{"keywords":[null,"ifPresent","isPresent","orElse"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4AA} Motivation","slug":"motivation","children":[{"level":3,"title":"The old way of doing it","slug":"the-old-way-of-doing-it","children":[]},{"level":3,"title":"The new way of doing it","slug":"the-new-way-of-doing-it","children":[]}]},{"level":2,"title":"\u{1F4D6} Methods","slug":"methods","children":[{"level":3,"title":"get()","slug":"get","children":[]},{"level":3,"title":"isPresent()","slug":"ispresent","children":[]},{"level":3,"title":"orElse(...)","slug":"orelse","children":[]},{"level":3,"title":"ifPresent(...)","slug":"ifpresent","children":[]},{"level":3,"title":"filter(...)","slug":"filter","children":[]},{"level":3,"title":"map(...)","slug":"map","children":[]},{"level":3,"title":"flatMap(...)","slug":"flatmap","children":[]}]},{"level":2,"title":"\u{1F4DA} Further Read","slug":"further-read","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/essential-knowledge/optionals.md"}');export{e as data}; diff --git a/assets/overview.html.53b5f728.js b/assets/overview.html.53b5f728.js new file mode 100644 index 0000000..2d937fd --- /dev/null +++ b/assets/overview.html.53b5f728.js @@ -0,0 +1 @@ +import{_ as c,r as i,o as d,c as l,a as e,b as o,w as s,e as r,d as t}from"./app.151ccb98.js";var h="/assets/lifecycle_command.b2e278fd.png",m="/assets/lifecycle_interaction.38162b05.png";const p={},u=r('

    Interactions

    Interactions are a means of accepting user input through Discord. They have been introduced to provide a more standardized, controlled way for commands than parsing messages. They can even be used with applications that do not provide a bot user.

    \u{1F4AC} Message Commands

    The "old" way of doing commands was done through parsed text messages, like !ping, !userinfo James or !mute James 100s. While such commands are easy in theory, they come with several problems, such as:

    • Conflicts between Bots using the same command format / prefix.
    • Bots have to be able to read all messages and find those that are directed at them
    • Information about command structure can only be provided in info texts and error messages

    Message Command Lifecycle

    \u2709\uFE0F Interaction Types

    ',7),_=t("Interactions come in a variety of shapes. The most complex and versatile is the "),f=t("command interaction"),b=t(", which allows for commands directed at a particular bot with information and assistance on subcommands and parameters being integrated into the discord client."),g={href:"https://javadoc.io/doc/org.javacord/javacord-api/latest/org/javacord/api/interaction/ContextMenu.html",target:"_blank",rel:"noopener noreferrer"},v=t("Context Menu commands"),y=t(" are available from the context menu in the client either on a message or a server member."),w=t("Message components"),x=t(" come in the flavor of buttons, select menus and other form elements and can be attached directly to a message."),I=r('

    \u267B\uFE0F Lifecycle

    INFO

    Creation of interactions is detailed on the pages linked in the previous section.

    Unlike chat message commands, interactions and interaction commands need to be registered with Discord. In order for a bot's interactions to be available in a server, the bot must be added to the server with the applications.commands OAUTH scope. The scope is included in links created by DiscordApi#createInviteLink. If your bot is older, it may need to be invited with the new link to add the scope. It is not necessary to remove the bot from the server to do this.

    Interaction Command Lifecycle

    \u{1F4C8} Advantages

    While being more complicated to utilize, interactions have many benefits over pure text commands.

    ',6),k=e("li",null,"Better Validation: Commands can not be sent with parameters of the wrong type or missing required parameters",-1),C=e("li",null,"No conflicts: Interactions are separated by bot and only sent to the proper bot",-1),N=e("li",null,'"Privacy": If no public response is sent by the bot, the exchange is invisible to other chat participants',-1),D=e("li",null,"Integration: Interactions are integrated into the client's user interface",-1),T=t("Conversations: "),L=t("Message components"),A=t(" can be used in replies to interactions, allowing for nested dialogues."),B=e("div",{class:"custom-container warning"},[e("p",{class:"custom-container-title"},"WARNING"),e("p",null,"If a bot replies to a slash command with a public message, the command used, including all parameters, is visible to other users.")],-1),M=e("h2",{id:"applications-vs-bots",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#applications-vs-bots","aria-hidden":"true"},"#"),t(" \u{1F916} Applications vs. Bots")],-1),V=t("Interactions can used by any application, not only bots. While interactions can also be handled through webhooks, Javacord only offers support for dealing with them through the gateway. See the "),W={href:"https://discord.com/developers/docs/interactions/receiving-and-responding",target:"_blank",rel:"noopener noreferrer"},j=t("Discord Documentation"),R=t(" for more information."),q=e("div",{class:"custom-container warning"},[e("p",{class:"custom-container-title"},"WARNING"),e("p",null,"The methods of handling interactions can not be mixed. If you register a webhook for your interaction commands, the bot will no longer receive any interaction events.")],-1),E=e("h2",{id:"see-also",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#see-also","aria-hidden":"true"},"#"),t(" \u{1F50D} See also")],-1),J={href:"https://discord.com/developers/docs/interactions/application-commands",target:"_blank",rel:"noopener noreferrer"},S=t("Application Commands (Discord Documentation)"),z={href:"https://discord.com/developers/docs/interactions/message-components",target:"_blank",rel:"noopener noreferrer"},G=t("Message Components (Discord Documentation)");function O(U,F){const a=i("RouterLink"),n=i("ExternalLinkIcon");return d(),l("div",null,[u,e("p",null,[_,o(a,{to:"/wiki/basic-tutorials/interactions/commands.html"},{default:s(()=>[f]),_:1}),b]),e("p",null,[e("a",g,[v,o(n)]),y]),e("p",null,[o(a,{to:"/wiki/basic-tutorials/interactions/components.html"},{default:s(()=>[w]),_:1}),x]),I,e("ul",null,[k,C,N,D,e("li",null,[T,o(a,{to:"/wiki/basic-tutorials/interactions/components.html"},{default:s(()=>[L]),_:1}),A])]),B,M,e("p",null,[V,e("a",W,[j,o(n)]),R]),q,E,e("ul",null,[e("li",null,[e("a",J,[S,o(n)])]),e("li",null,[e("a",z,[G,o(n)])])])])}var P=c(p,[["render",O],["__file","overview.html.vue"]]);export{P as default}; diff --git a/assets/overview.html.d664223d.js b/assets/overview.html.d664223d.js new file mode 100644 index 0000000..9e7fc05 --- /dev/null +++ b/assets/overview.html.d664223d.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-16fe8d71","path":"/wiki/basic-tutorials/interactions/overview.html","title":"Interactions","lang":"en-US","frontmatter":{"keywords":["interaction","slash command","command","context menu","autocomplete"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4AC} Message Commands","slug":"message-commands","children":[]},{"level":2,"title":"\u2709\uFE0F Interaction Types","slug":"interaction-types","children":[]},{"level":2,"title":"\u267B\uFE0F Lifecycle","slug":"lifecycle","children":[]},{"level":2,"title":"\u{1F4C8} Advantages","slug":"advantages","children":[]},{"level":2,"title":"\u{1F916} Applications vs. Bots","slug":"applications-vs-bots","children":[]},{"level":2,"title":"\u{1F50D} See also","slug":"see-also","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/interactions/overview.md"}');export{e as data}; diff --git a/assets/performance-tweaks.html.1ed90e55.js b/assets/performance-tweaks.html.1ed90e55.js new file mode 100644 index 0000000..1ffb8ea --- /dev/null +++ b/assets/performance-tweaks.html.1ed90e55.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-2037d84f","path":"/wiki/advanced-topics/performance-tweaks.html","title":"Performance Tweaks","lang":"en-US","frontmatter":{"keywords":["performance","tweaks","startup wait","message cache","tuning"]},"excerpt":"","headers":[{"level":2,"title":"\u2702\uFE0F Disabling Startup Wait","slug":"disabling-startup-wait","children":[]},{"level":2,"title":"\u2699\uFE0F Fine Tuning the Message Cache","slug":"fine-tuning-the-message-cache","children":[]},{"level":2,"title":"\u{1F48E} Using the Updater classes","slug":"using-the-updater-classes","children":[{"level":3,"title":"Example","slug":"example","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/performance-tweaks.md"}');export{e as data}; diff --git a/assets/performance-tweaks.html.909e9a4b.js b/assets/performance-tweaks.html.909e9a4b.js new file mode 100644 index 0000000..254dd35 --- /dev/null +++ b/assets/performance-tweaks.html.909e9a4b.js @@ -0,0 +1,25 @@ +import{_ as n,o as a,c as s,e}from"./app.151ccb98.js";const t={},p=e(`

    Performance Tweaks

    \u2702\uFE0F Disabling Startup Wait

    By default, Javacord waits for all servers and members to be loaded on startup. You can disable this behavior in the DiscordApiBuilder before logging in:

    new DiscordApiBuilder()
    +    .setToken("abc")
    +    .setWaitForServersOnStartup(false)
    +    .login()
    +    .thenAccept(api -> {
    +        // Do something
    +    }).exceptionally(ExceptionLogger.get());
    +

    Depending on the size of your bot, this can significantly speed up the login process. This comes with one downside however: The api.getServers() collection is empty directly after logging in. You will receive ServerBecomesAvailableEvents for every server which finished loading.

    \u2699\uFE0F Fine Tuning the Message Cache

    In order to reduce memory usage, you can completely disable the message cache or reduce the number of cached messages. By default, Javacord caches up to 50 messages per channel and removes messages from the cache which are older than 12 hours. You can lower this limit by using DiscordApi#setMessageCacheSize(Capacity, StorageTimeInSeconds).

    // Cache a maximum of 10 messages per channel for and remove messages older than 1 hour
    +api.setMessageCacheSize(10, 60*60);
    +

    You can even set this limit on a per-channel basis:

    TextChannel channel = ...;
    +channel.getMessageCache().setCapacity(10);
    +channel.getMessageCache().setStorageTimeInSeconds(60*60);
    +

    \u{1F48E} Using the Updater classes

    If you update several settings of an entity (server, channel, ...) at once, you should use the updater for this entity instead of the updateXyz(...) methods.

    Example

    // Sends 1 request to Discord
    +ServerTextChannel channel = ...;
    +new ServerTextChannelUpdater(channel)
    +    .setName("example-channel")
    +    .setTopic("This is an example channel")
    +    .setNsfwFlag(true)
    +    .update();
    +

    instead of

    // Sends 3 requests to Discord
    +ServerTextChannel channel = ...;
    +channel.updateName("example-channel");
    +channel.updateTopic("This is an example channel");
    +channel.updateNsfwFlag(true);
    +
    `,16),c=[p];function o(i,l){return a(),s("div",null,c)}var r=n(t,[["render",o],["__file","performance-tweaks.html.vue"]]);export{r as default}; diff --git a/assets/ping-pong-white.53343497.gif b/assets/ping-pong-white.53343497.gif new file mode 100644 index 0000000..41e5530 Binary files /dev/null and b/assets/ping-pong-white.53343497.gif differ diff --git a/assets/playing-audio.html.1204e1b2.js b/assets/playing-audio.html.1204e1b2.js new file mode 100644 index 0000000..f4085e2 --- /dev/null +++ b/assets/playing-audio.html.1204e1b2.js @@ -0,0 +1,82 @@ +import{_ as c,r as t,o as i,c as l,a,b as s,w as u,d as n,e as o}from"./app.151ccb98.js";const r={},d=a("h1",{id:"playing-audio",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#playing-audio","aria-hidden":"true"},"#"),n(" Playing Audio")],-1),k={class:"custom-container warning"},v=a("p",{class:"custom-container-title"},"WARNING",-1),m=n("Support for audio was added to Javacord very recently. If you encounter any bugs, please "),b={href:"https://github.com/Javacord/Javacord/issues/new",target:"_blank",rel:"noopener noreferrer"},y=n("create an issue on GitHub"),h=n("!"),w=n("Javacord allows your bot to connect to voice channels and play audio (e.g., music). This short tutorial gives you an introduction on how to connect to a voice channel and play your "),_={href:"https://youtu.be/qRC4Vk6kisY",target:"_blank",rel:"noopener noreferrer"},f=n("favorite music"),g=n("."),A=a("h2",{id:"connect-to-a-voice-channel",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#connect-to-a-voice-channel","aria-hidden":"true"},"#"),n(" \u{1F50C} Connect to a voice channel")],-1),x=n("Connecting to a voice channel is very straight forward: Calling "),P=a("code",null,"#connect()",-1),S=n(" on an instance of "),L=a("code",null,"ServerVoiceChannel",-1),T=n(" will connect your bot to this voice channel and return a "),C=n("future"),F=n(" with an "),j=a("code",null,"AudioConnection",-1),N=n(" object."),M=o(`

    Example

    The following example will connect the bot to the voice channel of the user that typed !music in the chat:

    ServerVoiceChannel channel = ...;
    +channel.connect().thenAccept(audioConnection -> {
    +    // Do stuff
    +}).exceptionally(e -> {
    +    // Failed to connect to voice channel (no permissions?)
    +    e.printStackTrace();
    +    return null;
    +});
    +

    \u{1F442} Playing music

    `,4),O=n("There are plenty of sources for audio (e.g., YouTube, local files, etc.). The current de facto standard library for extracting audio from these sources with Java is the "),V={href:"https://github.com/lavalink-devs/lavaplayer",target:"_blank",rel:"noopener noreferrer"},J=n("LavaPlayer"),E=n(" library."),I=o(`

    To use it with Javacord, you have to add it as a dependency to your project (e.g., with Gradle or Maven) and create a Javacord audio source like this:

    public class LavaplayerAudioSource extends AudioSourceBase {
    +
    +    private final AudioPlayer audioPlayer;
    +    private AudioFrame lastFrame;
    +
    +    /**
    +     * Creates a new lavaplayer audio source.
    +     *
    +     * @param api A discord api instance.
    +     * @param audioPlayer An audio player from Lavaplayer.
    +     */
    +    public LavaplayerAudioSource(DiscordApi api, AudioPlayer audioPlayer) {
    +        super(api);
    +        this.audioPlayer = audioPlayer;
    +    }
    +
    +    @Override
    +    public byte[] getNextFrame() {
    +        if (lastFrame == null) {
    +            return null;
    +        }
    +        return applyTransformers(lastFrame.getData());
    +    }
    +
    +    @Override
    +    public boolean hasFinished() {
    +        return false;
    +    }
    +
    +    @Override
    +    public boolean hasNextFrame() {
    +        lastFrame = audioPlayer.provide();
    +        return lastFrame != null;
    +    }
    +
    +    @Override
    +    public AudioSource copy() {
    +        return new LavaplayerAudioSource(getApi(), audioPlayer);
    +    }
    +}
    +

    With this audio source, you can now start using Lavaplayer, e.g. to play a YouTube video:

    // Create a player manager
    +AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
    +playerManager.registerSourceManager(new YoutubeAudioSourceManager());
    +AudioPlayer player = playerManager.createPlayer();
    +
    +// Create an audio source and add it to the audio connection's queue
    +AudioSource source = new LavaplayerAudioSource(api, player);
    +audioConnection.setAudioSource(source);
    +
    +// You can now use the AudioPlayer like you would normally do with Lavaplayer, e.g.,
    +playerManager.loadItem("https://www.youtube.com/watch?v=NvS351QKFV4", new AudioLoadResultHandler() {
    +    @Override
    +    public void trackLoaded(AudioTrack track) {
    +        player.playTrack(track);
    +    }
    +
    +    @Override
    +    public void playlistLoaded(AudioPlaylist playlist) {
    +        for (AudioTrack track : playlist.getTracks()) {
    +            player.playTrack(track);
    +        }
    +    }
    +
    +    @Override
    +    public void noMatches() {
    +        // Notify the user that we've got nothing
    +    }
    +
    +    @Override
    +    public void loadFailed(FriendlyException throwable) {
    +        // Notify the user that everything exploded
    +    }
    +});
    +
    `,4);function R(Y,q){const e=t("ExternalLinkIcon"),p=t("RouterLink");return i(),l("div",null,[d,a("div",k,[v,a("p",null,[m,a("a",b,[y,s(e)]),h])]),a("p",null,[w,a("a",_,[f,s(e)]),g]),A,a("p",null,[x,P,S,L,T,s(p,{to:"/wiki/essential-knowledge/completable-futures/"},{default:u(()=>[C]),_:1}),F,j,N]),M,a("p",null,[O,a("a",V,[J,s(e)]),E]),I])}var D=c(r,[["render",R],["__file","playing-audio.html.vue"]]);export{D as default}; diff --git a/assets/playing-audio.html.15139e1b.js b/assets/playing-audio.html.15139e1b.js new file mode 100644 index 0000000..11b7e64 --- /dev/null +++ b/assets/playing-audio.html.15139e1b.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-0b11c848","path":"/wiki/advanced-topics/playing-audio.html","title":"Playing Audio","lang":"en-US","frontmatter":{"keywords":["audio","music","voice"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F50C} Connect to a voice channel","slug":"connect-to-a-voice-channel","children":[{"level":3,"title":"Example","slug":"example","children":[]}]},{"level":2,"title":"\u{1F442} Playing music","slug":"playing-music","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/playing-audio.md"}');export{e as data}; diff --git a/assets/privacy-policy.html.2b93162c.js b/assets/privacy-policy.html.2b93162c.js new file mode 100644 index 0000000..10b797c --- /dev/null +++ b/assets/privacy-policy.html.2b93162c.js @@ -0,0 +1 @@ +import{_ as c,o as a,c as t,a as e,d as r}from"./app.151ccb98.js";const i={},o=e("h1",{id:"privacy-policy",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#privacy-policy","aria-hidden":"true"},"#"),r(" Privacy Policy")],-1),s=e("iframe",{src:"/pp.html",style:{height:"calc(100vh - 250px)",width:"100%"}},null,-1),l=[o,s];function _(n,d){return a(),t("div",null,l)}var p=c(i,[["render",_],["__file","privacy-policy.html.vue"]]);export{p as default}; diff --git a/assets/privacy-policy.html.e6350755.js b/assets/privacy-policy.html.e6350755.js new file mode 100644 index 0000000..9467fdb --- /dev/null +++ b/assets/privacy-policy.html.e6350755.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-b106f6ee","path":"/privacy-policy.html","title":"Privacy Policy","lang":"en-US","frontmatter":{"search":false},"excerpt":"","headers":[],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"privacy-policy.md"}');export{e as data}; diff --git a/assets/proxies.html.b2817cee.js b/assets/proxies.html.b2817cee.js new file mode 100644 index 0000000..0ac7b0a --- /dev/null +++ b/assets/proxies.html.b2817cee.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-47a9d05a","path":"/wiki/advanced-topics/proxies.html","title":"Proxies","lang":"en-US","frontmatter":{"keywords":["proxy","connection","socks","socks4","socks5"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F468}\u200D\u{1F4BB} Configuring a Proxy ...","slug":"configuring-a-proxy","children":[{"level":3,"title":"... using System Properties","slug":"using-system-properties","children":[]},{"level":3,"title":"... using a System Default Proxy Selector","slug":"using-a-system-default-proxy-selector","children":[]},{"level":3,"title":"... using an Explicitly Set Proxy","slug":"using-an-explicitly-set-proxy","children":[]},{"level":3,"title":"... using an Explicitly Set Proxy Selector","slug":"using-an-explicitly-set-proxy-selector","children":[]},{"level":3,"title":"Precedence of the Configuration Options","slug":"precedence-of-the-configuration-options","children":[]}]},{"level":2,"title":"\u{1F511} Configuring Proxy Authentication ...","slug":"configuring-proxy-authentication","children":[{"level":3,"title":"... using a System Default Authenticator","slug":"using-a-system-default-authenticator","children":[]},{"level":3,"title":"... using an Explicitly Set Authenticator","slug":"using-an-explicitly-set-authenticator","children":[]}]},{"level":2,"title":"\u{1F4A1} Proxy Types","slug":"proxy-types","children":[{"level":3,"title":"HTTP","slug":"http","children":[]},{"level":3,"title":"SOCKS 4","slug":"socks-4","children":[]},{"level":3,"title":"SOCKS 4a","slug":"socks-4a","children":[]},{"level":3,"title":"SOCKS 5","slug":"socks-5","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/proxies.md"}');export{e as data}; diff --git a/assets/proxies.html.b9736f17.js b/assets/proxies.html.b9736f17.js new file mode 100644 index 0000000..04f3adf --- /dev/null +++ b/assets/proxies.html.b9736f17.js @@ -0,0 +1 @@ +import{_ as e,o as t,c as o,e as a}from"./app.151ccb98.js";const i={},r=a('

    Proxies

    There are basically two kinds of proxies: HTTP proxies and SOCKS proxies. Both may or may not support or require authentication depending on version, capabilities, and configuration. Due to the underlying libraries used, currently, Javacord fully supports HTTP proxies and partially supports SOCKS proxies.

    Javacord uses HTTPS connections to communicate with the Discord REST API and a WSS connection to communicate with the Discord WebSocket endpoint. Both these protocols are secure protocols and thus do not honor settings for HTTP connections, only settings for HTTPS connections.

    \u{1F468}\u200D\u{1F4BB} Configuring a Proxy ...

    ... using System Properties

    If you did not explicitly set a proxy in the DiscordApiBuilder and did not set a system default ProxySelector, the default proxy selector of the JRE is used. This proxy selector honors, amongst others, the relevant standard system properties https.proxyHost, https.proxyPort, socksProxyHost, socksProxyPort, and socksProxyVersion. Use the former two to configure an HTTP proxy, or the latter three to configure a SOCKS proxy, although you will not need socksProxyVersion, as SOCKS4 is currently not supported.

    ... using a System Default Proxy Selector

    You can use java.net.ProxySelector.setDefault(ProxySelector) to set a system default proxy selector that replaces the default one. In its implementation, you can dynamically determine which proxy to use for each connection.

    ... using an Explicitly Set Proxy

    Using the method DiscordApiBuilder.setProxy(Proxy) you can set a proxy instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the unrelated code running in the JVM.

    ... using an Explicitly Set Proxy Selector

    Using the method DiscordApiBuilder.setProxySelector(ProxySelector) you can set a proxy selector instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the remaining code running in the JVM. In its implementation, you can dynamically determine which proxy to use for each connection.

    Precedence of the Configuration Options

    • if an explicit proxy is set, it is used
    • if an explicit proxy selector is set, it is used
    • if both an explicit proxy and an explicit proxy selector are set, this is a configuration error and will cause an exception to be thrown
    • if neither explicit option is set, the system default proxy selector is used
    • if no system default proxy selector was explicitly set, the JRE default that honors the system properties is used

    \u{1F511} Configuring Proxy Authentication ...

    ... using a System Default Authenticator

    You can use java.net.Authenticator.setDefault(Authenticator) to set a system default authenticator that is used to provide username and password pairs for connections. This authenticator is only used if the proxy supports the Basic authentication scheme. If you need to support any other authentication scheme, use an explicitly configured authenticator. The java.net.Authenticator interface is too inflexible to support this.

    ... using an Explicitly Set Authenticator

    Using the method DiscordApiBuilder.setProxyAuthenticator(Authenticator), you can set a custom authenticator that is much more powerful than the java.net.Authenticator. You get much more information about the connection to be established, and you can return any HTTP header that is necessary for a successful authentication. This should cover all sorts of available authentication mechanisms.

    \u{1F4A1} Proxy Types

    HTTP

    HTTP proxies are fully supported.

    SOCKS 4

    SOCKS 4 is currently not supported.

    The WebSocket library we use does not support SOCKS proxies at all, and the HTTP library we use has a bug that prevents SOCKS 4 to be used. Additionally, you would need to use at least Java 9 or a separate socket factory supporting SOCKS 4, as the JRE implementation is not working in Java 8 and got fixed only in Java 9+.

    SOCKS 4a

    SOCKS 4a is currently only partially supported.

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only. Additionally, you would need to use a separate socket factory supporting SOCKS 4a, as the JRE implementation is not capable of doing SOCKS 4a, only SOCKS 4 and SOCKS 5 are supported at the time of creation of this wiki article.

    SOCKS 5

    SOCKS 5 is currently only partially supported.

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only.

    ',31),s=[r];function n(c,d){return t(),o("div",null,s)}var l=e(i,[["render",n],["__file","proxies.html.vue"]]);export{l as default}; diff --git a/assets/ratelimits.html.4f60547f.js b/assets/ratelimits.html.4f60547f.js new file mode 100644 index 0000000..1438762 --- /dev/null +++ b/assets/ratelimits.html.4f60547f.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-fc3b23ca","path":"/wiki/advanced-topics/ratelimits.html","title":"Ratelimits","lang":"en-US","frontmatter":{"keywords":["ratelimits"]},"excerpt":"","headers":[{"level":2,"title":"\u2757 The Most Important Ratelimits","slug":"the-most-important-ratelimits","children":[]},{"level":2,"title":"\u{1F4AA} Dealing with Ratelimits","slug":"dealing-with-ratelimits","children":[{"level":3,"title":"Example","slug":"example","children":[]}]},{"level":2,"title":"\u274C Can I disable ratelimits?","slug":"can-i-disable-ratelimits","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/ratelimits.md"}');export{e as data}; diff --git a/assets/ratelimits.html.a990bfd1.js b/assets/ratelimits.html.a990bfd1.js new file mode 100644 index 0000000..ba83d7c --- /dev/null +++ b/assets/ratelimits.html.a990bfd1.js @@ -0,0 +1,14 @@ +import{_ as n,o as a,c as s,e as t}from"./app.151ccb98.js";const e={},i=t(`

    Ratelimits

    Ratelimits is a Discord restriction which prevents you from performing actions in a very fast rate. Most ratelimits are on a per-channel or a per-server basis.

    \u2757 The Most Important Ratelimits

    ActionRatelimitType
    Send Messages5 / 5sper channel
    Delete Messages5 / 1sper channel
    Add/Remove Reactions1 / 0.25sper channel
    Edit Server Members10 / 10sper server
    Edit Member Nickname1 / 1sper server
    Edit Bot Username2 / 1hper account
    Update Channels2 / 10mper account
    All Actions Combined50 / 1sper account

    \u{1F4AA} Dealing with Ratelimits

    Usually Javacord takes care about these limitations for you. As a user, there's nothing you have to do, but you should at least know that ratelimits exist.

    Example

    The following code

    // Who even needs loops?
    +channel.sendMessage("Ratelimit Example #1");
    +channel.sendMessage("Ratelimit Example #2");
    +channel.sendMessage("Ratelimit Example #3");
    +channel.sendMessage("Ratelimit Example #4");
    +channel.sendMessage("Ratelimit Example #5");
    +channel.sendMessage("Ratelimit Example #6");
    +channel.sendMessage("Ratelimit Example #7");
    +channel.sendMessage("Ratelimit Example #8");
    +channel.sendMessage("Ratelimit Example #9");
    +channel.sendMessage("Ratelimit Example #10");
    +channel.sendMessage("Ratelimit Example #11");
    +channel.sendMessage("Ratelimit Example #12");
    +

    would look like this in the client:

    You can clearly see the delay between every 5 sent messages.

    \u274C Can I disable ratelimits?

    No. Ratelimits are a limitation from Discord itself, which you cannot circumvent.

    `,14),p=[i];function o(c,l){return a(),s("div",null,p)}var d=n(e,[["render",o],["__file","ratelimits.html.vue"]]);export{d as default}; diff --git a/assets/respond_with_modal.9601f2a7.png b/assets/respond_with_modal.9601f2a7.png new file mode 100644 index 0000000..e1ce916 Binary files /dev/null and b/assets/respond_with_modal.9601f2a7.png differ diff --git a/assets/responding.html.3c5c7e21.js b/assets/responding.html.3c5c7e21.js new file mode 100644 index 0000000..c7aa19f --- /dev/null +++ b/assets/responding.html.3c5c7e21.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-00728006","path":"/wiki/basic-tutorials/interactions/responding.html","title":"Responding to interactions","lang":"en-US","frontmatter":{"keywords":["interaction responding","modal","autocomplete"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F4AC} Responding immediately after receiving an interaction.","slug":"responding-immediately-after-receiving-an-interaction","children":[]},{"level":2,"title":"\u{1F4AC} Responding after some time when receiving an interaction.","slug":"responding-after-some-time-when-receiving-an-interaction","children":[{"level":3,"title":"Sending followup messages","slug":"sending-followup-messages","children":[]}]},{"level":2,"title":"Responding with a Modal","slug":"responding-with-a-modal","children":[]},{"level":2,"title":"\u{1F4AC} SlashCommand interaction only response methods","slug":"slashcommand-interaction-only-response-methods","children":[{"level":3,"title":"How to know what slash command was invoked?","slug":"how-to-know-what-slash-command-was-invoked","children":[]},{"level":3,"title":"Respond to an AutoComplete interaction triggered from a SlashCommand","slug":"respond-to-an-autocomplete-interaction-triggered-from-a-slashcommand","children":[]}]},{"level":2,"title":"\u{1F4AC} Message Component interaction only response methods","slug":"message-component-interaction-only-response-methods","children":[{"level":3,"title":"A more complete example of how to respond to Component interactions","slug":"a-more-complete-example-of-how-to-respond-to-component-interactions","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/interactions/responding.md"}');export{e as data}; diff --git a/assets/responding.html.a1a19125.js b/assets/responding.html.a1a19125.js new file mode 100644 index 0000000..482f52d --- /dev/null +++ b/assets/responding.html.a1a19125.js @@ -0,0 +1,77 @@ +import{_ as t,r as e,o,c as p,a as c,b as i,w as u,e as s,d as n}from"./app.151ccb98.js";var l="/assets/respond_with_modal.9601f2a7.png";const r={},d=s(`

    Responding to interactions

    There are many ways to respond to interactions and some are only available for certain interactions. The following will be usable for every interaction.

    \u{1F4AC} Responding immediately after receiving an interaction.

    event.getInteraction()
    +        .createImmediateResponder()
    +        .setContent("YOUR_RESPONSE")
    +        .respond();
    +

    INFO

    Note that you have to respond withing 3 seconds, or the command will fail. If you need longer than 3 seconds you have to respond with respondLater() which allows you to respond within 15 minutes.

    Because of this time limitation, sending any files when creating an immediate response is not possible. If you want a file to be embedded either use respondLater or include a web link in the message content. Depending on the media type of the link and the server configuration, Discord will then display an appropriate embed for the file.

    When you want to respond ephemerally, you can use the setFlags method. Your new responder would look like the following:

    event.getInteraction()
    +        .createImmediateResponder()
    +        .setContent("YOUR_RESPONSE")
    +        .setFlags(MessageFlag.EPHEMERAL)
    +        .respond();
    +

    \u{1F4AC} Responding after some time when receiving an interaction.

    If your computations takes longer than the 3 seconds limit, you can respond later and the Discord Client will show that your bot is thinking until you respond.

    event.getInteraction()
    +        .respondLater()
    +        .thenAccept(interactionOriginalResponseUpdater -> {
    +            interactionOriginalResponseUpdater.setContent("Update message after some time").update();
    +        });
    +

    You can respond ephemerally when responding later too. For that you have pass a true boolean to the respondLater method.

    event.getInteraction()
    +        .respondLater(true)
    +        .thenAccept(interactionOriginalResponseUpdater -> {
    +            interactionOriginalResponseUpdater.setContent("Update message after some time").update();
    +        });
    +

    Sending followup messages

    Followup messages can be sent within 15 minutes after the command has been invoked. You can send as many followup messages as you want.

    api.addSlashCommandCreateListener(event -> {
    +    SlashCommandInteraction slashCommandInteraction = event.getSlashCommandInteraction();
    +    slashCommandInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {
    +        interactionOriginalResponseUpdater.setContent("You will receive the answer in a few minutes!").update();
    +
    +        // time < 15 minutes
    +        
    +        slashCommandInteraction.createFollowupMessageBuilder()
    +                .setContent("Thank you for your patience, it took a while but the answer to the universe is 42")
    +                .send();
    +    });
    +});
    +

    Responding with a Modal

    A modal is a popup dialog which can be shown when responding to an interaction. It focuses the users to explicitly fill out this form to continue with the workflow. Currently, only the TextInput (SelectMenu has been seen working too, but is not yet officially supported) is supported.

    api.addMessageComponentCreateListener(event -> {
    +    event.getInteraction().respondWithModal("modalId","Modal Title",
    +        ActionRow.of(TextInput.create(TextInputStyle.SHORT, "text_input_id", "This is a Text Input Field")));
    +});
    +

    Which results in

    Modal

    \u{1F4AC} SlashCommand interaction only response methods

    How to know what slash command was invoked?

    For example, you have created a slash command with the name "settings" and a subcommand "color". If you want to check if exactly this command has been used, you can check it as follows:

    api.addSlashCommandCreateListener(event -> {
    +    SlashCommandInteraction interaction = event.getSlashCommandInteraction();
    +    if (interaction.getFullCommandName().equals("settings color")) {
    +        //Code if command matches the full name
    +    }
    +});
    +

    Respond to an AutoComplete interaction triggered from a SlashCommand

    api.addAutocompleteCreateListener(event -> {
    +    event.getAutocompleteInteraction()
    +    .respondWithChoices(Arrays.asList(
    +        SlashCommandOptionChoice.create("one", 1),
    +            SlashCommandOptionChoice.create("two", 2))
    +    );
    +});
    +

    \u{1F4AC} Message Component interaction only response methods

    When dealing with message components, you don't necessarily have to respond or update a message. You can simply acknowledge the interaction and let the user know that the task is done.

    api.addMessageComponentCreateListener(event -> {
    +    event.getMessageComponentInteraction().acknowledge();
    +});
    +

    A more complete example of how to respond to Component interactions

    `,30),k=n("The following code snipped shows how you can respond to the example created in "),m=n("Components"),v=n("."),h=s(`
    api.addMessageComponentCreateListener(event -> {
    +    MessageComponentInteraction messageComponentInteraction = event.getMessageComponentInteraction();
    +    String customId = messageComponentInteraction.getCustomId();
    +
    +    switch (customId) {
    +        case "success":
    +            messageComponentInteraction.createImmediateResponder()
    +                    .setContent("You clicked a button!")
    +                    .respond();
    +            break;
    +        case "danger":
    +            messageComponentInteraction.getMessage().ifPresent(Message::delete);
    +            break;
    +        case "secondary":
    +            messageComponentInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {
    +                //Code to respond after 5 minutes
    +            });
    +            break;
    +        case "options":
    +            messageComponentInteraction.createImmediateResponder()
    +					.setContent("You selected an option in a select menu!")
    +					.respond();
    +            break;
    +    }
    +});
    +
    `,1);function g(b,f){const a=e("RouterLink");return o(),p("div",null,[d,c("p",null,[k,i(a,{to:"/wiki/basic-tutorials/interactions/components.html"},{default:u(()=>[m]),_:1}),v]),h])}var y=t(r,[["render",g],["__file","responding.html.vue"]]);export{y as default}; diff --git a/assets/run-the-bot.f048bed2.png b/assets/run-the-bot.f048bed2.png new file mode 100644 index 0000000..cdfc184 Binary files /dev/null and b/assets/run-the-bot.f048bed2.png differ diff --git a/assets/running-eclipse-configurations-bar.32d3b9a3.png b/assets/running-eclipse-configurations-bar.32d3b9a3.png new file mode 100644 index 0000000..8088cb7 Binary files /dev/null and b/assets/running-eclipse-configurations-bar.32d3b9a3.png differ diff --git a/assets/running-eclipse-configurations-create.e4d5d1d0.png b/assets/running-eclipse-configurations-create.e4d5d1d0.png new file mode 100644 index 0000000..d81c4af Binary files /dev/null and b/assets/running-eclipse-configurations-create.e4d5d1d0.png differ diff --git a/assets/running-eclipse-configurations-empty.5810cf5b.png b/assets/running-eclipse-configurations-empty.5810cf5b.png new file mode 100644 index 0000000..841c782 Binary files /dev/null and b/assets/running-eclipse-configurations-empty.5810cf5b.png differ diff --git a/assets/running-idea-configurations-create.11183fb8.png b/assets/running-idea-configurations-create.11183fb8.png new file mode 100644 index 0000000..a522d75 Binary files /dev/null and b/assets/running-idea-configurations-create.11183fb8.png differ diff --git a/assets/running.html.15e65516.js b/assets/running.html.15e65516.js new file mode 100644 index 0000000..48ea2d6 --- /dev/null +++ b/assets/running.html.15e65516.js @@ -0,0 +1 @@ +const i=JSON.parse('{"key":"v-8012cfce","path":"/wiki/basic-tutorials/running.html","title":"Running and Deploying your Bot","lang":"en-US","frontmatter":{"keywords":["run bot","running bot","execute bot","deploy","deployment","application"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F477} Running from your IDE","slug":"running-from-your-ide","children":[{"level":3,"title":"IntelliJ IDEA","slug":"intellij-idea","children":[]},{"level":3,"title":"Eclipse","slug":"eclipse","children":[]}]},{"level":2,"title":"\u{1F4E6} Deploying and Running as a Standalone Application","slug":"deploying-and-running-as-a-standalone-application","children":[{"level":3,"title":"Building a Distribution with Gradle","slug":"building-a-distribution-with-gradle","children":[]},{"level":3,"title":"Building a Distribution with Maven","slug":"building-a-distribution-with-maven","children":[]},{"level":3,"title":"Running","slug":"running","children":[]}]},{"level":2,"title":"\u{1F4A9} Building a Fat Jar","slug":"building-a-fat-jar","children":[{"level":3,"title":"With Gradle","slug":"with-gradle","children":[]},{"level":3,"title":"With Maven","slug":"with-maven","children":[]}]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/basic-tutorials/running.md"}');export{i as data}; diff --git a/assets/running.html.1784634d.js b/assets/running.html.1784634d.js new file mode 100644 index 0000000..853ebed --- /dev/null +++ b/assets/running.html.1784634d.js @@ -0,0 +1,166 @@ +import{_ as r,r as t,o as d,c as g,b as a,w as o,a as n,e,d as s}from"./app.151ccb98.js";var k="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATUAAAAeCAIAAADCYR4SAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAGs0lEQVR4Xu2cTW/jNhCG93cF68S2/CV/23L8kW2Kvaa76K1AWxRJeih6K9rGRbHYXuNrUfTm/rm+4lAUQ5MyKduyHXDwIBBHwzFD8BVHkpM3b68qHo/nNHkz8ubN26ma3z89ntPF69PjOV28Pj2e06VQfZbKlf5o/O729sv37z2HAzOMecZsK/PvOTsK1Wd/OLr54rbWCv3SORyYW8ww5hmzrZzynB2F6hOLpt5qK07PIcA8Y7YVp+fsKFSfKL38zlkMmGfMtuL0nB22+rwMg8bPw9avo+Zv49YfkXLWEr9iiuSIs10qV0fjcdBoUjOoN9CEUwQcnVqj1e722p0uDk5qYApW+oQ4m7+Mwr8moPZDv9QIlABLvD6LxGm2h6NxOagpztyUg/psNpvP50G9CXCAJpxKWDaX0HWz1enhPnqMDDAcoAknTinB9jRaIYZ2IxnGBqcSdiJs1+cLcX7Xe7tDger1WSROs71YLCAAbCn72kygLeQUVkv2Unu4egymBFvSanfQdxxF2NLpN63WGlE0gTPsdOXIvcMnwmBKsGCLPkvNKspaEieofLXT053sFXO3XK+XHxUncXH58Wm9ur8ub3Uy/+x+tf6P2Xr1GF2qAdlcfFhSx/Hk8VmXfy9cXB8wOeGqT7Lr62m9uetmUq7WptMZz8gMW1al5rZ/MhmmxiQfF6XUVIJtQFGLjv3BkJqXlSoS0vFgOMIp1xE6wSfCYEqwIEuf8s4ZfuYSrd7ll2jGionX62r5tNIvWXt9snW/fvrAnWjeJ8c2sJxp9/1iuqAciHz6JMtd7mJrAtPplCeSDGUknVW6mGAyTE3xi6Y9UCbGIAoEbOkiD5y4gkCl1DwEfBYMpgQLsvTZ+GlAmmx9mpQXjdYy2lGiGSsmelg9P8zop3IKWOqTds5d1HVQCZ2RPmH5yl0oExrgKTYMpxCgdDFBOhSmnM0BPl1snkDWJ+gx9Yrm3uFTYDAlWJC5f3Zrrd/HsTjfNeJmvyYkCuleRc7FgGnFMF3FC5ftomlFSpshqs3nh0exsrVOKX55pytoRS8YCZir5SGuZmG4LjAPj0maxg99cTY5poOn5QqVOoaBip2yoW6XkydNNTnMNDY4XdlRn2Q5yl0UjdQXt3bkwQF5cPsnwrZCshQm/Njlrqp59nYkwRVHNLv9wXweC8My4Tfffv/3P/8qwKmEmaAZMJkSLNhy/wmJlm/4LMdNSaLhn5GrRI36xAJlshRCjZ1sQdN6xb66TgWgOilJ3CXJIzzcL/ViYkhT0R0v7jlJUVwYGtVpR6KPVOT08qy+i83YKJs9R9EnBIDlTn17/QGaAAfkGY0jWSHZkCyFCT/yYKNrhg5SJ+T9M+x0kbMZxpWgfUJFovbiBGwCjKYEC7boc5Pg6zbXp7tETStGLmtjAdC6lMSWrmadkzryLrqlrPixrUEPcvc0v9apHcm27rGfPWqCZUnaemwUYM+O+sxX3/LOmaZ0MTGZXJMyyYSfZ1kscCtbcXlnI99/4kpB4gQ8nV1CIVEncQL+GQZTggXO+iwF6X2pq0S1K4atP17dkfGtTKsKnVNKpb//PIo+xYem1bu2i/XYKMCeXfSZ+/kQCkWse0qyWd92uj370rTRCrk0mcFDfSkVGS4i9u9FcMeJPPItKMFzMbNJCGW6ihPwDzCYEixw1ifILVG9PlG/Jas/biYaY+uSi82yvgW0ZVFA3GTPb+Vekmw0GjA7N0eSXgtYCbrRPfm92CdunNUltxzb5gHCNsmnzx3fr7B3IB1KtVnf4hgBSpcM0JHECcOoIB44KZVsCJN7ZUDvP6PJpJq8/wQ8i2T2Ce3hqQ2mBAvy6BMoEm0ux2+D7YWQdsVgx1Bu2NISN6kPXzwf0jll2CpPnsSIfS9xCklna0ANyBzJernUdY8FHJ+V3hvhl2Xx+udDlmPbPEDYJq76zFfQKtBqyzalSzYYErqQRKkvy8HNaf8k5O8PITE8PBezHAkt4R9gMCVYkFOfIJXop+hqnj5DysBpxZwmYotT/CeI02zv6/t9KG5B2O5gofOll1i706WzSpetYFPG7SKgm0Oezv3+U4Z//5Y9rOLpdkt4IPLrE0Ci9R8HluIEr0Cf2ABzfCfpKBx3tstBfTAczZhBWkHddpFsBUJCzhzPb03sPeEe2UmfrmDFlM7w78tEpcpK0PPYPDHPr+BqqCX3+08Te0+4RwrVp//77MLwf5/9OihUn/7/mxQA5tb/f5NXQ6H6xNLx/x+sAPz/B3s1FKpPj8fjhNenx3O6eH16PKfKVeV/xLaIHH+o3JwAAAAASUVORK5CYII=",m="/assets/running-idea-configurations-create.11183fb8.png",v="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVQAAAAZCAIAAADhQUHkAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTM0A1t6AAAHsElEQVR4Xu2cXVPbRhSG+7uY2MYfsiXbyNgyYEhpaWjSIdBMZ9pMPiYDTmbaXHQmJbUJH6HT5sKe3LRJ2zv65/qujrReH1myJONgjDTPxe7R2ZVAerS7MvizW5lsQkLCDSSRPyHhhjJ1+VOLWbPeuL25+cXWVsIcgEuJC4rLyi50wrVj6vKby/WNzzcLupHcLnMALiIuJS4oLivblXDtmLr8uFE0vcyCCdcaXFBcVhZMuHZMXX5MFJMxf87ABcVlZcGEa0eQ/GkjX3y5rB/WS68b+pHF9oYk+C552TkbC2uSMAtcpfz5nPbCzKxqVM2saKgiOEi4agpFvVxdKleqKKQWZ+jEGL7yw/zSq7px3gSFZ2aqmGcJIUnkn0siya+1a+lqzPvHS9rU9G7DOLEyq0WAAqoIsrRgiuXql19tP3z89OdXh73+e4ACqghiF0sOT1E31tZaG8q2traGIEubEUbLP2T+k6VbE8zbJxwiEvlnk0iXVdxIp1buQeVW7nKGwcxK0XjbNM5szpuL60WWMJYPf/8bAEsOiV6uwPaGZeW1Ig34uULRspoIGpX4D5QwrAduLFkyQv5UKYfZPpkPsvcnel3nd5eoY7sflKa2IhbSe52L/v7KIot/GrxH3+ledHadKtu7sNu96O4trLR7dtDbVu4aRETOxX/u1jtYk7tmh8jy25R+aSxulNjeqKTLeaxDZZ8Aa9J0o8DSgmG2l4wKxvwfHj2hKksOA+b6kNysLVM1nc1pJZ3KteU6dmUL0eYmkXAs99lYsoTLr4754uFqF3I78f2/CfKT4U5ZyHwhjbUO+qq9Yc5czaEHgex8dognPxF7FVD8sQb01+79qYCZP+1lTfyQ2jPbWTU80B4zfLnCLxRLEJ7KCGItgEcAVaeBY7nPxpIlXH78+pzf5qmYTeldi6qx/Q+Qn0UY10l+CN9vW2kRge2dg3bP1nUhvbbfH86MKL+oiqdJd8fufHoYVfP10cnZb+8YCGIXSwaTyC+ItQrQD+t6Z2jMV9GPGkhgTfyQ2se2nQHz5bAPVPnBkv1okNVLx7HcZ2PJEs/IXy3ovzaE+bfFOiptFqT/eC5krMhTl2D50z/9MRKZMFZ+TLlpeixm10I2ZQaOAdl2Us1xexCZFKR8GrGHIvZROt0+Rl64JxN6B20uvyu5W0BDu4noQRYoQS2IIV1GlN6GIuoPJc/BOSXK3HWCyMGjR/wAymLB+3P5Uas3j07OVfNRRZClEZPKbxNjFbC4UaS2mbr7tr+uUSR7N8J7NT/5tX0zpceZlUD1cnVJVqtmrdUS1mHAz+TGL0kePn7KTgkgyNL8cCz32ViyZMSaH/7jVzyoKv4bx1ZU/6ctP4uoM3B1KT6UY+vkmY2rglGaCFKamgDBLoaPDuhYIs153AydjHJoKig+u7tkVywi5Vfj6BknZkdwLuKIiEjnxXEHTwf+c1G3I1lt3T4++53MRwFVliC5EvkxWdCemdS28KiKKkCBItrzmphNeFqN5K+P/6iayTj60d9Y2e3I7+fVkd+oVPEsKBlisgz3sKtkjD8x5n948wFJ7rexZMkI+b3kH5Tp9yuI6P+05aebHhsJae9y73t3Ks5zhAYiZ9DhcGRgsjwKEmRvSlwifMMw7tpOS3254JdNqNBTHj3e3lhkMK0QJ+mM4djYrGFkeeTPRWU/Nre2T8/fARTYLpVJ5Y817eedjII18ePo+FQ1TcZlP5jnYhSU8bGoa/56wyLzgePf+nrDsrL5MeJI/yOZD5xj+GwsWRJK/lR+8C5AEMX/qcovb25pCPZy8Tw5TAk1h6px5LcTdrDgl8Nsty1PaXDConDR6/fl9MTbG4vIc2MnyTJHllmTMPKDezt7gAUZk8gf+4VfqlLI3tGpE++0P79XRoJMDubO3W+k+QCR8pJ4WUhdEfqJlbtfDvk5N1b5GO3VZT/h+GdvrVZr7Gd+0D6q+cA5gM/GkiWh5Aex/Z9Q/pGIAdad6zoFcZe7tz7K/W6n79z03pwQ035bNq6TkzBy2k99yjjl09zbrdKhnQI8ZJMC6odFqB/nxOyy57TVTF4eaj78IJiQePJP+FFfZrWY/dqgrrzT/ty3FSSwJgHst19I+be27/b67xGkrlTQv9oqAPqc32o2c+7n/MDxT9mWzLAfSYTH6dpnY8mSsPID5n+p2wjzN5XB8gfgP+arjgnlxBxY2D4UVIbWkTmiH3v6zF/4DQvslEXVXTt4X/gR4qHgzg4A9PYO70rBPsl+u5HaZb2p58aeMvIkxa4Q0361CetqQiLLfxl/5CPvvQBYk2C++/7hnx8+kv8oIKJ2FWnkJ9S/8FtfF2/7Hf/sLczIHw/nAD4bS5ZEkB8M/D+1Mq1QT9kA+cfCmiTMDpHkv6w/78WcH+TuGcaJ+waaeNuEpbSXNRmLudx4ddgBZl3894rsM+qaX8X523775b/jX7g1/ycmmvwA/mvPayHNB5HukoTrwtVe1rSpafum/qahH1vai1qmeWlSQft4b/v9gPYh3/Z/eiLLHxXcJcm/9M4Zc/wvvbE/5/cj5Of8V8LU5U++zGP+SL7MYz6YuvzJ13jNE7iIydd4zQ1Tlx+3S/IFnvNE8gWec8PU5U9ISJhFMtn/AULrFA9ITIq9AAAAAElFTkSuQmCC",b="/assets/running-eclipse-configurations-empty.5810cf5b.png",h="/assets/running-eclipse-configurations-create.e4d5d1d0.png",f="/assets/running-eclipse-configurations-bar.32d3b9a3.png";const y={},A=e('

    Running and Deploying your Bot

    If you took the time to write a bot, at some point you'll also want to run it, either for use in production or for debugging from the IDE.

    \u{1F477} Running from your IDE

    While developing your bot, you will want to run your bot directly from the IDE in order to quickly test changes and new features. For this, create a Run/Debug Configuration in your IDE of choice with your bot's main class. Remember to also add any necessary parameters and environment variables.

    A working Run/Debug configuration will also enable you to run your bot with a debugger. A debugger is often considered a developer's most important tool, so make sure to familiarize yourself with the debugging integration for your IDE of choice.

    IntelliJ IDEA

    This assumes your project is set up correctly, preferably with Gradle, can be built without errors, and does not yet have any run/debug configurations.

    1. Locate and click the Add Configuration... button in the top bar next to the start button.

    2. In the newly opened window, click the + button in the top left and select Application

    3. Give a name for your configuration and select the module to use the classpath of (usually yourproject.main).

    4. Select your Main class. Use the ... button to search for it or provide the fully qualified name. If it can not be found, you most likely selected the wrong module in step 3.

    5. Optional: Set command line arguments and environment variables. For the environment variables, use the button to the right of the input field for a more convenient input window.

    6. Click Apply to finalize the configuration, then OK to close the window.

    7. Select your configuration in the drop-down menu and run or debug it with the buttons to the right.

    Eclipse

    This assumes your project is set up correctly, can be built without errors, and does not yet have any run/debug configurations.

    1. In the menu bar, click "Run" then "Run Configurations...".

    2. In the newly opened window, select "Java Application" on the left side, then click the leftmost button in the row above the tree view. A new configuration will appear.

    3. Give a name to your configuration.

    4. Set the project and the main class. To easily select it, use the "Browse..." and "Search..." buttons.

    5. Optional: Set command line (and VM) arguments as well as environment variables in their respective tabs.

    6. Click Apply to save your configuration, then Close to close the window.

    7. Run or debug your bot via the Buttons in the top row, the Run menu, or the shortcuts Ctrl+F11 for running and F11 for debugging.

    \u{1F4E6} Deploying and Running as a Standalone Application

    Running from the IDE is only recommended during development and strongly discouraged for production use. Generally, you'll want your build tool to create a convenient distribution format for you to use.

    Building a Distribution with Gradle

    ',32),w=s("For Gradle, only two further steps are necessary for a basic application. On top of the steps described in the "),x=n("a",{href:"/wiki/getting-started/intellij-gradle"},"Getting Started Section",-1),C=s(", also add the "),I={href:"https://docs.gradle.org/current/userguide/application_plugin.html",target:"_blank",rel:"noopener noreferrer"},j=s("Application Plugin"),_=s(" and define your "),S=n("code",null,"mainClass",-1),M=s(" as the fully qualified name of your main class. If you're using an older version of Gradle (earlier than 6.4), the attribute is instead called "),T=n("code",null,"mainClassName",-1),P=s("."),G=n("div",{class:"custom-container tip"},[n("p",{class:"custom-container-title"},"INFO"),n("p",null,[s("As with many Gradle solutions, there is actually a whole lot going on under the hood. The "),n("code",null,"application"),s(" plugin implicitly also applies the "),n("code",null,"java"),s(" and "),n("code",null,"distribution"),s(" plugins. Refer to the documentations of the involved plugins for more ways to fine-tune the process.")])],-1),q=n("p",null,"Your modified build file should now look similar to this:",-1),L=n("div",{class:"language-kts ext-kts line-numbers-mode"},[n("pre",{class:"language-kts"},[n("code",null,[s("plugins "),n("span",{class:"token punctuation"},"{"),s(` + application +`),n("span",{class:"token punctuation"},"}"),s(` + +version `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"1.0.0"')]),s(` + +java `),n("span",{class:"token punctuation"},"{"),s(` + sourceCompatibility `),n("span",{class:"token operator"},"="),s(" JavaVersion"),n("span",{class:"token punctuation"},"."),s(`VERSION_1_8 +`),n("span",{class:"token punctuation"},"}"),s(` + +application `),n("span",{class:"token punctuation"},"{"),s(` + mainClass`),n("span",{class:"token punctuation"},"."),n("span",{class:"token function"},"set"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"com.github.yourname.BotMain"')]),n("span",{class:"token punctuation"},")"),s(` + `),n("span",{class:"token comment"},'// mainClassName.set("com.github.yourname.BotMain") // Gradle < 6.4'),s(` +`),n("span",{class:"token punctuation"},"}"),s(` + +repositories `),n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token function"},"mavenCentral"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` + +dependencies `),n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token function"},"implementation"),n("span",{class:"token punctuation"},"("),n("span",{class:"token string-literal singleline"},[n("span",{class:"token string"},'"org.javacord:javacord:{{latestVersion}}"')]),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),V=n("div",{class:"language-groovy ext-groovy line-numbers-mode"},[n("pre",{class:"language-groovy"},[n("code",null,[s("plugins "),n("span",{class:"token punctuation"},"{"),s(` + id `),n("span",{class:"token string"},"'application'"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` + +version `),n("span",{class:"token string"},"'1.0.0'"),s(` + +java `),n("span",{class:"token punctuation"},"{"),s(` + sourceCompatibility `),n("span",{class:"token operator"},"="),s(" JavaVersion"),n("span",{class:"token punctuation"},"."),s(`VERSION_1_8 +`),n("span",{class:"token punctuation"},"}"),s(` + +application `),n("span",{class:"token punctuation"},"{"),s(` + mainClass `),n("span",{class:"token operator"},"="),s(),n("span",{class:"token string"},"'com.github.yourname.BotMain'"),s(` + `),n("span",{class:"token comment"},"// mainClassName = 'com.github.yourname.BotMain' // for Gradle versions < 6.4"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` + +repositories `),n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token function"},"mavenCentral"),n("span",{class:"token punctuation"},"("),n("span",{class:"token punctuation"},")"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` + +dependencies `),n("span",{class:"token punctuation"},"{"),s(` + implementation `),n("span",{class:"token string"},"'org.javacord:javacord:{{latestVersion}}'"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),E=e('

    Now you can execute the distZip or distTar task with Gradle. The task will create a distribution and package it in an archive file that will be placed in the build/distributions directory. Extract the content of those files on your server or whichever machine you want to run your bot on.

    The distribution usually only contains the directories bin and lib. From the distribution directory, run either bin/yourbot or bin/yourbot.bat, depending on whether you're running the bot on Linux / macOS or windows.

    Building a Distribution with Maven

    ',3),Y=s("For Maven, add the "),F={href:"https://www.mojohaus.org/appassembler/appassembler-maven-plugin/usage-program.html",target:"_blank",rel:"noopener noreferrer"},N=s("Appassembler"),J=s(" plugin to your "),K=n("code",null,"pom.xml",-1),R=s(". The plugin will create a distribution, but not bundle it in a neat archive file, so we'll also add the assembly plugin. We'll bind both to the "),B=n("code",null,"package",-1),D=s(" lifecycle phase."),Z=e(`
    <project>
    +  ...
    +    <build>
    +        <plugins>
    +            <plugin>
    +                <groupId>org.codehaus.mojo</groupId>
    +                <artifactId>appassembler-maven-plugin</artifactId>
    +                <version>1.10</version>
    +                <configuration>
    +                    <programs>
    +                        <program>
    +                            <mainClass>org.javacord.examplebot.Main</mainClass>
    +                            <id>examplebot</id>
    +                        </program>
    +                    </programs>
    +                </configuration>
    +                <executions>
    +                    <execution>
    +                        <id>create-distribution</id>
    +                        <phase>package</phase>
    +                        <goals>
    +                            <goal>assemble</goal>
    +                        </goals>
    +                    </execution>
    +                </executions>
    +            </plugin>
    +            <plugin>
    +                <artifactId>maven-assembly-plugin</artifactId>
    +                <version>3.3.0</version>
    +                <configuration>
    +                    <descriptors>
    +                        <!-- This must match the location of the descriptor -->
    +                        <descriptor>src/assembly/distribution.xml</descriptor>
    +                    </descriptors>
    +                </configuration>
    +                <executions>
    +                    <execution>
    +                        <id>create-archive</id>
    +                        <phase>package</phase>
    +                        <goals>
    +                            <goal>single</goal>
    +                        </goals>
    +                    </execution>
    +                </executions>
    +            </plugin>
    +        </plugins>
    +    </build>
    +</project>
    +

    Sadly, none of the built-in assembly descriptors match our use case, so we'll put our custom one into src/assembly/distribution.xml:

    <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    +    <id>distribution</id>
    +    <formats>
    +        <!-- See https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html for supported formats -->
    +        <format>tar.gz</format>
    +        <format>tar.bz2</format>
    +        <format>zip</format>
    +    </formats>
    +    <fileSets>
    +        <fileSet>
    +            <!-- This will also include your project readme, license and similar files-->
    +            <directory>\${project.basedir}</directory>
    +            <outputDirectory>/</outputDirectory>
    +            <includes>
    +                <include>README*</include>
    +                <include>LICENSE*</include>
    +                <include>NOTICE*</include>
    +            </includes>
    +        </fileSet>
    +        <fileSet>
    +            <!-- Change this if you reconfigured the appassembler output directory -->
    +            <directory>\${project.build.directory}/appassembler</directory>
    +            <outputDirectory>/</outputDirectory>
    +        </fileSet>
    +    </fileSets>
    +</assembly>
    +

    Now when you execute mvn package, a distribution with start scripts for Windows and Linux/macOS will be generated which is then packaged into archive files for every format you specified in the assembly descriptor. You can find the raw distribution (without readme and license files) in target/appassembler and the archive files directly in target.

    Running

    After creating your distribution via Gradle or Maven and extracting/copying it to the machine you want to run it from, you should have a directory containing both a bin and a lib (or repo) directory. Depending on your platform, you can now run the bin/yourbot or bin/yourbot.bat script.

    These automatically generated scripts will then invoke java with your dependencies on the classpath and run your main class. Your working directory will be the directory you ran the script from.

    \u{1F4A9} Building a Fat Jar

    Although it is an abuse of the way java works, sometimes you will be forced to create a fat jar, or an uber jar. This is a jar file that contains your application and all its dependencies. This is sometimes used as a lazy way of building a convenient distribution, but should be foregone in favor of the above mentioned distributions.

    However, in some cases (more often than not Bukkit/Spigot addons) it is necessary to provide a fat jar, since the host application's loading mechanism can only handle singular jar files. If you are subject to such a case of bad design, please complain to the maintainer of whatever host application you are using, then use the following instructions to forsake all that is good and just and create a fat jar. Remember to grit your teeth the whole time.

    With Gradle

    `,11),z=s("For Gradle, use the "),X={href:"https://github.com/johnrengelman/shadow",target:"_blank",rel:"noopener noreferrer"},H=s("shadow"),Q=s(" plugin. If you want the fat jar to be executable, you will need to specify a main class via the application plugin."),W=e(`
    plugins {
    +    id 'java'
    +    # ...
    +    id 'com.github.johnrengelman.shadow' version '7.1.2'
    +}
    +

    With gradlew shadowJar you can now create a shaded (fat) jar. It will be named build/libs/yourbot-1.0.0-all.jar or similar, according to your project settings.

    With Maven

    `,3),O=s("For Maven, add the "),U={href:"https://maven.apache.org/plugins/maven-shade-plugin/usage.html",target:"_blank",rel:"noopener noreferrer"},$=s("maven-shade-plugin"),nn=s(" to your build. As with the other solutions, configure your main class."),sn=e(`

    Some of your dependencies might be signed .jar files. Unfortunately, this will likely break your fat jar. Remove the signatures by defining an exclusion filter as demonstrated below. Let the thought that you had to disable a security feature just to make this work serve as a reminder that creating a fat jar is not how jars are meant to be used.

    <project>
    +  ...
    +  <build>
    +    <plugins>
    +      <plugin>
    +        <groupId>org.apache.maven.plugins</groupId>
    +        <artifactId>maven-shade-plugin</artifactId>
    +        <version>3.2.3</version>
    +        <configuration>
    +            <shadedArtifactAttached>true</shadedArtifactAttached>
    +            <shadedClassifierName>fat</shadedClassifierName>
    +            <transformers>
    +                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    +                    <manifestEntries>
    +                       <Main-Class>com.github.yourname.BotMain</Main-Class>
    +                    </manifestEntries>
    +                </transformer>
    +            </transformers>
    +            <filters>
    +                <filter>
    +                    <artifact>*:*</artifact>
    +                    <excludes>
    +                        <exclude>META-INF/*.SF</exclude>
    +                        <exclude>META-INF/*.DSA</exclude>
    +                        <exclude>META-INF/*.RSA</exclude>
    +                    </excludes>
    +                </filter>
    +            </filters>
    +        </configuration>
    +        <executions>
    +          <execution>
    +            <phase>package</phase>
    +            <goals>
    +              <goal>shade</goal>
    +            </goals>
    +          </execution>
    +        </executions>
    +      </plugin>
    +    </plugins>
    +  </build>
    +</project>
    +

    Running mvn package will now additionally create the yourbot-1.0.0-fat.jar.

    `,3);function an(tn,en){const i=t("LatestVersion"),c=t("ClientOnly"),p=t("ExternalLinkIcon"),l=t("CodeGroupItem"),u=t("CodeGroup");return d(),g("div",null,[A,a(c,null,{default:o(()=>[a(i)]),_:1}),n("p",null,[w,x,C,n("a",I,[j,a(p)]),_,S,M,T,P]),G,q,a(u,null,{default:o(()=>[a(l,{title:"build.gradle.kts",active:""},{default:o(()=>[L]),_:1}),a(l,{title:"build.gradle"},{default:o(()=>[V]),_:1})]),_:1}),E,n("p",null,[Y,n("a",F,[N,a(p)]),J,K,R,B,D]),Z,n("p",null,[z,n("a",X,[H,a(p)]),Q]),W,n("p",null,[O,n("a",U,[$,a(p)]),nn]),sn])}var on=r(y,[["render",an],["__file","running.html.vue"]]);export{on as default}; diff --git a/assets/search.0782d0d1.svg b/assets/search.0782d0d1.svg new file mode 100644 index 0000000..03d8391 --- /dev/null +++ b/assets/search.0782d0d1.svg @@ -0,0 +1 @@ + diff --git a/assets/select-gradle.959beb91.png b/assets/select-gradle.959beb91.png new file mode 100644 index 0000000..87bf8e1 Binary files /dev/null and b/assets/select-gradle.959beb91.png differ diff --git a/assets/sharding.html.592f58f7.js b/assets/sharding.html.592f58f7.js new file mode 100644 index 0000000..38bcc98 --- /dev/null +++ b/assets/sharding.html.592f58f7.js @@ -0,0 +1,48 @@ +import{_ as e,r as t,o,c as p,a as s,b as c,e as i,d as n}from"./app.151ccb98.js";const l={},u=i(`

    Sharding

    Discord allows (and forces) you to "split" larger bots into several independent parts. This behavior is called "sharding", and the independent parts are called "shards". You can think of shards as completely independent bots. Every shard is responsible for a disjoint set of servers.

    \u{1F469}\u200D\u{1F3ED} Sharding in Javacord

    Logging in with a single shard

    Logging in with a single shard is pretty much the same as logging in without sharding:

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("top secret")
    +    .setCurrentShard(0)
    +    .setTotalShards(2)
    +    .login().join();
    +System.out.println("Shard " + api.getCurrentShard() + " logged in!");
    +

    Note: current shard starts counting at 0! This means in the example above you would have current shard 0 and shard 1 with a total amount of 2 shards.

    Important: There must be a > 5-second delay between each shard-login

    Logging in with a fixed amount of shards

    You can manually set a fixed amount of total shards and log in all of them:

    public class Main {
    +
    +    public static void main(String[] args) {
    +        new DiscordApiBuilder()
    +            .setToken("top secret")
    +            .setTotalShards(10)
    +            .loginAllShards()
    +            .forEach(shardFuture -> shardFuture
    +                .thenAcceptAsync(Main::onShardLogin)
    +                .exceptionally(ExceptionLogger.get())
    +            );
    +    }
    +
    +    private static void onShardLogin(DiscordApi api) {
    +        System.out.println("Shard " + api.getCurrentShard() + " logged in!");
    +        // You can treat the shard like a normal bot account, e.g. registering listeners
    +        api.addMessageCreateListener(event -> {
    +            // ...
    +        });
    +    }
    +
    +}
    +

    loginAllShards() returns a collection with completable futures (Collection<CompletableFuture<DiscordApi>>). This method automatically obeys the > 5-second delay rule.

    You can "ask" Discord to recommend you a total amount of shards. This is done by using the DiscordApiBuilder#setRecommendedTotalShards() method, which returns a CompletableFuture<DiscordApiBuilder> after getting the required information.

    public class Main {
    +
    +    public static void main(String[] args) {
    +        new DiscordApiBuilder()
    +            .setToken("top secret")
    +            .setRecommendedTotalShards().join()
    +            .loginAllShards()
    +            .forEach(shardFuture -> shardFuture
    +                .thenAccept(Main::onShardLogin)
    +                .exceptionally(ExceptionLogger.get())
    +            );
    +    }
    +
    +    private static void onShardLogin(DiscordApi api) {
    +        // ...
    +    }
    +
    +}
    +

    \u{1F4A1} Behavior of Shards

    Managed servers

    You can calculate for which servers a shard is responsible using the server id:

    boolean isResponsible = (serverId >> 22) % totalShards == currentShard;
    +

    Private messages

    Private messages are always sent to the first shard (currentShard == 0).

    When do I need sharding?

    Sharding is forced for bots which are in more than 2500 servers.

    \u{1F304} Sharding for Very Large Bots

    `,24),r=n('Sharding for very large bots (> 150,000 servers) is a bit different from "normal" sharding. Discord will contact you once your bot reaches this state. Additional information can be found in the '),d={href:"https://discordapp.com/developers/docs/topics/gateway#sharding-for-large-bots",target:"_blank",rel:"noopener noreferrer"},k=n("official Discord api documentation"),h=n(".");function v(m,g){const a=t("ExternalLinkIcon");return o(),p("div",null,[u,s("p",null,[r,s("a",d,[k,c(a)]),h])])}var f=e(l,[["render",v],["__file","sharding.html.vue"]]);export{f as default}; diff --git a/assets/sharding.html.d6f88b19.js b/assets/sharding.html.d6f88b19.js new file mode 100644 index 0000000..bf10a1b --- /dev/null +++ b/assets/sharding.html.d6f88b19.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-595301cf","path":"/wiki/advanced-topics/sharding.html","title":"Sharding","lang":"en-US","frontmatter":{"keywords":["sharding","large"]},"excerpt":"","headers":[{"level":2,"title":"\u{1F469}\u200D\u{1F3ED} Sharding in Javacord","slug":"sharding-in-javacord","children":[{"level":3,"title":"Logging in with a single shard","slug":"logging-in-with-a-single-shard","children":[]},{"level":3,"title":"Logging in with a fixed amount of shards","slug":"logging-in-with-a-fixed-amount-of-shards","children":[]},{"level":3,"title":"Using the recommended shard amount","slug":"using-the-recommended-shard-amount","children":[]}]},{"level":2,"title":"\u{1F4A1} Behavior of Shards","slug":"behavior-of-shards","children":[{"level":3,"title":"Managed servers","slug":"managed-servers","children":[]},{"level":3,"title":"Private messages","slug":"private-messages","children":[]},{"level":3,"title":"When do I need sharding?","slug":"when-do-i-need-sharding","children":[]}]},{"level":2,"title":"\u{1F304} Sharding for Very Large Bots","slug":"sharding-for-very-large-bots","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/advanced-topics/sharding.md"}');export{e as data}; diff --git a/assets/style.18d74fae.css b/assets/style.18d74fae.css new file mode 100644 index 0000000..0aa0c0a --- /dev/null +++ b/assets/style.18d74fae.css @@ -0,0 +1 @@ +:root{--back-to-top-z-index: 5;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3}.back-to-top{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;height:1.2rem;background-color:var(--back-to-top-color);-webkit-mask:url(/assets/back-to-top.8efcbe56.svg) no-repeat;mask:url(/assets/back-to-top.8efcbe56.svg) no-repeat;z-index:var(--back-to-top-z-index)}.back-to-top:hover{background-color:var(--back-to-top-color-hover)}@media (max-width: 959px){.back-to-top{display:none}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #e7c000;--c-warning-bg: #fffae3;--c-warning-title: #ad9000;--c-warning-text: #746000;--c-warning-text-accent: var(--c-text);--c-danger: #cc0000;--c-danger-bg: #ffe0e0;--c-danger-title: #990000;--c-danger-text: #660000;--c-danger-text-accent: var(--c-text);--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: var(--c-warning);--c-badge-danger: var(--c-danger);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #ceab00;--c-warning-bg: #7e755b;--c-warning-title: #ceac03;--c-warning-text: #362e00;--c-danger: #940000;--c-danger-bg: #806161;--c-danger-title: #610000;--c-danger-text: #3a0000;--c-details-bg: #323843;--code-hl-bg-color: #363b46}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-lighter);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}a.header-anchor:hover{text-decoration:none}a.header-anchor:focus-visible{opacity:1}p,ul,ol{line-height:1.7}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning)}.badge.danger{background-color:var(--c-badge-danger)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.4;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.4em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}div[class*=language-].ext-c:before{content:"c"}div[class*=language-].ext-cpp:before{content:"cpp"}div[class*=language-].ext-cs:before{content:"cs"}div[class*=language-].ext-css:before{content:"css"}div[class*=language-].ext-dart:before{content:"dart"}div[class*=language-].ext-docker:before{content:"docker"}div[class*=language-].ext-fs:before{content:"fs"}div[class*=language-].ext-go:before{content:"go"}div[class*=language-].ext-html:before{content:"html"}div[class*=language-].ext-java:before{content:"java"}div[class*=language-].ext-js:before{content:"js"}div[class*=language-].ext-json:before{content:"json"}div[class*=language-].ext-kt:before{content:"kt"}div[class*=language-].ext-less:before{content:"less"}div[class*=language-].ext-makefile:before{content:"makefile"}div[class*=language-].ext-md:before{content:"md"}div[class*=language-].ext-php:before{content:"php"}div[class*=language-].ext-py:before{content:"py"}div[class*=language-].ext-rb:before{content:"rb"}div[class*=language-].ext-rs:before{content:"rs"}div[class*=language-].ext-sass:before{content:"sass"}div[class*=language-].ext-scss:before{content:"scss"}div[class*=language-].ext-sh:before{content:"sh"}div[class*=language-].ext-styl:before{content:"styl"}div[class*=language-].ext-ts:before{content:"ts"}div[class*=language-].ext-toml:before{content:"toml"}div[class*=language-].ext-vue:before{content:"vue"}div[class*=language-].ext-yml:before{content:"yml"}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--code-bg-color)}.code-group__ul{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:#ffffffe6;font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid rgba(255,255,255,.9)}.code-group__nav-tab-active{border-bottom:var(--c-brand) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width: 720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}.theme-default-content a:hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media (max-width: 719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.navbar-items{display:inline-block}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text-accent)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}}@media (min-width: 719px){.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text)}.navbar-item>a:hover,.navbar-item>a.router-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.router-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}@media (max-width: 719px){.navbar-dropdown-wrapper.open .navbar-dropdown-title,.navbar-dropdown-wrapper.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width: 720px){.navbar-dropdown-wrapper{height:1.8rem}.navbar-dropdown-wrapper:hover .navbar-dropdown,.navbar-dropdown-wrapper.open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper.open:blur{display:none}.navbar-dropdown-wrapper .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"\2190"}.page-nav .next{float:right}.page-nav .next a:after{content:"\2192"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.sidebar-heading.collapsible{cursor:pointer}.sidebar-item.sidebar-heading.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}:root{--c-brand: #6c83e0;--c-brand-light: #7b8fe3;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #ccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #e7c000;--c-warning-bg: #fffae3;--c-warning-title: #ad9000;--c-warning-text: #746000;--c-warning-text-accent: var(--c-text);--c-danger: #cc0000;--c-danger-bg: #ffe0e0;--c-danger-title: #990000;--c-danger-text: #660000;--c-danger-text-accent: var(--c-text);--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: var(--c-warning);--c-badge-danger: var(--c-danger);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.back-to-top{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}.pwa-popup{--pwa-popup-text-color: var(--c-text);--pwa-popup-bg-color: var(--c-bg);--pwa-popup-border-color: var(--c-brand);--pwa-popup-shadow: 0 4px 16px var(--c-brand);--pwa-popup-btn-text-color: var(--c-bg);--pwa-popup-btn-bg-color: var(--c-brand);--pwa-popup-btn-hover-bg-color: var(--c-brand-light)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #6c83e0;--c-brand-light: #7b8fe3;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #ceab00;--c-warning-bg: #7e755b;--c-warning-title: #ceac03;--c-warning-text: #362e00;--c-danger: #940000;--c-danger-bg: #806161;--c-danger-title: #610000;--c-danger-text: #3a0000;--c-details-bg: #323843;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}.search-box input{cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url(/assets/search.0782d0d1.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}@media (max-width: 720px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 420px){.search-box input:focus{width:8rem}.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}} diff --git a/assets/use-invite-link.6050cdc9.png b/assets/use-invite-link.6050cdc9.png new file mode 100644 index 0000000..ba52a59 Binary files /dev/null and b/assets/use-invite-link.6050cdc9.png differ diff --git a/assets/writing-your-first-bot.html.3edd185d.js b/assets/writing-your-first-bot.html.3edd185d.js new file mode 100644 index 0000000..637b3b7 --- /dev/null +++ b/assets/writing-your-first-bot.html.3edd185d.js @@ -0,0 +1,28 @@ +import{_ as o,r as i,o as p,c,a as n,b as t,w as e,d as s,e as u}from"./app.151ccb98.js";var l="/assets/ping-pong-white.53343497.gif";const r={},d=n("h1",{id:"writing-your-first-bot",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#writing-your-first-bot","aria-hidden":"true"},"#"),s(" Writing your first bot")],-1),k=n("p",null,"After you have successfully added Javacord as a dependency, created a bot user, and got its token, you are now ready to create your first simple bot! \u{1F389}",-1),v=n("h2",{id:"enabling-required-intents",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#enabling-required-intents","aria-hidden":"true"},"#"),s(" \u2757 Enabling required intents")],-1),m=s("By default, all non-privileged intents are enabled. To receive the message content, attachments, components, and embeds you need a special privileged intent "),g=n("code",null,"MESSAGE_CONTENT",-1),h=s(". To enable this privileged intent please see the "),b=s("Gateway Intents"),f=s(" wiki article."),y={class:"custom-container tip"},_=n("p",{class:"custom-container-title"},"Slash Commands",-1),w=s("Generally it is recommended to use "),q=s("Slash Commands"),x=s(" instead of text commands because they offer many advantages like auto-completion, fixed and optional arguments, different kind of arguments with built-in types: numbers(with ranges), text, channel and a lot more."),C=u(`

    \u{1F511} Log the bot in

    Everything starts with the DiscordApiBuilder class. It is used to create a DiscordApi object which is the most important class of your bot.

    DiscordApi api = new DiscordApiBuilder()
    +        .setToken("<your super secret token>")
    +        .addIntents(Intent.MESSAGE_CONTENT)
    +        .login().join();
    +

    After executing this code, you should already see your bot online in Discord. Of course, just being online is not enough, so let's add some more code!

    \u{1F442} Adding a listener

    After you got your api instance, let's continue by adding a listener that answers every !ping message with a simple Pong!.

    api.addMessageCreateListener(event -> {
    +    if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +        event.getChannel().sendMessage("Pong!");
    +    }
    +});
    +

    \u{1F469}\u200D\u{1F527} Putting it all together

    A good place for your code is the main(...) method that every executable Java program must have. Your complete class may look like this:

    public class MyFirstBot {
    +
    +    public static void main(String[] args) {
    +        // Log the bot in
    +        DiscordApi api = new DiscordApiBuilder()
    +                .setToken("<your super secret token>")
    +                .addIntents(Intent.MESSAGE_CONTENT)
    +                .login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +    }
    +
    +}
    +

    Congratulations, that's already everything you have to know for the beginning. Now, you can play around a little bit by exploring other listeners and methods. Or you just continue reading articles in the Basic Tutorials category.

    `,12);function A(j,E){const a=i("RouterLink");return p(),c("div",null,[d,k,v,n("p",null,[m,g,h,t(a,{to:"/wiki/basic-tutorials/gateway-intents.html#privileged-intents"},{default:e(()=>[b]),_:1}),f]),n("div",y,[_,n("p",null,[w,t(a,{to:"/wiki/basic-tutorials/interactions/commands.html"},{default:e(()=>[q]),_:1}),x])]),C])}var N=o(r,[["render",A],["__file","writing-your-first-bot.html.vue"]]);export{N as default}; diff --git a/assets/writing-your-first-bot.html.ab95f1ba.js b/assets/writing-your-first-bot.html.ab95f1ba.js new file mode 100644 index 0000000..42eb611 --- /dev/null +++ b/assets/writing-your-first-bot.html.ab95f1ba.js @@ -0,0 +1 @@ +const t=JSON.parse('{"key":"v-7bf86adb","path":"/wiki/getting-started/writing-your-first-bot.html","title":"Writing your first bot","lang":"en-US","frontmatter":{},"excerpt":"","headers":[{"level":2,"title":"\u2757 Enabling required intents","slug":"enabling-required-intents","children":[]},{"level":2,"title":"\u{1F511} Log the bot in","slug":"log-the-bot-in","children":[]},{"level":2,"title":"\u{1F442} Adding a listener","slug":"adding-a-listener","children":[]},{"level":2,"title":"\u{1F469}\u200D\u{1F527} Putting it all together","slug":"putting-it-all-together","children":[]}],"git":{"updatedTime":1692105785000,"contributors":[{"name":"Dominic Fellbaum","email":"d.fellbaum@hotmail.de","commits":1}]},"filePathRelative":"wiki/getting-started/writing-your-first-bot.md"}');export{t as data}; diff --git a/bot-search-index.json b/bot-search-index.json new file mode 100644 index 0000000..e456634 --- /dev/null +++ b/bot-search-index.json @@ -0,0 +1 @@ +[{"title":"Imprint","headers":[],"content":"

    # Imprint

    \n","path":"/imprint.html","keywords":[]},{"title":"Privacy Policy","headers":[],"content":"

    # Privacy Policy

    \n","path":"/privacy-policy.html","keywords":[]},{"title":"Bot Lifecycle","headers":[{"level":2,"title":"💡 The four states","slug":"the-four-states","children":[{"level":3,"title":"Connected","slug":"connected","children":[]},{"level":3,"title":"Disconnected","slug":"disconnected","children":[]},{"level":3,"title":"Resuming","slug":"resuming","children":[]},{"level":3,"title":"Reconnecting","slug":"reconnecting","children":[]}]},{"level":2,"title":"💊 How to handle disconnects","slug":"how-to-handle-disconnects","children":[]}],"content":"

    # Bot Lifecycle

    \n

    It's important to know the life-cycle of a discord bot to properly handle disconnects.\nThe following state diagram shows the 4 states a bot can have:

    \n

    \"\"

    \n

    # 💡 The four states

    \n

    # Connected

    \n

    The bot is connected to the websocket and receives all events.

    \n

    # Disconnected

    \n

    The bot is not connected to the websocket and receives no events. It's not uncommon for a bot to occasionally lose connection.\nThis can have various reasons, for example:

    \n
      \n
    • Your bot lost its internet connection
    • \n
    • Discord restarted the gateway server you are currently connected to
    • \n
    • A plane crashed into Discord's data center
    • \n
    \n

    The bot will periodically try to resume/reconnect to the websocket. It will start with a small frequency and increase it\nwith every failed reconnect attempt. You can modify the reconnect delay with the DiscordApi#setReconnectDelay(...) method.\nThe following example code would increase the delay linearly.\nThe 1st attempt would be delayed for 2 seconds, the 2nd attempt for 4 seconds, the 3rd attempts for 6 seconds, ...

    \n
    api.setReconnectDelay(attempt -> attempt * 2);\n
    \n

    Important: Bots can only reconnect 1000 times in a 24-hour period (every ~90 seconds). This limit is global and across all shards.\nUpon hitting this limit, all active sessions for the bot will be terminated, the bot's token will be reset, and\nyou will receive an email notification. This is the reason Javacord increases the reconnect delay with every attempt.

    \n
    \n

    By default, the $default_delay$ formula below is used to calculate the reconnect delay

    \n

    $$\ndefault_delay(a) = \\lfloor a^{1.5} - \\frac{a^{1.5}}{\\frac{1}{(0.1 \\cdot a)} + 1} \\rceil\n$$

    \n

    with $a$ being the attempt.

    \n

    The formula will generate the following reconnect delay:

    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    AttemptDelay
    11
    22
    34
    46
    57
    ......
    1016
    1523
    2030
    ......
    5059
    10091
    150115
    ......
    \n

    # Resuming

    \n

    Resuming is only possible for a short time after being disconnected. If the bot can successfully resume the connection,\nyou will not miss any events. Your bot will receive all events you missed while being disconnected. The cache gets updated\naccordingly.

    \n

    # Reconnecting

    \n

    If your bot reconnects (not resumes!), the whole cache gets wiped, and you will not receive any missed events.

    \n

    What does this mean?

    \n
      \n
    • References to entities (e.g. a Server, User, Channel, ...) will be outdated. This is why you should never store\nentities, but the id instead. See Entity Cache.
    • \n
    • You will miss events. There's no way to receive the missed events.
    • \n
    • Listeners attached to entities will not be affected, because they are bound to the entity's id, not the object itself.
    • \n
    \n

    # 💊 How to handle disconnects

    \n

    For most bots, there's nothing you have to do. All registered listeners are reconnect-resistant, which means if your bot\nis only reacting to events, it will work fine after a restart. For example, the following code will not be affected by a\nreconnect (besides maybe some missed !ping messages):

    \n
    api.addMessageCreateListener(event -> {\n    if (event.getMessage().getContent().equalsIgnoreCase(\"!ping\")) {\n        event.getChannel().sendMessage(\"Pong!\");\n    }\n});\n

    In case you want to handle reconnects (e.g. fetch the message history to detect missed messages), there are\nspecial connection-related listeners which can be used to track the state of the bot:

    \n
      \n
    • LostConnectionListener
    • \n
    • ReconnectListener
    • \n
    • ResumeListener
    • \n
    \n","path":"/wiki/advanced-topics/bot-lifecycle.html","keywords":["resume","reconnect","lifecycle","unavailable"]},{"title":"Entity Cache","headers":[{"level":2,"title":"🔮 What is in the cache?","slug":"what-is-in-the-cache","children":[]},{"level":2,"title":"❓ When are cached entities updated?","slug":"when-are-cached-entities-updated","children":[]},{"level":2,"title":"⌚ How long are cached entities valid?","slug":"how-long-are-cached-entities-valid","children":[]}],"content":"

    # Entity Cache

    \n

    Javacord keeps an internal cache for entities (e.g. Servers, Channels, Users, ...). It is important to know how the cache behaves to properly use it.

    \n

    # 🔮 What is in the cache?

    \n

    Nearly every entity known by the bot is guaranteed to be in the cache. There are a few exceptions though:

    \n

    # Users

    \n

    Users are only cached when you have the GUILD_MEMBERS intent enabled.\nSee Gateway Intents for more information.

    \n

    # Messages

    \n

    Not every single message is in the cache, which means you can encounter messages which exist but are not in the cache. This can happen for most message events, e.g. the ReactionAddEvent. You can, however, interact with these messages without having them in the cache. Every message event has methods like event.deleteMessage(), event.editMessage("New Content"). If you need the message (e.g. to get its content), you can request it using event.requestMessage().

    \n

    Additionally, you can use the static methods in the Message class which only require the channel and message id, e.g. Message.edit(api, channelId, messageId, "New content");. This is very useful if you want to store them in a database.

    \n

    # Webhooks and Invites

    \n

    Webhooks and Invites are not kept in the cache at all and won't receive any updates.

    \n

    # Embeds

    \n

    Embeds from message.getEmbed() won't receive updates. If a message's embed gets edited, getEmbed() will return a completely new embed object.

    \n

    # ❓ When are cached entities updated?

    \n

    Javacord's cache exclusively uses websocket events to keep the cache up to date. This means that the content of your objects might be outdated, even though you modified it yourself:

    \n
    Messages message = ...;\nSystem.out.println(message.getContent()); // Prints the old content, e.g. \"old content\"\nmessage.edit(\"new content\").join(); // Edits the message and waits for success\nSystem.out.println(message.getContent()); // Still prints \"old content\"\nThread.sleep(1000);\nSystem.out.println(message.getContent()); // Most likely prints \"new content\" now\n

    # ⌚ How long are cached entities valid?

    \n

    Even though entities are usually kept in the cache for a very long time, you should not keep references to these objects for a longer period of time, but store the id / use event methods:

    \n
    // Bad\nMessage message = ...;\nmessage.addReactionAddListener(event -> {\n  if (event.getEmoji().equalsEmoji(\"👎\")) {\n    message.delete(); // Prevents \"message\" from being garbage collected\n  }\n});\n\n// Good\nMessage message = ...;\nmessage.addReactionAddListener(event -> {\n  if (event.getEmoji().equalsEmoji(\"👎\")) {\n    event.deleteMessage(); // Does not use the message object\n  }\n});\n
    // Bad\nSet<User> usersWithBadMood = new HashSet<>();\napi.addReactionAddListener(event -> {\n  if (event.getEmoji().equalsEmoji(\"😦\")) {\n    usersWithBadMood.add(event.getUser());\n  }\n});\n\n// Good\nSet<Long> usersWithBadMood = new HashSet<>();\napi.addReactionAddListener(event -> {\n  if (event.getEmoji().equalsEmoji(\"😦\")) {\n    usersWithBadMood.add(event.getUser().getId());\n  }\n});\n

    Some examples of when cached entities are invalidated:

    \n
      \n
    • The bot lost its connection to Discord and had to reconnect (not resume)
    • \n
    • You weren't able to receive updates for an entity, e.g. for Channel, because you left and rejoined a server
    • \n
    \n","path":"/wiki/advanced-topics/entity-cache.html","keywords":["entity","cache","caching"]},{"title":"Performance Tweaks","headers":[{"level":2,"title":"✂️ Disabling Startup Wait","slug":"disabling-startup-wait","children":[]},{"level":2,"title":"⚙️ Fine Tuning the Message Cache","slug":"fine-tuning-the-message-cache","children":[]},{"level":2,"title":"💎 Using the Updater classes","slug":"using-the-updater-classes","children":[{"level":3,"title":"Example","slug":"example","children":[]}]}],"content":"

    # Performance Tweaks

    \n

    # ✂️ Disabling Startup Wait

    \n

    By default, Javacord waits for all servers and members to be loaded on startup. You can disable this behavior in the DiscordApiBuilder before logging in:

    \n
    new DiscordApiBuilder()\n    .setToken(\"abc\")\n    .setWaitForServersOnStartup(false)\n    .login()\n    .thenAccept(api -> {\n        // Do something\n    }).exceptionally(ExceptionLogger.get());\n

    Depending on the size of your bot, this can significantly speed up the login process. This comes with one downside however: The api.getServers() collection is empty directly after logging in. You will receive ServerBecomesAvailableEvents for every server which finished loading.

    \n

    # ⚙️ Fine Tuning the Message Cache

    \n

    In order to reduce memory usage, you can completely disable the message cache or reduce the number of cached messages. By default, Javacord caches up to 50 messages per channel and removes messages from the cache which are older than 12 hours. You can lower this limit by using DiscordApi#setMessageCacheSize(Capacity, StorageTimeInSeconds).

    \n
    // Cache a maximum of 10 messages per channel for and remove messages older than 1 hour\napi.setMessageCacheSize(10, 60*60);\n

    You can even set this limit on a per-channel basis:

    \n
    TextChannel channel = ...;\nchannel.getMessageCache().setCapacity(10);\nchannel.getMessageCache().setStorageTimeInSeconds(60*60);\n

    # 💎 Using the Updater classes

    \n

    If you update several settings of an entity (server, channel, ...) at once, you should use the updater for this entity instead of the updateXyz(...) methods.

    \n

    # Example

    \n
    // Sends 1 request to Discord\nServerTextChannel channel = ...;\nnew ServerTextChannelUpdater(channel)\n    .setName(\"example-channel\")\n    .setTopic(\"This is an example channel\")\n    .setNsfwFlag(true)\n    .update();\n

    instead of

    \n
    // Sends 3 requests to Discord\nServerTextChannel channel = ...;\nchannel.updateName(\"example-channel\");\nchannel.updateTopic(\"This is an example channel\");\nchannel.updateNsfwFlag(true);\n
    ","path":"/wiki/advanced-topics/performance-tweaks.html","keywords":["performance","tweaks","startup wait","message cache","tuning"]},{"title":"Playing Audio","headers":[{"level":2,"title":"🔌 Connect to a voice channel","slug":"connect-to-a-voice-channel","children":[{"level":3,"title":"Example","slug":"example","children":[]}]},{"level":2,"title":"👂 Playing music","slug":"playing-music","children":[]}],"content":"

    # Playing Audio

    \n

    WARNING

    \n

    Support for audio was added to Javacord very recently.\nIf you encounter any bugs, please create an issue on GitHub!

    \n
    \n

    Javacord allows your bot to connect to voice channels and play audio (e.g., music).\nThis short tutorial gives you an introduction on how to connect to a voice channel and play your\nfavorite music.

    \n

    # 🔌 Connect to a voice channel

    \n

    Connecting to a voice channel is very straight forward:\nCalling #connect() on an instance of ServerVoiceChannel will connect your bot to this voice channel and\nreturn a future with an AudioConnection object.

    \n

    # Example

    \n

    The following example will connect the bot to the voice channel of the user that typed !music in the chat:

    \n
    ServerVoiceChannel channel = ...;\nchannel.connect().thenAccept(audioConnection -> {\n    // Do stuff\n}).exceptionally(e -> {\n    // Failed to connect to voice channel (no permissions?)\n    e.printStackTrace();\n    return null;\n});\n

    # 👂 Playing music

    \n

    There are plenty of sources for audio (e.g., YouTube, local files, etc.).\nThe current de facto standard library for extracting audio from these sources with Java is the\nLavaPlayer library.

    \n

    To use it with Javacord, you have to add it as a dependency to your project (e.g., with Gradle or Maven) and\ncreate a Javacord audio source like this:

    \n
    public class LavaplayerAudioSource extends AudioSourceBase {\n\n    private final AudioPlayer audioPlayer;\n    private AudioFrame lastFrame;\n\n    /**\n     * Creates a new lavaplayer audio source.\n     *\n     * @param api A discord api instance.\n     * @param audioPlayer An audio player from Lavaplayer.\n     */\n    public LavaplayerAudioSource(DiscordApi api, AudioPlayer audioPlayer) {\n        super(api);\n        this.audioPlayer = audioPlayer;\n    }\n\n    @Override\n    public byte[] getNextFrame() {\n        if (lastFrame == null) {\n            return null;\n        }\n        return applyTransformers(lastFrame.getData());\n    }\n\n    @Override\n    public boolean hasFinished() {\n        return false;\n    }\n\n    @Override\n    public boolean hasNextFrame() {\n        lastFrame = audioPlayer.provide();\n        return lastFrame != null;\n    }\n\n    @Override\n    public AudioSource copy() {\n        return new LavaplayerAudioSource(getApi(), audioPlayer);\n    }\n}\n

    With this audio source, you can now start using Lavaplayer, e.g. to play a YouTube video:

    \n
    // Create a player manager\nAudioPlayerManager playerManager = new DefaultAudioPlayerManager();\nplayerManager.registerSourceManager(new YoutubeAudioSourceManager());\nAudioPlayer player = playerManager.createPlayer();\n\n// Create an audio source and add it to the audio connection's queue\nAudioSource source = new LavaplayerAudioSource(api, player);\naudioConnection.setAudioSource(source);\n\n// You can now use the AudioPlayer like you would normally do with Lavaplayer, e.g.,\nplayerManager.loadItem(\"https://www.youtube.com/watch?v=NvS351QKFV4\", new AudioLoadResultHandler() {\n    @Override\n    public void trackLoaded(AudioTrack track) {\n        player.playTrack(track);\n    }\n\n    @Override\n    public void playlistLoaded(AudioPlaylist playlist) {\n        for (AudioTrack track : playlist.getTracks()) {\n            player.playTrack(track);\n        }\n    }\n\n    @Override\n    public void noMatches() {\n        // Notify the user that we've got nothing\n    }\n\n    @Override\n    public void loadFailed(FriendlyException throwable) {\n        // Notify the user that everything exploded\n    }\n});\n
    ","path":"/wiki/advanced-topics/playing-audio.html","keywords":["audio","music","voice"]},{"title":"Proxies","headers":[{"level":2,"title":"👨‍💻 Configuring a Proxy ...","slug":"configuring-a-proxy","children":[{"level":3,"title":"... using System Properties","slug":"using-system-properties","children":[]},{"level":3,"title":"... using a System Default Proxy Selector","slug":"using-a-system-default-proxy-selector","children":[]},{"level":3,"title":"... using an Explicitly Set Proxy","slug":"using-an-explicitly-set-proxy","children":[]},{"level":3,"title":"... using an Explicitly Set Proxy Selector","slug":"using-an-explicitly-set-proxy-selector","children":[]},{"level":3,"title":"Precedence of the Configuration Options","slug":"precedence-of-the-configuration-options","children":[]}]},{"level":2,"title":"🔑 Configuring Proxy Authentication ...","slug":"configuring-proxy-authentication","children":[{"level":3,"title":"... using a System Default Authenticator","slug":"using-a-system-default-authenticator","children":[]},{"level":3,"title":"... using an Explicitly Set Authenticator","slug":"using-an-explicitly-set-authenticator","children":[]}]},{"level":2,"title":"💡 Proxy Types","slug":"proxy-types","children":[{"level":3,"title":"HTTP","slug":"http","children":[]},{"level":3,"title":"SOCKS 4","slug":"socks-4","children":[]},{"level":3,"title":"SOCKS 4a","slug":"socks-4a","children":[]},{"level":3,"title":"SOCKS 5","slug":"socks-5","children":[]}]}],"content":"

    # Proxies

    \n

    There are basically two kinds of proxies: HTTP proxies and SOCKS proxies. Both may or may not support or require authentication depending on version, capabilities, and configuration. Due to the underlying libraries used, currently, Javacord fully supports HTTP proxies and partially supports SOCKS proxies.

    \n

    Javacord uses HTTPS connections to communicate with the Discord REST API and a WSS connection to communicate with the Discord WebSocket endpoint. Both these protocols are secure protocols and thus do not honor settings for HTTP connections, only settings for HTTPS connections.

    \n

    # 👨‍💻 Configuring a Proxy ...

    \n

    # ... using System Properties

    \n

    If you did not explicitly set a proxy in the DiscordApiBuilder and did not set a system default ProxySelector, the default proxy selector of the JRE is used. This proxy selector honors, amongst others, the relevant standard system properties https.proxyHost, https.proxyPort, socksProxyHost, socksProxyPort, and socksProxyVersion. Use the former two to configure an HTTP proxy, or the latter three to configure a SOCKS proxy, although you will not need socksProxyVersion, as SOCKS4 is currently not supported.

    \n

    # ... using a System Default Proxy Selector

    \n

    You can use java.net.ProxySelector.setDefault(ProxySelector) to set a system default proxy selector that replaces the default one. In its implementation, you can dynamically determine which proxy to use for each connection.

    \n

    # ... using an Explicitly Set Proxy

    \n

    Using the method DiscordApiBuilder.setProxy(Proxy) you can set a proxy instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the unrelated code running in the JVM.

    \n

    # ... using an Explicitly Set Proxy Selector

    \n

    Using the method DiscordApiBuilder.setProxySelector(ProxySelector) you can set a proxy selector instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the remaining code running in the JVM. In its implementation, you can dynamically determine which proxy to use for each connection.

    \n

    # Precedence of the Configuration Options

    \n
      \n
    • if an explicit proxy is set, it is used
    • \n
    • if an explicit proxy selector is set, it is used
    • \n
    • if both an explicit proxy and an explicit proxy selector are set, this is a configuration error and will cause an exception to be thrown
    • \n
    • if neither explicit option is set, the system default proxy selector is used
    • \n
    • if no system default proxy selector was explicitly set, the JRE default that honors the system properties is used
    • \n
    \n

    # 🔑 Configuring Proxy Authentication ...

    \n

    # ... using a System Default Authenticator

    \n

    You can use java.net.Authenticator.setDefault(Authenticator) to set a system default authenticator that is used to provide username and password pairs for connections. This authenticator is only used if the proxy supports the Basic authentication scheme. If you need to support any other authentication scheme, use an explicitly configured authenticator. The java.net.Authenticator interface is too inflexible to support this.

    \n

    # ... using an Explicitly Set Authenticator

    \n

    Using the method DiscordApiBuilder.setProxyAuthenticator(Authenticator), you can set a custom authenticator that is much more powerful than the java.net.Authenticator. You get much more information about the connection to be established, and you can return any HTTP header that is necessary for a successful authentication. This should cover all sorts of available authentication mechanisms.

    \n

    # 💡 Proxy Types

    \n

    # HTTP

    \n

    HTTP proxies are fully supported.

    \n

    # SOCKS 4

    \n

    SOCKS 4 is currently not supported.

    \n

    The WebSocket library we use does not support SOCKS proxies at all, and the HTTP library we use has a bug that prevents SOCKS 4 to be used. Additionally, you would need to use at least Java 9 or a separate socket factory supporting SOCKS 4, as the JRE implementation is not working in Java 8 and got fixed only in Java 9+.

    \n

    # SOCKS 4a

    \n

    SOCKS 4a is currently only partially supported.

    \n

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only. Additionally, you would need to use a separate socket factory supporting SOCKS 4a, as the JRE implementation is not capable of doing SOCKS 4a, only SOCKS 4 and SOCKS 5 are supported at the time of creation of this wiki article.

    \n

    # SOCKS 5

    \n

    SOCKS 5 is currently only partially supported.

    \n

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only.

    \n","path":"/wiki/advanced-topics/proxies.html","keywords":["proxy","connection","socks","socks4","socks5"]},{"title":"Ratelimits","headers":[{"level":2,"title":"❗ The Most Important Ratelimits","slug":"the-most-important-ratelimits","children":[]},{"level":2,"title":"💪 Dealing with Ratelimits","slug":"dealing-with-ratelimits","children":[{"level":3,"title":"Example","slug":"example","children":[]}]},{"level":2,"title":"❌ Can I disable ratelimits?","slug":"can-i-disable-ratelimits","children":[]}],"content":"

    # Ratelimits

    \n

    Ratelimits is a Discord restriction which prevents you from performing actions in a very fast rate.\nMost ratelimits are on a per-channel or a per-server basis.

    \n

    # ❗ The Most Important Ratelimits

    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    ActionRatelimitType
    Send Messages5 / 5sper channel
    Delete Messages5 / 1sper channel
    Add/Remove Reactions1 / 0.25sper channel
    Edit Server Members10 / 10sper server
    Edit Member Nickname1 / 1sper server
    Edit Bot Username2 / 1hper account
    Update Channels2 / 10mper account
    All Actions Combined50 / 1sper account
    \n

    # 💪 Dealing with Ratelimits

    \n

    Usually Javacord takes care about these limitations for you.\nAs a user, there's nothing you have to do, but you should at least know that ratelimits exist.

    \n

    # Example

    \n

    The following code

    \n
    // Who even needs loops?\nchannel.sendMessage(\"Ratelimit Example #1\");\nchannel.sendMessage(\"Ratelimit Example #2\");\nchannel.sendMessage(\"Ratelimit Example #3\");\nchannel.sendMessage(\"Ratelimit Example #4\");\nchannel.sendMessage(\"Ratelimit Example #5\");\nchannel.sendMessage(\"Ratelimit Example #6\");\nchannel.sendMessage(\"Ratelimit Example #7\");\nchannel.sendMessage(\"Ratelimit Example #8\");\nchannel.sendMessage(\"Ratelimit Example #9\");\nchannel.sendMessage(\"Ratelimit Example #10\");\nchannel.sendMessage(\"Ratelimit Example #11\");\nchannel.sendMessage(\"Ratelimit Example #12\");\n

    would look like this in the client:

    \n
    \n

    \"\"

    \n
    \n

    You can clearly see the delay between every 5 sent messages.

    \n

    # ❌ Can I disable ratelimits?

    \n

    No. Ratelimits are a limitation from Discord itself, which you cannot circumvent.

    \n","path":"/wiki/advanced-topics/ratelimits.html","keywords":["ratelimits"]},{"title":"Sharding","headers":[{"level":2,"title":"👩‍🏭 Sharding in Javacord","slug":"sharding-in-javacord","children":[{"level":3,"title":"Logging in with a single shard","slug":"logging-in-with-a-single-shard","children":[]},{"level":3,"title":"Logging in with a fixed amount of shards","slug":"logging-in-with-a-fixed-amount-of-shards","children":[]},{"level":3,"title":"Using the recommended shard amount","slug":"using-the-recommended-shard-amount","children":[]}]},{"level":2,"title":"💡 Behavior of Shards","slug":"behavior-of-shards","children":[{"level":3,"title":"Managed servers","slug":"managed-servers","children":[]},{"level":3,"title":"Private messages","slug":"private-messages","children":[]},{"level":3,"title":"When do I need sharding?","slug":"when-do-i-need-sharding","children":[]}]},{"level":2,"title":"🌄 Sharding for Very Large Bots","slug":"sharding-for-very-large-bots","children":[]}],"content":"

    # Sharding

    \n

    Discord allows (and forces) you to "split" larger bots into several independent parts. This behavior is called "sharding", and the independent parts are called "shards". You can think of shards as completely independent bots. Every shard is responsible for a disjoint set of servers.

    \n

    # 👩‍🏭 Sharding in Javacord

    \n

    # Logging in with a single shard

    \n

    Logging in with a single shard is pretty much the same as logging in without sharding:

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"top secret\")\n    .setCurrentShard(0)\n    .setTotalShards(2)\n    .login().join();\nSystem.out.println(\"Shard \" + api.getCurrentShard() + \" logged in!\");\n
    \n

    Note: current shard starts counting at 0! This means in the example above you would have current shard 0 and shard 1 with a total amount of 2 shards.

    \n
    \n
    \n

    Important: There must be a > 5-second delay between each shard-login

    \n
    \n

    # Logging in with a fixed amount of shards

    \n

    You can manually set a fixed amount of total shards and log in all of them:

    \n
    public class Main {\n\n    public static void main(String[] args) {\n        new DiscordApiBuilder()\n            .setToken(\"top secret\")\n            .setTotalShards(10)\n            .loginAllShards()\n            .forEach(shardFuture -> shardFuture\n                .thenAcceptAsync(Main::onShardLogin)\n                .exceptionally(ExceptionLogger.get())\n            );\n    }\n\n    private static void onShardLogin(DiscordApi api) {\n        System.out.println(\"Shard \" + api.getCurrentShard() + \" logged in!\");\n        // You can treat the shard like a normal bot account, e.g. registering listeners\n        api.addMessageCreateListener(event -> {\n            // ...\n        });\n    }\n\n}\n

    loginAllShards() returns a collection with completable futures (Collection<CompletableFuture<DiscordApi>>). This method automatically obeys the > 5-second delay rule.

    \n\n

    You can "ask" Discord to recommend you a total amount of shards. This is done by using the DiscordApiBuilder#setRecommendedTotalShards() method, which returns a CompletableFuture<DiscordApiBuilder> after getting the required information.

    \n
    public class Main {\n\n    public static void main(String[] args) {\n        new DiscordApiBuilder()\n            .setToken(\"top secret\")\n            .setRecommendedTotalShards().join()\n            .loginAllShards()\n            .forEach(shardFuture -> shardFuture\n                .thenAccept(Main::onShardLogin)\n                .exceptionally(ExceptionLogger.get())\n            );\n    }\n\n    private static void onShardLogin(DiscordApi api) {\n        // ...\n    }\n\n}\n

    # 💡 Behavior of Shards

    \n

    # Managed servers

    \n

    You can calculate for which servers a shard is responsible using the server id:

    \n
    boolean isResponsible = (serverId >> 22) % totalShards == currentShard;\n

    # Private messages

    \n

    Private messages are always sent to the first shard (currentShard == 0).

    \n

    # When do I need sharding?

    \n

    Sharding is forced for bots which are in more than 2500 servers.

    \n

    # 🌄 Sharding for Very Large Bots

    \n

    Sharding for very large bots (> 150,000 servers) is a bit different from "normal" sharding. Discord will contact you once your bot reaches this state. Additional information can be found in the official Discord api documentation.

    \n","path":"/wiki/advanced-topics/sharding.html","keywords":["sharding","large"]},{"title":"Creating Channels, Invites, etc.","headers":[{"level":2,"title":"📕 Create Channels","slug":"create-channels","children":[]},{"level":2,"title":"📗 Create Webhooks","slug":"create-webhooks","children":[]},{"level":2,"title":"📘 Create Invites","slug":"create-invites","children":[]},{"level":2,"title":"📙 Create Servers","slug":"create-servers","children":[]}],"content":"

    # Creating Channels, Invites, etc.

    \n

    Javacord provides XyzBuilder classes to create new Discord entities like channels, webhooks, servers, and many more.

    \n

    # 📕 Create Channels

    \n

    You can get the channel builders for a specific server using the Server#createXyzChannelBuilder or by directly calling the constructor.\nCreating a ServerVoiceChannel would look like this:

    \n
    Server server = ...;\nServerVoiceChannel channel = new ServerVoiceChannelBuilder(server)\n    .setName(\"example-channel\")\n    .setUserlimit(10)\n    .create().join();\n

    # 📗 Create Webhooks

    \n

    You can get the WebhookBuilder for a specific text channel:

    \n
    ServerTextChannel channel = ...;\nWebhook webhook = new WebhookBuilder(channel)\n    .setName(\"Captain Hook\")\n    .setAvatar(new File(\"C:/Users/Bastian/Pictures/puppy.jpg\"))\n    .create().join();\n

    # 📘 Create Invites

    \n

    You can get the InviteBuilder for a specific server channel:

    \n
    ServerTextChannel channel = ...;\nInvite invite = new InviteBuilder(channel)\n    .setMaxAgeInSeconds(60*60*24)\n    .setMaxUses(42)\n    .create().join();\n

    # 📙 Create Servers

    \n

    You can get the ServerBuilder from the current api instance:

    \n
    DiscordApi api = ...;\nlong serverId = new ServerBuilder(api)\n    .setName(\"My Awesome Server\")\n    .setIcon(api.getYourself().getAvatar())\n    .setVerificationLevel(VerificationLevel.HIGH)\n    .setDefaultMessageNotificationLevel(DefaultMessageNotificationLevel.ONLY_MENTIONS)\n    .setRegion(Region.EU_CENTRAL)\n    .create().join();\n

    WARNING

    \n

    By default, bots can only create servers if they are in less than 10 servers. You can contact the Discord support to request a higher limit.

    \n
    \n","path":"/wiki/basic-tutorials/creating-entities.html","keywords":["creating entities","create entities","entity creation","create channels","channel creation","create webhooks","webhook creation","create invites","invite creation","create server","server creation"]},{"title":"Embeds","headers":[{"level":2,"title":"🔨 Creating an Embed","slug":"creating-an-embed","children":[]},{"level":2,"title":"📷 Supported Image Sources","slug":"supported-image-sources","children":[]},{"level":2,"title":"🔒 Embed Limits","slug":"embed-limits","children":[]},{"level":2,"title":"❓ FAQ","slug":"faq","children":[{"level":3,"title":"What is the second parameter of setAuthor(...)?","slug":"what-is-the-second-parameter-of-setauthor","children":[]},{"level":3,"title":"What's the difference between an inline field and a normal one?","slug":"what-s-the-difference-between-an-inline-field-and-a-normal-one","children":[]},{"level":3,"title":"Can I change the placement of inline fields?","slug":"can-i-change-the-placement-of-inline-fields","children":[]},{"level":3,"title":"How can I format text in an embed?","slug":"how-can-i-format-text-in-an-embed","children":[]}]}],"content":"

    # Embeds

    \n

    Embeds are attached to messages and have a special design.\nThe usually look like this:

    \n

    \"Embed\"

    \n

    # 🔨 Creating an Embed

    \n

    Javacord provides an EmbedBuilder which can be used to create embeds:

    \n
    // Create the embed\nEmbedBuilder embed = new EmbedBuilder()\n    .setTitle(\"Title\")\n    .setDescription(\"Description\")\n    .setAuthor(\"Author Name\", \"http://google.com/\", \"https://cdn.discordapp.com/embed/avatars/0.png\")\n    .addField(\"A field\", \"Some text inside the field\")\n    .addInlineField(\"An inline field\", \"More text\")\n    .addInlineField(\"Another inline field\", \"Even more text\")\n    .setColor(Color.BLUE)\n    .setFooter(\"Footer\", \"https://cdn.discordapp.com/embed/avatars/1.png\")\n    .setImage(new File(\"C:/Users/Bastian/Pictures/puppy.jpg\"))\n    .setThumbnail(new File(\"C:/Users/Bastian/Pictures/kitten2.png\"));\n// Send the embed\nchannel.sendMessage(embed);\n

    # 📷 Supported Image Sources

    \n

    By default, Discord expects embed images to be a link (e.g., the image link used in setFooter(...)), but you can also use attachments for images.\nIf you provide a non-url image source (e.g. the puppy.jpg file used in setImage(...)), Javacord automatically uploads them as an attachment to the message and uses this attachment for the embed.

    \n

    # 🔒 Embed Limits

    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    TypeLimit
    Title256 characters
    Description4096 characters
    Field AmountUp to 25 fields
    Field Name256 characters
    Field Value1024 characters
    Footer Text2048 characters
    Author Name256 characters
    \n

    In addition to the limits above, the sum of all characters in an embed structure must not exceed 6000 characters.

    \n

    # ❓ FAQ

    \n

    # What is the second parameter of setAuthor(...)?

    \n
    .setAuthor(\"Author Name\", \"http://google.com/\", \"https://cdn.discordapp.com/embed/avatars/0.png\")\n
      \n
    • First parameter: The name of the author
    • \n
    • Second parameter: A link for the author (e.g. their homepage). Can be null.
    • \n
    • Third parameter: The avatar of the author
    • \n
    \n
    \n

    \"\"

    \n
    \n

    # What's the difference between an inline field and a normal one?

    \n

    Normal fields always start in a new line, whereas several inline fields can be in the same line.

    \n

    # Can I change the placement of inline fields?

    \n

    No, Discord does not allow different embed layouts.

    \n

    # How can I format text in an embed?

    \n

    Discord allows for a subset of markdown to be used. See their docs for the specifics.

    \n","path":"/wiki/basic-tutorials/embeds.html","keywords":["EmbedBuilder","inline field","setTitle","setDescription","setAuthor","addField","addInlineField","setColor","setFooter","setImage","setThumbnail"]},{"title":"Emojis and Reactions","headers":[{"level":2,"title":"🚴‍♂️ Unicode Emojis","slug":"unicode-emojis","children":[{"level":3,"title":"What are Unicode emojis?","slug":"what-are-unicode-emojis","children":[]},{"level":3,"title":"How to use them in messages","slug":"how-to-use-them-in-messages","children":[]},{"level":3,"title":"How to use them for reactions","slug":"how-to-use-them-for-reactions","children":[]}]},{"level":2,"title":"🤸‍♀️ Custom Emojis","slug":"custom-emojis","children":[{"level":3,"title":"What are custom emojis?","slug":"what-are-custom-emojis","children":[]},{"level":3,"title":"How to use them in messages","slug":"how-to-use-them-in-messages-1","children":[]},{"level":3,"title":"How to use them for reactions","slug":"how-to-use-them-for-reactions-1","children":[]},{"level":3,"title":"How to get the tag","slug":"how-to-get-the-tag","children":[]}]},{"level":2,"title":"👑 Javacord Emoji \"Hierarchy\"","slug":"javacord-emoji-hierarchy","children":[{"level":3,"title":"What is a KnownCustomEmoji?","slug":"what-is-a-knowncustomemoji","children":[]}]},{"level":2,"title":"👌 Recommended libraries","slug":"recommended-libraries","children":[]}],"content":"

    # Emojis and Reactions

    \n

    There are two different kinds of emojis in Discord: Unicode emojis and custom emojis.

    \n

    # 🚴‍♂️ Unicode Emojis

    \n

    # What are Unicode emojis?

    \n

    Unicode emojis are "normal" text emojis which are supported by (nearly) all chat clients, including Discord. You can find a list with all Unicode emojis here: Full Emoji List.

    \n

    # How to use them in messages

    \n

    You can either directly add them in your code, e.g.

    \n
    channel.sendMessage(\"Hi! 😃\");\n

    or use the normal "tag" like you would in the Client:

    \n
    channel.sendMessage(\"Hi! :smiley:\");\n

    \"\"

    \n

    # How to use them for reactions

    \n

    Adding unicode reactions is only possible by using the "real" reaction. It doesn't support tags like :smiley:.

    \n
    message.addReaction(\"😃\"); // works\nmessage.addReaction(\":smiley:\"); // doesn't work\n

    \"\"

    \n

    # 🤸‍♀️ Custom Emojis

    \n

    # What are custom emojis?

    \n

    Custom emojis are emojis that are created in a server. You can get all custom emojis the bot knows by using DiscordApi#getCustomEmojis().

    \n

    \"\"

    \n

    # How to use them in messages

    \n

    To use custom emojis, you have to know its "tag", which has the format <:name:id>. You can get it by calling CustomEmoji#getMentionTag():

    \n
    channel.sendMessage(\"Hi! <:javacord:415465982715494402>\");\n
    CustomEmoji emoji = ...;\nchannel.sendMessage(\"Hi! \" + emoji.getMentionTag());\n

    # How to use them for reactions

    \n

    You can either directly use the custom emoji object or use the tag without the <: > if you don't have access a custom emoji object (e.g., because it's from a different shard):

    \n
    CustomEmoji emoji = ...;\nmessage.addReaction(emoji);\n
    message.addReaction(\"javacord:415465982715494402\");\n

    # How to get the tag

    \n

    Just add a \\ in front of the emoji and press Enter

    \n

    \"\"

    \n

    \"\"

    \n

    # 👑 Javacord Emoji "Hierarchy"

    \n

    In Javacord, all Emojis are a child of the Emoji interface:

    \n

    \"\"

    \n

    # What is a KnownCustomEmoji?

    \n

    Known custom emojis are emojis that the bot knows because it's a member of the server with this emoji. A custom emoji can be unknown if someone adds a reaction with an unknown emoji for example. A KnownCustomEmoji has additional methods like getServer() or updateName(String).

    \n

    # 👌 Recommended libraries

    \n

    If you are working a lot with Unicode emojis, it's recommended to use a library like JEmoji. It enables you to do things like the following:

    \n
    message.addReaction(EmojiManager.getByAlias(\":thumbsup:\"));\n
    ","path":"/wiki/basic-tutorials/emojis-and-reactions.html","keywords":["create emoji","emoji creation","unicode emoji","custom emojis","delete emojis","emoji deletion","send emoji","use emoji","KnownCustomEmoji"]},{"title":"Gateway Intents","headers":[{"level":2,"title":"📋 List of Intents","slug":"list-of-intents","children":[]},{"level":2,"title":"💡 What Happens When I Disable Some Intents?","slug":"what-happens-when-i-disable-some-intents","children":[]},{"level":2,"title":"👑 Privileged Intents","slug":"privileged-intents","children":[]},{"level":2,"title":"❗ Notable Intents","slug":"notable-intents","children":[{"level":3,"title":"GUILD_PRESENCES","slug":"guild-presences","children":[]},{"level":3,"title":"GUILD_MEMBERS","slug":"guild-members","children":[]},{"level":3,"title":"MESSAGE_CONTENT","slug":"message-content","children":[]}]},{"level":2,"title":"⚙️ Setting Intents","slug":"setting-intents","children":[{"level":3,"title":"Set All Non-Privileged Intents (Default)","slug":"set-all-non-privileged-intents-default","children":[]},{"level":3,"title":"Set All Non-Privileged Intents Except","slug":"set-all-non-privileged-intents-except","children":[]},{"level":3,"title":"Set All Intents","slug":"set-all-intents","children":[]},{"level":3,"title":"Set All Intents Except","slug":"set-all-intents-except","children":[]},{"level":3,"title":"Set Intents","slug":"set-intents","children":[]},{"level":3,"title":"Add Intents","slug":"add-intents","children":[]}]}],"content":"

    # Gateway Intents

    \n

    Discord allows you to "subscribe" to specific groups of events.\nThese "subscriptions" are called intent.\nDisabling intents that are not required for your bot can significantly increase your bot's performance.

    \n

    # 📋 List of Intents

    \n

    Below you can find a table with all intents supported by Discord.

    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    IntentSafe to DisablePrivileged
    GUILDS
    GUILD_MEMBERS✔️✔️
    GUILD_BANS⚠️*
    GUILD_EMOJIS⚠️*
    GUILD_INTEGRATIONS✔️
    GUILD_WEBHOOKS✔️
    GUILD_INVITES✔️
    GUILD_VOICE_STATES⚠️*
    GUILD_PRESENCES✔️✔️
    GUILD_MESSAGES✔️
    GUILD_MESSAGE_REACTIONS✔️
    GUILD_MESSAGE_TYPING✔️
    DIRECT_MESSAGES✔️
    DIRECT_MESSAGE_REACTIONS✔️
    DIRECT_MESSAGE_TYPING✔️
    MESSAGE_CONTENT✔️✔️
    AUTO_MODERATION_CONFIGURATION✔️
    AUTO_MODERATION_EXECUTION✔️
    \n

    * Will most likely work, but needs further testing

    \n

    Good to know!

    \n

    Guild is a synonym for servers, commonly used in Discord's API.\nSee Glossary.

    \n
    \n

    # 💡 What Happens When I Disable Some Intents?

    \n

    When you disable some of the listed intents, Javacord will not fire events that belong to the intents and\nwill not update these specific parts of the cache.

    \n

    At the moment, we don't have a list which events are affected by which intents (but it will come soon™️).\nHowever, most intents should be self-explanatory.\nE.g. when you disable the DIRECT_MESSAGES intent, your bot will not receive any private messages.

    \n

    # 👑 Privileged Intents

    \n

    Some intents are defined as "privileged" due to the sensitive nature of the data.\nTo use these intents, you have to go to your bot in the Developer Portal\n(where you created bot) and manually enable the intents:

    \n

    \"\"

    \n

    There are some additionally restrictions for bots that are in over 100 servers:

    \n
      \n
    • Your bot must be verified
    • \n
    • Your bot must be whitelisted to use this intents
    • \n
    \n

    Take a look at the official article from Discord about this topic and how to verify your bot:\nBot Verification and Data Whitelisting.

    \n

    # ❗ Notable Intents

    \n

    The following two intents are especially noteworthy: GUILD_MEMBERS and GUILD_PRESENCES.\nBesides being privileged, they have some special implications for Javacord:

    \n

    # GUILD_PRESENCES

    \n

    This intent is required to get updates about a user's status (i.e., if they are online, what game they are playing, ...).\nAdditionally, without this intent it might take considerably longer to cache all users because of ratelimits\n(up to 10 minutes for shards with 1000 servers).\nIt is advised against setting DiscordApiBuilder#setWaitForAllUsersOnStartup(true) without this intent, unless absolutely necessary.

    \n

    # GUILD_MEMBERS

    \n

    This intent is required to keep all users in Javacord's cache.\nWithout this intent, methods like Server#getMembers() or DiscordApi#getCachedUsers() will return empty collections.\nHowever, you will still be able to access users from objects like messages, e.g. Message#getUserAuthor() will still work.

    \n

    # MESSAGE_CONTENT

    \n

    This intent is a bit different to the other as it does not act as a toggle to receive any events.\nIt's sole purpose is to receive the message content, attachments, components, and embeds.\nOtherwise, these fields will be empty when you receive a Message object.

    \n

    # ⚙️ Setting Intents

    \n

    Javacord allows you to specify intents in the DiscordApiBuilder prior to login.\nThere are many options to set intents.\nThe following example code shows the most common ones:

    \n

    # Set All Non-Privileged Intents (Default)

    \n

    This method enables all non-privileged intents.\nThis is the default setting in Javacord.

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .setAllNonPrivilegedIntents()\n    .login()\n    .join();\n

    # Set All Non-Privileged Intents Except

    \n

    This method enabled all non-privileged intents, except the given ones.

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .setAllNonPrivilegedIntentsExcept(Intent.GUILD_WEBHOOKS)\n    .login()\n    .join();\n

    # Set All Intents

    \n

    This method enabled all intents.

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .setAllIntents()\n    .login()\n    .join();\n

    # Set All Intents Except

    \n

    This method enabled all intents, except the given ones.

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .setAllIntentsExcept(Intent.GUILD_PRESENCES, Intent.GUILD_WEBHOOKS)\n    .login()\n    .join();\n

    # Set Intents

    \n

    This method only enables the given intents.

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .setIntents(Intent.GUILDS, Intent.DIRECT_MESSAGES)\n    .login()\n    .join();\n

    # Add Intents

    \n

    This method adds the intents to the currently enabled ones(by default all non-privileged).\nThis is useful i.e. if you only want to enable 1 privileged intent like the MESSAGE_CONTENT

    \n
    DiscordApi api = new DiscordApiBuilder()\n    .setToken(\"topc secret\")\n    .addIntents(Intent.MESSAGE_CONTENT)\n    .login()\n    .join();\n
    ","path":"/wiki/basic-tutorials/gateway-intents.html","keywords":["Intents"]},{"title":"Glossary","headers":[],"content":"

    # Glossary

    \n

    This is a list with the most common Discord-related terms:

    \n
      \n
    • Guild - A synonym for server
    • \n
    • Selfbot - A client account bot, usually logged in to a user's own account
    • \n
    • Sharding - Splitting a bot into several independent shards, see Sharding
    • \n
    • Token - Used to login instead of requiring a username + password
    • \n
    • Embed - A "fancy" message, see Embed FAQ
    • \n
    • Ratelimit - Prevents you from spamming actions, see Ratelimit FAQ
    • \n
    • Websocket - A TCP "connection" to Discord that receives events, see Wikipedia
    • \n
    • Gateway - The address for the websocket
    • \n
    • Rest / Rest Request - REST is used to perform actions like sending messages. Rest Requests do not require an active websocket connection.
    • \n
    • Activity - The text underneath the username, usually Playing Xyz
    • \n
    • Rich Presence - A more detailed activity, see Discord Docs
    • \n
    \n","path":"/wiki/basic-tutorials/glossary.html","keywords":["Guild","Selfbot","Sharding","Token","Embed","Ratelimit","Websocket","Gateway","Rest Request","Activity","Rich Presence"]},{"title":"Listeners","headers":[{"level":2,"title":"👨‍🔧 Creating listeners","slug":"creating-listeners","children":[{"level":3,"title":"Inline Listeners","slug":"inline-listeners","children":[]},{"level":3,"title":"In their own class","slug":"in-their-own-class","children":[]},{"level":3,"title":"Before logging in","slug":"before-logging-in","children":[]},{"level":3,"title":"Object listeners","slug":"object-listeners","children":[]}]},{"level":2,"title":"💣 Removing listeners","slug":"removing-listeners","children":[{"level":3,"title":"Using the returned ListenerManager","slug":"using-the-returned-listenermanager","children":[]},{"level":3,"title":"Using the removeListener(...) method","slug":"using-the-removelistener-method","children":[]}]}],"content":"

    # Listeners

    \n

    # 👨‍🔧 Creating listeners

    \n

    Creating listeners is extremely easy in Javacord.\nYou can either use Java 8's lambda expressions to register listeners inline or just create a new class for them, if an inline listener would get too messy.

    \n

    # Inline Listeners

    \n
    api.addMessageCreateListener(event -> {\n    if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n        event.getChannel().sendMessage(\"Pong!\");\n    }\n});\n

    # In their own class

    \n
    api.addListener(new MyListener());\n

    and

    \n
    public class MyListener implements MessageCreateListener {\n\n    @Override\n    public void onMessageCreate(MessageCreateEvent event) {\n        if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n            event.getChannel().sendMessage(\"Pong!\");\n        }\n    }\n\n}\n

    # Before logging in

    \n

    Sometimes it might be useful to add listeners before calling the DiscordApiBuilder#login() method.

    \n
    DiscordApi api = new DiscordApiBuilder()\n        // An inline listener\n        .addMessageCreateListener(event -> {\n            Message message = event.getMessage();\n            if (message.getContent().equalsIgnoreCase(\"!ping\")) {\n                event.getChannel().sendMessage(\"Pong!\");\n            }\n        })\n        .addServerBecomesAvailableListener(event -> {\n            System.out.println(\"Loaded \" + event.getServer().getName());\n        })\n        // A listener in their own class\n        .addListener(new MyListener())\n         // Alternative syntax that can be used for classes that require a DiscordApi parameter in their constructor\n        .addListener(MyListener::new)\n        .setToken(\"top secret\")\n        .setWaitForServersOnStartup(false)\n        .login()\n        .join();\n
    \n

    Note: In most cases, it's enough to add listeners after logging in

    \n
    \n

    # Object listeners

    \n

    Another cool feature is the ability to attach listeners directly to objects. An example where this can be useful is, for example, reacting to reactions. The following code would delete the message if someone adds a 👎 reaction.

    \n
    message.addReactionAddListener(event -> {\n    if (event.getEmoji().equalsEmoji(\"👎\")) {\n        event.deleteMessage();\n    }\n}).removeAfter(30, TimeUnit.MINUTES);\n
    \n

    Seems like the bot is very sensitive to criticism.

    \n
    \n

    # 💣 Removing listeners

    \n

    There are two ways to remove a listener:

    \n

    # Using the returned ListenerManager

    \n

    Every time you register a listener, a ListenerManager is returned which can be used to unregister the listener:

    \n
    ListenerManager<MessageCreateListener> listenerManager = api.addMessageCreateListener(event -> {\n    // Do stuff\n});\n\nlistenerManager.remove();\n

    This manager also has some utility methods. You can, for example, remove a listener after a given time, which can be useful for object listeners:

    \n
    message.addReactionAddListener(event -> {\n  // Do stuff\n}).removeAfter(30, TimeUnit.MINUTES);\n

    # Using the removeListener(...) method

    \n

    You can remove any listener using the removeListener(...) method:

    \n
    MyListener listener = new MyListener();\napi.addListener(listener);\n// ...\napi.removeListener(listener);\n
    ","path":"/wiki/basic-tutorials/listeners.html","keywords":["creating listeners","listener creation","ListenerManager","removeListener","remove listener"]},{"title":"Logger Configuration","headers":[{"level":2,"title":"🥈 Fallback Logger","slug":"fallback-logger","children":[]},{"level":2,"title":"🥇 Using a Proper Logging Framework","slug":"using-a-proper-logging-framework","children":[{"level":3,"title":"Adding a Logging Framework","slug":"adding-a-logging-framework","children":[]},{"level":3,"title":"Configure Your Logging Framework","slug":"configure-your-logging-framework","children":[]},{"level":3,"title":"Logging the Relevant Shard","slug":"logging-the-relevant-shard","children":[]}]}],"content":"

    # Logger Configuration

    \n

    Logging is an important tool to keep track of what is going on in your application. Javacord uses the Log4j 2 API, which allows you to use your favorite logging framework to log messages in your own code and have all logging messages end up in the same destination. In case you do not add your own logging framework, a fallback logger is used that logs to the console.
    \nIf you want more control, add a proper logging framework that supports your needs and configure it accordingly. You can for example configure log messages on a per-class level, change log levels during runtime, or log to a file or database.

    \n

    # 🥈 Fallback Logger

    \n

    Javacord's fallback logger is a simple Log4j logger which always logs INFO level and higher. It allows you to enable DEBUG and TRACE logging manually. As log levels are hierarchical, enabling TRACE will also implicitly enable DEBUG, and disabling DEBUG will also implicitly disable TRACE.

    \n
    // Enable debug logging\nFallbackLoggerConfiguration.setDebug(true);\n\n// Enable trace logging\nFallbackLoggerConfiguration.setTrace(true);\n

    Changing the log level of the fallback logger only affects newly created loggers. Pre-existing loggers will not have their log level changed. So if you want to configure the fallback logger, you should do this as one of the first actions in your bot code. If you want to change log levels during runtime, you should use a proper logging framework like Log4j 2 Core or another library that supports this.

    \n

    All fallback logger messages are printed to the standard output stream (System.out) and thus usually to your console. If you want to log to a file, database, or anything else, you should consider using a proper logging framework which allows you to configure this behavior.

    \n

    This is how a log line from the fallback logger will look like:

    \n
    <time with date            ><level><logger name, usually the logging class              > <message            > <the thread context, here the shard number>\n2018-08-03 20:00:06.080+0200 DEBUG org.javacord.core.util.gateway.DiscordWebSocketAdapter Received HELLO packet {shard=0}\n

    # 🥇 Using a Proper Logging Framework

    \n

    # Adding a Logging Framework

    \n

    Adding a logging framework of your choice is very straightforward. You can just add it as a dependency, and it will be detected by Log4j automatically. The following example adds Log4j 2 using Gradle:

    \n
    dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-core:2.17.0' }\n

    You can also use an SLF4J compatible logging framework using log4j-to-slf4j. The following example adds Logback Classic using Gradle:

    \n
    dependencies {\n    runtimeOnly 'org.apache.logging.log4j:log4j-to-slf4j:2.17.0'\n    runtimeOnly 'ch.qos.logback:logback-classic:1.2.3'\n}\n

    # Configure Your Logging Framework

    \n\n

    # Logging the Relevant Shard

    \n

    Javacord adds the relevant shard to each log message. The facility that stores this information has a different name depending on which logging framework you use. For Log4j 2, this is called Thread Context Map and can be added in a pattern layout with %X{shard}, or you can add the whole thread context map by using %X. For Logback Classic, it is called MDC and can be added with the same pattern expressions as for Log4j.

    \n","path":"/wiki/basic-tutorials/logger-config.html","keywords":["log4j","log4j2","slf4j","logback","logging","logger"]},{"title":"Using the MessageBuilder","headers":[{"level":2,"title":"🕵️‍♀️ Example","slug":"example","children":[]},{"level":2,"title":"📍 Allowed Mentions","slug":"allowed-mentions","children":[]}],"content":"

    # Using the MessageBuilder

    \n

    The MessageBuilder class is a more powerful alternative to the TextChannel#sendMessage(...) method.

    \n

    It can be used to construct more complex messages and supports some additional features that are not possible\nwith a simple TextChannel#sendMessage(...) call.

    \n

    # 🕵️‍♀️ Example

    \n

    The following code

    \n
    new MessageBuilder()\n    .append(\"Look at these \")\n    .append(\"awesome\", MessageDecoration.BOLD, MessageDecoration.UNDERLINE)\n    .append(\" animal pictures! 😃\")\n    .appendCode(\"java\", \"System.out.println(\\\"Sweet!\\\");\")\n    .addAttachment(new File(\"C:/Users/Bastian/Pictures/kitten.jpg\"))\n    .addAttachment(new File(\"C:/Users/Bastian/Pictures/puppy.jpg\"))\n    .setEmbed(new EmbedBuilder()\n            .setTitle(\"WOW\")\n            .setDescription(\"Really cool pictures!\")\n            .setColor(Color.ORANGE))\n    .send(channel);\n

    will be displayed like this:

    \n

    \"\"

    \n

    # 📍 Allowed Mentions

    \n

    The allowed mentions object lets you control what should be mentioned (pinged) in a message if it contains mentions.

    \n

    The following code will ping:

    \n
      \n
    • The user0
    • \n
    • All mentioned roles in the message
    • \n
    \n

    And will not ping:

    \n
      \n
    • @everyone and @here
    • \n
    • The user1
    • \n
    \n
    AllowedMentions allowedMentions = new AllowedMentionsBuilder()\n                .addUser(user0.getId())\n                .setMentionRoles(true)\n                .setMentionEveryoneAndHere(false)\n                .build();\n\n        new MessageBuilder()\n                .setAllowedMentions(allowedMentions)\n                .append(user0.getMentionTag())\n                .append(user1.getMentionTag())\n                .append(role.getMentionTag())\n                .append(role2.getMentionTag())\n                .append(\"@everyone\")\n                .send(channel);\n

    If you add a user to the mentions object and set setMentionUsers(true) it will ping every mentioned user. The same applies for setMentionRoles(true)

    \n","path":"/wiki/basic-tutorials/message-builder.html","keywords":["create messages","message creation","sendMessage"]},{"title":"Running and Deploying your Bot","headers":[{"level":2,"title":"👷 Running from your IDE","slug":"running-from-your-ide","children":[{"level":3,"title":"IntelliJ IDEA","slug":"intellij-idea","children":[]},{"level":3,"title":"Eclipse","slug":"eclipse","children":[]}]},{"level":2,"title":"📦 Deploying and Running as a Standalone Application","slug":"deploying-and-running-as-a-standalone-application","children":[{"level":3,"title":"Building a Distribution with Gradle","slug":"building-a-distribution-with-gradle","children":[]},{"level":3,"title":"Building a Distribution with Maven","slug":"building-a-distribution-with-maven","children":[]},{"level":3,"title":"Running","slug":"running","children":[]}]},{"level":2,"title":"💩 Building a Fat Jar","slug":"building-a-fat-jar","children":[{"level":3,"title":"With Gradle","slug":"with-gradle","children":[]},{"level":3,"title":"With Maven","slug":"with-maven","children":[]}]}],"content":"

    # Running and Deploying your Bot

    \n

    If you took the time to write a bot, at some point you'll also want to run it, either for use in production or for debugging from the IDE.

    \n

    # 👷 Running from your IDE

    \n

    While developing your bot, you will want to run your bot directly from the IDE in order to quickly test changes and new features. For this, create a Run/Debug Configuration in your IDE of choice with your bot's main class. Remember to also add any necessary parameters and environment variables.

    \n

    A working Run/Debug configuration will also enable you to run your bot with a debugger. A debugger is often considered a developer's most important tool, so make sure to familiarize yourself with the debugging integration for your IDE of choice.

    \n

    # IntelliJ IDEA

    \n

    This assumes your project is set up correctly, preferably with Gradle, can be built without errors, and does not yet have any run/debug configurations.

    \n

    1. Locate and click the Add Configuration... button in the top bar next to the start button.

    \n

    \"\"

    \n

    2. In the newly opened window, click the + button in the top left and select Application

    \n

    3. Give a name for your configuration and select the module to use the classpath of (usually yourproject.main).

    \n

    4. Select your Main class. Use the ... button to search for it or provide the fully qualified name. If it can not be found, you most likely selected the wrong module in step 3.

    \n

    5. Optional: Set command line arguments and environment variables. For the environment variables, use the button to the right of the input field for a more convenient input window.

    \n

    6. Click Apply to finalize the configuration, then OK to close the window.

    \n

    \"\"

    \n

    7. Select your configuration in the drop-down menu and run or debug it with the buttons to the right.

    \n

    \"\"

    \n

    # Eclipse

    \n

    This assumes your project is set up correctly, can be built without errors, and does not yet have any run/debug configurations.

    \n

    1. In the menu bar, click "Run" then "Run Configurations...".

    \n

    2. In the newly opened window, select "Java Application" on the left side, then click the leftmost button in the row above the tree view. A new configuration will appear.

    \n

    \"\"

    \n

    3. Give a name to your configuration.

    \n

    4. Set the project and the main class. To easily select it, use the "Browse..." and "Search..." buttons.

    \n

    5. Optional: Set command line (and VM) arguments as well as environment variables in their respective tabs.

    \n

    6. Click Apply to save your configuration, then Close to close the window.

    \n

    \"\"

    \n

    7. Run or debug your bot via the Buttons in the top row, the Run menu, or the shortcuts Ctrl+F11 for running and F11 for debugging.

    \n

    \"\"

    \n

    # 📦 Deploying and Running as a Standalone Application

    \n

    Running from the IDE is only recommended during development and strongly discouraged for production use. Generally, you'll want your build tool to create a convenient distribution format for you to use.

    \n

    # Building a Distribution with Gradle

    \n\n \n\n

    For Gradle, only two further steps are necessary for a basic application. On top of the steps described in the Getting Started Section, also add the Application Plugin and define your mainClass as the fully qualified name of your main class. If you're using an older version of Gradle (earlier than 6.4), the attribute is instead called mainClassName.

    \n

    INFO

    \n

    As with many Gradle solutions, there is actually a whole lot going on under the hood. The application plugin implicitly also applies the java and distribution plugins. Refer to the documentations of the involved plugins for more ways to fine-tune the process.

    \n
    \n

    Your modified build file should now look similar to this:

    \n\n \n
    plugins {\n    application\n}\n \nversion = \"1.0.0\"\n \njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n}\n \napplication {\n    mainClass.set(\"com.github.yourname.BotMain\")\n    // mainClassName.set(\"com.github.yourname.BotMain\")  // Gradle < 6.4\n}\n \nrepositories {\n    mavenCentral()\n}\n \ndependencies {\n    implementation(\"org.javacord:javacord:{{latestVersion}}\")\n}\n
    \n \n
    plugins {\n    id 'application'\n}\n \nversion '1.0.0'\n \njava {\n    sourceCompatibility = JavaVersion.VERSION_1_8\n}\n \napplication {\n    mainClass = 'com.github.yourname.BotMain'\n    // mainClassName = 'com.github.yourname.BotMain' // for Gradle versions < 6.4\n}\n \nrepositories {\n    mavenCentral()\n}\n \ndependencies {\n    implementation 'org.javacord:javacord:{{latestVersion}}'\n}\n
    \n
    \n

    Now you can execute the distZip or distTar task with Gradle. The task will create a distribution and package it in an archive file that will be placed in the build/distributions directory. Extract the content of those files on your server or whichever machine you want to run your bot on.

    \n

    The distribution usually only contains the directories bin and lib. From the distribution directory, run either bin/yourbot or bin/yourbot.bat, depending on whether you're running the bot on Linux / macOS or windows.

    \n

    # Building a Distribution with Maven

    \n

    For Maven, add the Appassembler plugin to your pom.xml. The plugin will create a distribution, but not bundle it in a neat archive file, so we'll also add the assembly plugin. We'll bind both to the package lifecycle phase.

    \n
    <project>\n  ...\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>appassembler-maven-plugin</artifactId>\n                <version>1.10</version>\n                <configuration>\n                    <programs>\n                        <program>\n                            <mainClass>org.javacord.examplebot.Main</mainClass>\n                            <id>examplebot</id>\n                        </program>\n                    </programs>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>create-distribution</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>assemble</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <artifactId>maven-assembly-plugin</artifactId>\n                <version>3.3.0</version>\n                <configuration>\n                    <descriptors>\n                        <!-- This must match the location of the descriptor -->\n                        <descriptor>src/assembly/distribution.xml</descriptor>\n                    </descriptors>\n                </configuration>\n                <executions>\n                    <execution>\n                        <id>create-archive</id>\n                        <phase>package</phase>\n                        <goals>\n                            <goal>single</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n</project>\n

    Sadly, none of the built-in assembly descriptors match our use case, so we'll put our custom one into src/assembly/distribution.xml:

    \n
    <assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\">\n    <id>distribution</id>\n    <formats>\n        <!-- See https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html for supported formats -->\n        <format>tar.gz</format>\n        <format>tar.bz2</format>\n        <format>zip</format>\n    </formats>\n    <fileSets>\n        <fileSet>\n            <!-- This will also include your project readme, license and similar files-->\n            <directory>${project.basedir}</directory>\n            <outputDirectory>/</outputDirectory>\n            <includes>\n                <include>README*</include>\n                <include>LICENSE*</include>\n                <include>NOTICE*</include>\n            </includes>\n        </fileSet>\n        <fileSet>\n            <!-- Change this if you reconfigured the appassembler output directory -->\n            <directory>${project.build.directory}/appassembler</directory>\n            <outputDirectory>/</outputDirectory>\n        </fileSet>\n    </fileSets>\n</assembly>\n

    Now when you execute mvn package, a distribution with start scripts for Windows and Linux/macOS will be generated which is then packaged into archive files for every format you specified in the assembly descriptor. You can find the raw distribution (without readme and license files) in target/appassembler and the archive files directly in target.

    \n

    # Running

    \n

    After creating your distribution via Gradle or Maven and extracting/copying it to the machine you want to run it from, you should have a directory containing both a bin and a lib (or repo) directory. Depending on your platform, you can now run the bin/yourbot or bin/yourbot.bat script.

    \n

    These automatically generated scripts will then invoke java with your dependencies on the classpath and run your main class. Your working directory will be the directory you ran the script from.

    \n

    # 💩 Building a Fat Jar

    \n

    Although it is an abuse of the way java works, sometimes you will be forced to create a fat jar, or an uber jar. This is a jar file that contains your application and all its dependencies. This is sometimes used as a lazy way of building a convenient distribution, but should be foregone in favor of the above mentioned distributions.

    \n

    However, in some cases (more often than not Bukkit/Spigot addons) it is necessary to provide a fat jar, since the host application's loading mechanism can only handle singular jar files. If you are subject to such a case of bad design, please complain to the maintainer of whatever host application you are using, then use the following instructions to forsake all that is good and just and create a fat jar. Remember to grit your teeth the whole time.

    \n

    # With Gradle

    \n

    For Gradle, use the shadow plugin. If you want the fat jar to be executable, you will need to specify a main class via the application plugin.

    \n
    plugins {\n    id 'java'\n    # ...\n    id 'com.github.johnrengelman.shadow' version '7.1.2'\n}\n

    With gradlew shadowJar you can now create a shaded (fat) jar. It will be named build/libs/yourbot-1.0.0-all.jar or similar, according to your project settings.

    \n

    # With Maven

    \n

    For Maven, add the maven-shade-plugin to your build. As with the other solutions, configure your main class.

    \n

    Some of your dependencies might be signed .jar files. Unfortunately, this will likely break your fat jar. Remove the signatures by defining an exclusion filter as demonstrated below. Let the thought that you had to disable a security feature just to make this work serve as a reminder that creating a fat jar is not how jars are meant to be used.

    \n
    <project>\n  ...\n  <build>\n    <plugins>\n      <plugin>\n        <groupId>org.apache.maven.plugins</groupId>\n        <artifactId>maven-shade-plugin</artifactId>\n        <version>3.2.3</version>\n        <configuration>\n            <shadedArtifactAttached>true</shadedArtifactAttached>\n            <shadedClassifierName>fat</shadedClassifierName>\n            <transformers>\n                <transformer implementation=\"org.apache.maven.plugins.shade.resource.ManifestResourceTransformer\">\n                    <manifestEntries>\n                       <Main-Class>com.github.yourname.BotMain</Main-Class>\n                    </manifestEntries>\n                </transformer>\n            </transformers>\n            <filters>\n                <filter>\n                    <artifact>*:*</artifact>\n                    <excludes>\n                        <exclude>META-INF/*.SF</exclude>\n                        <exclude>META-INF/*.DSA</exclude>\n                        <exclude>META-INF/*.RSA</exclude>\n                    </excludes>\n                </filter>\n            </filters>\n        </configuration>\n        <executions>\n          <execution>\n            <phase>package</phase>\n            <goals>\n              <goal>shade</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n</project>\n

    Running mvn package will now additionally create the yourbot-1.0.0-fat.jar.

    \n","path":"/wiki/basic-tutorials/running.html","keywords":["run bot","running bot","execute bot","deploy","deployment","application"]},{"title":"Completable Futures","headers":[{"level":2,"title":"🤔 What the heck is a future?","slug":"what-the-heck-is-a-future","children":[]},{"level":2,"title":"📖 Methods","slug":"methods","children":[{"level":3,"title":"join()","slug":"join","children":[]},{"level":3,"title":"thenAccept(...)","slug":"thenaccept","children":[]},{"level":3,"title":"exceptionally(...)","slug":"exceptionally","children":[]},{"level":3,"title":"thenCompose()","slug":"thencompose","children":[]}]},{"level":2,"title":"📚 Further Read","slug":"further-read","children":[]}],"content":"

    # Completable Futures

    \n

    WARNING

    \n

    This tutorial assumes that you are familiar with lambda expressions.\nTake a look at the lambda introduction first, if you are not!

    \n
    \n

    As Javacord is heavily multithreaded, you must understand the concept of\nFutures\nin general, as well as their most common implementation, the\nCompletableFuture.\nThis little introduction gives you a quick overview of the basics you need to know in order to work with Futures.

    \n

    # 🤔 What the heck is a future?

    \n

    A future is basically a wrapper, that will contain a value in the future but might not contain it right now.\nThis is useful, if a method call requires some time and should not block the execution of your current code.\nYou can easily see the difference with a primitive speed comparison:

    \n
    long currentTime = System.currentTimeMillis();\nchannel.sendMessage(\"Test 1\");\nchannel.sendMessage(\"Test 2\");\nchannel.sendMessage(\"Test 3\");\nchannel.sendMessage(\"Test 4\");\nchannel.sendMessage(\"Test 5\");\n// Prints \"4 ms\"\nSystem.out.println((System.currentTimeMillis() - currentTime) + \" ms\");\n
    long currentTime = System.currentTimeMillis();\nchannel.sendMessage(\"Test 1\").join();\nchannel.sendMessage(\"Test 2\").join();\nchannel.sendMessage(\"Test 3\").join();\nchannel.sendMessage(\"Test 4\").join();\nchannel.sendMessage(\"Test 5\").join();\n// Prints \"894 ms\"\nSystem.out.println((System.currentTimeMillis() - currentTime) + \" ms\");\n

    TIP

    \n

    join() blocks the current thread until the method finished. This will be explained later.

    \n
    \n

    # 📖 Methods

    \n

    # join()

    \n

    The join method blocks the current thread until the method finished.\nIt returns the method's result or throws a CompletionException if anything failed.

    \n

    The following example would create a new text channel in a given server and sends a message directly afterwards.

    \n
    // Create the channel\nServerTextChannel channel = new ServerTextChannelBuilder(server)\n    .setName(\"new-channel\")\n    .create()\n    .join();\n// Send a message in the new channel\nMessage message = channel.sendMessage(\"First!\").join();\n// Adds an reaction to the message. Even though this method doesn't return anything,\n// join() ensures, that an exception is thrown in case something went wrong\nmessage.addReaction(\"👍\").join();\n

    DANGER

    \n

    You should avoid join() for methods which will be called frequently.

    \n
    \n

    TIP

    \n

    While join() can become a performance issue when you call it very frequently, it is very convenient to use and easy to understand.\nIf you are new to programming and just want to get your first bot working, this is a good method to start with.

    \n

    Once you gathered more experience, we highly advise against using join as it negatively impacts your bot's performance!

    \n
    \n

    # thenAccept(...)

    \n

    The thenAccept method accepts a Consumer, that consumes the result of the method and is executed asynchronously.\nIt is the method you usually want to use most of the time.

    \n

    The following example would create a new text channel in a given server and send a message directly afterwards.

    \n
    new ServerTextChannelBuilder(server)\n    .setName(\"new-channel\")\n    .create()\n    .thenAccept(channel -> {\n        channel.sendMessage(\"First!\").thenAccept(message -> {\n            message.addReaction(\"👍\");\n        });\n    });\n

    DANGER

    \n

    The example code above has a major problem: Any exception that might occur will be completely ignored.\nThis makes it very hard to find bugs.

    \n

    For example, if your bot doesn't have the permissions to create a new channel, it will just fail silently.

    \n
    \n

    # exceptionally(...)

    \n

    The exceptionally method accepts a Function as parameter, which consumes possible exceptions and returns a fallback value.

    \n

    The following example would create a new text channel in a given server and send a message directly afterwards.\nIf something fails (e.g., if the bot isn't allowed to create a text channel in the server), it will log an exception.

    \n
    new ServerTextChannelBuilder(server)\n    .setName(\"new-channel\")\n    .create()\n    .thenAccept(channel -> {\n        channel.sendMessage(\"First!\").thenAccept(message -> {\n            message.addReaction(\"👍\").exceptionally(e -> {\n                e.printStackTrace(); // Adding the reaction failed\n                return null;\n            });\n        }).exceptionally(e -> {\n            e.printStackTrace(); // Message sending failed\n            return null;\n        });\n    }).exceptionally(e -> {\n        e.printStackTrace(); // Channel creation failed    \n        return null;\n    });\n

    Wow! This looks ugly 🤮.\nBut worry not! There are many options to improve this code!

    \n

    To make things simpler for you, Javacord has the ExceptionLogger class, which can be used here.\nIt logs every exception you didn't catch manually.

    \n
    new ServerTextChannelBuilder(server)\n    .setName(\"new-channel\")\n    .create()\n    .thenAccept(channel -> {\n        channel.sendMessage(\"First!\").thenAccept(message -> {\n            message.addReaction(\"👍\").exceptionally(ExceptionLogger.get());\n        }).exceptionally(ExceptionLogger.get());\n    }).exceptionally(ExceptionLogger.get());\n

    Okay! This is at least a little better, but still not really perfect 🤔.

    \n

    # thenCompose()

    \n

    The thenCompose methods allows you to chain futures.\nIt takes a Function as parameter, that\nconsumes the future's value and expects a new future to be returned.

    \n

    The example to create a text channel can now be written like this:

    \n
    new ServerTextChannelBuilder(server)\n        .setName(\"new-channel\")\n        .create() \n        .thenCompose(channel -> channel.sendMessage(\"First!\"))\n        .thenCompose(message -> message.addReaction(\"👍\"))\n        .exceptionally(ExceptionLogger.get());\n

    Finally 🎉! Now we only need a single exceptionally(...) call at the end.\nWe also got rid of the nested callbacks (usually referred to as "callback hell").

    \n

    For better understanding, here's the example with comments that tell you the type at each line:

    \n
    new ServerTextChannelBuilder(server) // ServerTextChannelBuilder\n        .setName(\"new-channel\") // ServerTextChannelBuilder\n        .create() // CompletableFuture<ServerTextChannel>\n        .thenCompose(channel -> channel.sendMessage(\"First!\")) // CompletableFuture<Message>\n        .thenCompose(message -> message.addReaction(\"👍\")) // CompletableFuture<Void>\n        .exceptionally(ExceptionLogger.get()); // CompletableFuture<Void>\n

    # 📚 Further Read

    \n

    This tutorial only focuses on the absolute basics.\nFor a more detailed introduction to CompletableFutures, you can take a look at\nthis tutorial.

    \n

    You should also take a look at the JavaDoc for a complete list of methods: CompletableFuture JavaDoc.

    \n","path":"/wiki/essential-knowledge/completable-futures.html","keywords":["CompletableFuture","exceptionally","ExceptionLogger","join","thenAcceptAsync"]},{"title":"Lambdas","headers":[{"level":2,"title":"📚 Further Read","slug":"further-read","children":[]}],"content":"

    # Lambdas

    \n

    Lambdas are used to implement functional interfaces.\nSimply said, functional interfaces are interfaces with a single method definition.\nAll listeners in Javacord are functional interfaces and look like this internally (simplified):

    \n
    @FunctionalInterface\npublic interface MessageCreateListener {\n    void onMessageCreate(MessageCreateEvent event);\n}\n

    Before Java 8, you would have implemented this kind of listener as an anonymous class, which would look like this:

    \n
    api.addMessageCreateListener(new MessageCreateListener() {\n    @Override\n    public void onMessageCreate(MessageCreateEvent event) {\n        // Do stuff\n        event.pinMessage();\n    }\n});\n

    In Java 8, this can be replaced with a lambda expression, which does exactly the same thing, but in a more readable fashion.\nThe method parameter (in this case event) is written in front of the -> arrow, and the method body is written after it.

    \n
    api.addMessageCreateListener(event -> {\n    // Do stuff\n    event.pinMessage();\n});\n

    TIP

    \n

    If the method has more than one parameter, it would look like this:

    \n
    (param1, param2) -> { ... }\n
    \n

    There's even a shorter version: If you are only executing one statement, you can get rid of the { } brackets as well:

    \n
    api.addMessageCreateListener(event -> event.pinMessage());\n

    However, the above method can be shortened even more, by replacing the lambda expression with a so called "method reference".

    \n
    api.addMessageCreateListener(MessageEvent::pinMessage);\n

    There are also plenty classes in Java 8, that make use of lambda expressions.\nOne example would be the Optional class, which is explained here.

    \n

    # 📚 Further Read

    \n

    This tutorial only focuses on the absolute basics.\nFor an in-depth introduction to lambda expressions, you can take a look at\nOracle's article about lambda expressions.

    \n","path":"/wiki/essential-knowledge/lambdas.html","keywords":["lambdas"]},{"title":"Optionals","headers":[{"level":2,"title":"💪 Motivation","slug":"motivation","children":[{"level":3,"title":"The old way of doing it","slug":"the-old-way-of-doing-it","children":[]},{"level":3,"title":"The new way of doing it","slug":"the-new-way-of-doing-it","children":[]}]},{"level":2,"title":"📖 Methods","slug":"methods","children":[{"level":3,"title":"get()","slug":"get","children":[]},{"level":3,"title":"isPresent()","slug":"ispresent","children":[]},{"level":3,"title":"orElse(...)","slug":"orelse","children":[]},{"level":3,"title":"ifPresent(...)","slug":"ifpresent","children":[]},{"level":3,"title":"filter(...)","slug":"filter","children":[]},{"level":3,"title":"map(...)","slug":"map","children":[]},{"level":3,"title":"flatMap(...)","slug":"flatmap","children":[]}]},{"level":2,"title":"📚 Further Read","slug":"further-read","children":[]}],"content":"

    # Optionals

    \n

    WARNING

    \n

    This tutorial assumes that you are familiar with lambda expressions.\nTake a look at the lambda introduction first, if you are not!

    \n
    \n

    # 💪 Motivation

    \n

    The Optional class is widely used in Javacord.\nBasically, every method that might return a null value will return an Optional in Javacord instead.\nOptionals help you to avoid NullPointerExceptions and make it very clear if a method may not have a result.\nHere's a small example:

    \n

    # The old way of doing it

    \n
    User user = api.getCachedUserById(123L);\nif (user != null) {\n  user.sendMessage(\"Hi!\");\n}\n

    # The new way of doing it

    \n
    api.getCachedUserById(123L).ifPresent(user -> \n  user.sendMessage(\"Hi!\")\n);\n

    You can imagine an Optional like a box 📦 that may or may not contain a value.\nBefore accessing this value, you have to "unpack" this box first.

    \n

    # 📖 Methods

    \n

    The Optional class has many useful methods which can all be found in the JavaDocs.\nThis tutorial gives a short introduction to the most common ones.

    \n

    # get()

    \n

    The get method returns the value of the Optional or throws a NoSuchElementException if it does not contain a value.

    \n
    TextChannel channel = api.getTextChannelById(123L).get();\nchannel.sendMessage(\"Hi\");\n

    DANGER

    \n

    You should never use this method blindly but only if you are 100% sure the optional contains a value.

    \n

    Every time you use this method carelessly, a kitten dies 🙀!\nTrue story.

    \n
    \n

    # isPresent()

    \n

    The isPresent methods checks, if the Optional contains a value.

    \n
    Optional<TextChannel> channel = api.getTextChannelById(123L);\nif (channel.isPresent()) {\n  // A text channel with the id 123 exists. It's safe to call #get() now\n  channel.get().sendMessage(\"Hi\");\n}\n

    # orElse(...)

    \n

    The orElse methods returns the value of the Optional if it is present. Otherwise, it returns the given default value.

    \n
    // The user may not have a nickname on the given server. \n// In this case, we use the user's \"regular\" name.\nString displayName = user.getNickname(server).orElse(user.getName());\n

    The example above is (mostly) equivalent to the example below but much more concise.

    \n
    String displayName = \"\";\nOptional<String> nickname = user.getNickname(server);\nif (nickname.isPresent()) {\n  displayName = nickname.get();\n} else {\n  displayName = user.getName();\n}\n

    TIP

    \n

    In this case you can just use user.getDisplayName(server) instead.

    \n
    \n

    # ifPresent(...)

    \n

    The ifPresent method is very similar to an if (value != null) { ... } check.\nIt takes a Consumer as it's argument.\nThis consumer is called if the Optional contains a value.\nTogether with lambda expressions this can be a very handy method.

    \n
    api.getTextChannelById(123L).ifPresent(channel -> {\n  channel.sendMessage(\"Hi!\");\n});\n

    The example above is (mostly) equivalent to the example below but more concise.

    \n
    Optional<TextChannel> channel = api.getTextChannelById(123L);\nif (channel.isPresent()) {\n  channel.get().sendMessage(\"Hi!\");\n}\n

    # filter(...)

    \n

    The filter method filters the Optional for a given criteria.

    \n
    Optional<User> botUser = api.getCachedUserById(123L).filter(User::isBot);\n

    The example above is equivalent to the example below but more concise.

    \n
    Optional<User> user = api.getCachedUserById(123L);\nOptional<User> botUser;\nif (user.isPresent() && user.get().isBot()) {\n  botUser = user;\n} else {\n  botUser = Optional.empty();\n}\n

    # map(...)

    \n

    The map method "converts" the type of an Optional.\nThis is useful, if the type of an Optional does not contain the final value you need.

    \n

    The following example gets the name of the bots current activity (the "Playing xyz" status) or "None" if the bot has no current activity.

    \n
    String activityName = api.getYourself().getActivity().map(Activity::getName).orElse(\"None\");\n

    For better understanding, here's the exact same code but with the types as comments:

    \n
    String activityName =  api.getYourself() // User\n        .getActivity() // Optional<Activity>\n        .map(Activity::getName) // Optional<String>\n        .orElse(\"None\"); // String\n

    # flatMap(...)

    \n

    The flatMap method if very similar to the map methods.\nIt is used to map values that itself are Optionals to prevent Optional nesting (a "box in a box").

    \n
    String activityName = api.getCachedUserById(123L) // Optional<User>\n        .flatMap(User::getActivity) // Optional<Activity>\n        .map(Activity::getName) // Optional<String>\n        .orElse(\"None\"); // String\n

    Without flatMap, the code would look like this:

    \n
    String activityName = api.getCachedUserById(123L) // Optional<User>\n        .map(User::getActivity) // Optional<Optional<Activity>>\n        .filter(Optional::isPresent) // Optional<Optional<Activity>>\n        .map(Optional::get) // Optional<Activity>\n        .map(Activity::getName) // Optional<String>\n        .orElse(\"None\"); // String\n

    # 📚 Further Read

    \n

    This tutorial only focuses on the absolute basics.\nFor an in-depth introduction to Optionals, you can take a look at\nOracle's article about optionals.

    \n","path":"/wiki/essential-knowledge/optionals.html","keywords":[null,"ifPresent","isPresent","orElse"]},{"title":"Introduction","headers":[{"level":2,"title":"📚 Structure of the wiki","slug":"structure-of-the-wiki","children":[]},{"level":2,"title":"🤝 Support","slug":"support","children":[]}],"content":"

    # Introduction

    \n

    Welcome to the Javacord wiki! 👋

    \n

    This wiki will help you to get started with your first Discord bot as fast as possible.

    \n

    # 📚 Structure of the wiki

    \n

    The wiki is divided into four groups:

    \n
      \n
    • Getting Started focuses on teaching you how to setup up everything to get the most basic bot working.
    • \n
    • Basic tutorials contains articles about various concepts and classes of Javacord. Take a look at the headlines of each article and decide yourself, if it is relevant for you.
    • \n
    • Advanced Topics focuses on some more advanced topics that are not strictly necessary to start working with Javacord, but might become handy later on.
    • \n
    • Essential Knowledge teaches you the most important Java features/classes that you should know to comfortably work with Javacord. If you already have decent Java knowledge, you can skip this completely.
    • \n
    \n

    # 🤝 Support

    \n

    While the wiki is great and covers many aspects of Javacord, we highly recommended you to join our Discord server if you have any questions:

    \n\n","path":"/wiki/","keywords":[]},{"title":"Creating a Bot Account","headers":[{"level":2,"title":"💡 Create a bot and get its token","slug":"create-a-bot-and-get-its-token","children":[]},{"level":2,"title":"➕ How to add a bot to your server","slug":"how-to-add-a-bot-to-your-server","children":[{"level":3,"title":"Use Javacord to create the invite link","slug":"use-javacord-to-create-the-invite-link","children":[]},{"level":3,"title":"Create the invite link manually","slug":"create-the-invite-link-manually","children":[]}]},{"level":2,"title":"🙋‍♂️ Use the invite link","slug":"use-the-invite-link","children":[]}],"content":"

    # Creating a Bot Account

    \n

    After you added Javacord as a dependency with your favorite build manager, you should now create a bot account on the Discord website.\nThis article will guide you through the process.

    \n

    # 💡 Create a bot and get its token

    \n

    # 1. Open https://discord.com/developers/applications/me and click on "Create an application".

    \n

    \"\"

    \n

    # 2. Switch to Bot

    \n

    TIP

    \n

    If you want to, you can rename your application first

    \n
    \n

    \"\"

    \n

    # 3. Click on Add bot and confirm the popup

    \n

    \"\"\n\"\"

    \n

    # 4. Copy the bot's token. In this case the token would be NDc[...]pCs. You can just click on Copy.

    \n

    DANGER

    \n

    This token is used to login your bot. Keep it secret!

    \n
    \n

    \"\"

    \n

    # 5. If you want to, you can change the bot's name and avatar on this page, too.

    \n

    # ➕ How to add a bot to your server

    \n

    Bots cannot join a server on their own like normal Discord users can.\nInstead, the owner of a server has to invite the bot using a so called Invite Link.\nThere are multiple ways to create the invite link:

    \n

    # Use Javacord to create the invite link

    \n

    The easiest way to obtain an invite link for your bot is by letting Javacord do it for you.\nSimply execute the following code, and it will print the invite link to your console:

    \n
    DiscordApi api = new DiscordApiBuilder().setToken(\"your token\").login().join();\nSystem.out.println(api.createBotInvite());\n

    If you don't have Javacord setup yet, you can also create the invite link manually.

    \n\n

    # Get the client id

    \n

    In order to add a bot to your server you need its client id.

    \n

    You can get your client id from the same page where you created it.

    \n

    \"\"

    \n

    With this id you can create an invite link for your bot.

    \n

    If you are the owner or admin of the server, you can use this link to add your bot to your server. Otherwise, you have to give the link to the server owner/admins and ask them to add your bot.

    \n

    TIP

    \n

    Unlike the token, you don't have to keep your client id secret

    \n
    \n

    # Create the url

    \n

    Just use the following link and replace 123456789 with your own client id.

    \n

    https://discord.com/api/oauth2/authorize?client_id=123456789&scope=applications.commands%20bot&permissions=0

    \n

    You can calculate the permissions (in the link above it's the 0) on the page where you created the bot:

    \n

    \"\"

    \n

    # 🙋‍♂️ Use the invite link

    \n

    You can now open the link and add the bot to your server:

    \n

    \"\"

    \n

    TIP

    \n

    Only the owner and admins of a server can invite bots. If you do not own a server yet, it is recommended to create one for testing.

    \n
    \n","path":"/wiki/getting-started/creating-a-bot-account.html","keywords":["bot creation","get token","add bot","bot invite link"]},{"title":"Download / Installation","headers":[{"level":2,"title":"📦 Javacord Dependency","slug":"javacord-dependency","children":[]},{"level":2,"title":"📝 Optional Logger Dependency","slug":"optional-logger-dependency","children":[]}],"content":"

    # Download / Installation

    \n\n \n\n

    The recommended way to get Javacord is to use a build manager, like Gradle or Maven.
    \nIf you are not familiar with build managers, you can follow one of the beginner ide setup guides (see navigation) or\ndownload Javacord directly from GitHub.

    \n

    # 📦 Javacord Dependency

    \n\n \n
    repositories { mavenCentral() }\ndependencies { implementation 'org.javacord:javacord:$latest-version' }\n
    \n \n
    <dependency>\n    <groupId>org.javacord</groupId>\n    <artifactId>javacord</artifactId>\n    <version>$latest-version</version>\n    <type>pom</type>\n</dependency>\n
    \n \n
    libraryDependencies ++= Seq(\"org.javacord\" % \"javacord\" % \"$latest-version\")\n
    \n
    \n
    Click to view snapshot repositories\n

    Snapshots are automatically deployed from the development\nbranch.

    \n\n \n
    repositories {\n    maven {\n        url \"https://oss.sonatype.org/content/repositories/snapshots/\"\n    }\n}\ndependencies {\n    implementation 'org.javacord:javacord:$latest-snapshot-version'\n}\n
    \n \n
    <repository>\n    <id>snapshots-repo</id>\n    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n</repository>\n
    <dependency>\n    <groupId>org.javacord</groupId>\n    <artifactId>javacord</artifactId>\n    <version>$latest-snapshot-version</version>\n    <type>pom</type>\n</dependency>\n
    \n \n
    resolvers += \"snapshots-repo\" at \"https://oss.sonatype.org/content/repositories/snapshots/\"\nlibraryDependencies ++= Seq(\"org.javacord\" % \"javacord\" % \"$latest-snapshot-version\")\n
    \n
    \n
    \n

    # 📝 Optional Logger Dependency

    \n

    In addition to Javacord, it is also recommended to install a Log4j-2-compatible logging framework.\nA logging framework can be used to provide a more sophisticated logging experience with being able to configure log\nformat, log targets (console, file, database, Discord direct message, ...), log levels per class, and much more.

    \n

    For example, Log4j Core:

    \n\n \n
    dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-core:2.17.0' }\n
    \n \n
    <dependency>\n    <groupId>org.apache.logging.log4j</groupId>\n    <artifactId>log4j-core</artifactId>\n    <version>2.17.0</version>\n</dependency>\n
    \n \n
    libraryDependencies ++= Seq(\"org.apache.logging.log4j\" % \"log4j-core\" % \"2.17.0\")\n
    \n
    \n

    Take a look at the logger configuration wiki article for further information.

    \n","path":"/wiki/getting-started/download-installation.html","keywords":["download and installation","maven","gradle"]},{"title":"Frequently Asked Questions","headers":[{"level":2,"title":"Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?","slug":"q-why-do-i-receive-empty-no-content-messages-in-i-e-the-messagecreatelistener","children":[]},{"level":2,"title":"Q: What is ... in the code examples?","slug":"q-what-is-in-the-code-examples","children":[]},{"level":2,"title":"Q: Why is my code not working?","slug":"q-why-is-my-code-not-working","children":[{"level":3,"title":"How to properly ask a question to get fast support?","slug":"how-to-properly-ask-a-question-to-get-fast-support","children":[]}]},{"level":2,"title":"Q: What differs Javacord from JDA and D4J?","slug":"q-what-differs-javacord-from-jda-and-d4j","children":[]}],"content":"

    # Frequently Asked Questions

    \n

    Here you will find answers to some of the most asked questions.

    \n

    # Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?

    \n

    You are missing the privileged MESSAGE_CONTENT intent. For more information of how to enable privileged intents and enable them in your code see Gateway Intents.

    \n

    # Q: What is ... in the code examples?

    \n

    You have to replace the ... with an instance that can be assigned to the datatype seen left.

    \n

    For example, if you see TextChannel channel = ..., you have to replace ... with an instance that is a TextChannel which you can get from the API api.getTextChannelById(CHANNEL_ID) (note this returns an Optional<TextChannel>) or from an event like messageCreateEvent.getChannel().

    \n

    # Q: Why is my code not working?

    \n

    There are multiple reasons why your code might not work. The most common ones are:

    \n
      \n
    1. Your code is not being reached. So make sure your code actually gets executed with a print statement or a debugger.
    2. \n
    3. Add at least .exceptionally(ExceptionLogger.get()) to every CompletableFuture (like when sending a message) to show any exceptions that might come from Discord.
    4. \n
    5. Methods like User#getRoles(Server) do not return the roles of the user. To fix this make sure to add the GUILD_MEMBERS intent.
    6. \n
    7. You are getting a NoSuchElementException. Congratulations, you have killed a kitten! You are most likely getting this Exception because you handle Optionals wrong. Read the article on Optionals to learn how to use them correctly.
    8. \n
    \n

    If none of these tips will help you, you can ask your question in our Discord Server.

    \n

    # How to properly ask a question to get fast support?

    \n

    Don't ask:

    \n
    Why is my code not working?\n//Code\n
    Why am I getting Exception X?\n

    To ensure all information is provided that is needed to solve your issue, you should ask your question in a format like:

    \n
    I have an issue with:   YOUR_ISSUE\nI want to do:           WHAT_YOU_WANT_TO_DO\nCurrently this happens: WHAT_HAPPENS_NOW\n\n//Code\n\n//Exception\nThe exception is thrown in the following line(not the number): CODE_LINE\n

    # Q: What differs Javacord from JDA and D4J?

    \n

    While all 3 libraries are Wrappers for the programming language Java, they use different techniques and concepts for their API.

    \n
      \n
    • Javacord: Uses Java classes for its API like CompletableFuture for async requests and Optional for return types which may be null.\n
        \n
      • Sending a Message: channel.sendMessage("Javacord")
      • \n
      • Checking if the Author of a message is a user: message.getMessageAuthor().asUser().isPresent()
      • \n
      \n
    • \n
    • JDA: Has its own wrapper to execute requests and returns null if values are not present.\n
        \n
      • Sending a Message: channel.sendMessage("JDA").queue()
      • \n
      • Checking if the Author of a message is a user: message.getMember() != null
      • \n
      \n
    • \n
    • Discord4J: Takes on the reactive approach.\n
        \n
      • Sending a Message: channel.createMessage("Pong!").block();
      • \n
      \n
    • \n
    \n","path":"/wiki/getting-started/faq.html","keywords":["faq","...","deploy","code not working","ask a question","library difference"]},{"title":"Writing your first bot","headers":[{"level":2,"title":"❗ Enabling required intents","slug":"enabling-required-intents","children":[]},{"level":2,"title":"🔑 Log the bot in","slug":"log-the-bot-in","children":[]},{"level":2,"title":"👂 Adding a listener","slug":"adding-a-listener","children":[]},{"level":2,"title":"👩‍🔧 Putting it all together","slug":"putting-it-all-together","children":[]}],"content":"

    # Writing your first bot

    \n

    After you have successfully added Javacord as a dependency, created a bot user, and got its token, you are now ready to create your first simple bot! 🎉

    \n

    # ❗ Enabling required intents

    \n

    By default, all non-privileged intents are enabled. To receive the message content, attachments, components, and embeds you need a special privileged intent MESSAGE_CONTENT.\nTo enable this privileged intent please see the Gateway Intents wiki article.

    \n

    Slash Commands

    \n

    Generally it is recommended to use Slash Commands instead of text commands because they offer many advantages\nlike auto-completion, fixed and optional arguments, different kind of arguments with built-in types: numbers(with ranges), text, channel and a lot more.

    \n
    \n

    # 🔑 Log the bot in

    \n

    Everything starts with the DiscordApiBuilder class.\nIt is used to create a DiscordApi object which is the most important class of your bot.

    \n
    DiscordApi api = new DiscordApiBuilder()\n        .setToken(\"<your super secret token>\")\n        .addIntents(Intent.MESSAGE_CONTENT)\n        .login().join();\n

    After executing this code, you should already see your bot online in Discord.\nOf course, just being online is not enough, so let's add some more code!

    \n

    # 👂 Adding a listener

    \n

    After you got your api instance, let's continue by adding a listener that answers every !ping message with a simple Pong!.

    \n
    api.addMessageCreateListener(event -> {\n    if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n        event.getChannel().sendMessage(\"Pong!\");\n    }\n});\n

    \"\"

    \n

    # 👩‍🔧 Putting it all together

    \n

    A good place for your code is the main(...) method that every executable Java program must have.\nYour complete class may look like this:

    \n
    public class MyFirstBot {\n\n    public static void main(String[] args) {\n        // Log the bot in\n        DiscordApi api = new DiscordApiBuilder()\n                .setToken(\"<your super secret token>\")\n                .addIntents(Intent.MESSAGE_CONTENT)\n                .login().join();\n\n        // Add a listener which answers with \"Pong!\" if someone writes \"!ping\"\n        api.addMessageCreateListener(event -> {\n            if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n                event.getChannel().sendMessage(\"Pong!\");\n            }\n        });\n    }\n\n}\n

    Congratulations, that's already everything you have to know for the beginning.\nNow, you can play around a little bit by exploring other listeners and methods.\nOr you just continue reading articles in the Basic Tutorials category.

    \n","path":"/wiki/getting-started/writing-your-first-bot.html","keywords":[]},{"title":"Interaction Commands aka. Slash Commands","headers":[{"level":2,"title":"💡 Creating a Command","slug":"creating-a-command","children":[{"level":3,"title":"📔 Notes on creating commands:","slug":"notes-on-creating-commands","children":[]}]},{"level":2,"title":"⤵️ Get your commands","slug":"get-your-commands","children":[]},{"level":2,"title":"🔨 Updating Commands","slug":"updating-commands","children":[]},{"level":2,"title":"✍️ Bulk overwriting commands","slug":"bulk-overwriting-commands","children":[]},{"level":2,"title":"👮‍♂️ Permissions","slug":"permissions","children":[]},{"level":2,"title":"❗ Limits","slug":"limits","children":[{"level":3,"title":"Registering a command","slug":"registering-a-command","children":[]},{"level":3,"title":"General","slug":"general","children":[]}]}],"content":"

    # Interaction Commands aka. Slash Commands

    \n

    INFO

    \n

    There are a lot of convenient methods which aim to make your life easier with i.e., not\nbeing able to have an invalid configuration of your builder.\nTherefore, the following examples will only show the usage with the convenient methods.

    \n
    \n

    # 💡 Creating a Command

    \n

    INFO

    \n

    There are 2 different types of Commands:

    \n
      \n
    • Global | Available for every Server once your Bot gets invited: Created with createGlobal(DiscordApi).
    • \n
    • Server | Only available on the specific Server: Created with createForServer(Server).
    • \n
    \n
    \n

    Let's get started with the most basic command, a ping command.

    \n
    SlashCommand command = SlashCommand.with(\"ping\", \"Checks the functionality of this command\")\n    .createGlobal(api)\n    .join();\n

    That's all you have to do!

    \n

    Let's have a look at a more complex command which involves nearly all possibilities:

    \n
    SlashCommand command =\n        SlashCommand.with(\"channel\", \"A command dedicated to channels\",\n            Arrays.asList(\n                SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND_GROUP, \"edit\", \"Edits a channel\",\n                    Arrays.asList(\n                        SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND, \"allow\", \"Allows a permission to a user for a channel\",\n                            Arrays.asList(\n                                SlashCommandOption.create(SlashCommandOptionType.CHANNEL, \"channel\", \"The channel to modify\", true),\n                                SlashCommandOption.create(SlashCommandOptionType.USER, \"user\", \"The user which permissions should be changed\", true),\n                                SlashCommandOption.createWithChoices(SlashCommandOptionType.DECIMAL, \"permission\", \"The permission to allow\", true,\n                                    Arrays.asList(\n                                        SlashCommandOptionChoice.create(\"manage\", 0),\n                                        SlashCommandOptionChoice.create(\"show\", 1)))\n        ))))))\n        .createGlobal(api)\n        .join();\n

    Let that sink in first!

    \n

    What are we doing here?

    \n
      \n
    1. We create a base command called channel.
    2. \n
    3. It has a SUB_COMMAND_GROUP called edit which basically is just a folder where you can put your commands in.
    4. \n
    5. There's a SUB_COMMAND called allow which is our actual command. Therefore, our complete argument looks like channel edit allow.
    6. \n
    7. The SUB_COMMAND has 3 arguments:\n
        \n
      1. The channel which should be edited.
      2. \n
      3. The user which permissions should be changed.
      4. \n
      5. A predefined list of available permissions the command executor can choose of.
      6. \n
      \n
    8. \n
    \n

    \"\"

    \n

    # 📔 Notes on creating commands:

    \n

    # The REQUIRED attribute

    \n

    You can only mark the last argument as being not required. This means it can be optionally set by the command executor.\nIn the above example you could i.e. set the PERMISSIONS argument to false.

    \n

    # Command structure

    \n

    Your command has to follow these structures in order to be successfully created:

    \n
    Command structure\n
    VALID\n\ncommand\n|\n|__ subcommand\n|\n|__ subcommand\n\n----\n\ncommand\n|\n|__ subcommand-group\n    |\n    |__ subcommand\n|\n|__ subcommand-group\n    |\n    |__ subcommand\n\n----\n\nVALID\n\ncommand\n|\n|__ subcommand-group\n    |\n    |__ subcommand\n|\n|__ subcommand\n\n-------\n\nINVALID\n\n\ncommand\n|\n|__ subcommand-group\n    |\n    |__ subcommand-group\n|\n|__ subcommand-group\n    |\n    |__ subcommand-group\n\n----\n\nINVALID\n\ncommand\n|\n|__ subcommand\n    |\n    |__ subcommand-group\n|\n|__ subcommand\n    |\n    |__ subcommand-group\n
    \n

    # ⤵️ Get your commands

    \n

    All global commands:

    \n
    Set<SlashCommand> commands = api.getGlobalSlashCommands().join();\n

    All commands only available on a single server:

    \n
    Server server = ...;\nSet<SlashCommand> commands = api.getServerSlashCommands(server).join();\n

    WARNING

    \n

    Getting all commands from a server only contains the commands you have created on this specific server.\nTherefore, the returned list does not include any global command!

    \n
    \n

    # 🔨 Updating Commands

    \n

    When updating your commands you only have to include what you actually want to change.\nThe following updater will change the previous created command and change its base name from channel to channels.

    \n
    SlashCommand updatedCommand =\n            new SlashCommandUpdater(commandId)\n                .setName(\"channels\")\n                .updateGlobal(api)\n                .join();\n

    # ✍️ Bulk overwriting commands

    \n

    If you have to update / create multiple commands at once it advised to use the batch updater to only have to do 1 request.

    \n
    DiscordApi api = ...;\n\nSet<SlashCommandBuilder> builders = new HashSet<>();\nbuilders.add(new SlashCommandBuilder().setName(\"server\").setDescription(\"A command for the server\"));\nbuilders.add(new SlashCommandBuilder().setName(\"permission\").setDescription(\"A command for permissions\"));\n                                \napi.bulkOverwriteGlobalApplicationCommands(builders).join();\n

    # 👮‍♂️ Permissions

    \n

    Permissions exist to enable / disable the usage of your commands for certain things. These things may be:

    \n
      \n
    • Permissions
    • \n
    • DMs
    • \n
    \n

    When you create a command you can specify which permissions are required to use it.\nIn addition to the required permissions, you can also specify whether the command should be available in DMs.

    \n
    SlashCommand.with(\"ping\",\"Ping!\")\n    .setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR, PermissionType.BAN_MEMBERS)\n    //.setDefaultDisabled() Effectively the same as setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR) but this will lead to the default type by Discord.\n    .setEnabledInDms(false)\n    .createGlobal(api)\n    .join();\n

    INFO

    \n

    Once your bot has been invited to a server, you can not change the permissions afterwards on this server.\nThen it's up to the server administrators / owner to correctly set up the commands for users / roles / channels.

    \n
    \n

    # ❗ Limits

    \n

    # Registering a command

    \n
      \n
    • Server commands are specific to the server you specify when making them. Server commands are not available in DMs. Command names are unique per application within each scope (global and server). That means:
    • \n
    • Your app cannot have two global commands with the same name
    • \n
    • Your app cannot have two server commands within the same name on the same guild
    • \n
    • Your app can have a global and guild command with the same name
    • \n
    • Multiple apps can have commands with the same names
    • \n
    \n

    # General

    \n
      \n
    • An app can have up to 100 top-level global commands with unique names
    • \n
    • An app can have up to an additional 100 server commands per server
    • \n
    • An app can have up to 25 subcommand groups on a top-level command
    • \n
    • An app can have up to 25 subcommands within a subcommand group
    • \n
    • Commands can have up to 25 options
    • \n
    • Options can have up to 25 choices
    • \n
    • Maximum of 4000 characters for combined name, description, and value properties for each command and its subcommands and groups
    • \n
    • Limitations on nesting subcommands and groups
    • \n
    • Global rate limit of 200 slash command creates per day per server
    • \n
    \n","path":"/wiki/basic-tutorials/interactions/commands.html","keywords":["interaction","slash command"]},{"title":"Message Components","headers":[{"level":2,"title":"❔ What are components?","slug":"what-are-components","children":[]},{"level":2,"title":"💡 Sending a message with a component","slug":"sending-a-message-with-a-component","children":[]}],"content":"

    # Message Components

    \n

    # ❔ What are components?

    \n

    Components are interactive elements like buttons or hidden elements like the ActionRow which use is for displaying the visible components. You can add them to a message and interact with users in a very convenient way.\nCurrently, the only interactive components available at the moment are buttons. They differ in style and behaviour(link redirect) seen in the picture below:\n\"\"

    \n

    # 💡 Sending a message with a component

    \n

    Sending a component with your message is a simple as that:

    \n
    TextChannel channel = ...;\n\nnew MessageBuilder()\n    .setContent(\"Click on one of these Buttons!\")\n    .addComponents(\n        ActionRow.of(Button.success(\"success\", \"Send a message\"),\n            Button.danger(\"danger\", \"Delete this message\"),\n            Button.secondary(\"secondary\", \"Remind me after 5 minutes\")))\n    .send(channel);\n

    \"\"

    \n

    You simply add a High Level component like an ActionRow which is a container for displaying your components.\nIn turn the ActionRow consist of the components you can interact with like Buttons.

    \n

    This works for Select Menus as well:

    \n
    TextChannel channel = ...;\n\nnew MessageBuilder()\n    .setContent(\"Select an option of this list!\")\n    .addComponents(\n        ActionRow.of(SelectMenu.create(\"options\", \"Click here to show the options\", 1, 1,\n            Arrays.asList(SelectMenuOption.create(\"Option One\", \"You selected Option One!\", \"Click here to select Option One\"),\n                SelectMenuOption.create(\"Option Two\", \"You selected Option Two!\", \"Click here to select Option Two\"),\n                SelectMenuOption.create(\"Option Three\", \"You selected Option Three!\", \"Click here to select Option Three\")))))\n    .send(channel);\n

    \"\"

    \n

    \"\"

    \n","path":"/wiki/basic-tutorials/interactions/components.html","keywords":["interaction","component","button","actionrow","selectmenus"]},{"title":"Interactions","headers":[{"level":2,"title":"💬 Message Commands","slug":"message-commands","children":[]},{"level":2,"title":"✉️ Interaction Types","slug":"interaction-types","children":[]},{"level":2,"title":"♻️ Lifecycle","slug":"lifecycle","children":[]},{"level":2,"title":"📈 Advantages","slug":"advantages","children":[]},{"level":2,"title":"🤖 Applications vs. Bots","slug":"applications-vs-bots","children":[]},{"level":2,"title":"🔍 See also","slug":"see-also","children":[]}],"content":"

    # Interactions

    \n

    Interactions are a means of accepting user input through Discord. They have been introduced to provide a more standardized,\ncontrolled way for commands than parsing messages. They can even be used with applications that do not provide a bot user.

    \n

    # 💬 Message Commands

    \n

    The "old" way of doing commands was done through parsed text messages, like !ping, !userinfo James or\n!mute James 100s. While such commands are easy in theory, they come with several problems, such as:

    \n
      \n
    • Conflicts between Bots using the same command format / prefix.
    • \n
    • Bots have to be able to read all messages and find those that are directed at them
    • \n
    • Information about command structure can only be provided in info texts and error messages
    • \n
    \n

    \"Message

    \n

    # ✉️ Interaction Types

    \n

    Interactions come in a variety of shapes. The most complex and versatile is the command interaction,\nwhich allows for commands directed at a particular bot with information and assistance on subcommands and parameters\nbeing integrated into the discord client.

    \n

    Context Menu commands\nare available from the context menu in the client either on a message or a server member.

    \n

    Message components come in the flavor of buttons, select menus and other form elements and can be attached directly\nto a message.

    \n

    # ♻️ Lifecycle

    \n

    INFO

    \n

    Creation of interactions is detailed on the pages linked in the previous section.

    \n
    \n

    Unlike chat message commands, interactions and interaction commands need to be registered with Discord. In order for\na bot's interactions to be available in a server, the bot must be added to the server with the applications.commands\nOAUTH scope. The scope is included in links created by DiscordApi#createInviteLink. If your bot is older, it may need to\nbe invited with the new link to add the scope. It is not necessary to remove the bot from the server to do this.

    \n

    \"Interaction

    \n

    # 📈 Advantages

    \n

    While being more complicated to utilize, interactions have many benefits over pure text commands.

    \n
      \n
    • Better Validation: Commands can not be sent with parameters of the wrong type or missing required parameters
    • \n
    • No conflicts: Interactions are separated by bot and only sent to the proper bot
    • \n
    • "Privacy": If no public response is sent by the bot, the exchange is invisible to other chat participants
    • \n
    • Integration: Interactions are integrated into the client's user interface
    • \n
    • Conversations: Message components can be used in replies to interactions, allowing for nested dialogues.
    • \n
    \n

    WARNING

    \n

    If a bot replies to a slash command with a public message, the command used, including all parameters, is visible to\nother users.

    \n
    \n

    # 🤖 Applications vs. Bots

    \n

    Interactions can used by any application, not only bots. While interactions can also be handled through webhooks,\nJavacord only offers support for dealing with them through the gateway. See the\nDiscord Documentation for more information.

    \n

    WARNING

    \n

    The methods of handling interactions can not be mixed. If you register a webhook for your interaction commands, the bot\nwill no longer receive any interaction events.

    \n
    \n

    # 🔍 See also

    \n\n","path":"/wiki/basic-tutorials/interactions/overview.html","keywords":["interaction","slash command","command","context menu","autocomplete"]},{"title":"Responding to interactions","headers":[{"level":2,"title":"💬 Responding immediately after receiving an interaction.","slug":"responding-immediately-after-receiving-an-interaction","children":[]},{"level":2,"title":"💬 Responding after some time when receiving an interaction.","slug":"responding-after-some-time-when-receiving-an-interaction","children":[{"level":3,"title":"Sending followup messages","slug":"sending-followup-messages","children":[]}]},{"level":2,"title":"Responding with a Modal","slug":"responding-with-a-modal","children":[]},{"level":2,"title":"💬 SlashCommand interaction only response methods","slug":"slashcommand-interaction-only-response-methods","children":[{"level":3,"title":"How to know what slash command was invoked?","slug":"how-to-know-what-slash-command-was-invoked","children":[]},{"level":3,"title":"Respond to an AutoComplete interaction triggered from a SlashCommand","slug":"respond-to-an-autocomplete-interaction-triggered-from-a-slashcommand","children":[]}]},{"level":2,"title":"💬 Message Component interaction only response methods","slug":"message-component-interaction-only-response-methods","children":[{"level":3,"title":"A more complete example of how to respond to Component interactions","slug":"a-more-complete-example-of-how-to-respond-to-component-interactions","children":[]}]}],"content":"

    # Responding to interactions

    \n

    There are many ways to respond to interactions and some are only available for certain interactions.\nThe following will be usable for every interaction.

    \n

    # 💬 Responding immediately after receiving an interaction.

    \n
    event.getInteraction()\n        .createImmediateResponder()\n        .setContent(\"YOUR_RESPONSE\")\n        .respond();\n

    INFO

    \n

    Note that you have to respond withing 3 seconds, or the command will fail. If you need longer than 3 seconds you have to\nrespond with respondLater() which allows you to respond within 15 minutes.

    \n

    Because of this time limitation, sending any files when creating an immediate response is not possible.\nIf you want a file to be embedded either use respondLater or include a web link in the message content.\nDepending on the media type of the link and the server configuration, Discord will then display an appropriate embed for the file.

    \n
    \n

    When you want to respond ephemerally, you can use the setFlags method. Your new responder would look like the\nfollowing:

    \n
    event.getInteraction()\n        .createImmediateResponder()\n        .setContent(\"YOUR_RESPONSE\")\n        .setFlags(MessageFlag.EPHEMERAL)\n        .respond();\n

    # 💬 Responding after some time when receiving an interaction.

    \n

    If your computations takes longer than the 3 seconds limit, you can respond later and the Discord Client will show that\nyour bot is thinking until you respond.

    \n
    event.getInteraction()\n        .respondLater()\n        .thenAccept(interactionOriginalResponseUpdater -> {\n            interactionOriginalResponseUpdater.setContent(\"Update message after some time\").update();\n        });\n

    You can respond ephemerally when responding later too. For that you have pass a true boolean to the respondLater method.

    \n
    event.getInteraction()\n        .respondLater(true)\n        .thenAccept(interactionOriginalResponseUpdater -> {\n            interactionOriginalResponseUpdater.setContent(\"Update message after some time\").update();\n        });\n

    # Sending followup messages

    \n

    Followup messages can be sent within 15 minutes after the command has been invoked. You can send as many followup\nmessages as you want.

    \n
    api.addSlashCommandCreateListener(event -> {\n    SlashCommandInteraction slashCommandInteraction = event.getSlashCommandInteraction();\n    slashCommandInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {\n        interactionOriginalResponseUpdater.setContent(\"You will receive the answer in a few minutes!\").update();\n\n        // time < 15 minutes\n        \n        slashCommandInteraction.createFollowupMessageBuilder()\n                .setContent(\"Thank you for your patience, it took a while but the answer to the universe is 42\")\n                .send();\n    });\n});\n

    # Responding with a Modal

    \n

    A modal is a popup dialog which can be shown when responding to an interaction. It focuses the users to explicitly fill out this form to continue with the workflow.\nCurrently, only the TextInput (SelectMenu has been seen working too, but is not yet officially supported) is supported.

    \n
    api.addMessageComponentCreateListener(event -> {\n    event.getInteraction().respondWithModal(\"modalId\",\"Modal Title\",\n        ActionRow.of(TextInput.create(TextInputStyle.SHORT, \"text_input_id\", \"This is a Text Input Field\")));\n});\n

    Which results in

    \n

    \"Modal\"

    \n

    # 💬 SlashCommand interaction only response methods

    \n

    # How to know what slash command was invoked?

    \n

    For example, you have created a slash command with the name "settings" and a subcommand "color". If you want to check if\nexactly this command has been used, you can check it as follows:

    \n
    api.addSlashCommandCreateListener(event -> {\n    SlashCommandInteraction interaction = event.getSlashCommandInteraction();\n    if (interaction.getFullCommandName().equals(\"settings color\")) {\n        //Code if command matches the full name\n    }\n});\n

    # Respond to an AutoComplete interaction triggered from a SlashCommand

    \n
    api.addAutocompleteCreateListener(event -> {\n    event.getAutocompleteInteraction()\n    .respondWithChoices(Arrays.asList(\n        SlashCommandOptionChoice.create(\"one\", 1),\n            SlashCommandOptionChoice.create(\"two\", 2))\n    );\n});\n

    # 💬 Message Component interaction only response methods

    \n

    When dealing with message components, you don't necessarily have to respond or update a message.\nYou can simply acknowledge the interaction and let the user know that the task is done.

    \n
    api.addMessageComponentCreateListener(event -> {\n    event.getMessageComponentInteraction().acknowledge();\n});\n

    # A more complete example of how to respond to Component interactions

    \n

    The following code snipped shows how you can respond to the example created in Components.

    \n
    api.addMessageComponentCreateListener(event -> {\n    MessageComponentInteraction messageComponentInteraction = event.getMessageComponentInteraction();\n    String customId = messageComponentInteraction.getCustomId();\n\n    switch (customId) {\n        case \"success\":\n            messageComponentInteraction.createImmediateResponder()\n                    .setContent(\"You clicked a button!\")\n                    .respond();\n            break;\n        case \"danger\":\n            messageComponentInteraction.getMessage().ifPresent(Message::delete);\n            break;\n        case \"secondary\":\n            messageComponentInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {\n                //Code to respond after 5 minutes\n            });\n            break;\n        case \"options\":\n            messageComponentInteraction.createImmediateResponder()\n\t\t\t\t\t.setContent(\"You selected an option in a select menu!\")\n\t\t\t\t\t.respond();\n            break;\n    }\n});\n
    ","path":"/wiki/basic-tutorials/interactions/responding.html","keywords":["interaction responding","modal","autocomplete"]},{"title":"Eclipse + Maven","headers":[{"level":2,"title":"🔧 Setup","slug":"setup","children":[]},{"level":2,"title":"🏃‍♀️ Run the code","slug":"run-the-code","children":[]}],"content":"

    # Eclipse + Maven

    \n\n \n\nThis tutorial provides a beginner-friendly click by click guide to set up Javacord with Eclipse and Maven.\nIf you are already familiar with Eclipse and Maven, you can just see the artifact locations at [Download / Installation](/wiki/getting-started/download-installation.md).\n

    Info

    \n

    We recommend to use Intellij + Gradle unless you already have experience with one of the other IDEs or build managers.

    \n
    \n

    # 🔧 Setup

    \n

    # 1. Start Eclipse

    \n

    # 2. Create a new project (File -> New -> Project)

    \n

    \"\"

    \n

    # 3. Select Maven Project

    \n

    # 4. Click Next

    \n

    \"\"

    \n

    # 5. Check Create a simple project

    \n

    # 6. Click Next

    \n

    \"\"

    \n

    # 7. Enter a group id (e.g. com.github.yourname)

    \n

    # 8. Enter an artifact id (e.g. myfirstbot)

    \n

    # 9. Click Finish

    \n

    \"\"

    \n

    # 10. Double click on the pom.xml file

    \n

    \"\"

    \n

    # 11. Select pom.xml

    \n

    \"\"

    \n

    # 12. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

    \n
    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>your.package.name</groupId>\n    <artifactId>myfirstbot</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.javacord</groupId>\n            <artifactId>javacord</artifactId>\n            <version>$latest-version</version>\n            <type>pom</type>\n        </dependency>\n    </dependencies>\n\n</project>\n

    # 13. Create a new package inside the src/main/java folder

    \n

    \"\"\n\"\"

    \n

    # 14. Create a new class inside this package

    \n

    \"\"\n\"\"

    \n

    # 15. Save the project (you should do this from time to time)

    \n

    \"\"

    \n

    # 16. Now you can start coding! Example code:

    \n
    package com.github.yourname.myfirstbot;\n\nimport org.javacord.api.DiscordApi;\nimport org.javacord.api.DiscordApiBuilder;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        // Insert your bot's token here\n        String token = \"your token\";\n\n        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();\n\n        // Add a listener which answers with \"Pong!\" if someone writes \"!ping\"\n        api.addMessageCreateListener(event -> {\n            if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n                event.getChannel().sendMessage(\"Pong!\");\n            }\n        });\n\n        // Print the invite url of your bot\n        System.out.println(\"You can invite the bot by using the following url: \" + api.createBotInvite());\n    }\n\n}\n

    # 🏃‍♀️ Run the code

    \n

    You can run your code by clicking on the small green arrow\n\"\"

    \n","path":"/wiki/getting-started/setup/eclipse-maven.html","keywords":[]},{"title":"IntelliJ + Gradle","headers":[{"level":2,"title":"🔧 Setup","slug":"setup","children":[]},{"level":2,"title":"🏃‍♀️ Run the code","slug":"run-the-code","children":[]}],"content":"

    # IntelliJ + Gradle

    \n\n \n\n

    This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Gradle.\nIf you are already familiar with IntelliJ and Gradle, you can just see the artifact locations at Download / Installation.

    \n

    # 🔧 Setup

    \n

    # 1. Start IntelliJ

    \n

    # 2. Create a new project (File -> New -> Project)

    \n

    \"\"

    \n

    # 3. Select Gradle

    \n

    # 4. Make sure to select an SDK which is 1.8 (or greater)

    \n

    # 5. Click Next

    \n

    \"\"

    \n

    # 6. Enter a group id (e.g. com.github.yourname)

    \n

    You can choose whatever you want

    \n

    # 7. Enter an artifact id (e.g. myfirstbot)

    \n

    You can choose whatever you want

    \n

    # 8. Click Next

    \n

    \"\"

    \n

    # 9. Check Use auto-import

    \n

    # 10. Click Next

    \n

    \"\"

    \n

    # 11. Click Finish

    \n

    \"\"

    \n

    # 12. Locate the build.gradle file and open it

    \n

    \"\"

    \n

    # 12. Add the Javacord dependency. Your build.gradle file should now look like this

    \n
    plugins {\n    id 'java'\n}\n\ngroup 'com.github.yourname'\nversion '1.0-SNAPSHOT'\n\nsourceCompatibility = 1.8\n\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation 'org.javacord:javacord:$latest-version'\n}\n

    # 13. Create a new package in the src/main/java folder

    \n

    \"\"\n\"\"

    \n

    # 14. Create a new class inside this package

    \n

    \"\"\n\"\"

    \n

    # 15. You can now start coding!

    \n

    Example code:

    \n
    package com.github.yourname;\n\nimport org.javacord.api.DiscordApi;\nimport org.javacord.api.DiscordApiBuilder;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        // Insert your bot's token here\n        String token = \"your token\";\n\n        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();\n\n        // Add a listener which answers with \"Pong!\" if someone writes \"!ping\"\n        api.addMessageCreateListener(event -> {\n            if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n                event.getChannel().sendMessage(\"Pong!\");\n            }\n        });\n\n        // Print the invite url of your bot\n        System.out.println(\"You can invite the bot by using the following url: \" + api.createBotInvite());\n    }\n\n}\n

    # 🏃‍♀️ Run the code

    \n

    You can run your code by clicking on the small green arrow\n\"\"

    \n","path":"/wiki/getting-started/setup/intellij-gradle.html","keywords":[]},{"title":"IntelliJ + Maven","headers":[{"level":2,"title":"🔧 Setup","slug":"setup","children":[]},{"level":2,"title":"🏃‍♀️ Run the code","slug":"run-the-code","children":[]},{"level":2,"title":"🚧 Possible problems","slug":"possible-problems","children":[]}],"content":"

    # IntelliJ + Maven

    \n\n \n\n

    This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Maven.\nIf you are already familiar with IntelliJ and Maven, you can just see the artifact locations at Download / Installation.

    \n

    Info

    \n

    We recommend to use Intellij + Gradle unless you already have experience with one of the other IDEs or build managers.

    \n
    \n

    # 🔧 Setup

    \n

    # 1. Start IntelliJ

    \n

    # 2. Create a new project (File -> New -> Project)

    \n

    \"\"

    \n

    # 3. Select Maven

    \n

    # 4. Make sure to select an SDK which is 1.8 (or greater)

    \n

    # 5.* Click Next

    \n

    \"\"

    \n

    # 6. Enter a group id (e.g. com.github.yourname)

    \n

    # 7. Enter an artifact id (e.g. myfirstbot)

    \n

    # 8. Click Next

    \n

    \"\"

    \n

    # 9. Click on Finish

    \n

    \"\"

    \n

    # 10. Your project should now look like this. First click on Enable Auto-Import

    \n

    \"\"

    \n

    # 11. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

    \n
    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>your.package.name</groupId>\n    <artifactId>myfirstbot</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.javacord</groupId>\n            <artifactId>javacord</artifactId>\n            <version>$latest-version</version>\n            <type>pom</type>\n        </dependency>\n    </dependencies>\n\n</project>\n

    # 12. Create a new package

    \n

    \"\"\n\"\"

    \n

    # 13. Create a new class inside this package

    \n

    \"\"\n\"\"

    \n

    # 14. You can now start coding! Example code:

    \n
    package com.github.yourname;\n\nimport org.javacord.api.DiscordApi;\nimport org.javacord.api.DiscordApiBuilder;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        // Insert your bot's token here\n        String token = \"your token\";\n\n        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();\n\n        // Add a listener which answers with \"Pong!\" if someone writes \"!ping\"\n        api.addMessageCreateListener(event -> {\n            if (event.getMessageContent().equalsIgnoreCase(\"!ping\")) {\n                event.getChannel().sendMessage(\"Pong!\");\n            }\n        });\n\n        // Print the invite url of your bot\n        System.out.println(\"You can invite the bot by using the following url: \" + api.createBotInvite());\n    }\n    \n}\n

    # 🏃‍♀️ Run the code

    \n

    You can run your code by clicking on the small green arrow\n\"\"

    \n

    # 🚧 Possible problems

    \n

    Note: If you get the following error:\n\"\"

    \n

    you have to change your language level to 1.8

    \n

    \"\"

    \n","path":"/wiki/getting-started/setup/intellij-maven.html","keywords":[]}] \ No newline at end of file diff --git a/favicon-96x96.png b/favicon-96x96.png new file mode 100644 index 0000000..71be633 Binary files /dev/null and b/favicon-96x96.png differ diff --git a/im.html b/im.html new file mode 100644 index 0000000..55c789a --- /dev/null +++ b/im.html @@ -0,0 +1,32 @@ + + +
    +

    Legal Disclosure

    + Information in accordance with section 5 TMG
    +
    + nnamreppO naitsaB
    + 12 .rtS-hcasieR-noV
    + htoR 45119
    +
    +

    Contact

    + Telephone: 3426112 8751 94+
    + E-Mail: gro [tod] drocavaj [ta] tcatnoc
    +
    +

    Disclaimer

    +

    Accountability for content

    + The contents of our pages have been created with the utmost care. However, we cannot guarantee the contents' accuracy, completeness or topicality. According to statutory provisions, we are furthermore responsible for our own content on these web pages. In this context, please note that we are accordingly not obliged to monitor merely the transmitted or saved information of third parties, or investigate circumstances pointing to illegal activity. Our obligations to remove or block the use of information under generally applicable laws remain unaffected by this as per §§ 8 to 10 of the Telemedia Act (TMG).
    +
    +

    Accountability for links

    + Responsibility for the content of external links (to web pages of third parties) lies solely with the operators of the linked pages. No violations were evident to us at the time of linking. Should any legal infringement become known to us, we will remove the respective link immediately.
    +
    +

    Copyright

    + Our web pages and their contents are subject to German copyright law. Unless expressly permitted by law (§ 44a et seq. of the copyright law), every form of utilizing, reproducing or processing works subject to copyright protection on our web pages requires the prior consent of the respective owner of the rights. Individual reproductions of a work are allowed only for private use, so must not serve either directly or indirectly for earnings. Unauthorized utilization of copyrighted works is punishable (§ 106 of the copyright law).
    +
    + Source: http://www.muster-vorlagen.net/
    + +
    \ No newline at end of file diff --git a/img/javacord-readme/message-builder.png b/img/javacord-readme/message-builder.png new file mode 100644 index 0000000..5413e29 Binary files /dev/null and b/img/javacord-readme/message-builder.png differ diff --git a/img/javacord-readme/ping-pong-white.gif b/img/javacord-readme/ping-pong-white.gif new file mode 100644 index 0000000..41e5530 Binary files /dev/null and b/img/javacord-readme/ping-pong-white.gif differ diff --git a/img/javacord-readme/sensitive-bot-round.gif b/img/javacord-readme/sensitive-bot-round.gif new file mode 100644 index 0000000..2f20001 Binary files /dev/null and b/img/javacord-readme/sensitive-bot-round.gif differ diff --git a/img/javacord3_banner.png b/img/javacord3_banner.png new file mode 100644 index 0000000..77db409 Binary files /dev/null and b/img/javacord3_banner.png differ diff --git a/imprint.html b/imprint.html new file mode 100644 index 0000000..51e7877 --- /dev/null +++ b/imprint.html @@ -0,0 +1,33 @@ + + + + + + + + + Imprint | Javacord + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..7515968 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + + + + + + + Javacord + + + + +
    hero

    An easy to use multithreaded library for creating Discord bots in Java.

    View Wiki →

    💡 About Javacord

    Javacord is a modern library that focuses on simplicity and speed 🚀. By reducing itself to standard Java classes and features like Optionals and CompletableFutures, it is extremely easy to use for every Java developer, as it does not require you to learn any new frameworks or complex abstractions. It has rich documentation and an awesome community 💪 on Discord that loves to help with any specific problems and questions.

    👩‍🏫 Learn more

    This website is the home to our wiki for everyone who want to learn Javacord. If you haven't already, you should first take a look at our GitHub pageopen in new window before you continue with the wiki.

    🤝 Discord Server

    Did you know that Javacord has a Discord server? No? Well, now you do! 😉

    Javacord's Discord community is an excellent resource if you have questions about the library. You can join it by clicking 👉 hereopen in new window 👈.

    + + + diff --git a/pp.html b/pp.html new file mode 100644 index 0000000..e7c9dda --- /dev/null +++ b/pp.html @@ -0,0 +1,122 @@ + + +
    + +

    Effective date: October 27, 2018

    + + +

    Javacord ("us", "we", or "our") operates the https://javacord.org website (the "Service").

    + +

    This page informs you of our policies regarding the collection, use, and disclosure of personal data when you use our Service and the choices you have associated with that data. Our Privacy Policy for Javacord is managed through Free Privacy Policy.

    + +

    We use your data to provide and improve the Service. By using the Service, you agree to the collection and use of information in accordance with this policy. Unless otherwise defined in this Privacy Policy, terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, accessible from https://javacord.org

    + + +

    Information Collection And Use

    + +

    We collect several different types of information for various purposes to provide and improve our Service to you.

    + +

    Types of Data Collected

    + +

    Personal Data

    + +

    While using our Service, we may ask you to provide us with certain personally identifiable information that can be used to contact or identify you ("Personal Data"). Personally identifiable information may include, but is not limited to:

    + +
      +
    • Cookies and Usage Data
    • +
    + +

    Usage Data

    + +

    We may also collect information how the Service is accessed and used ("Usage Data"). This Usage Data may include information such as your computer's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that you visit, the time and date of your visit, the time spent on those pages, unique device identifiers and other diagnostic data.

    + +

    Tracking & Cookies Data

    +

    We use cookies and similar tracking technologies to track the activity on our Service and hold certain information.

    +

    Cookies are files with small amount of data which may include an anonymous unique identifier. Cookies are sent to your browser from a website and stored on your device. Tracking technologies also used are beacons, tags, and scripts to collect and track information and to improve and analyze our Service.

    +

    You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. However, if you do not accept cookies, you may not be able to use some portions of our Service.

    +

    Examples of Cookies we use:

    +
      +
    • Session Cookies. We use Session Cookies to operate our Service.
    • +
    • Preference Cookies. We use Preference Cookies to remember your preferences and various settings.
    • +
    • Security Cookies. We use Security Cookies for security purposes.
    • +
    + +

    Use of Data

    + +

    We use the collected data for various purposes:

    +
      +
    • To provide and maintain the Service
    • +
    • To notify you about changes to our Service
    • +
    • To allow you to participate in interactive features of our Service when you choose to do so
    • +
    • To provide customer care and support
    • +
    • To provide analysis or valuable information so that we can improve the Service
    • +
    • To monitor the usage of the Service
    • +
    • To detect, prevent and address technical issues
    • +
    + +

    Transfer Of Data

    +

    Your information, including Personal Data, may be transferred to — and maintained on — computers located outside of your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from your jurisdiction.

    +

    If you are located outside Germany and choose to provide information to us, please note that we transfer the data, including Personal Data, to Germany and process it there.

    +

    Your consent to this Privacy Policy followed by your submission of such information represents your agreement to that transfer.

    +

    We will take all steps reasonably necessary to ensure that your data is treated securely and in accordance with this Privacy Policy and no transfer of your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of your data and other personal information.

    + +

    Disclosure Of Data

    + +

    Legal Requirements

    +

    We may disclose your Personal Data in the good faith belief that such action is necessary to:

    +
      +
    • To comply with a legal obligation
    • +
    • To protect and defend the rights or property of us
    • +
    • To prevent or investigate possible wrongdoing in connection with the Service
    • +
    • To protect the personal safety of users of the Service or the public
    • +
    • To protect against legal liability
    • +
    + +

    Security Of Data

    +

    The security of your data is important to us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While we strive to use commercially acceptable means to protect your Personal Data, we cannot guarantee its absolute security.

    + +

    Service Providers

    +

    We may employ third party companies and individuals to facilitate our Service ("Service Providers"), to provide the Service on our behalf, to perform Service-related services or to assist us in analyzing how our Service is used.

    +

    These third parties have access to your Personal Data only to perform these tasks on our behalf and are obligated not to disclose or use it for any other purpose.

    + +

    Analytics

    +

    We may use third-party Service Providers to monitor and analyze the use of our Service.

    +
      +
    • +

      Google Analytics

      +

      Google Analytics is a web analytics service offered by Google that tracks and reports website traffic. Google uses the data collected to track and monitor the use of our Service. This data is shared with other Google services. Google may use the collected data to contextualize and personalize the ads of its own advertising network.

      +

      You can opt-out of having made your activity on the Service available to Google Analytics by installing the Google Analytics opt-out browser add-on. The add-on prevents the Google Analytics JavaScript (ga.js, analytics.js, and dc.js) from sharing information with Google Analytics about visits activity.

      For more information on the privacy practices of Google, please visit the Google Privacy & Terms web page: https://policies.google.com/privacy?hl=en

      +
    • +
    + + +

    Links To Other Sites

    +

    Our Service may contain links to other sites that are not operated by us. If you click on a third party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit.

    +

    We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.

    + + +

    Children's Privacy

    +

    Our Service does not address anyone under the age of 18 ("Children").

    +

    We do not knowingly collect personally identifiable information from anyone under the age of 18. If you are a parent or guardian and you are aware that your Children has provided us with Personal Data, please contact us. If we become aware that we have collected Personal Data from children without verification of parental consent, we take steps to remove that information from our servers.

    + + +

    Changes To This Privacy Policy

    +

    We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page.

    +

    We will let you know via email and/or a prominent notice on our Service, prior to the change becoming effective and update the "effective date" at the top of this Privacy Policy.

    +

    You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.

    + + +

    Contact Us

    +

    If you have any questions about this Privacy Policy, please contact us:

    +
      +
    • By email: gro [tod] drocavaj [ta] tcatnoc
    • +
    + +
    + +
    \ No newline at end of file diff --git a/privacy-policy.html b/privacy-policy.html new file mode 100644 index 0000000..500c332 --- /dev/null +++ b/privacy-policy.html @@ -0,0 +1,33 @@ + + + + + + + + + Privacy Policy | Javacord + + + + + + + + diff --git a/wiki/advanced-topics/bot-lifecycle.html b/wiki/advanced-topics/bot-lifecycle.html new file mode 100644 index 0000000..8ae4606 --- /dev/null +++ b/wiki/advanced-topics/bot-lifecycle.html @@ -0,0 +1,39 @@ + + + + + + + + + Bot Lifecycle | Javacord + + + + +

    Bot Lifecycle

    It's important to know the life-cycle of a discord bot to properly handle disconnects. The following state diagram shows the 4 states a bot can have:

    💡 The four states

    Connected

    The bot is connected to the websocket and receives all events.

    Disconnected

    The bot is not connected to the websocket and receives no events. It's not uncommon for a bot to occasionally lose connection. This can have various reasons, for example:

    • Your bot lost its internet connection
    • Discord restarted the gateway server you are currently connected to
    • A plane crashed into Discord's data center

    The bot will periodically try to resume/reconnect to the websocket. It will start with a small frequency and increase it with every failed reconnect attempt. You can modify the reconnect delay with the DiscordApi#setReconnectDelay(...) method. The following example code would increase the delay linearly. The 1st attempt would be delayed for 2 seconds, the 2nd attempt for 4 seconds, the 3rd attempts for 6 seconds, ...

    api.setReconnectDelay(attempt -> attempt * 2);
    +

    Important: Bots can only reconnect 1000 times in a 24-hour period (every ~90 seconds). This limit is global and across all shards. Upon hitting this limit, all active sessions for the bot will be terminated, the bot's token will be reset, and you will receive an email notification. This is the reason Javacord increases the reconnect delay with every attempt.

    By default, the $default_delay$ formula below is used to calculate the reconnect delay

    $$ default_delay(a) = \lfloor a^{1.5} - \frac{a^{1.5}}{\frac{1}{(0.1 \cdot a)} + 1} \rceil $$

    with $a$ being the attempt.

    The formula will generate the following reconnect delay:

    AttemptDelay
    11
    22
    34
    46
    57
    ......
    1016
    1523
    2030
    ......
    5059
    10091
    150115
    ......

    Resuming

    Resuming is only possible for a short time after being disconnected. If the bot can successfully resume the connection, you will not miss any events. Your bot will receive all events you missed while being disconnected. The cache gets updated accordingly.

    Reconnecting

    If your bot reconnects (not resumes!), the whole cache gets wiped, and you will not receive any missed events.

    What does this mean?

    • References to entities (e.g. a Server, User, Channel, ...) will be outdated. This is why you should never store entities, but the id instead. See Entity Cache.
    • You will miss events. There's no way to receive the missed events.
    • Listeners attached to entities will not be affected, because they are bound to the entity's id, not the object itself.

    💊 How to handle disconnects

    For most bots, there's nothing you have to do. All registered listeners are reconnect-resistant, which means if your bot is only reacting to events, it will work fine after a restart. For example, the following code will not be affected by a reconnect (besides maybe some missed !ping messages):

    api.addMessageCreateListener(event -> {
    +    if (event.getMessage().getContent().equalsIgnoreCase("!ping")) {
    +        event.getChannel().sendMessage("Pong!");
    +    }
    +});
    +

    In case you want to handle reconnects (e.g. fetch the message history to detect missed messages), there are special connection-related listeners which can be used to track the state of the bot:

    • LostConnectionListener
    • ReconnectListener
    • ResumeListener
    + + + diff --git a/wiki/advanced-topics/entity-cache.html b/wiki/advanced-topics/entity-cache.html new file mode 100644 index 0000000..e787f33 --- /dev/null +++ b/wiki/advanced-topics/entity-cache.html @@ -0,0 +1,69 @@ + + + + + + + + + Entity Cache | Javacord + + + + +

    Entity Cache

    Javacord keeps an internal cache for entities (e.g. Servers, Channels, Users, ...). It is important to know how the cache behaves to properly use it.

    🔮 What is in the cache?

    Nearly every entity known by the bot is guaranteed to be in the cache. There are a few exceptions though:

    Users

    Users are only cached when you have the GUILD_MEMBERS intent enabled. See Gateway Intents for more information.

    Messages

    Not every single message is in the cache, which means you can encounter messages which exist but are not in the cache. This can happen for most message events, e.g. the ReactionAddEventopen in new window. You can, however, interact with these messages without having them in the cache. Every message event has methods like event.deleteMessage(), event.editMessage("New Content"). If you need the message (e.g. to get its content), you can request it using event.requestMessage().

    Additionally, you can use the static methods in the Messageopen in new window class which only require the channel and message id, e.g. Message.edit(api, channelId, messageId, "New content");. This is very useful if you want to store them in a database.

    Webhooks and Invites

    Webhooks and Invites are not kept in the cache at all and won't receive any updates.

    Embeds

    Embeds from message.getEmbed() won't receive updates. If a message's embed gets edited, getEmbed() will return a completely new embed object.

    ❓ When are cached entities updated?

    Javacord's cache exclusively uses websocket events to keep the cache up to date. This means that the content of your objects might be outdated, even though you modified it yourself:

    Messages message = ...;
    +System.out.println(message.getContent()); // Prints the old content, e.g. "old content"
    +message.edit("new content").join(); // Edits the message and waits for success
    +System.out.println(message.getContent()); // Still prints "old content"
    +Thread.sleep(1000);
    +System.out.println(message.getContent()); // Most likely prints "new content" now
    +

    ⌚ How long are cached entities valid?

    Even though entities are usually kept in the cache for a very long time, you should not keep references to these objects for a longer period of time, but store the id / use event methods:

    // Bad
    +Message message = ...;
    +message.addReactionAddListener(event -> {
    +  if (event.getEmoji().equalsEmoji("👎")) {
    +    message.delete(); // Prevents "message" from being garbage collected
    +  }
    +});
    +
    +// Good
    +Message message = ...;
    +message.addReactionAddListener(event -> {
    +  if (event.getEmoji().equalsEmoji("👎")) {
    +    event.deleteMessage(); // Does not use the message object
    +  }
    +});
    +
    // Bad
    +Set<User> usersWithBadMood = new HashSet<>();
    +api.addReactionAddListener(event -> {
    +  if (event.getEmoji().equalsEmoji("😦")) {
    +    usersWithBadMood.add(event.getUser());
    +  }
    +});
    +
    +// Good
    +Set<Long> usersWithBadMood = new HashSet<>();
    +api.addReactionAddListener(event -> {
    +  if (event.getEmoji().equalsEmoji("😦")) {
    +    usersWithBadMood.add(event.getUser().getId());
    +  }
    +});
    +

    Some examples of when cached entities are invalidated:

    • The bot lost its connection to Discord and had to reconnect (not resume)
    • You weren't able to receive updates for an entity, e.g. for Channel, because you left and rejoined a server
    + + + diff --git a/wiki/advanced-topics/performance-tweaks.html b/wiki/advanced-topics/performance-tweaks.html new file mode 100644 index 0000000..9e0a9d9 --- /dev/null +++ b/wiki/advanced-topics/performance-tweaks.html @@ -0,0 +1,57 @@ + + + + + + + + + Performance Tweaks | Javacord + + + + +

    Performance Tweaks

    ✂️ Disabling Startup Wait

    By default, Javacord waits for all servers and members to be loaded on startup. You can disable this behavior in the DiscordApiBuilder before logging in:

    new DiscordApiBuilder()
    +    .setToken("abc")
    +    .setWaitForServersOnStartup(false)
    +    .login()
    +    .thenAccept(api -> {
    +        // Do something
    +    }).exceptionally(ExceptionLogger.get());
    +

    Depending on the size of your bot, this can significantly speed up the login process. This comes with one downside however: The api.getServers() collection is empty directly after logging in. You will receive ServerBecomesAvailableEvents for every server which finished loading.

    ⚙️ Fine Tuning the Message Cache

    In order to reduce memory usage, you can completely disable the message cache or reduce the number of cached messages. By default, Javacord caches up to 50 messages per channel and removes messages from the cache which are older than 12 hours. You can lower this limit by using DiscordApi#setMessageCacheSize(Capacity, StorageTimeInSeconds).

    // Cache a maximum of 10 messages per channel for and remove messages older than 1 hour
    +api.setMessageCacheSize(10, 60*60);
    +

    You can even set this limit on a per-channel basis:

    TextChannel channel = ...;
    +channel.getMessageCache().setCapacity(10);
    +channel.getMessageCache().setStorageTimeInSeconds(60*60);
    +

    💎 Using the Updater classes

    If you update several settings of an entity (server, channel, ...) at once, you should use the updater for this entity instead of the updateXyz(...) methods.

    Example

    // Sends 1 request to Discord
    +ServerTextChannel channel = ...;
    +new ServerTextChannelUpdater(channel)
    +    .setName("example-channel")
    +    .setTopic("This is an example channel")
    +    .setNsfwFlag(true)
    +    .update();
    +

    instead of

    // Sends 3 requests to Discord
    +ServerTextChannel channel = ...;
    +channel.updateName("example-channel");
    +channel.updateTopic("This is an example channel");
    +channel.updateNsfwFlag(true);
    +
    + + + diff --git a/wiki/advanced-topics/playing-audio.html b/wiki/advanced-topics/playing-audio.html new file mode 100644 index 0000000..9c0dac1 --- /dev/null +++ b/wiki/advanced-topics/playing-audio.html @@ -0,0 +1,114 @@ + + + + + + + + + Playing Audio | Javacord + + + + +

    Playing Audio

    WARNING

    Support for audio was added to Javacord very recently. If you encounter any bugs, please create an issue on GitHubopen in new window!

    Javacord allows your bot to connect to voice channels and play audio (e.g., music). This short tutorial gives you an introduction on how to connect to a voice channel and play your favorite musicopen in new window.

    🔌 Connect to a voice channel

    Connecting to a voice channel is very straight forward: Calling #connect() on an instance of ServerVoiceChannel will connect your bot to this voice channel and return a future with an AudioConnection object.

    Example

    The following example will connect the bot to the voice channel of the user that typed !music in the chat:

    ServerVoiceChannel channel = ...;
    +channel.connect().thenAccept(audioConnection -> {
    +    // Do stuff
    +}).exceptionally(e -> {
    +    // Failed to connect to voice channel (no permissions?)
    +    e.printStackTrace();
    +    return null;
    +});
    +

    👂 Playing music

    There are plenty of sources for audio (e.g., YouTube, local files, etc.). The current de facto standard library for extracting audio from these sources with Java is the LavaPlayeropen in new window library.

    To use it with Javacord, you have to add it as a dependency to your project (e.g., with Gradle or Maven) and create a Javacord audio source like this:

    public class LavaplayerAudioSource extends AudioSourceBase {
    +
    +    private final AudioPlayer audioPlayer;
    +    private AudioFrame lastFrame;
    +
    +    /**
    +     * Creates a new lavaplayer audio source.
    +     *
    +     * @param api A discord api instance.
    +     * @param audioPlayer An audio player from Lavaplayer.
    +     */
    +    public LavaplayerAudioSource(DiscordApi api, AudioPlayer audioPlayer) {
    +        super(api);
    +        this.audioPlayer = audioPlayer;
    +    }
    +
    +    @Override
    +    public byte[] getNextFrame() {
    +        if (lastFrame == null) {
    +            return null;
    +        }
    +        return applyTransformers(lastFrame.getData());
    +    }
    +
    +    @Override
    +    public boolean hasFinished() {
    +        return false;
    +    }
    +
    +    @Override
    +    public boolean hasNextFrame() {
    +        lastFrame = audioPlayer.provide();
    +        return lastFrame != null;
    +    }
    +
    +    @Override
    +    public AudioSource copy() {
    +        return new LavaplayerAudioSource(getApi(), audioPlayer);
    +    }
    +}
    +

    With this audio source, you can now start using Lavaplayer, e.g. to play a YouTube video:

    // Create a player manager
    +AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
    +playerManager.registerSourceManager(new YoutubeAudioSourceManager());
    +AudioPlayer player = playerManager.createPlayer();
    +
    +// Create an audio source and add it to the audio connection's queue
    +AudioSource source = new LavaplayerAudioSource(api, player);
    +audioConnection.setAudioSource(source);
    +
    +// You can now use the AudioPlayer like you would normally do with Lavaplayer, e.g.,
    +playerManager.loadItem("https://www.youtube.com/watch?v=NvS351QKFV4", new AudioLoadResultHandler() {
    +    @Override
    +    public void trackLoaded(AudioTrack track) {
    +        player.playTrack(track);
    +    }
    +
    +    @Override
    +    public void playlistLoaded(AudioPlaylist playlist) {
    +        for (AudioTrack track : playlist.getTracks()) {
    +            player.playTrack(track);
    +        }
    +    }
    +
    +    @Override
    +    public void noMatches() {
    +        // Notify the user that we've got nothing
    +    }
    +
    +    @Override
    +    public void loadFailed(FriendlyException throwable) {
    +        // Notify the user that everything exploded
    +    }
    +});
    +
    + + + diff --git a/wiki/advanced-topics/proxies.html b/wiki/advanced-topics/proxies.html new file mode 100644 index 0000000..1c6f480 --- /dev/null +++ b/wiki/advanced-topics/proxies.html @@ -0,0 +1,33 @@ + + + + + + + + + Proxies | Javacord + + + + +

    Proxies

    There are basically two kinds of proxies: HTTP proxies and SOCKS proxies. Both may or may not support or require authentication depending on version, capabilities, and configuration. Due to the underlying libraries used, currently, Javacord fully supports HTTP proxies and partially supports SOCKS proxies.

    Javacord uses HTTPS connections to communicate with the Discord REST API and a WSS connection to communicate with the Discord WebSocket endpoint. Both these protocols are secure protocols and thus do not honor settings for HTTP connections, only settings for HTTPS connections.

    👨‍💻 Configuring a Proxy ...

    ... using System Properties

    If you did not explicitly set a proxy in the DiscordApiBuilder and did not set a system default ProxySelector, the default proxy selector of the JRE is used. This proxy selector honors, amongst others, the relevant standard system properties https.proxyHost, https.proxyPort, socksProxyHost, socksProxyPort, and socksProxyVersion. Use the former two to configure an HTTP proxy, or the latter three to configure a SOCKS proxy, although you will not need socksProxyVersion, as SOCKS4 is currently not supported.

    ... using a System Default Proxy Selector

    You can use java.net.ProxySelector.setDefault(ProxySelector) to set a system default proxy selector that replaces the default one. In its implementation, you can dynamically determine which proxy to use for each connection.

    ... using an Explicitly Set Proxy

    Using the method DiscordApiBuilder.setProxy(Proxy) you can set a proxy instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the unrelated code running in the JVM.

    ... using an Explicitly Set Proxy Selector

    Using the method DiscordApiBuilder.setProxySelector(ProxySelector) you can set a proxy selector instance directly in the DiscordApiBuilder that is solely used for Javacord connections and does not affect the remaining code running in the JVM. In its implementation, you can dynamically determine which proxy to use for each connection.

    Precedence of the Configuration Options

    • if an explicit proxy is set, it is used
    • if an explicit proxy selector is set, it is used
    • if both an explicit proxy and an explicit proxy selector are set, this is a configuration error and will cause an exception to be thrown
    • if neither explicit option is set, the system default proxy selector is used
    • if no system default proxy selector was explicitly set, the JRE default that honors the system properties is used

    🔑 Configuring Proxy Authentication ...

    ... using a System Default Authenticator

    You can use java.net.Authenticator.setDefault(Authenticator) to set a system default authenticator that is used to provide username and password pairs for connections. This authenticator is only used if the proxy supports the Basic authentication scheme. If you need to support any other authentication scheme, use an explicitly configured authenticator. The java.net.Authenticator interface is too inflexible to support this.

    ... using an Explicitly Set Authenticator

    Using the method DiscordApiBuilder.setProxyAuthenticator(Authenticator), you can set a custom authenticator that is much more powerful than the java.net.Authenticator. You get much more information about the connection to be established, and you can return any HTTP header that is necessary for a successful authentication. This should cover all sorts of available authentication mechanisms.

    💡 Proxy Types

    HTTP

    HTTP proxies are fully supported.

    SOCKS 4

    SOCKS 4 is currently not supported.

    The WebSocket library we use does not support SOCKS proxies at all, and the HTTP library we use has a bug that prevents SOCKS 4 to be used. Additionally, you would need to use at least Java 9 or a separate socket factory supporting SOCKS 4, as the JRE implementation is not working in Java 8 and got fixed only in Java 9+.

    SOCKS 4a

    SOCKS 4a is currently only partially supported.

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only. Additionally, you would need to use a separate socket factory supporting SOCKS 4a, as the JRE implementation is not capable of doing SOCKS 4a, only SOCKS 4 and SOCKS 5 are supported at the time of creation of this wiki article.

    SOCKS 5

    SOCKS 5 is currently only partially supported.

    The WebSocket library we use does not support SOCKS proxies at all, so it could be used for the REST connections only.

    + + + diff --git a/wiki/advanced-topics/ratelimits.html b/wiki/advanced-topics/ratelimits.html new file mode 100644 index 0000000..3b5d7f9 --- /dev/null +++ b/wiki/advanced-topics/ratelimits.html @@ -0,0 +1,46 @@ + + + + + + + + + Ratelimits | Javacord + + + + +

    Ratelimits

    Ratelimits is a Discord restriction which prevents you from performing actions in a very fast rate. Most ratelimits are on a per-channel or a per-server basis.

    ❗ The Most Important Ratelimits

    ActionRatelimitType
    Send Messages5 / 5sper channel
    Delete Messages5 / 1sper channel
    Add/Remove Reactions1 / 0.25sper channel
    Edit Server Members10 / 10sper server
    Edit Member Nickname1 / 1sper server
    Edit Bot Username2 / 1hper account
    Update Channels2 / 10mper account
    All Actions Combined50 / 1sper account

    💪 Dealing with Ratelimits

    Usually Javacord takes care about these limitations for you. As a user, there's nothing you have to do, but you should at least know that ratelimits exist.

    Example

    The following code

    // Who even needs loops?
    +channel.sendMessage("Ratelimit Example #1");
    +channel.sendMessage("Ratelimit Example #2");
    +channel.sendMessage("Ratelimit Example #3");
    +channel.sendMessage("Ratelimit Example #4");
    +channel.sendMessage("Ratelimit Example #5");
    +channel.sendMessage("Ratelimit Example #6");
    +channel.sendMessage("Ratelimit Example #7");
    +channel.sendMessage("Ratelimit Example #8");
    +channel.sendMessage("Ratelimit Example #9");
    +channel.sendMessage("Ratelimit Example #10");
    +channel.sendMessage("Ratelimit Example #11");
    +channel.sendMessage("Ratelimit Example #12");
    +

    would look like this in the client:

    You can clearly see the delay between every 5 sent messages.

    ❌ Can I disable ratelimits?

    No. Ratelimits are a limitation from Discord itself, which you cannot circumvent.

    + + + diff --git a/wiki/advanced-topics/sharding.html b/wiki/advanced-topics/sharding.html new file mode 100644 index 0000000..c3dff6b --- /dev/null +++ b/wiki/advanced-topics/sharding.html @@ -0,0 +1,80 @@ + + + + + + + + + Sharding | Javacord + + + + +

    Sharding

    Discord allows (and forces) you to "split" larger bots into several independent parts. This behavior is called "sharding", and the independent parts are called "shards". You can think of shards as completely independent bots. Every shard is responsible for a disjoint set of servers.

    👩‍🏭 Sharding in Javacord

    Logging in with a single shard

    Logging in with a single shard is pretty much the same as logging in without sharding:

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("top secret")
    +    .setCurrentShard(0)
    +    .setTotalShards(2)
    +    .login().join();
    +System.out.println("Shard " + api.getCurrentShard() + " logged in!");
    +

    Note: current shard starts counting at 0! This means in the example above you would have current shard 0 and shard 1 with a total amount of 2 shards.

    Important: There must be a > 5-second delay between each shard-login

    Logging in with a fixed amount of shards

    You can manually set a fixed amount of total shards and log in all of them:

    public class Main {
    +
    +    public static void main(String[] args) {
    +        new DiscordApiBuilder()
    +            .setToken("top secret")
    +            .setTotalShards(10)
    +            .loginAllShards()
    +            .forEach(shardFuture -> shardFuture
    +                .thenAcceptAsync(Main::onShardLogin)
    +                .exceptionally(ExceptionLogger.get())
    +            );
    +    }
    +
    +    private static void onShardLogin(DiscordApi api) {
    +        System.out.println("Shard " + api.getCurrentShard() + " logged in!");
    +        // You can treat the shard like a normal bot account, e.g. registering listeners
    +        api.addMessageCreateListener(event -> {
    +            // ...
    +        });
    +    }
    +
    +}
    +

    loginAllShards() returns a collection with completable futures (Collection<CompletableFuture<DiscordApi>>). This method automatically obeys the > 5-second delay rule.

    You can "ask" Discord to recommend you a total amount of shards. This is done by using the DiscordApiBuilder#setRecommendedTotalShards() method, which returns a CompletableFuture<DiscordApiBuilder> after getting the required information.

    public class Main {
    +
    +    public static void main(String[] args) {
    +        new DiscordApiBuilder()
    +            .setToken("top secret")
    +            .setRecommendedTotalShards().join()
    +            .loginAllShards()
    +            .forEach(shardFuture -> shardFuture
    +                .thenAccept(Main::onShardLogin)
    +                .exceptionally(ExceptionLogger.get())
    +            );
    +    }
    +
    +    private static void onShardLogin(DiscordApi api) {
    +        // ...
    +    }
    +
    +}
    +

    💡 Behavior of Shards

    Managed servers

    You can calculate for which servers a shard is responsible using the server id:

    boolean isResponsible = (serverId >> 22) % totalShards == currentShard;
    +

    Private messages

    Private messages are always sent to the first shard (currentShard == 0).

    When do I need sharding?

    Sharding is forced for bots which are in more than 2500 servers.

    🌄 Sharding for Very Large Bots

    Sharding for very large bots (> 150,000 servers) is a bit different from "normal" sharding. Discord will contact you once your bot reaches this state. Additional information can be found in the official Discord api documentationopen in new window.

    + + + diff --git a/wiki/basic-tutorials/creating-entities.html b/wiki/basic-tutorials/creating-entities.html new file mode 100644 index 0000000..03da8f5 --- /dev/null +++ b/wiki/basic-tutorials/creating-entities.html @@ -0,0 +1,56 @@ + + + + + + + + + Creating Channels, Invites, etc. | Javacord + + + + +

    Creating Channels, Invites, etc.

    Javacord provides XyzBuilder classes to create new Discord entities like channels, webhooks, servers, and many more.

    📕 Create Channels

    You can get the channel builders for a specific server using the Server#createXyzChannelBuilder or by directly calling the constructor. Creating a ServerVoiceChannel would look like this:

    Server server = ...;
    +ServerVoiceChannel channel = new ServerVoiceChannelBuilder(server)
    +    .setName("example-channel")
    +    .setUserlimit(10)
    +    .create().join();
    +

    📗 Create Webhooks

    You can get the WebhookBuilder for a specific text channel:

    ServerTextChannel channel = ...;
    +Webhook webhook = new WebhookBuilder(channel)
    +    .setName("Captain Hook")
    +    .setAvatar(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
    +    .create().join();
    +

    📘 Create Invites

    You can get the InviteBuilder for a specific server channel:

    ServerTextChannel channel = ...;
    +Invite invite = new InviteBuilder(channel)
    +    .setMaxAgeInSeconds(60*60*24)
    +    .setMaxUses(42)
    +    .create().join();
    +

    📙 Create Servers

    You can get the ServerBuilder from the current api instance:

    DiscordApi api = ...;
    +long serverId = new ServerBuilder(api)
    +    .setName("My Awesome Server")
    +    .setIcon(api.getYourself().getAvatar())
    +    .setVerificationLevel(VerificationLevel.HIGH)
    +    .setDefaultMessageNotificationLevel(DefaultMessageNotificationLevel.ONLY_MENTIONS)
    +    .setRegion(Region.EU_CENTRAL)
    +    .create().join();
    +

    WARNING

    By default, bots can only create servers if they are in less than 10 servers. You can contact the Discord support to request a higher limit.

    + + + diff --git a/wiki/basic-tutorials/embeds.html b/wiki/basic-tutorials/embeds.html new file mode 100644 index 0000000..73a063f --- /dev/null +++ b/wiki/basic-tutorials/embeds.html @@ -0,0 +1,48 @@ + + + + + + + + + Embeds | Javacord + + + + +

    Embeds

    Embeds are attached to messages and have a special design. The usually look like this:

    Embed

    🔨 Creating an Embed

    Javacord provides an EmbedBuilder which can be used to create embeds:

    // Create the embed
    +EmbedBuilder embed = new EmbedBuilder()
    +    .setTitle("Title")
    +    .setDescription("Description")
    +    .setAuthor("Author Name", "http://google.com/", "https://cdn.discordapp.com/embed/avatars/0.png")
    +    .addField("A field", "Some text inside the field")
    +    .addInlineField("An inline field", "More text")
    +    .addInlineField("Another inline field", "Even more text")
    +    .setColor(Color.BLUE)
    +    .setFooter("Footer", "https://cdn.discordapp.com/embed/avatars/1.png")
    +    .setImage(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
    +    .setThumbnail(new File("C:/Users/Bastian/Pictures/kitten2.png"));
    +// Send the embed
    +channel.sendMessage(embed);
    +

    📷 Supported Image Sources

    By default, Discord expects embed images to be a link (e.g., the image link used in setFooter(...)), but you can also use attachments for images. If you provide a non-url image source (e.g. the puppy.jpg file used in setImage(...)), Javacord automatically uploads them as an attachment to the message and uses this attachment for the embed.

    🔒 Embed Limits

    TypeLimit
    Title256 characters
    Description4096 characters
    Field AmountUp to 25 fields
    Field Name256 characters
    Field Value1024 characters
    Footer Text2048 characters
    Author Name256 characters

    In addition to the limits above, the sum of all characters in an embed structure must not exceed 6000 characters.

    ❓ FAQ

    What is the second parameter of setAuthor(...)?

    .setAuthor("Author Name", "http://google.com/", "https://cdn.discordapp.com/embed/avatars/0.png")
    +
    • First parameter: The name of the author
    • Second parameter: A link for the author (e.g. their homepage). Can be null.
    • Third parameter: The avatar of the author

    What's the difference between an inline field and a normal one?

    Normal fields always start in a new line, whereas several inline fields can be in the same line.

    Can I change the placement of inline fields?

    No, Discord does not allow different embed layouts.

    How can I format text in an embed?

    Discord allows for a subset of markdown to be used. See their docsopen in new window for the specifics.

    + + + diff --git a/wiki/basic-tutorials/emojis-and-reactions.html b/wiki/basic-tutorials/emojis-and-reactions.html new file mode 100644 index 0000000..0a65532 --- /dev/null +++ b/wiki/basic-tutorials/emojis-and-reactions.html @@ -0,0 +1,44 @@ + + + + + + + + + Emojis and Reactions | Javacord + + + + +

    Emojis and Reactions

    There are two different kinds of emojis in Discord: Unicode emojis and custom emojis.

    🚴‍♂️ Unicode Emojis

    What are Unicode emojis?

    Unicode emojis are "normal" text emojis which are supported by (nearly) all chat clients, including Discord. You can find a list with all Unicode emojis here: Full Emoji Listopen in new window.

    How to use them in messages

    You can either directly add them in your code, e.g.

    channel.sendMessage("Hi! 😃");
    +

    or use the normal "tag" like you would in the Client:

    channel.sendMessage("Hi! :smiley:");
    +

    How to use them for reactions

    Adding unicode reactions is only possible by using the "real" reaction. It doesn't support tags like :smiley:.

    message.addReaction("😃"); // works
    +message.addReaction(":smiley:"); // doesn't work
    +

    🤸‍♀️ Custom Emojis

    What are custom emojis?

    Custom emojis are emojis that are created in a server. You can get all custom emojis the bot knows by using DiscordApi#getCustomEmojis().

    How to use them in messages

    To use custom emojis, you have to know its "tag", which has the format <:name:id>. You can get it by calling CustomEmoji#getMentionTag():

    channel.sendMessage("Hi! <:javacord:415465982715494402>");
    +
    CustomEmoji emoji = ...;
    +channel.sendMessage("Hi! " + emoji.getMentionTag());
    +

    How to use them for reactions

    You can either directly use the custom emoji object or use the tag without the <: > if you don't have access a custom emoji object (e.g., because it's from a different shard):

    CustomEmoji emoji = ...;
    +message.addReaction(emoji);
    +
    message.addReaction("javacord:415465982715494402");
    +

    How to get the tag

    Just add a \ in front of the emoji and press Enter

    👑 Javacord Emoji "Hierarchy"

    In Javacord, all Emojis are a child of the Emoji interface:

    What is a KnownCustomEmoji?

    Known custom emojis are emojis that the bot knows because it's a member of the server with this emoji. A custom emoji can be unknown if someone adds a reaction with an unknown emoji for example. A KnownCustomEmoji has additional methods like getServer() or updateName(String).

    If you are working a lot with Unicode emojis, it's recommended to use a library like JEmojiopen in new window. It enables you to do things like the following:

    message.addReaction(EmojiManager.getByAlias(":thumbsup:"));
    +
    + + + diff --git a/wiki/basic-tutorials/gateway-intents.html b/wiki/basic-tutorials/gateway-intents.html new file mode 100644 index 0000000..fb4ba91 --- /dev/null +++ b/wiki/basic-tutorials/gateway-intents.html @@ -0,0 +1,63 @@ + + + + + + + + + Gateway Intents | Javacord + + + + +

    Gateway Intents

    Discord allows you to "subscribe" to specific groups of events. These "subscriptions" are called intent. Disabling intents that are not required for your bot can significantly increase your bot's performance.

    📋 List of Intents

    Below you can find a table with all intents supported by Discord.

    IntentSafe to DisablePrivileged
    GUILDS
    GUILD_MEMBERS✔️✔️
    GUILD_BANS⚠️*
    GUILD_EMOJIS⚠️*
    GUILD_INTEGRATIONS✔️
    GUILD_WEBHOOKS✔️
    GUILD_INVITES✔️
    GUILD_VOICE_STATES⚠️*
    GUILD_PRESENCES✔️✔️
    GUILD_MESSAGES✔️
    GUILD_MESSAGE_REACTIONS✔️
    GUILD_MESSAGE_TYPING✔️
    DIRECT_MESSAGES✔️
    DIRECT_MESSAGE_REACTIONS✔️
    DIRECT_MESSAGE_TYPING✔️
    MESSAGE_CONTENT✔️✔️
    AUTO_MODERATION_CONFIGURATION✔️
    AUTO_MODERATION_EXECUTION✔️

    * Will most likely work, but needs further testing

    Good to know!

    Guild is a synonym for servers, commonly used in Discord's API. See Glossary.

    💡 What Happens When I Disable Some Intents?

    When you disable some of the listed intents, Javacord will not fire events that belong to the intents and will not update these specific parts of the cache.

    At the moment, we don't have a list which events are affected by which intents (but it will come soon™️). However, most intents should be self-explanatory. E.g. when you disable the DIRECT_MESSAGES intent, your bot will not receive any private messages.

    👑 Privileged Intents

    Some intents are defined as "privileged" due to the sensitive nature of the data. To use these intents, you have to go to your bot in the Developer Portalopen in new window (where you created bot) and manually enable the intents:

    There are some additionally restrictions for bots that are in over 100 servers:

    • Your bot must be verified
    • Your bot must be whitelisted to use this intents

    Take a look at the official article from Discord about this topic and how to verify your bot: Bot Verification and Data Whitelistingopen in new window.

    ❗ Notable Intents

    The following two intents are especially noteworthy: GUILD_MEMBERS and GUILD_PRESENCES. Besides being privileged, they have some special implications for Javacord:

    GUILD_PRESENCES

    This intent is required to get updates about a user's status (i.e., if they are online, what game they are playing, ...). Additionally, without this intent it might take considerably longer to cache all users because of ratelimits (up to 10 minutes for shards with 1000 servers). It is advised against setting DiscordApiBuilder#setWaitForAllUsersOnStartup(true) without this intent, unless absolutely necessary.

    GUILD_MEMBERS

    This intent is required to keep all users in Javacord's cache. Without this intent, methods like Server#getMembers() or DiscordApi#getCachedUsers() will return empty collections. However, you will still be able to access users from objects like messages, e.g. Message#getUserAuthor() will still work.

    MESSAGE_CONTENT

    This intent is a bit different to the other as it does not act as a toggle to receive any events. It's sole purpose is to receive the message content, attachments, components, and embeds. Otherwise, these fields will be empty when you receive a Message object.

    ⚙️ Setting Intents

    Javacord allows you to specify intents in the DiscordApiBuilder prior to login. There are many options to set intents. The following example code shows the most common ones:

    Set All Non-Privileged Intents (Default)

    This method enables all non-privileged intents. This is the default setting in Javacord.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllNonPrivilegedIntents()
    +    .login()
    +    .join();
    +

    Set All Non-Privileged Intents Except

    This method enabled all non-privileged intents, except the given ones.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllNonPrivilegedIntentsExcept(Intent.GUILD_WEBHOOKS)
    +    .login()
    +    .join();
    +

    Set All Intents

    This method enabled all intents.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllIntents()
    +    .login()
    +    .join();
    +

    Set All Intents Except

    This method enabled all intents, except the given ones.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setAllIntentsExcept(Intent.GUILD_PRESENCES, Intent.GUILD_WEBHOOKS)
    +    .login()
    +    .join();
    +

    Set Intents

    This method only enables the given intents.

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .setIntents(Intent.GUILDS, Intent.DIRECT_MESSAGES)
    +    .login()
    +    .join();
    +

    Add Intents

    This method adds the intents to the currently enabled ones(by default all non-privileged). This is useful i.e. if you only want to enable 1 privileged intent like the MESSAGE_CONTENT

    DiscordApi api = new DiscordApiBuilder()
    +    .setToken("topc secret")
    +    .addIntents(Intent.MESSAGE_CONTENT)
    +    .login()
    +    .join();
    +
    + + + diff --git a/wiki/basic-tutorials/glossary.html b/wiki/basic-tutorials/glossary.html new file mode 100644 index 0000000..5b0f7c6 --- /dev/null +++ b/wiki/basic-tutorials/glossary.html @@ -0,0 +1,33 @@ + + + + + + + + + Glossary | Javacord + + + + +

    Glossary

    This is a list with the most common Discord-related terms:

    • Guild - A synonym for server
    • Selfbot - A client account bot, usually logged in to a user's own account
    • Sharding - Splitting a bot into several independent shards, see Sharding
    • Token - Used to login instead of requiring a username + password
    • Embed - A "fancy" message, see Embed FAQ
    • Ratelimit - Prevents you from spamming actions, see Ratelimit FAQ
    • Websocket - A TCPopen in new window "connection" to Discord that receives events, see Wikipediaopen in new window
    • Gateway - The address for the websocket
    • Rest / Rest Request - RESTopen in new window is used to perform actions like sending messages. Rest Requests do not require an active websocket connection.
    • Activity - The text underneath the username, usually Playing Xyz
    • Rich Presence - A more detailed activity, see Discord Docsopen in new window
    + + + diff --git a/wiki/basic-tutorials/interactions/commands.html b/wiki/basic-tutorials/interactions/commands.html new file mode 100644 index 0000000..ea559c5 --- /dev/null +++ b/wiki/basic-tutorials/interactions/commands.html @@ -0,0 +1,133 @@ + + + + + + + + + Interaction Commands aka. Slash Commands | Javacord + + + + +

    Interaction Commands aka. Slash Commands

    INFO

    There are a lot of convenient methods which aim to make your life easier with i.e., not being able to have an invalid configuration of your builder. Therefore, the following examples will only show the usage with the convenient methods.

    💡 Creating a Command

    INFO

    There are 2 different types of Commands:

    • Global | Available for every Server once your Bot gets invited: Created with createGlobal(DiscordApi).
    • Server | Only available on the specific Server: Created with createForServer(Server).

    Let's get started with the most basic command, a ping command.

    SlashCommand command = SlashCommand.with("ping", "Checks the functionality of this command")
    +    .createGlobal(api)
    +    .join();
    +

    That's all you have to do!

    Let's have a look at a more complex command which involves nearly all possibilities:

    SlashCommand command =
    +        SlashCommand.with("channel", "A command dedicated to channels",
    +            Arrays.asList(
    +                SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND_GROUP, "edit", "Edits a channel",
    +                    Arrays.asList(
    +                        SlashCommandOption.createWithOptions(SlashCommandOptionType.SUB_COMMAND, "allow", "Allows a permission to a user for a channel",
    +                            Arrays.asList(
    +                                SlashCommandOption.create(SlashCommandOptionType.CHANNEL, "channel", "The channel to modify", true),
    +                                SlashCommandOption.create(SlashCommandOptionType.USER, "user", "The user which permissions should be changed", true),
    +                                SlashCommandOption.createWithChoices(SlashCommandOptionType.DECIMAL, "permission", "The permission to allow", true,
    +                                    Arrays.asList(
    +                                        SlashCommandOptionChoice.create("manage", 0),
    +                                        SlashCommandOptionChoice.create("show", 1)))
    +        ))))))
    +        .createGlobal(api)
    +        .join();
    +

    Let that sink in first!

    What are we doing here?

    1. We create a base command called channel.
    2. It has a SUB_COMMAND_GROUP called edit which basically is just a folder where you can put your commands in.
    3. There's a SUB_COMMAND called allow which is our actual command. Therefore, our complete argument looks like channel edit allow.
    4. The SUB_COMMAND has 3 arguments:
      1. The channel which should be edited.
      2. The user which permissions should be changed.
      3. A predefined list of available permissions the command executor can choose of.

    📔 Notes on creating commands:

    The REQUIRED attribute

    You can only mark the last argument as being not required. This means it can be optionally set by the command executor. In the above example you could i.e. set the PERMISSIONS argument to false.

    Command structure

    Your command has to follow these structures in order to be successfully created:

    Command structure
    VALID
    +
    +command
    +|
    +|__ subcommand
    +|
    +|__ subcommand
    +
    +----
    +
    +command
    +|
    +|__ subcommand-group
    +    |
    +    |__ subcommand
    +|
    +|__ subcommand-group
    +    |
    +    |__ subcommand
    +
    +----
    +
    +VALID
    +
    +command
    +|
    +|__ subcommand-group
    +    |
    +    |__ subcommand
    +|
    +|__ subcommand
    +
    +-------
    +
    +INVALID
    +
    +
    +command
    +|
    +|__ subcommand-group
    +    |
    +    |__ subcommand-group
    +|
    +|__ subcommand-group
    +    |
    +    |__ subcommand-group
    +
    +----
    +
    +INVALID
    +
    +command
    +|
    +|__ subcommand
    +    |
    +    |__ subcommand-group
    +|
    +|__ subcommand
    +    |
    +    |__ subcommand-group
    +

    ⤵️ Get your commands

    All global commands:

    Set<SlashCommand> commands = api.getGlobalSlashCommands().join();
    +

    All commands only available on a single server:

    Server server = ...;
    +Set<SlashCommand> commands = api.getServerSlashCommands(server).join();
    +

    WARNING

    Getting all commands from a server only contains the commands you have created on this specific server. Therefore, the returned list does not include any global command!

    🔨 Updating Commands

    When updating your commands you only have to include what you actually want to change. The following updater will change the previous created command and change its base name from channel to channels.

    SlashCommand updatedCommand =
    +            new SlashCommandUpdater(commandId)
    +                .setName("channels")
    +                .updateGlobal(api)
    +                .join();
    +

    ✍️ Bulk overwriting commands

    If you have to update / create multiple commands at once it advised to use the batch updater to only have to do 1 request.

    DiscordApi api = ...;
    +
    +Set<SlashCommandBuilder> builders = new HashSet<>();
    +builders.add(new SlashCommandBuilder().setName("server").setDescription("A command for the server"));
    +builders.add(new SlashCommandBuilder().setName("permission").setDescription("A command for permissions"));
    +                                
    +api.bulkOverwriteGlobalApplicationCommands(builders).join();
    +

    👮‍♂️ Permissions

    Permissions exist to enable / disable the usage of your commands for certain things. These things may be:

    • Permissions
    • DMs

    When you create a command you can specify which permissions are required to use it. In addition to the required permissions, you can also specify whether the command should be available in DMs.

    SlashCommand.with("ping","Ping!")
    +    .setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR, PermissionType.BAN_MEMBERS)
    +    //.setDefaultDisabled() Effectively the same as setDefaultEnabledForPermissions(PermissionType.ADMINISTRATOR) but this will lead to the default type by Discord.
    +    .setEnabledInDms(false)
    +    .createGlobal(api)
    +    .join();
    +

    INFO

    Once your bot has been invited to a server, you can not change the permissions afterwards on this server. Then it's up to the server administrators / owner to correctly set up the commands for users / roles / channels.

    ❗ Limits

    Registering a command

    • Server commands are specific to the server you specify when making them. Server commands are not available in DMs. Command names are unique per application within each scope (global and server). That means:
    • Your app cannot have two global commands with the same name
    • Your app cannot have two server commands within the same name on the same guild
    • Your app can have a global and guild command with the same name
    • Multiple apps can have commands with the same names

    General

    • An app can have up to 100 top-level global commands with unique names
    • An app can have up to an additional 100 server commands per server
    • An app can have up to 25 subcommand groups on a top-level command
    • An app can have up to 25 subcommands within a subcommand group
    • Commands can have up to 25 options
    • Options can have up to 25 choices
    • Maximum of 4000 characters for combined name, description, and value properties for each command and its subcommands and groups
    • Limitations on nesting subcommands and groups
    • Global rate limit of 200 slash command creates per day per server
    + + + diff --git a/wiki/basic-tutorials/interactions/components.html b/wiki/basic-tutorials/interactions/components.html new file mode 100644 index 0000000..eb264a8 --- /dev/null +++ b/wiki/basic-tutorials/interactions/components.html @@ -0,0 +1,52 @@ + + + + + + + + + Message Components | Javacord + + + + +

    Message Components

    ❔ What are components?

    Components are interactive elements like buttons or hidden elements like the ActionRow which use is for displaying the visible components. You can add them to a message and interact with users in a very convenient way. Currently, the only interactive components available at the moment are buttons. They differ in style and behaviour(link redirect) seen in the picture below:

    💡 Sending a message with a component

    Sending a component with your message is a simple as that:

    TextChannel channel = ...;
    +
    +new MessageBuilder()
    +    .setContent("Click on one of these Buttons!")
    +    .addComponents(
    +        ActionRow.of(Button.success("success", "Send a message"),
    +            Button.danger("danger", "Delete this message"),
    +            Button.secondary("secondary", "Remind me after 5 minutes")))
    +    .send(channel);
    +

    You simply add a High Level component like an ActionRow which is a container for displaying your components. In turn the ActionRow consist of the components you can interact with like Buttons.

    This works for Select Menus as well:

    TextChannel channel = ...;
    +
    +new MessageBuilder()
    +    .setContent("Select an option of this list!")
    +    .addComponents(
    +        ActionRow.of(SelectMenu.create("options", "Click here to show the options", 1, 1,
    +            Arrays.asList(SelectMenuOption.create("Option One", "You selected Option One!", "Click here to select Option One"),
    +                SelectMenuOption.create("Option Two", "You selected Option Two!", "Click here to select Option Two"),
    +                SelectMenuOption.create("Option Three", "You selected Option Three!", "Click here to select Option Three")))))
    +    .send(channel);
    +

    + + + diff --git a/wiki/basic-tutorials/interactions/overview.html b/wiki/basic-tutorials/interactions/overview.html new file mode 100644 index 0000000..42e1e21 --- /dev/null +++ b/wiki/basic-tutorials/interactions/overview.html @@ -0,0 +1,33 @@ + + + + + + + + + Interactions | Javacord + + + + +

    Interactions

    Interactions are a means of accepting user input through Discord. They have been introduced to provide a more standardized, controlled way for commands than parsing messages. They can even be used with applications that do not provide a bot user.

    💬 Message Commands

    The "old" way of doing commands was done through parsed text messages, like !ping, !userinfo James or !mute James 100s. While such commands are easy in theory, they come with several problems, such as:

    • Conflicts between Bots using the same command format / prefix.
    • Bots have to be able to read all messages and find those that are directed at them
    • Information about command structure can only be provided in info texts and error messages

    Message Command Lifecycle

    ✉️ Interaction Types

    Interactions come in a variety of shapes. The most complex and versatile is the command interaction, which allows for commands directed at a particular bot with information and assistance on subcommands and parameters being integrated into the discord client.

    Context Menu commandsopen in new window are available from the context menu in the client either on a message or a server member.

    Message components come in the flavor of buttons, select menus and other form elements and can be attached directly to a message.

    ♻️ Lifecycle

    INFO

    Creation of interactions is detailed on the pages linked in the previous section.

    Unlike chat message commands, interactions and interaction commands need to be registered with Discord. In order for a bot's interactions to be available in a server, the bot must be added to the server with the applications.commands OAUTH scope. The scope is included in links created by DiscordApi#createInviteLink. If your bot is older, it may need to be invited with the new link to add the scope. It is not necessary to remove the bot from the server to do this.

    Interaction Command Lifecycle

    📈 Advantages

    While being more complicated to utilize, interactions have many benefits over pure text commands.

    • Better Validation: Commands can not be sent with parameters of the wrong type or missing required parameters
    • No conflicts: Interactions are separated by bot and only sent to the proper bot
    • "Privacy": If no public response is sent by the bot, the exchange is invisible to other chat participants
    • Integration: Interactions are integrated into the client's user interface
    • Conversations: Message components can be used in replies to interactions, allowing for nested dialogues.

    WARNING

    If a bot replies to a slash command with a public message, the command used, including all parameters, is visible to other users.

    🤖 Applications vs. Bots

    Interactions can used by any application, not only bots. While interactions can also be handled through webhooks, Javacord only offers support for dealing with them through the gateway. See the Discord Documentationopen in new window for more information.

    WARNING

    The methods of handling interactions can not be mixed. If you register a webhook for your interaction commands, the bot will no longer receive any interaction events.

    🔍 See also

    + + + diff --git a/wiki/basic-tutorials/interactions/responding.html b/wiki/basic-tutorials/interactions/responding.html new file mode 100644 index 0000000..30fb76a --- /dev/null +++ b/wiki/basic-tutorials/interactions/responding.html @@ -0,0 +1,109 @@ + + + + + + + + + Responding to interactions | Javacord + + + + +

    Responding to interactions

    There are many ways to respond to interactions and some are only available for certain interactions. The following will be usable for every interaction.

    💬 Responding immediately after receiving an interaction.

    event.getInteraction()
    +        .createImmediateResponder()
    +        .setContent("YOUR_RESPONSE")
    +        .respond();
    +

    INFO

    Note that you have to respond withing 3 seconds, or the command will fail. If you need longer than 3 seconds you have to respond with respondLater() which allows you to respond within 15 minutes.

    Because of this time limitation, sending any files when creating an immediate response is not possible. If you want a file to be embedded either use respondLater or include a web link in the message content. Depending on the media type of the link and the server configuration, Discord will then display an appropriate embed for the file.

    When you want to respond ephemerally, you can use the setFlags method. Your new responder would look like the following:

    event.getInteraction()
    +        .createImmediateResponder()
    +        .setContent("YOUR_RESPONSE")
    +        .setFlags(MessageFlag.EPHEMERAL)
    +        .respond();
    +

    💬 Responding after some time when receiving an interaction.

    If your computations takes longer than the 3 seconds limit, you can respond later and the Discord Client will show that your bot is thinking until you respond.

    event.getInteraction()
    +        .respondLater()
    +        .thenAccept(interactionOriginalResponseUpdater -> {
    +            interactionOriginalResponseUpdater.setContent("Update message after some time").update();
    +        });
    +

    You can respond ephemerally when responding later too. For that you have pass a true boolean to the respondLater method.

    event.getInteraction()
    +        .respondLater(true)
    +        .thenAccept(interactionOriginalResponseUpdater -> {
    +            interactionOriginalResponseUpdater.setContent("Update message after some time").update();
    +        });
    +

    Sending followup messages

    Followup messages can be sent within 15 minutes after the command has been invoked. You can send as many followup messages as you want.

    api.addSlashCommandCreateListener(event -> {
    +    SlashCommandInteraction slashCommandInteraction = event.getSlashCommandInteraction();
    +    slashCommandInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {
    +        interactionOriginalResponseUpdater.setContent("You will receive the answer in a few minutes!").update();
    +
    +        // time < 15 minutes
    +        
    +        slashCommandInteraction.createFollowupMessageBuilder()
    +                .setContent("Thank you for your patience, it took a while but the answer to the universe is 42")
    +                .send();
    +    });
    +});
    +

    Responding with a Modal

    A modal is a popup dialog which can be shown when responding to an interaction. It focuses the users to explicitly fill out this form to continue with the workflow. Currently, only the TextInput (SelectMenu has been seen working too, but is not yet officially supported) is supported.

    api.addMessageComponentCreateListener(event -> {
    +    event.getInteraction().respondWithModal("modalId","Modal Title",
    +        ActionRow.of(TextInput.create(TextInputStyle.SHORT, "text_input_id", "This is a Text Input Field")));
    +});
    +

    Which results in

    Modal

    💬 SlashCommand interaction only response methods

    How to know what slash command was invoked?

    For example, you have created a slash command with the name "settings" and a subcommand "color". If you want to check if exactly this command has been used, you can check it as follows:

    api.addSlashCommandCreateListener(event -> {
    +    SlashCommandInteraction interaction = event.getSlashCommandInteraction();
    +    if (interaction.getFullCommandName().equals("settings color")) {
    +        //Code if command matches the full name
    +    }
    +});
    +

    Respond to an AutoComplete interaction triggered from a SlashCommand

    api.addAutocompleteCreateListener(event -> {
    +    event.getAutocompleteInteraction()
    +    .respondWithChoices(Arrays.asList(
    +        SlashCommandOptionChoice.create("one", 1),
    +            SlashCommandOptionChoice.create("two", 2))
    +    );
    +});
    +

    💬 Message Component interaction only response methods

    When dealing with message components, you don't necessarily have to respond or update a message. You can simply acknowledge the interaction and let the user know that the task is done.

    api.addMessageComponentCreateListener(event -> {
    +    event.getMessageComponentInteraction().acknowledge();
    +});
    +

    A more complete example of how to respond to Component interactions

    The following code snipped shows how you can respond to the example created in Components.

    api.addMessageComponentCreateListener(event -> {
    +    MessageComponentInteraction messageComponentInteraction = event.getMessageComponentInteraction();
    +    String customId = messageComponentInteraction.getCustomId();
    +
    +    switch (customId) {
    +        case "success":
    +            messageComponentInteraction.createImmediateResponder()
    +                    .setContent("You clicked a button!")
    +                    .respond();
    +            break;
    +        case "danger":
    +            messageComponentInteraction.getMessage().ifPresent(Message::delete);
    +            break;
    +        case "secondary":
    +            messageComponentInteraction.respondLater().thenAccept(interactionOriginalResponseUpdater -> {
    +                //Code to respond after 5 minutes
    +            });
    +            break;
    +        case "options":
    +            messageComponentInteraction.createImmediateResponder()
    +					.setContent("You selected an option in a select menu!")
    +					.respond();
    +            break;
    +    }
    +});
    +
    + + + diff --git a/wiki/basic-tutorials/listeners.html b/wiki/basic-tutorials/listeners.html new file mode 100644 index 0000000..a6dda45 --- /dev/null +++ b/wiki/basic-tutorials/listeners.html @@ -0,0 +1,85 @@ + + + + + + + + + Listeners | Javacord + + + + +

    Listeners

    👨‍🔧 Creating listeners

    Creating listeners is extremely easy in Javacord. You can either use Java 8's lambda expressions to register listeners inline or just create a new class for them, if an inline listener would get too messy.

    Inline Listeners

    api.addMessageCreateListener(event -> {
    +    if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +        event.getChannel().sendMessage("Pong!");
    +    }
    +});
    +

    In their own class

    api.addListener(new MyListener());
    +

    and

    public class MyListener implements MessageCreateListener {
    +
    +    @Override
    +    public void onMessageCreate(MessageCreateEvent event) {
    +        if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +            event.getChannel().sendMessage("Pong!");
    +        }
    +    }
    +
    +}
    +

    Before logging in

    Sometimes it might be useful to add listeners before calling the DiscordApiBuilder#login() method.

    DiscordApi api = new DiscordApiBuilder()
    +        // An inline listener
    +        .addMessageCreateListener(event -> {
    +            Message message = event.getMessage();
    +            if (message.getContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        })
    +        .addServerBecomesAvailableListener(event -> {
    +            System.out.println("Loaded " + event.getServer().getName());
    +        })
    +        // A listener in their own class
    +        .addListener(new MyListener())
    +         // Alternative syntax that can be used for classes that require a DiscordApi parameter in their constructor
    +        .addListener(MyListener::new)
    +        .setToken("top secret")
    +        .setWaitForServersOnStartup(false)
    +        .login()
    +        .join();
    +

    Note: In most cases, it's enough to add listeners after logging in

    Object listeners

    Another cool feature is the ability to attach listeners directly to objects. An example where this can be useful is, for example, reacting to reactions. The following code would delete the message if someone adds a 👎 reaction.

    message.addReactionAddListener(event -> {
    +    if (event.getEmoji().equalsEmoji("👎")) {
    +        event.deleteMessage();
    +    }
    +}).removeAfter(30, TimeUnit.MINUTES);
    +

    Seems like the bot is very sensitive to criticism.

    💣 Removing listeners

    There are two ways to remove a listener:

    Using the returned ListenerManager

    Every time you register a listener, a ListenerManager is returned which can be used to unregister the listener:

    ListenerManager<MessageCreateListener> listenerManager = api.addMessageCreateListener(event -> {
    +    // Do stuff
    +});
    +
    +listenerManager.remove();
    +

    This manager also has some utility methods. You can, for example, remove a listener after a given time, which can be useful for object listeners:

    message.addReactionAddListener(event -> {
    +  // Do stuff
    +}).removeAfter(30, TimeUnit.MINUTES);
    +

    Using the removeListener(...) method

    You can remove any listener using the removeListener(...) method:

    MyListener listener = new MyListener();
    +api.addListener(listener);
    +// ...
    +api.removeListener(listener);
    +
    + + + diff --git a/wiki/basic-tutorials/logger-config.html b/wiki/basic-tutorials/logger-config.html new file mode 100644 index 0000000..25b8e11 --- /dev/null +++ b/wiki/basic-tutorials/logger-config.html @@ -0,0 +1,45 @@ + + + + + + + + + Logger Configuration | Javacord + + + + +

    Logger Configuration

    Logging is an important tool to keep track of what is going on in your application. Javacord uses the Log4j 2 APIopen in new window, which allows you to use your favorite logging framework to log messages in your own code and have all logging messages end up in the same destination. In case you do not add your own logging framework, a fallback logger is used that logs to the console.
    If you want more control, add a proper logging framework that supports your needs and configure it accordingly. You can for example configure log messages on a per-class level, change log levels during runtime, or log to a file or database.

    🥈 Fallback Logger

    Javacord's fallback logger is a simple Log4j logger which always logs INFO level and higher. It allows you to enable DEBUG and TRACE logging manually. As log levels are hierarchical, enabling TRACE will also implicitly enable DEBUG, and disabling DEBUG will also implicitly disable TRACE.

    // Enable debug logging
    +FallbackLoggerConfiguration.setDebug(true);
    +
    +// Enable trace logging
    +FallbackLoggerConfiguration.setTrace(true);
    +

    Changing the log level of the fallback logger only affects newly created loggers. Pre-existing loggers will not have their log level changed. So if you want to configure the fallback logger, you should do this as one of the first actions in your bot code. If you want to change log levels during runtime, you should use a proper logging framework like Log4j 2 Core or another library that supports this.

    All fallback logger messages are printed to the standard output stream (System.out) and thus usually to your console. If you want to log to a file, database, or anything else, you should consider using a proper logging framework which allows you to configure this behavior.

    This is how a log line from the fallback logger will look like:

    <time with date            ><level><logger name, usually the logging class              > <message            > <the thread context, here the shard number>
    +2018-08-03 20:00:06.080+0200 DEBUG org.javacord.core.util.gateway.DiscordWebSocketAdapter Received HELLO packet {shard=0}
    +

    🥇 Using a Proper Logging Framework

    Adding a Logging Framework

    Adding a logging framework of your choice is very straightforward. You can just add it as a dependency, and it will be detected by Log4j automatically. The following example adds Log4j 2 using Gradle:

    dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-core:2.17.0' }
    +

    You can also use an SLF4J compatible logging framework using log4j-to-slf4j. The following example adds Logback Classic using Gradle:

    dependencies {
    +    runtimeOnly 'org.apache.logging.log4j:log4j-to-slf4j:2.17.0'
    +    runtimeOnly 'ch.qos.logback:logback-classic:1.2.3'
    +}
    +

    Configure Your Logging Framework

    Logging the Relevant Shard

    Javacord adds the relevant shard to each log message. The facility that stores this information has a different name depending on which logging framework you use. For Log4j 2, this is called Thread Context Map and can be added in a pattern layout with %X{shard}, or you can add the whole thread context map by using %X. For Logback Classic, it is called MDC and can be added with the same pattern expressions as for Log4j.

    + + + diff --git a/wiki/basic-tutorials/message-builder.html b/wiki/basic-tutorials/message-builder.html new file mode 100644 index 0000000..1b494ad --- /dev/null +++ b/wiki/basic-tutorials/message-builder.html @@ -0,0 +1,59 @@ + + + + + + + + + Using the MessageBuilder | Javacord + + + + +

    Using the MessageBuilder

    The MessageBuilder class is a more powerful alternative to the TextChannel#sendMessage(...) method.

    It can be used to construct more complex messages and supports some additional features that are not possible with a simple TextChannel#sendMessage(...) call.

    🕵️‍♀️ Example

    The following code

    new MessageBuilder()
    +    .append("Look at these ")
    +    .append("awesome", MessageDecoration.BOLD, MessageDecoration.UNDERLINE)
    +    .append(" animal pictures! 😃")
    +    .appendCode("java", "System.out.println(\"Sweet!\");")
    +    .addAttachment(new File("C:/Users/Bastian/Pictures/kitten.jpg"))
    +    .addAttachment(new File("C:/Users/Bastian/Pictures/puppy.jpg"))
    +    .setEmbed(new EmbedBuilder()
    +            .setTitle("WOW")
    +            .setDescription("Really cool pictures!")
    +            .setColor(Color.ORANGE))
    +    .send(channel);
    +

    will be displayed like this:

    📍 Allowed Mentions

    The allowed mentions object lets you control what should be mentioned (pinged) in a message if it contains mentions.

    The following code will ping:

    • The user0
    • All mentioned roles in the message

    And will not ping:

    • @everyone and @here
    • The user1
    AllowedMentions allowedMentions = new AllowedMentionsBuilder()
    +                .addUser(user0.getId())
    +                .setMentionRoles(true)
    +                .setMentionEveryoneAndHere(false)
    +                .build();
    +
    +        new MessageBuilder()
    +                .setAllowedMentions(allowedMentions)
    +                .append(user0.getMentionTag())
    +                .append(user1.getMentionTag())
    +                .append(role.getMentionTag())
    +                .append(role2.getMentionTag())
    +                .append("@everyone")
    +                .send(channel);
    +

    If you add a user to the mentions object and set setMentionUsers(true) it will ping every mentioned user. The same applies for setMentionRoles(true)

    + + + diff --git a/wiki/basic-tutorials/running.html b/wiki/basic-tutorials/running.html new file mode 100644 index 0000000..d48ff18 --- /dev/null +++ b/wiki/basic-tutorials/running.html @@ -0,0 +1,198 @@ + + + + + + + + + Running and Deploying your Bot | Javacord + + + + +

    Running and Deploying your Bot

    If you took the time to write a bot, at some point you'll also want to run it, either for use in production or for debugging from the IDE.

    👷 Running from your IDE

    While developing your bot, you will want to run your bot directly from the IDE in order to quickly test changes and new features. For this, create a Run/Debug Configuration in your IDE of choice with your bot's main class. Remember to also add any necessary parameters and environment variables.

    A working Run/Debug configuration will also enable you to run your bot with a debugger. A debugger is often considered a developer's most important tool, so make sure to familiarize yourself with the debugging integration for your IDE of choice.

    IntelliJ IDEA

    This assumes your project is set up correctly, preferably with Gradle, can be built without errors, and does not yet have any run/debug configurations.

    1. Locate and click the Add Configuration... button in the top bar next to the start button.

    2. In the newly opened window, click the + button in the top left and select Application

    3. Give a name for your configuration and select the module to use the classpath of (usually yourproject.main).

    4. Select your Main class. Use the ... button to search for it or provide the fully qualified name. If it can not be found, you most likely selected the wrong module in step 3.

    5. Optional: Set command line arguments and environment variables. For the environment variables, use the button to the right of the input field for a more convenient input window.

    6. Click Apply to finalize the configuration, then OK to close the window.

    7. Select your configuration in the drop-down menu and run or debug it with the buttons to the right.

    Eclipse

    This assumes your project is set up correctly, can be built without errors, and does not yet have any run/debug configurations.

    1. In the menu bar, click "Run" then "Run Configurations...".

    2. In the newly opened window, select "Java Application" on the left side, then click the leftmost button in the row above the tree view. A new configuration will appear.

    3. Give a name to your configuration.

    4. Set the project and the main class. To easily select it, use the "Browse..." and "Search..." buttons.

    5. Optional: Set command line (and VM) arguments as well as environment variables in their respective tabs.

    6. Click Apply to save your configuration, then Close to close the window.

    7. Run or debug your bot via the Buttons in the top row, the Run menu, or the shortcuts Ctrl+F11 for running and F11 for debugging.

    📦 Deploying and Running as a Standalone Application

    Running from the IDE is only recommended during development and strongly discouraged for production use. Generally, you'll want your build tool to create a convenient distribution format for you to use.

    Building a Distribution with Gradle

    For Gradle, only two further steps are necessary for a basic application. On top of the steps described in the Getting Started Section, also add the Application Pluginopen in new window and define your mainClass as the fully qualified name of your main class. If you're using an older version of Gradle (earlier than 6.4), the attribute is instead called mainClassName.

    INFO

    As with many Gradle solutions, there is actually a whole lot going on under the hood. The application plugin implicitly also applies the java and distribution plugins. Refer to the documentations of the involved plugins for more ways to fine-tune the process.

    Your modified build file should now look similar to this:

    plugins {
    +    application
    +}
    + 
    +version = "1.0.0"
    + 
    +java {
    +    sourceCompatibility = JavaVersion.VERSION_1_8
    +}
    + 
    +application {
    +    mainClass.set("com.github.yourname.BotMain")
    +    // mainClassName.set("com.github.yourname.BotMain")  // Gradle < 6.4
    +}
    + 
    +repositories {
    +    mavenCentral()
    +}
    + 
    +dependencies {
    +    implementation("org.javacord:javacord:{{latestVersion}}")
    +}
    +
    plugins {
    +    id 'application'
    +}
    + 
    +version '1.0.0'
    + 
    +java {
    +    sourceCompatibility = JavaVersion.VERSION_1_8
    +}
    + 
    +application {
    +    mainClass = 'com.github.yourname.BotMain'
    +    // mainClassName = 'com.github.yourname.BotMain' // for Gradle versions < 6.4
    +}
    + 
    +repositories {
    +    mavenCentral()
    +}
    + 
    +dependencies {
    +    implementation 'org.javacord:javacord:{{latestVersion}}'
    +}
    +

    Now you can execute the distZip or distTar task with Gradle. The task will create a distribution and package it in an archive file that will be placed in the build/distributions directory. Extract the content of those files on your server or whichever machine you want to run your bot on.

    The distribution usually only contains the directories bin and lib. From the distribution directory, run either bin/yourbot or bin/yourbot.bat, depending on whether you're running the bot on Linux / macOS or windows.

    Building a Distribution with Maven

    For Maven, add the Appassembleropen in new window plugin to your pom.xml. The plugin will create a distribution, but not bundle it in a neat archive file, so we'll also add the assembly plugin. We'll bind both to the package lifecycle phase.

    <project>
    +  ...
    +    <build>
    +        <plugins>
    +            <plugin>
    +                <groupId>org.codehaus.mojo</groupId>
    +                <artifactId>appassembler-maven-plugin</artifactId>
    +                <version>1.10</version>
    +                <configuration>
    +                    <programs>
    +                        <program>
    +                            <mainClass>org.javacord.examplebot.Main</mainClass>
    +                            <id>examplebot</id>
    +                        </program>
    +                    </programs>
    +                </configuration>
    +                <executions>
    +                    <execution>
    +                        <id>create-distribution</id>
    +                        <phase>package</phase>
    +                        <goals>
    +                            <goal>assemble</goal>
    +                        </goals>
    +                    </execution>
    +                </executions>
    +            </plugin>
    +            <plugin>
    +                <artifactId>maven-assembly-plugin</artifactId>
    +                <version>3.3.0</version>
    +                <configuration>
    +                    <descriptors>
    +                        <!-- This must match the location of the descriptor -->
    +                        <descriptor>src/assembly/distribution.xml</descriptor>
    +                    </descriptors>
    +                </configuration>
    +                <executions>
    +                    <execution>
    +                        <id>create-archive</id>
    +                        <phase>package</phase>
    +                        <goals>
    +                            <goal>single</goal>
    +                        </goals>
    +                    </execution>
    +                </executions>
    +            </plugin>
    +        </plugins>
    +    </build>
    +</project>
    +

    Sadly, none of the built-in assembly descriptors match our use case, so we'll put our custom one into src/assembly/distribution.xml:

    <assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    +    <id>distribution</id>
    +    <formats>
    +        <!-- See https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html for supported formats -->
    +        <format>tar.gz</format>
    +        <format>tar.bz2</format>
    +        <format>zip</format>
    +    </formats>
    +    <fileSets>
    +        <fileSet>
    +            <!-- This will also include your project readme, license and similar files-->
    +            <directory>${project.basedir}</directory>
    +            <outputDirectory>/</outputDirectory>
    +            <includes>
    +                <include>README*</include>
    +                <include>LICENSE*</include>
    +                <include>NOTICE*</include>
    +            </includes>
    +        </fileSet>
    +        <fileSet>
    +            <!-- Change this if you reconfigured the appassembler output directory -->
    +            <directory>${project.build.directory}/appassembler</directory>
    +            <outputDirectory>/</outputDirectory>
    +        </fileSet>
    +    </fileSets>
    +</assembly>
    +

    Now when you execute mvn package, a distribution with start scripts for Windows and Linux/macOS will be generated which is then packaged into archive files for every format you specified in the assembly descriptor. You can find the raw distribution (without readme and license files) in target/appassembler and the archive files directly in target.

    Running

    After creating your distribution via Gradle or Maven and extracting/copying it to the machine you want to run it from, you should have a directory containing both a bin and a lib (or repo) directory. Depending on your platform, you can now run the bin/yourbot or bin/yourbot.bat script.

    These automatically generated scripts will then invoke java with your dependencies on the classpath and run your main class. Your working directory will be the directory you ran the script from.

    💩 Building a Fat Jar

    Although it is an abuse of the way java works, sometimes you will be forced to create a fat jar, or an uber jar. This is a jar file that contains your application and all its dependencies. This is sometimes used as a lazy way of building a convenient distribution, but should be foregone in favor of the above mentioned distributions.

    However, in some cases (more often than not Bukkit/Spigot addons) it is necessary to provide a fat jar, since the host application's loading mechanism can only handle singular jar files. If you are subject to such a case of bad design, please complain to the maintainer of whatever host application you are using, then use the following instructions to forsake all that is good and just and create a fat jar. Remember to grit your teeth the whole time.

    With Gradle

    For Gradle, use the shadowopen in new window plugin. If you want the fat jar to be executable, you will need to specify a main class via the application plugin.

    plugins {
    +    id 'java'
    +    # ...
    +    id 'com.github.johnrengelman.shadow' version '7.1.2'
    +}
    +

    With gradlew shadowJar you can now create a shaded (fat) jar. It will be named build/libs/yourbot-1.0.0-all.jar or similar, according to your project settings.

    With Maven

    For Maven, add the maven-shade-pluginopen in new window to your build. As with the other solutions, configure your main class.

    Some of your dependencies might be signed .jar files. Unfortunately, this will likely break your fat jar. Remove the signatures by defining an exclusion filter as demonstrated below. Let the thought that you had to disable a security feature just to make this work serve as a reminder that creating a fat jar is not how jars are meant to be used.

    <project>
    +  ...
    +  <build>
    +    <plugins>
    +      <plugin>
    +        <groupId>org.apache.maven.plugins</groupId>
    +        <artifactId>maven-shade-plugin</artifactId>
    +        <version>3.2.3</version>
    +        <configuration>
    +            <shadedArtifactAttached>true</shadedArtifactAttached>
    +            <shadedClassifierName>fat</shadedClassifierName>
    +            <transformers>
    +                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
    +                    <manifestEntries>
    +                       <Main-Class>com.github.yourname.BotMain</Main-Class>
    +                    </manifestEntries>
    +                </transformer>
    +            </transformers>
    +            <filters>
    +                <filter>
    +                    <artifact>*:*</artifact>
    +                    <excludes>
    +                        <exclude>META-INF/*.SF</exclude>
    +                        <exclude>META-INF/*.DSA</exclude>
    +                        <exclude>META-INF/*.RSA</exclude>
    +                    </excludes>
    +                </filter>
    +            </filters>
    +        </configuration>
    +        <executions>
    +          <execution>
    +            <phase>package</phase>
    +            <goals>
    +              <goal>shade</goal>
    +            </goals>
    +          </execution>
    +        </executions>
    +      </plugin>
    +    </plugins>
    +  </build>
    +</project>
    +

    Running mvn package will now additionally create the yourbot-1.0.0-fat.jar.

    + + + diff --git a/wiki/essential-knowledge/completable-futures.html b/wiki/essential-knowledge/completable-futures.html new file mode 100644 index 0000000..f8f5076 --- /dev/null +++ b/wiki/essential-knowledge/completable-futures.html @@ -0,0 +1,104 @@ + + + + + + + + + Completable Futures | Javacord + + + + +

    Completable Futures

    WARNING

    This tutorial assumes that you are familiar with lambda expressions. Take a look at the lambda introduction first, if you are not!

    As Javacord is heavily multithreaded, you must understand the concept of Futuresopen in new window in general, as well as their most common implementation, the CompletableFutureopen in new window. This little introduction gives you a quick overview of the basics you need to know in order to work with Futures.

    🤔 What the heck is a future?

    A future is basically a wrapper, that will contain a value in the future but might not contain it right now. This is useful, if a method call requires some time and should not block the execution of your current code. You can easily see the difference with a primitive speed comparison:

    long currentTime = System.currentTimeMillis();
    +channel.sendMessage("Test 1");
    +channel.sendMessage("Test 2");
    +channel.sendMessage("Test 3");
    +channel.sendMessage("Test 4");
    +channel.sendMessage("Test 5");
    +// Prints "4 ms"
    +System.out.println((System.currentTimeMillis() - currentTime) + " ms");
    +
    long currentTime = System.currentTimeMillis();
    +channel.sendMessage("Test 1").join();
    +channel.sendMessage("Test 2").join();
    +channel.sendMessage("Test 3").join();
    +channel.sendMessage("Test 4").join();
    +channel.sendMessage("Test 5").join();
    +// Prints "894 ms"
    +System.out.println((System.currentTimeMillis() - currentTime) + " ms");
    +

    TIP

    join() blocks the current thread until the method finished. This will be explained later.

    📖 Methods

    join()

    The join method blocks the current thread until the method finished. It returns the method's result or throws a CompletionException if anything failed.

    The following example would create a new text channel in a given server and sends a message directly afterwards.

    // Create the channel
    +ServerTextChannel channel = new ServerTextChannelBuilder(server)
    +    .setName("new-channel")
    +    .create()
    +    .join();
    +// Send a message in the new channel
    +Message message = channel.sendMessage("First!").join();
    +// Adds an reaction to the message. Even though this method doesn't return anything,
    +// join() ensures, that an exception is thrown in case something went wrong
    +message.addReaction("👍").join();
    +

    DANGER

    You should avoid join() for methods which will be called frequently.

    TIP

    While join() can become a performance issue when you call it very frequently, it is very convenient to use and easy to understand. If you are new to programming and just want to get your first bot working, this is a good method to start with.

    Once you gathered more experience, we highly advise against using join as it negatively impacts your bot's performance!

    thenAccept(...)

    The thenAccept method accepts a Consumer, that consumes the result of the method and is executed asynchronously. It is the method you usually want to use most of the time.

    The following example would create a new text channel in a given server and send a message directly afterwards.

    new ServerTextChannelBuilder(server)
    +    .setName("new-channel")
    +    .create()
    +    .thenAccept(channel -> {
    +        channel.sendMessage("First!").thenAccept(message -> {
    +            message.addReaction("👍");
    +        });
    +    });
    +

    DANGER

    The example code above has a major problem: Any exception that might occur will be completely ignored. This makes it very hard to find bugs.

    For example, if your bot doesn't have the permissions to create a new channel, it will just fail silently.

    exceptionally(...)

    The exceptionally method accepts a Function as parameter, which consumes possible exceptions and returns a fallback value.

    The following example would create a new text channel in a given server and send a message directly afterwards. If something fails (e.g., if the bot isn't allowed to create a text channel in the server), it will log an exception.

    new ServerTextChannelBuilder(server)
    +    .setName("new-channel")
    +    .create()
    +    .thenAccept(channel -> {
    +        channel.sendMessage("First!").thenAccept(message -> {
    +            message.addReaction("👍").exceptionally(e -> {
    +                e.printStackTrace(); // Adding the reaction failed
    +                return null;
    +            });
    +        }).exceptionally(e -> {
    +            e.printStackTrace(); // Message sending failed
    +            return null;
    +        });
    +    }).exceptionally(e -> {
    +        e.printStackTrace(); // Channel creation failed    
    +        return null;
    +    });
    +

    Wow! This looks ugly 🤮. But worry not! There are many options to improve this code!

    To make things simpler for you, Javacord has the ExceptionLogger class, which can be used here. It logs every exception you didn't catch manually.

    new ServerTextChannelBuilder(server)
    +    .setName("new-channel")
    +    .create()
    +    .thenAccept(channel -> {
    +        channel.sendMessage("First!").thenAccept(message -> {
    +            message.addReaction("👍").exceptionally(ExceptionLogger.get());
    +        }).exceptionally(ExceptionLogger.get());
    +    }).exceptionally(ExceptionLogger.get());
    +

    Okay! This is at least a little better, but still not really perfect 🤔.

    thenCompose()

    The thenCompose methods allows you to chain futures. It takes a Functionopen in new window as parameter, that consumes the future's value and expects a new future to be returned.

    The example to create a text channel can now be written like this:

    new ServerTextChannelBuilder(server)
    +        .setName("new-channel")
    +        .create() 
    +        .thenCompose(channel -> channel.sendMessage("First!"))
    +        .thenCompose(message -> message.addReaction("👍"))
    +        .exceptionally(ExceptionLogger.get());
    +

    Finally 🎉! Now we only need a single exceptionally(...) call at the end. We also got rid of the nested callbacks (usually referred to as "callback hell").

    For better understanding, here's the example with comments that tell you the type at each line:

    new ServerTextChannelBuilder(server) // ServerTextChannelBuilder
    +        .setName("new-channel") // ServerTextChannelBuilder
    +        .create() // CompletableFuture<ServerTextChannel>
    +        .thenCompose(channel -> channel.sendMessage("First!")) // CompletableFuture<Message>
    +        .thenCompose(message -> message.addReaction("👍")) // CompletableFuture<Void>
    +        .exceptionally(ExceptionLogger.get()); // CompletableFuture<Void>
    +

    📚 Further Read

    This tutorial only focuses on the absolute basics. For a more detailed introduction to CompletableFutures, you can take a look at this tutorialopen in new window.

    You should also take a look at the JavaDoc for a complete list of methods: CompletableFuture JavaDocopen in new window.

    + + + diff --git a/wiki/essential-knowledge/lambdas.html b/wiki/essential-knowledge/lambdas.html new file mode 100644 index 0000000..60f6539 --- /dev/null +++ b/wiki/essential-knowledge/lambdas.html @@ -0,0 +1,51 @@ + + + + + + + + + Lambdas | Javacord + + + + +

    Lambdas

    Lambdas are used to implement functional interfacesopen in new window. Simply said, functional interfaces are interfaces with a single method definition. All listeners in Javacord are functional interfaces and look like this internally (simplified):

    @FunctionalInterface
    +public interface MessageCreateListener {
    +    void onMessageCreate(MessageCreateEvent event);
    +}
    +

    Before Java 8, you would have implemented this kind of listener as an anonymous classopen in new window, which would look like this:

    api.addMessageCreateListener(new MessageCreateListener() {
    +    @Override
    +    public void onMessageCreate(MessageCreateEvent event) {
    +        // Do stuff
    +        event.pinMessage();
    +    }
    +});
    +

    In Java 8, this can be replaced with a lambda expression, which does exactly the same thing, but in a more readable fashion. The method parameter (in this case event) is written in front of the -> arrow, and the method body is written after it.

    api.addMessageCreateListener(event -> {
    +    // Do stuff
    +    event.pinMessage();
    +});
    +

    TIP

    If the method has more than one parameter, it would look like this:

    (param1, param2) -> { ... }
    +

    There's even a shorter version: If you are only executing one statement, you can get rid of the { } brackets as well:

    api.addMessageCreateListener(event -> event.pinMessage());
    +

    However, the above method can be shortened even more, by replacing the lambda expression with a so called "method referenceopen in new window".

    api.addMessageCreateListener(MessageEvent::pinMessage);
    +

    There are also plenty classes in Java 8, that make use of lambda expressions. One example would be the Optional class, which is explained here.

    📚 Further Read

    This tutorial only focuses on the absolute basics. For an in-depth introduction to lambda expressions, you can take a look at Oracle's article about lambda expressionsopen in new window.

    + + + diff --git a/wiki/essential-knowledge/optionals.html b/wiki/essential-knowledge/optionals.html new file mode 100644 index 0000000..86633a1 --- /dev/null +++ b/wiki/essential-knowledge/optionals.html @@ -0,0 +1,87 @@ + + + + + + + + + Optionals | Javacord + + + + +

    Optionals

    WARNING

    This tutorial assumes that you are familiar with lambda expressions. Take a look at the lambda introduction first, if you are not!

    💪 Motivation

    The Optional class is widely used in Javacord. Basically, every method that might return a null value will return an Optional in Javacord instead. Optionals help you to avoid NullPointerExceptions and make it very clear if a method may not have a result. Here's a small example:

    The old way of doing it

    User user = api.getCachedUserById(123L);
    +if (user != null) {
    +  user.sendMessage("Hi!");
    +}
    +

    The new way of doing it

    api.getCachedUserById(123L).ifPresent(user -> 
    +  user.sendMessage("Hi!")
    +);
    +

    You can imagine an Optional like a box 📦 that may or may not contain a value. Before accessing this value, you have to "unpack" this box first.

    📖 Methods

    The Optional class has many useful methods which can all be found in the JavaDocsopen in new window. This tutorial gives a short introduction to the most common ones.

    get()

    The get method returns the value of the Optional or throws a NoSuchElementException if it does not contain a value.

    TextChannel channel = api.getTextChannelById(123L).get();
    +channel.sendMessage("Hi");
    +

    DANGER

    You should never use this method blindly but only if you are 100% sure the optional contains a value.

    Every time you use this method carelessly, a kitten dies 🙀! True story.

    isPresent()

    The isPresent methods checks, if the Optional contains a value.

    Optional<TextChannel> channel = api.getTextChannelById(123L);
    +if (channel.isPresent()) {
    +  // A text channel with the id 123 exists. It's safe to call #get() now
    +  channel.get().sendMessage("Hi");
    +}
    +

    orElse(...)

    The orElse methods returns the value of the Optional if it is present. Otherwise, it returns the given default value.

    // The user may not have a nickname on the given server. 
    +// In this case, we use the user's "regular" name.
    +String displayName = user.getNickname(server).orElse(user.getName());
    +

    The example above is (mostly) equivalent to the example below but much more concise.

    String displayName = "";
    +Optional<String> nickname = user.getNickname(server);
    +if (nickname.isPresent()) {
    +  displayName = nickname.get();
    +} else {
    +  displayName = user.getName();
    +}
    +

    TIP

    In this case you can just use user.getDisplayName(server) instead.

    ifPresent(...)

    The ifPresent method is very similar to an if (value != null) { ... } check. It takes a Consumeropen in new window as it's argument. This consumer is called if the Optional contains a value. Together with lambda expressions this can be a very handy method.

    api.getTextChannelById(123L).ifPresent(channel -> {
    +  channel.sendMessage("Hi!");
    +});
    +

    The example above is (mostly) equivalent to the example below but more concise.

    Optional<TextChannel> channel = api.getTextChannelById(123L);
    +if (channel.isPresent()) {
    +  channel.get().sendMessage("Hi!");
    +}
    +

    filter(...)

    The filter method filters the Optional for a given criteria.

    Optional<User> botUser = api.getCachedUserById(123L).filter(User::isBot);
    +

    The example above is equivalent to the example below but more concise.

    Optional<User> user = api.getCachedUserById(123L);
    +Optional<User> botUser;
    +if (user.isPresent() && user.get().isBot()) {
    +  botUser = user;
    +} else {
    +  botUser = Optional.empty();
    +}
    +

    map(...)

    The map method "converts" the type of an Optional. This is useful, if the type of an Optional does not contain the final value you need.

    The following example gets the name of the bots current activity (the "Playing xyz" status) or "None" if the bot has no current activity.

    String activityName = api.getYourself().getActivity().map(Activity::getName).orElse("None");
    +

    For better understanding, here's the exact same code but with the types as comments:

    String activityName =  api.getYourself() // User
    +        .getActivity() // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    flatMap(...)

    The flatMap method if very similar to the map methods. It is used to map values that itself are Optionals to prevent Optional nesting (a "box in a box").

    String activityName = api.getCachedUserById(123L) // Optional<User>
    +        .flatMap(User::getActivity) // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    Without flatMap, the code would look like this:

    String activityName = api.getCachedUserById(123L) // Optional<User>
    +        .map(User::getActivity) // Optional<Optional<Activity>>
    +        .filter(Optional::isPresent) // Optional<Optional<Activity>>
    +        .map(Optional::get) // Optional<Activity>
    +        .map(Activity::getName) // Optional<String>
    +        .orElse("None"); // String
    +

    📚 Further Read

    This tutorial only focuses on the absolute basics. For an in-depth introduction to Optionals, you can take a look at Oracle's article about optionalsopen in new window.

    + + + diff --git a/wiki/getting-started/creating-a-bot-account.html b/wiki/getting-started/creating-a-bot-account.html new file mode 100644 index 0000000..d88dedd --- /dev/null +++ b/wiki/getting-started/creating-a-bot-account.html @@ -0,0 +1,35 @@ + + + + + + + + + Creating a Bot Account | Javacord + + + + +

    Creating a Bot Account

    After you added Javacord as a dependency with your favorite build manager, you should now create a bot account on the Discord website. This article will guide you through the process.

    💡 Create a bot and get its token

    1. Open https://discord.com/developers/applications/meopen in new window and click on "Create an application".

    2. Switch to Bot

    TIP

    If you want to, you can rename your application first

    3. Click on Add bot and confirm the popup

    4. Copy the bot's token. In this case the token would be NDc[...]pCs. You can just click on Copy.

    DANGER

    This token is used to login your bot. Keep it secret!

    5. If you want to, you can change the bot's name and avatar on this page, too.

    ➕ How to add a bot to your server

    Bots cannot join a server on their own like normal Discord users can. Instead, the owner of a server has to invite the bot using a so called Invite Link. There are multiple ways to create the invite link:

    The easiest way to obtain an invite link for your bot is by letting Javacord do it for you. Simply execute the following code, and it will print the invite link to your console:

    DiscordApi api = new DiscordApiBuilder().setToken("your token").login().join();
    +System.out.println(api.createBotInvite());
    +

    If you don't have Javacord setup yet, you can also create the invite link manually.

    Get the client id

    In order to add a bot to your server you need its client id.

    You can get your client id from the same pageopen in new window where you created it.

    With this id you can create an invite link for your bot.

    If you are the owner or admin of the server, you can use this link to add your bot to your server. Otherwise, you have to give the link to the server owner/admins and ask them to add your bot.

    TIP

    Unlike the token, you don't have to keep your client id secret

    Create the url

    Just use the following link and replace 123456789 with your own client id.

    https://discord.com/api/oauth2/authorize?client_id=123456789&scope=applications.commands%20bot&permissions=0

    You can calculate the permissions (in the link above it's the 0) on the page where you created the bot:

    You can now open the link and add the bot to your server:

    TIP

    Only the owner and admins of a server can invite bots. If you do not own a server yet, it is recommended to create one for testing.

    + + + diff --git a/wiki/getting-started/download-installation.html b/wiki/getting-started/download-installation.html new file mode 100644 index 0000000..888fbd8 --- /dev/null +++ b/wiki/getting-started/download-installation.html @@ -0,0 +1,69 @@ + + + + + + + + + Download / Installation | Javacord + + + + +

    Download / Installation

    The recommended way to get Javacord is to use a build manager, like Gradle or Maven.
    If you are not familiar with build managers, you can follow one of the beginner ide setup guides (see navigation) or download Javacord directly from GitHubopen in new window.

    📦 Javacord Dependency

    repositories { mavenCentral() }
    +dependencies { implementation 'org.javacord:javacord:$latest-version' }
    +
    <dependency>
    +    <groupId>org.javacord</groupId>
    +    <artifactId>javacord</artifactId>
    +    <version>$latest-version</version>
    +    <type>pom</type>
    +</dependency>
    +
    libraryDependencies ++= Seq("org.javacord" % "javacord" % "$latest-version")
    +
    Click to view snapshot repositories

    Snapshots are automatically deployed from the developmentopen in new window branch.

    repositories {
    +    maven {
    +        url "https://oss.sonatype.org/content/repositories/snapshots/"
    +    }
    +}
    +dependencies {
    +    implementation 'org.javacord:javacord:$latest-snapshot-version'
    +}
    +
    <repository>
    +    <id>snapshots-repo</id>
    +    <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
    +</repository>
    +
    <dependency>
    +    <groupId>org.javacord</groupId>
    +    <artifactId>javacord</artifactId>
    +    <version>$latest-snapshot-version</version>
    +    <type>pom</type>
    +</dependency>
    +
    resolvers += "snapshots-repo" at "https://oss.sonatype.org/content/repositories/snapshots/"
    +libraryDependencies ++= Seq("org.javacord" % "javacord" % "$latest-snapshot-version")
    +

    📝 Optional Logger Dependency

    In addition to Javacord, it is also recommended to install a Log4j-2-compatible logging framework. A logging framework can be used to provide a more sophisticated logging experience with being able to configure log format, log targets (console, file, database, Discord direct message, ...), log levels per class, and much more.

    For example, Log4j Core:

    dependencies { runtimeOnly 'org.apache.logging.log4j:log4j-core:2.17.0' }
    +
    <dependency>
    +    <groupId>org.apache.logging.log4j</groupId>
    +    <artifactId>log4j-core</artifactId>
    +    <version>2.17.0</version>
    +</dependency>
    +
    libraryDependencies ++= Seq("org.apache.logging.log4j" % "log4j-core" % "2.17.0")
    +

    Take a look at the logger configuration wiki article for further information.

    + + + diff --git a/wiki/getting-started/faq.html b/wiki/getting-started/faq.html new file mode 100644 index 0000000..176c6f2 --- /dev/null +++ b/wiki/getting-started/faq.html @@ -0,0 +1,44 @@ + + + + + + + + + Frequently Asked Questions | Javacord + + + + +

    Frequently Asked Questions

    Here you will find answers to some of the most asked questions.

    Q: Why do I receive empty (no content) messages in i.e. the MessageCreateListener?

    You are missing the privileged MESSAGE_CONTENT intent. For more information of how to enable privileged intents and enable them in your code see Gateway Intents.

    Q: What is ... in the code examples?

    You have to replace the ... with an instance that can be assigned to the datatype seen left.

    For example, if you see TextChannel channel = ..., you have to replace ... with an instance that is a TextChannel which you can get from the API api.getTextChannelById(CHANNEL_ID) (note this returns an Optional<TextChannel>) or from an event like messageCreateEvent.getChannel().

    Q: Why is my code not working?

    There are multiple reasons why your code might not work. The most common ones are:

    1. Your code is not being reached. So make sure your code actually gets executed with a print statement or a debugger.
    2. Add at least .exceptionally(ExceptionLogger.get()) to every CompletableFuture (like when sending a message) to show any exceptions that might come from Discord.
    3. Methods like User#getRoles(Server) do not return the roles of the user. To fix this make sure to add the GUILD_MEMBERS intent.
    4. You are getting a NoSuchElementException. Congratulations, you have killed a kitten! You are most likely getting this Exception because you handle Optionals wrong. Read the article on Optionals to learn how to use them correctly.

    If none of these tips will help you, you can ask your question in our Discord Serveropen in new window.

    How to properly ask a question to get fast support?

    Don't ask:

    Why is my code not working?
    +//Code
    +
    Why am I getting Exception X?
    +

    To ensure all information is provided that is needed to solve your issue, you should ask your question in a format like:

    I have an issue with:   YOUR_ISSUE
    +I want to do:           WHAT_YOU_WANT_TO_DO
    +Currently this happens: WHAT_HAPPENS_NOW
    +
    +//Code
    +
    +//Exception
    +The exception is thrown in the following line(not the number): CODE_LINE
    +

    Q: What differs Javacord from JDA and D4J?

    While all 3 libraries are Wrappers for the programming language Java, they use different techniques and concepts for their API.

    • Javacord: Uses Java classes for its API like CompletableFuture for async requests and Optional for return types which may be null.
      • Sending a Message: channel.sendMessage("Javacord")
      • Checking if the Author of a message is a user: message.getMessageAuthor().asUser().isPresent()
    • JDA: Has its own wrapper to execute requests and returns null if values are not present.
      • Sending a Message: channel.sendMessage("JDA").queue()
      • Checking if the Author of a message is a user: message.getMember() != null
    • Discord4J: Takes on the reactive approach.
      • Sending a Message: channel.createMessage("Pong!").block();
    + + + diff --git a/wiki/getting-started/setup/eclipse-maven.html b/wiki/getting-started/setup/eclipse-maven.html new file mode 100644 index 0000000..4d114d4 --- /dev/null +++ b/wiki/getting-started/setup/eclipse-maven.html @@ -0,0 +1,78 @@ + + + + + + + + + Eclipse + Maven | Javacord + + + + +

    Eclipse + Maven

    This tutorial provides a beginner-friendly click by click guide to set up Javacord with Eclipse and Maven. If you are already familiar with Eclipse and Maven, you can just see the artifact locations at [Download / Installation](/wiki/getting-started/download-installation.md).

    Info

    We recommend to use Intellij + Gradle unless you already have experience with one of the other IDEs or build managers.

    🔧 Setup

    1. Start Eclipse

    2. Create a new project (File -> New -> Project)

    3. Select Maven Project

    4. Click Next

    5. Check Create a simple project

    6. Click Next

    7. Enter a group id (e.g. com.github.yourname)

    8. Enter an artifact id (e.g. myfirstbot)

    9. Click Finish

    10. Double click on the pom.xml file

    11. Select pom.xml

    12. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    +<project xmlns="http://maven.apache.org/POM/4.0.0"
    +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    +    <modelVersion>4.0.0</modelVersion>
    +
    +    <groupId>your.package.name</groupId>
    +    <artifactId>myfirstbot</artifactId>
    +    <version>1.0-SNAPSHOT</version>
    +
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.javacord</groupId>
    +            <artifactId>javacord</artifactId>
    +            <version>$latest-version</version>
    +            <type>pom</type>
    +        </dependency>
    +    </dependencies>
    +
    +</project>
    +

    13. Create a new package inside the src/main/java folder

    14. Create a new class inside this package

    15. Save the project (you should do this from time to time)

    16. Now you can start coding! Example code:

    package com.github.yourname.myfirstbot;
    +
    +import org.javacord.api.DiscordApi;
    +import org.javacord.api.DiscordApiBuilder;
    +
    +public class Main {
    +
    +    public static void main(String[] args) {
    +        // Insert your bot's token here
    +        String token = "your token";
    +
    +        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +
    +        // Print the invite url of your bot
    +        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
    +    }
    +
    +}
    +

    🏃‍♀️ Run the code

    You can run your code by clicking on the small green arrow

    + + + diff --git a/wiki/getting-started/setup/intellij-gradle.html b/wiki/getting-started/setup/intellij-gradle.html new file mode 100644 index 0000000..edb44be --- /dev/null +++ b/wiki/getting-started/setup/intellij-gradle.html @@ -0,0 +1,74 @@ + + + + + + + + + IntelliJ + Gradle | Javacord + + + + +

    IntelliJ + Gradle

    This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Gradle. If you are already familiar with IntelliJ and Gradle, you can just see the artifact locations at Download / Installation.

    🔧 Setup

    1. Start IntelliJ

    2. Create a new project (File -> New -> Project)

    3. Select Gradle

    4. Make sure to select an SDK which is 1.8 (or greater)

    5. Click Next

    6. Enter a group id (e.g. com.github.yourname)

    You can choose whatever you want

    7. Enter an artifact id (e.g. myfirstbot)

    You can choose whatever you want

    8. Click Next

    9. Check Use auto-import

    10. Click Next

    11. Click Finish

    12. Locate the build.gradle file and open it

    12. Add the Javacord dependency. Your build.gradle file should now look like this

    plugins {
    +    id 'java'
    +}
    +
    +group 'com.github.yourname'
    +version '1.0-SNAPSHOT'
    +
    +sourceCompatibility = 1.8
    +
    +repositories {
    +    mavenCentral()
    +}
    +
    +dependencies {
    +    implementation 'org.javacord:javacord:$latest-version'
    +}
    +

    13. Create a new package in the src/main/java folder

    14. Create a new class inside this package

    15. You can now start coding!

    Example code:

    package com.github.yourname;
    +
    +import org.javacord.api.DiscordApi;
    +import org.javacord.api.DiscordApiBuilder;
    +
    +public class Main {
    +
    +    public static void main(String[] args) {
    +        // Insert your bot's token here
    +        String token = "your token";
    +
    +        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +
    +        // Print the invite url of your bot
    +        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
    +    }
    +
    +}
    +

    🏃‍♀️ Run the code

    You can run your code by clicking on the small green arrow

    + + + diff --git a/wiki/getting-started/setup/intellij-maven.html b/wiki/getting-started/setup/intellij-maven.html new file mode 100644 index 0000000..9d33c3e --- /dev/null +++ b/wiki/getting-started/setup/intellij-maven.html @@ -0,0 +1,78 @@ + + + + + + + + + IntelliJ + Maven | Javacord + + + + +

    IntelliJ + Maven

    This tutorial provides a beginner-friendly click by click guide to set up Javacord with Intellij and Maven. If you are already familiar with IntelliJ and Maven, you can just see the artifact locations at Download / Installation.

    Info

    We recommend to use Intellij + Gradle unless you already have experience with one of the other IDEs or build managers.

    🔧 Setup

    1. Start IntelliJ

    2. Create a new project (File -> New -> Project)

    3. Select Maven

    4. Make sure to select an SDK which is 1.8 (or greater)

    5.* Click Next

    6. Enter a group id (e.g. com.github.yourname)

    7. Enter an artifact id (e.g. myfirstbot)

    8. Click Next

    9. Click on Finish

    10. Your project should now look like this. First click on Enable Auto-Import

    11. Now you have to add Javacord as a dependency by editing the pom.xml file. Your file should now look like this:

    <?xml version="1.0" encoding="UTF-8"?>
    +<project xmlns="http://maven.apache.org/POM/4.0.0"
    +         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    +         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    +    <modelVersion>4.0.0</modelVersion>
    +
    +    <groupId>your.package.name</groupId>
    +    <artifactId>myfirstbot</artifactId>
    +    <version>1.0-SNAPSHOT</version>
    +
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.javacord</groupId>
    +            <artifactId>javacord</artifactId>
    +            <version>$latest-version</version>
    +            <type>pom</type>
    +        </dependency>
    +    </dependencies>
    +
    +</project>
    +

    12. Create a new package

    13. Create a new class inside this package

    14. You can now start coding! Example code:

    package com.github.yourname;
    +
    +import org.javacord.api.DiscordApi;
    +import org.javacord.api.DiscordApiBuilder;
    +
    +public class Main {
    +
    +    public static void main(String[] args) {
    +        // Insert your bot's token here
    +        String token = "your token";
    +
    +        DiscordApi api = new DiscordApiBuilder().setToken(token).login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +
    +        // Print the invite url of your bot
    +        System.out.println("You can invite the bot by using the following url: " + api.createBotInvite());
    +    }
    +    
    +}
    +

    🏃‍♀️ Run the code

    You can run your code by clicking on the small green arrow

    🚧 Possible problems

    Note: If you get the following error:

    you have to change your language level to 1.8

    + + + diff --git a/wiki/getting-started/writing-your-first-bot.html b/wiki/getting-started/writing-your-first-bot.html new file mode 100644 index 0000000..8d504b3 --- /dev/null +++ b/wiki/getting-started/writing-your-first-bot.html @@ -0,0 +1,60 @@ + + + + + + + + + Writing your first bot | Javacord + + + + +

    Writing your first bot

    After you have successfully added Javacord as a dependency, created a bot user, and got its token, you are now ready to create your first simple bot! 🎉

    ❗ Enabling required intents

    By default, all non-privileged intents are enabled. To receive the message content, attachments, components, and embeds you need a special privileged intent MESSAGE_CONTENT. To enable this privileged intent please see the Gateway Intents wiki article.

    Slash Commands

    Generally it is recommended to use Slash Commands instead of text commands because they offer many advantages like auto-completion, fixed and optional arguments, different kind of arguments with built-in types: numbers(with ranges), text, channel and a lot more.

    🔑 Log the bot in

    Everything starts with the DiscordApiBuilder class. It is used to create a DiscordApi object which is the most important class of your bot.

    DiscordApi api = new DiscordApiBuilder()
    +        .setToken("<your super secret token>")
    +        .addIntents(Intent.MESSAGE_CONTENT)
    +        .login().join();
    +

    After executing this code, you should already see your bot online in Discord. Of course, just being online is not enough, so let's add some more code!

    👂 Adding a listener

    After you got your api instance, let's continue by adding a listener that answers every !ping message with a simple Pong!.

    api.addMessageCreateListener(event -> {
    +    if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +        event.getChannel().sendMessage("Pong!");
    +    }
    +});
    +

    👩‍🔧 Putting it all together

    A good place for your code is the main(...) method that every executable Java program must have. Your complete class may look like this:

    public class MyFirstBot {
    +
    +    public static void main(String[] args) {
    +        // Log the bot in
    +        DiscordApi api = new DiscordApiBuilder()
    +                .setToken("<your super secret token>")
    +                .addIntents(Intent.MESSAGE_CONTENT)
    +                .login().join();
    +
    +        // Add a listener which answers with "Pong!" if someone writes "!ping"
    +        api.addMessageCreateListener(event -> {
    +            if (event.getMessageContent().equalsIgnoreCase("!ping")) {
    +                event.getChannel().sendMessage("Pong!");
    +            }
    +        });
    +    }
    +
    +}
    +

    Congratulations, that's already everything you have to know for the beginning. Now, you can play around a little bit by exploring other listeners and methods. Or you just continue reading articles in the Basic Tutorials category.

    + + + diff --git a/wiki/index.html b/wiki/index.html new file mode 100644 index 0000000..feafd14 --- /dev/null +++ b/wiki/index.html @@ -0,0 +1,33 @@ + + + + + + + + + Introduction | Javacord + + + + +

    Introduction

    Welcome to the Javacord wiki! 👋

    This wiki will help you to get started with your first Discord bot as fast as possible.

    📚 Structure of the wiki

    The wiki is divided into four groups:

    • Getting Started focuses on teaching you how to setup up everything to get the most basic bot working.
    • Basic tutorials contains articles about various concepts and classes of Javacord. Take a look at the headlines of each article and decide yourself, if it is relevant for you.
    • Advanced Topics focuses on some more advanced topics that are not strictly necessary to start working with Javacord, but might become handy later on.
    • Essential Knowledge teaches you the most important Java features/classes that you should know to comfortably work with Javacord. If you already have decent Java knowledge, you can skip this completely.

    🤝 Support

    While the wiki is great and covers many aspects of Javacord, we highly recommended you to join our Discord server if you have any questions:

    + + +