auto-champion-select 1.8.10 → 1.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode('.dropdown-champions-default{position:inherit;inline-size:100%}.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-menu),.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-content),.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-options){top:auto;bottom:100%;transform-origin:bottom}.dropdown-champions-default .controlado-champion-option__content{display:inline-flex;align-items:center;gap:8px;width:100%;min-width:0;line-height:20px;vertical-align:middle}.dropdown-champions-default .controlado-champion-option__icon{flex:0 0 20px;width:20px;height:20px;border:1px solid rgba(200,170,110,.65);border-radius:50%;background:#010a13;object-fit:cover;box-sizing:border-box;pointer-events:none}.dropdown-champions-default .controlado-champion-option__name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.champion-priority-selector{display:grid;gap:0;padding:2px 0 3px}.champion-priority-selector__scroll{position:relative;inline-size:100%;height:58px;border:1px solid rgba(120,90,40,.7);border-top:0;background:rgba(30,35,40,.5);box-sizing:border-box}.champion-priority-selector__track{display:inline-flex;align-items:center;gap:12px;min-width:max-content;min-height:45px;padding:10px 8px 8px;box-sizing:border-box}.champion-priority-selector__empty{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;color:#5f5b4f;font-family:var(--font-body);font-size:11px;line-height:13px;text-align:center;white-space:nowrap;pointer-events:none}.champion-priority-selector__icon{position:relative;flex:0 0 auto;width:34px;height:34px;border:1px solid #c8aa6e;border-radius:50%;background:#1e2328;box-shadow:0 0 0 1px #010a13e6;box-sizing:border-box;cursor:grab;outline:none}.champion-priority-selector__icon:focus{border-color:#f0e6d2}.champion-priority-selector__icon--dragging{cursor:grabbing;filter:brightness(1.15);z-index:1}.champion-priority-selector__icon:before,.champion-priority-selector__remove,.champion-priority-selector__position-badge{display:grid;place-items:center;width:17px;height:17px;padding:0;border-radius:50%;box-sizing:border-box;font-family:var(--font-body);font-size:10px;font-weight:700;line-height:1}.champion-priority-selector__icon:before{content:attr(data-rank);position:absolute;left:-6px;top:-6px;border:1px solid #c8aa6e;background:#010a13;color:#f0e6d2;text-align:center;pointer-events:none}.champion-priority-selector__icon img{display:block;width:100%;height:100%;border-radius:50%;object-fit:cover;pointer-events:none}.champion-priority-selector__remove{position:absolute;right:-6px;bottom:-6px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid #785a28;background:#1e2328;color:#c8aa6e;cursor:pointer;font-family:var(--font-display);font-size:0;text-align:center}.champion-priority-selector__remove:before{content:attr(data-icon);font-family:var(--font-display);font-size:7px;line-height:1;transform:translate(.4px,-.1px)}.champion-priority-selector__remove:hover{border-color:#c8aa6e;color:#f0e6d2}.champion-priority-selector__position-badge{position:absolute;left:-6px;bottom:-6px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid #785a28;background:#010a13;color:#c8aa6e;cursor:pointer}.champion-priority-selector__position-badge:hover,.champion-priority-selector__position-badge:focus{border-color:#c8aa6e;color:#f0e6d2;outline:none}.champion-priority-selector__position-badge--empty{background:#1e2328;font-size:0}.champion-priority-selector__position-badge--empty:before{content:"+";font-family:var(--font-body);font-size:12px;line-height:1;transform:translate(.25px,.4px)}.champion-priority-selector__position-badge img{display:block;width:11px;height:11px;object-fit:contain;transform:translate(.1px)}.champion-priority-selector__position-badge:after{content:attr(data-count);position:absolute;right:-5px;top:-5px;display:none;place-items:center;width:11px;min-width:11px;height:11px;border:1px solid #785a28;border-radius:50%;background:#010a13;box-sizing:border-box;color:#f0e6d2;font-family:var(--font-body);font-size:7px;font-weight:700;line-height:1;text-align:center}.champion-priority-selector__position-badge[data-count]:after{display:grid}.champion-priority-selector__position-menu{position:fixed;z-index:10002;display:flex;gap:4px;padding:5px;border:1px solid #785a28;background:#010a13;box-shadow:0 0 0 1px #010a13d9,0 4px 16px #0000008c;box-sizing:border-box}.champion-priority-selector__position-menu[hidden]{display:none}.champion-priority-selector__position-option{display:grid;place-items:center;width:28px;height:28px;padding:0;border:1px solid #463714;background:#1e2328;box-sizing:border-box;cursor:pointer}.champion-priority-selector__position-option:hover,.champion-priority-selector__position-option--selected{border-color:#c8aa6e;background:#0f1b2d}.champion-priority-selector__position-option img{display:block;width:18px;height:18px;pointer-events:none}.auto-select-checkboxes-div{display:flex;border-top:thin solid #1e282d}.auto-select-checkbox{margin:auto}.auto-select-checkbox:last-child{margin-right:17px}.auto-select-champ-select-menu{position:absolute;right:0;bottom:36px;z-index:10000;width:min(340px,calc(100vw - 32px));color:#a09b8c;font-family:var(--font-body);font-size:12px;font-weight:400;line-height:normal;pointer-events:none;text-transform:none}.auto-select-champ-select-menu-button-wrapper{position:relative;display:block;width:38px;height:32px;flex:0 0 38px;box-sizing:border-box}.auto-select-champ-select-menu__header{position:relative;display:block;width:38px;height:32px;border:none;background-image:url(/fe/lol-social/chat_button.png);background-color:transparent;background-position:0 0;background-repeat:no-repeat;background-size:cover;box-shadow:none;cursor:pointer;outline:none;padding:0}.auto-select-champ-select-menu__header:before{content:"";position:absolute;left:50%;top:50%;width:20px;height:20px;background:url(/fe/lol-static-assets/images/nav-icon-collections.svg) center / 20px 20px no-repeat;filter:drop-shadow(0 0 1px #010a13);transform:translate(-50%,-50%)}@keyframes auto-select-champ-select-menu-mark-pulse{0%{filter:brightness(1);box-shadow:#c89b3c 0 0}15%{filter:brightness(1);box-shadow:#c89b3c 0 0}to{filter:brightness(2);box-shadow:#c89b3c 0 0 12px 2px}}.auto-select-champ-select-menu__header:after{content:"";position:absolute;right:5px;top:5px;width:6px;height:6px;border:0;border-radius:50%;background:#c89b3c;box-sizing:border-box;pointer-events:none;animation:auto-select-champ-select-menu-mark-pulse .9s ease-in-out infinite alternate}.auto-select-champ-select-menu__header:hover{background-position:0 -32px}.auto-select-champ-select-menu__header:active{background-position:0 -64px}.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header{background-position:0 -96px}.auto-select-champ-select-menu-button-wrapper--open{z-index:10000}.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header:hover{background-position:0 -128px}.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header:active{background-position:0 -160px}.auto-select-champ-select-menu__content{display:grid;gap:4px;width:100%;margin-bottom:4px;border:1px solid #785a28;background:rgba(1,10,19,.96);box-shadow:0 0 18px #000000a6;box-sizing:border-box;pointer-events:auto;padding:8px}.auto-select-champ-select-menu__title{display:flex;align-items:center;justify-content:center;min-height:24px;padding-bottom:4px;border-bottom:thin solid #1e282d;color:#f0e6d2;font-family:var(--font-display);font-size:14px;font-weight:700;text-align:center}.auto-select-champ-select-menu .auto-select-checkboxes-div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));align-items:center;border-top:0;border-bottom:thin solid #1e282d;padding-bottom:6px}.auto-select-champ-select-menu .auto-select-checkbox{justify-self:center;margin:0}.auto-select-champ-select-menu .dropdown-champions-default{min-height:32px}.auto-select-champ-select-menu .champion-priority-selector__scroll{height:56px}.auto-select-champ-select-menu--collapsed .auto-select-champ-select-menu__content{display:none}')),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode('.dropdown-champions-default{position:inherit;inline-size:100%}.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-menu),.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-content),.dropdown-champions-default.dropdown-drop-up::part(ui-dropdown-options){top:auto;bottom:100%;transform-origin:bottom}.dropdown-champions-default .controlado-champion-option__content{display:inline-flex;align-items:center;gap:8px;width:100%;min-width:0;line-height:20px;vertical-align:middle}.dropdown-champions-default .controlado-champion-option__icon{flex:0 0 20px;width:20px;height:20px;border:1px solid rgba(200,170,110,.65);border-radius:50%;background:#010a13;object-fit:cover;box-sizing:border-box;pointer-events:none}.dropdown-champions-default .controlado-champion-option__name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.champion-priority-selector{display:grid;gap:0;padding:2px 0 3px}.champion-priority-selector__scroll{position:relative;inline-size:100%;height:58px;border:1px solid rgba(120,90,40,.7);border-top:0;background:rgba(30,35,40,.5);box-sizing:border-box}.champion-priority-selector__track{display:inline-flex;align-items:center;gap:12px;min-width:max-content;min-height:45px;padding:10px 8px 8px;box-sizing:border-box}.champion-priority-selector__empty{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;color:#5f5b4f;font-family:var(--font-body);font-size:11px;line-height:13px;text-align:center;white-space:nowrap;pointer-events:none}.champion-priority-selector__icon{position:relative;flex:0 0 auto;width:34px;height:34px;border:1px solid #c8aa6e;border-radius:50%;background:#1e2328;box-shadow:0 0 0 1px #010a13e6;box-sizing:border-box;cursor:grab;outline:none}.champion-priority-selector__icon:focus{border-color:#f0e6d2}.champion-priority-selector__icon--dragging{cursor:grabbing;filter:brightness(1.15);z-index:1}.champion-priority-selector__icon:before,.champion-priority-selector__remove,.champion-priority-selector__position-badge{display:grid;place-items:center;width:17px;height:17px;padding:0;border-radius:50%;box-sizing:border-box;font-family:var(--font-body);font-size:10px;font-weight:700;line-height:1}.champion-priority-selector__icon:before{content:attr(data-rank);position:absolute;left:-6px;top:-6px;border:1px solid #c8aa6e;background:#010a13;color:#f0e6d2;text-align:center;pointer-events:none}.champion-priority-selector__icon img{display:block;width:100%;height:100%;border-radius:50%;object-fit:cover;pointer-events:none}.champion-priority-selector__remove{position:absolute;right:-6px;bottom:-6px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid #785a28;background:#1e2328;color:#c8aa6e;cursor:pointer;font-family:var(--font-display);font-size:0;text-align:center}.champion-priority-selector__remove:before{content:attr(data-icon);font-family:var(--font-display);font-size:7px;line-height:1;transform:translate(.4px,-.1px)}.champion-priority-selector__remove:hover{border-color:#c8aa6e;color:#f0e6d2}.champion-priority-selector__position-badge{position:absolute;left:-6px;bottom:-6px;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid #785a28;background:#010a13;color:#c8aa6e;cursor:pointer}.champion-priority-selector__position-badge:hover,.champion-priority-selector__position-badge:focus{border-color:#c8aa6e;color:#f0e6d2;outline:none}.champion-priority-selector__position-badge--empty{background:#1e2328;font-size:0}.champion-priority-selector__position-badge--empty:before{content:"+";font-family:var(--font-body);font-size:12px;line-height:1;transform:translate(.25px,.4px)}.champion-priority-selector__position-badge img{display:block;width:11px;height:11px;object-fit:contain;transform:translate(.1px)}.champion-priority-selector__position-badge:after{content:attr(data-count);position:absolute;right:-5px;top:-5px;display:none;place-items:center;width:11px;min-width:11px;height:11px;border:1px solid #785a28;border-radius:50%;background:#010a13;box-sizing:border-box;color:#f0e6d2;font-family:var(--font-body);font-size:7px;font-weight:700;line-height:1;text-align:center}.champion-priority-selector__position-badge[data-count]:after{display:grid}.champion-priority-selector__position-menu{position:fixed;z-index:10002;display:flex;gap:4px;padding:5px;border:1px solid #785a28;background:#010a13;box-shadow:0 0 0 1px #010a13d9,0 4px 16px #0000008c;box-sizing:border-box}.champion-priority-selector__position-menu[hidden]{display:none}.champion-priority-selector__position-option{display:grid;place-items:center;width:28px;height:28px;padding:0;border:1px solid #463714;background:#1e2328;box-sizing:border-box;cursor:pointer}.champion-priority-selector__position-option:hover,.champion-priority-selector__position-option--selected{border-color:#c8aa6e;background:#0f1b2d}.champion-priority-selector__position-option img{display:block;width:18px;height:18px;pointer-events:none}.auto-select-checkboxes-div{display:flex;border-top:thin solid #1e282d}.auto-select-checkbox{margin:auto}.auto-select-checkbox:last-child{margin-right:17px}.auto-select-champ-select-menu{position:absolute;right:0;bottom:36px;z-index:10000;width:min(340px,calc(100vw - 32px));color:#a09b8c;font-family:var(--font-body);font-size:12px;font-weight:400;line-height:normal;pointer-events:none;text-transform:none}.auto-select-champ-select-menu-button-wrapper{position:relative;display:block;width:38px;height:32px;flex:0 0 38px;box-sizing:border-box}.auto-select-champ-select-menu__header{position:relative;display:block;width:38px;height:32px;border:1px solid #785a28;background:#1e2328;box-shadow:inset 0 0 0 1px #010a13;box-sizing:border-box;cursor:pointer;outline:none;padding:0}.auto-select-champ-select-menu__header:before{content:"";position:absolute;left:50%;top:50%;width:20px;height:20px;background-color:#c8aa6e;-webkit-mask:url(/fe/lol-static-assets/images/nav-icon-collections.svg) center / 20px 20px no-repeat;mask:url(/fe/lol-static-assets/images/nav-icon-collections.svg) center / 20px 20px no-repeat;pointer-events:none;transform:translate(-50%,-50%)}.auto-select-champ-select-menu__header:hover{border-color:#c8aa6e;background:#2a3038}.auto-select-champ-select-menu__header:hover:before{background-color:#f0e6d2}.auto-select-champ-select-menu__header:active,.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header{border-color:#c89b3c;background:#111820}.auto-select-champ-select-menu-button-wrapper--open{z-index:10000}.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header:hover{border-color:#c8aa6e;background:#1e2328}.auto-select-champ-select-menu-button-wrapper--open .auto-select-champ-select-menu__header:before{background-color:#f0e6d2}.auto-select-champ-select-menu__content{display:grid;gap:4px;width:100%;margin-bottom:4px;border:1px solid #785a28;background:rgba(1,10,19,.96);box-shadow:0 0 18px #000000a6;box-sizing:border-box;pointer-events:auto;padding:8px}.auto-select-champ-select-menu__title{display:flex;align-items:center;justify-content:center;min-height:24px;padding-bottom:4px;border-bottom:thin solid #1e282d;color:#f0e6d2;font-family:var(--font-display);font-size:14px;font-weight:700;text-align:center}.auto-select-champ-select-menu .auto-select-checkboxes-div{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));align-items:center;border-top:0;border-bottom:thin solid #1e282d;padding-bottom:6px}.auto-select-champ-select-menu .auto-select-checkbox{justify-self:center;margin:0}.auto-select-champ-select-menu .dropdown-champions-default{min-height:32px}.auto-select-champ-select-menu .champion-priority-selector__scroll{height:56px}.auto-select-champ-select-menu--collapsed .auto-select-champ-select-menu__content{display:none}')),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
2
2
  /**
3
3
  * @author balaclava
4
4
  * @name auto-champion-select
5
5
  * @link https://github.com/controlado/auto-champion-select#readme
6
6
  * @description Pick or ban automatically! 🐧
7
- * @version 1.8.10
7
+ * @version 1.8.11
8
8
  * @license GPL-3.0-or-later
9
9
  */
10
- import{request as w,sleep as g,linkEndpoint as z}from"https://cdn.jsdelivr.net/npm/balaclava-utils@latest";const Pt="1.8.10",It={enabled:!1},kt={enabled:!1,force:!1,quickAction:!1,champions:[432,235],positionsByChampionId:{235:["BOTTOM","UTILITY"],432:["UTILITY"]}},Ot={enabled:!1,force:!1,quickAction:!1,champions:[157,777,238,38]},Tt={controladoAutoAccept:It,controladoPick:kt,controladoBan:Ot};function d(o){const t=Number(o);return Number.isSafeInteger(t)&&t>0?t:null}function at(o,t=null){const e=[],n=new Set,i=_t(t);if(!Array.isArray(o))return e;for(const s of o){const r=d(s);r===null||n.has(r)||i&&!i.has(r)||(e.push(r),n.add(r))}return e}function _t(o){return o?new Set(Array.from(o,d).filter(t=>t!==null)):null}const k=[{value:"TOP",label:"Top",iconPath:"/fe/lol-parties/icon-position-top.png"},{value:"JUNGLE",label:"Jungle",iconPath:"/fe/lol-parties/icon-position-jungle.png"},{value:"MIDDLE",label:"Mid",iconPath:"/fe/lol-parties/icon-position-middle.png"},{value:"BOTTOM",label:"ADC",iconPath:"/fe/lol-parties/icon-position-bottom.png"},{value:"UTILITY",label:"Support",iconPath:"/fe/lol-parties/icon-position-utility.png"}],Lt=new Set(k.map(o=>o.value));function T(o){const t=String(o??"").toUpperCase();return Lt.has(t)?t:null}function x(o,t=null){const e={};if(!o||typeof o!="object"||Array.isArray(o))return e;const n=vt(t);for(const[i,s]of Object.entries(o)){const r=d(i),a=String(r);if(r===null||n&&!n.has(a)||!Array.isArray(s))continue;const c=[],h=new Set;for(const u of s){const C=T(u);!C||h.has(C)||(c.push(C),h.add(C))}c.length>0&&(e[a]=c)}return e}function vt(o){if(!o)return null;const t=new Set;for(const e of o){const n=d(e);n!==null&&t.add(String(n))}return t}function U(o){return k.find(t=>t.value===o)}function ct(o,t){var n;const e=d(t);return e===null?[]:((n=o==null?void 0:o.positionsByChampionId)==null?void 0:n[String(e)])||[]}function Mt(o,t,e){const n=ct(o,t);if(n.length===0)return!0;const i=T(e);return i!==null&&n.includes(i)}const G=new Set;function Rt(o,t){return Object.prototype.hasOwnProperty.call(o,t)}function P(o,t){return Rt(o,t)}function $(){if(!globalThis.DataStore)throw new Error("DataStore is not available");return globalThis.DataStore}function xt(o){return o!==null&&typeof o=="object"&&!Array.isArray(o)}function F(o){if(o!==void 0)return JSON.parse(JSON.stringify(o))}function lt(o){const t=Tt[o];if(!t)throw new Error(`Unknown auto-champion-select config key: ${o}`);return F(t)}function dt(o,t,e={}){const n=lt(o),i=xt(t)?F(t):{},s={...n,...i},r={enabled:s.enabled===!0};return P(n,"force")&&(r.force=s.force===!0),P(n,"quickAction")&&(r.quickAction=s.quickAction===!0),P(n,"champions")&&(r.champions=at(s.champions,e.allowedChampionIds)),P(n,"positionsByChampionId")&&(r.positionsByChampionId=x(s.positionsByChampionId,e.selectedChampionIds||r.champions)),r}function ht(o,t,e={}){const n=t===void 0?lt(o):t;return dt(o,n,e)}function b(o,t={}){const n=$().get(o);return ht(o,n,t)}function Bt(o,t,e={}){const n=$(),i=dt(o,t,e);return n.set(o,i),i}function Dt(o,t){return JSON.stringify(o)===JSON.stringify(t)}function Nt(o,t,e){G.has(o)||(G.add(o),console.warn(`auto-champion-select: Repaired ${o} in DataStore. The stored value used a different schema; plugins like reynbow/auto-champ-lock write this shape. Saved normalized config.`,{storedConfig:t,normalizedConfig:e}))}function ut(o,t={}){const e=$(),n=e.get(o),i=ht(o,n,t);return(n===void 0||!Dt(n,i))&&(n!==void 0&&Nt(o,n,i),e.set(o,i)),i}function O(o,t,e={}){const n=b(o,e),i=F(n),s=t(i)||i;return Bt(o,s,e)}const p=Object.freeze({readyCheckAccept:"/lol-matchmaking/v1/ready-check/accept",gameflowPhase:"/lol-gameflow/v1/gameflow-phase",wallet:"/lol-inventory/v1/wallet",ownedChampions:"/lol-champions/v1/owned-champions-minimal",championSummary:"/lol-game-data/assets/v1/champion-summary.json",champSelectSession:"/lol-champ-select/v1/session",championIcon(o){return`/lol-game-data/assets/v1/champion-icons/${o}.png`},champSelectAction(o){return`/lol-champ-select/v1/session/actions/${o}`}}),$t=1e3;function pt(o){let t=null;return function(){return t||(t=Promise.resolve().then(o).finally(()=>{t=null})),t}}function mt(o){const t=[],e=new Set;if(!Array.isArray(o))return t;for(const n of o){if(!n||typeof n!="object")continue;const i=d(n.id),s=typeof n.name=="string"?n.name:"";i===null||!s||e.has(s)||(t.push({...n,id:i,name:s,squarePortraitPath:n.squarePortraitPath||p.championIcon(i)}),e.add(s))}return t.sort((n,i)=>n.name.localeCompare(i.name)),t}async function Ft(){let o=await w("GET",p.ownedChampions);for(;!o.ok;)console.debug("auto-champion-select(owned-champions-minimal): Retrying..."),o=await w("GET",p.ownedChampions),await g($t);return mt(await o.json())}async function qt(){const o=await w("GET",p.championSummary);return mt(await o.json())}const zt=pt(Ft),Ut=pt(qt),H=300,Y=2e3,Gt=4e3,f=Object.freeze({APPLIED:"applied",TRY_NEXT_CHAMPION:"try-next-champion",ABORT_ACTION:"abort-action"});function B(o){return Array.isArray(o)?o.flat():[]}function Ht(o){return B(o).filter(t=>t.type==="ban"&&t.completed===!0).map(t=>t.championId)}function Q(o){return Array.isArray(o)?o:[]}function _(o){const t=new Set;for(const e of o){const n=d(e);n!==null&&t.add(n)}return t}function W(o){return o.type==="pick"?0:1}function j(){return{pickConfig:b("controladoPick"),banConfig:b("controladoBan")}}function Yt(o){return o.quickAction===!0}function Qt(){const o=Gt-Y;return Y+Math.floor(Math.random()*(o+1))}class Wt{constructor(){this.session=null,this.actions=null,this.localPlayerCellId=null,this.teammateIntentChampionIds=new Set,this.localPlayerIntentChampionId=null,this.pickedChampionIds=new Set,this.bannedChampionIds=new Set,this.localPlayerAssignedPosition=null,this.watchTask=null,this.watchVersion=0,this.mounted=!1}mount(){this.mounted||(this.mounted=!0,this.watchVersion+=1,this.watchTask||(this.watchTask=this.watch()))}unmount(){this.mounted&&(this.mounted=!1,this.watchVersion+=1)}async watch(){try{for(;this.mounted;){const t=this.watchVersion;let e=!1;try{await this.refreshSessionState(),e=!0}catch(n){console.debug("auto-champion-select: Failed to update champion select",n)}if(!e||!this.mounted||t!==this.watchVersion){this.mounted&&t===this.watchVersion&&await g(H);continue}try{await this.runAutoSelectCycle()}catch(n){console.debug("auto-champion-select: Failed to run champion select cycle",n)}this.mounted&&t===this.watchVersion&&await g(H)}}finally{this.watchTask=null}}async refreshSessionState(){var s,r;const t=await w("GET",p.champSelectSession);if(!t.ok)throw new Error(`Session request failed with status ${t.status}`);this.session=await t.json(),this.actions=this.session.actions,this.localPlayerCellId=this.session.localPlayerCellId;const e=Q(this.session.myTeam),n=Q(this.session.theirTeam),i=e.find(a=>a.cellId===this.localPlayerCellId);this.localPlayerAssignedPosition=T(i==null?void 0:i.assignedPosition),this.localPlayerIntentChampionId=d(i==null?void 0:i.championPickIntent),this.pickedChampionIds=_([...e,...n].map(a=>a.championId)),this.bannedChampionIds=_([...((s=this.session.bans)==null?void 0:s.myTeamBans)||[],...((r=this.session.bans)==null?void 0:r.theirTeamBans)||[],...Ht(this.actions)]),this.teammateIntentChampionIds=_(e.map(a=>a.championPickIntent))}async runAutoSelectCycle(){const{pickConfig:t,banConfig:e}=j();if(!t.enabled&&!e.enabled)return;const n=this.getLocalPlayerActions();if(n.length===0){console.debug("auto-champion-select: No local player sub actions found, skipping..."),this.unmount();return}for(const i of n){if(!this.isActionAvailable(i))continue;const s=this.getConfigForActionType(i.type,{pickConfig:t,banConfig:e});if(!(s!=null&&s.enabled))continue;let r=i,a=s,c=this.getAvailableChampionIdsForAction(r,a);if(c.length===0)continue;const h=c.find(u=>this.isPickIntentAlreadyApplied(r,u));if(h!==void 0)c=[h];else if(!Yt(a)){const u=await this.delayAndRefreshPendingAction(r);if(!u)return;r=u.action,a=u.config,c=u.championIds}for(const u of c){const C=await this.attemptChampionForAction(r,u,a);if(C===f.APPLIED)break;if(C===f.ABORT_ACTION)return}}}getLocalPlayerActions(){return B(this.actions).filter(t=>this.isPendingLocalPlayerAction(t)).sort((t,e)=>W(t)-W(e))}isPendingLocalPlayerAction(t){return t.actorCellId===this.localPlayerCellId&&t.completed===!1}isActionAvailable(t){return t.type==="pick"||t.isInProgress===!0}getConfigForActionType(t,e){return t==="pick"?e.pickConfig:t==="ban"?e.banConfig:null}getAvailableChampionIdsForAction(t,e){return e.champions.map(n=>d(n)).filter(n=>n!==null&&!this.isChampionUnavailableForAction(t,n,e))}async delayAndRefreshPendingAction(t){const e=Qt();console.debug(`auto-champion-select: Quick action is off, waiting ${e}ms before ${t.type}...`);const n=this.watchVersion;if(await g(e),!this.mounted||n!==this.watchVersion)return null;let i=null;try{await this.refreshSessionState(),i=this.findPendingAction(t)}catch(a){return console.debug("auto-champion-select: Failed to refresh champion select after action delay",a),null}if(!i||!this.isActionAvailable(i))return null;const s=this.getConfigForActionType(t.type,j());if(!(s!=null&&s.enabled))return null;const r=this.getAvailableChampionIdsForAction(i,s);return r.length===0?null:{action:i,config:s,championIds:r}}async attemptChampionForAction(t,e,n){const i=d(e);if(i===null||this.isChampionUnavailableForAction(t,i,n))return f.TRY_NEXT_CHAMPION;if(this.isPickIntentAlreadyApplied(t,i)||(console.debug(`auto-champion-select: Trying to ${t.type} ${i}...`),(await this.selectChampion(t.id,i)).ok))return f.APPLIED;console.debug(`auto-champion-select: Failed to ${t.type} ${i}, refreshing champ select state...`);const r=await this.refreshPendingAction(t);return!r||!this.isActionAvailable(r)||!this.isChampionUnavailableForAction(r,i,n)?f.ABORT_ACTION:(console.debug(`auto-champion-select: ${i} is unavailable after refresh, trying next ${t.type}...`),f.TRY_NEXT_CHAMPION)}isPickIntentAlreadyApplied(t,e){return t.type==="pick"&&t.isInProgress!==!0&&(this.localPlayerIntentChampionId===e||d(t.championId)===e)}async refreshPendingAction(t){try{await this.refreshSessionState()}catch(e){return console.debug("auto-champion-select: Failed to refresh champion select action",e),null}return this.findPendingAction(t)}findPendingAction(t){return B(this.actions).find(e=>e.id===t.id&&e.actorCellId===t.actorCellId&&e.type===t.type&&e.completed===!1)||null}isChampionUnavailableForAction(t,e,n){if(t.type==="pick"&&!Mt(n,e,this.localPlayerAssignedPosition)){const i=ct(n,e);return this.localPlayerAssignedPosition?console.debug(`auto-champion-select: Picking ${e} but assigned position ${this.localPlayerAssignedPosition} is not in ${i.join(", ")}, skipping...`):console.debug(`auto-champion-select: Picking ${e} but no assigned position is available for ${i.join(", ")} restriction, skipping...`),!0}if(this.bannedChampionIds.has(e))return console.debug(`auto-champion-select: Banning ${e} but it's already banned, skipping...`),!0;if(t.type==="ban"&&this.teammateIntentChampionIds.has(e))if(n.force===!0)console.debug(`auto-champion-select: Banning ${e} but it's already picked, forcing...`);else return console.debug(`auto-champion-select: Banning ${e} but it's already picked, skipping...`),!0;if(t.type==="pick"&&this.pickedChampionIds.has(e))if(n.force===!0)console.debug(`auto-champion-select: Picking ${e} but it's already picked, forcing...`);else return console.debug(`auto-champion-select: Picking ${e} but it's already picked, skipping...`),!0;return!1}selectChampion(t,e){const n=p.champSelectAction(t);return w("PATCH",n,{body:{championId:e,completed:!0}})}}const m=Object.freeze({dropdown:"lol-uikit-framed-dropdown",dropdownOption:"lol-uikit-dropdown-option",radioInputOption:"lol-uikit-radio-input-option",scrollable:"lol-uikit-scrollable",socialRosterGroup:"lol-social-roster-group"}),l=Object.freeze({socialRoster:".lol-social-roster",champSelectButtons:".bottom-right-buttons",firstChampSelectSquareButton:"lol-social-chat-toggle-button, .missions-tracker-button-component, .champ-select-voice-button-wrapper",dropdownRoot:".ui-dropdown",dropdownCurrent:".ui-dropdown-current",dropdownCurrentContent:".ui-dropdown-current-content",dropdownOption:m.dropdownOption,dropdownOptionSelected:`${m.dropdownOption}[selected]`,dropdownOptionsContainer:".ui-dropdown-options-container",dropdownOptions:".ui-dropdown-options",dropdownScrollable:m.scrollable,socialRosterGroupArrow:".arrow",socialRosterGroupHeader:".group-header",socialRosterGroupLabel:"span"});function V(){return document.querySelector(l.socialRoster)}function D(){return document.querySelector(l.champSelectButtons)}const jt=50,Vt=100,L=210,X="controlado-placeholder",K="controlado-search",v="controlado-quick-action",ft="controlado-filter-icon",y="controlado-filter-icon--trash",gt="controlado-filter-input",E="controlado-quick-action-toggle",Ct="controlado-quick-action-toggle--active",wt="controlado-tag",bt="controlado-tag--search",Xt="controlado-champion-option",Kt="controlado-champion-option__content",Jt="controlado-champion-option__icon",Zt="controlado-champion-option__name",te=`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.1 2 4 13.2h6.7L9.9 22 20 9.8h-6.9L13.1 2z'/%3E%3C/svg%3E")`,J=`
10
+ import{request as w,sleep as g,linkEndpoint as z}from"https://cdn.jsdelivr.net/npm/balaclava-utils@latest";const Pt="1.8.11",It={enabled:!1},kt={enabled:!1,force:!1,quickAction:!1,champions:[432,235],positionsByChampionId:{235:["BOTTOM","UTILITY"],432:["UTILITY"]}},Ot={enabled:!1,force:!1,quickAction:!1,champions:[157,777,238,38]},Tt={controladoAutoAccept:It,controladoPick:kt,controladoBan:Ot};function d(o){const t=Number(o);return Number.isSafeInteger(t)&&t>0?t:null}function at(o,t=null){const e=[],n=new Set,i=_t(t);if(!Array.isArray(o))return e;for(const s of o){const r=d(s);r===null||n.has(r)||i&&!i.has(r)||(e.push(r),n.add(r))}return e}function _t(o){return o?new Set(Array.from(o,d).filter(t=>t!==null)):null}const k=[{value:"TOP",label:"Top",iconPath:"/fe/lol-parties/icon-position-top.png"},{value:"JUNGLE",label:"Jungle",iconPath:"/fe/lol-parties/icon-position-jungle.png"},{value:"MIDDLE",label:"Mid",iconPath:"/fe/lol-parties/icon-position-middle.png"},{value:"BOTTOM",label:"ADC",iconPath:"/fe/lol-parties/icon-position-bottom.png"},{value:"UTILITY",label:"Support",iconPath:"/fe/lol-parties/icon-position-utility.png"}],Lt=new Set(k.map(o=>o.value));function T(o){const t=String(o??"").toUpperCase();return Lt.has(t)?t:null}function x(o,t=null){const e={};if(!o||typeof o!="object"||Array.isArray(o))return e;const n=vt(t);for(const[i,s]of Object.entries(o)){const r=d(i),a=String(r);if(r===null||n&&!n.has(a)||!Array.isArray(s))continue;const c=[],h=new Set;for(const u of s){const C=T(u);!C||h.has(C)||(c.push(C),h.add(C))}c.length>0&&(e[a]=c)}return e}function vt(o){if(!o)return null;const t=new Set;for(const e of o){const n=d(e);n!==null&&t.add(String(n))}return t}function U(o){return k.find(t=>t.value===o)}function ct(o,t){var n;const e=d(t);return e===null?[]:((n=o==null?void 0:o.positionsByChampionId)==null?void 0:n[String(e)])||[]}function Mt(o,t,e){const n=ct(o,t);if(n.length===0)return!0;const i=T(e);return i!==null&&n.includes(i)}const G=new Set;function Rt(o,t){return Object.prototype.hasOwnProperty.call(o,t)}function P(o,t){return Rt(o,t)}function $(){if(!globalThis.DataStore)throw new Error("DataStore is not available");return globalThis.DataStore}function xt(o){return o!==null&&typeof o=="object"&&!Array.isArray(o)}function F(o){if(o!==void 0)return JSON.parse(JSON.stringify(o))}function lt(o){const t=Tt[o];if(!t)throw new Error(`Unknown auto-champion-select config key: ${o}`);return F(t)}function dt(o,t,e={}){const n=lt(o),i=xt(t)?F(t):{},s={...n,...i},r={enabled:s.enabled===!0};return P(n,"force")&&(r.force=s.force===!0),P(n,"quickAction")&&(r.quickAction=s.quickAction===!0),P(n,"champions")&&(r.champions=at(s.champions,e.allowedChampionIds)),P(n,"positionsByChampionId")&&(r.positionsByChampionId=x(s.positionsByChampionId,e.selectedChampionIds||r.champions)),r}function ht(o,t,e={}){const n=t===void 0?lt(o):t;return dt(o,n,e)}function b(o,t={}){const n=$().get(o);return ht(o,n,t)}function Bt(o,t,e={}){const n=$(),i=dt(o,t,e);return n.set(o,i),i}function Dt(o,t){return JSON.stringify(o)===JSON.stringify(t)}function Nt(o,t,e){G.has(o)||(G.add(o),console.warn(`auto-champion-select: Repaired ${o} in DataStore. The stored value used a different schema; plugins like reynbow/auto-champ-lock write this shape. Saved normalized config.`,{storedConfig:t,normalizedConfig:e}))}function ut(o,t={}){const e=$(),n=e.get(o),i=ht(o,n,t);return(n===void 0||!Dt(n,i))&&(n!==void 0&&Nt(o,n,i),e.set(o,i)),i}function O(o,t,e={}){const n=b(o,e),i=F(n),s=t(i)||i;return Bt(o,s,e)}const p=Object.freeze({readyCheckAccept:"/lol-matchmaking/v1/ready-check/accept",gameflowPhase:"/lol-gameflow/v1/gameflow-phase",wallet:"/lol-inventory/v1/wallet",ownedChampions:"/lol-champions/v1/owned-champions-minimal",championSummary:"/lol-game-data/assets/v1/champion-summary.json",champSelectSession:"/lol-champ-select/v1/session",championIcon(o){return`/lol-game-data/assets/v1/champion-icons/${o}.png`},champSelectAction(o){return`/lol-champ-select/v1/session/actions/${o}`}}),$t=1e3;function pt(o){let t=null;return function(){return t||(t=Promise.resolve().then(o).finally(()=>{t=null})),t}}function mt(o){const t=[],e=new Set;if(!Array.isArray(o))return t;for(const n of o){if(!n||typeof n!="object")continue;const i=d(n.id),s=typeof n.name=="string"?n.name:"";i===null||!s||e.has(s)||(t.push({...n,id:i,name:s,squarePortraitPath:n.squarePortraitPath||p.championIcon(i)}),e.add(s))}return t.sort((n,i)=>n.name.localeCompare(i.name)),t}async function Ft(){let o=await w("GET",p.ownedChampions);for(;!o.ok;)console.debug("auto-champion-select(owned-champions-minimal): Retrying..."),o=await w("GET",p.ownedChampions),await g($t);return mt(await o.json())}async function qt(){const o=await w("GET",p.championSummary);return mt(await o.json())}const zt=pt(Ft),Ut=pt(qt),H=300,Y=2e3,Gt=4e3,f=Object.freeze({APPLIED:"applied",TRY_NEXT_CHAMPION:"try-next-champion",ABORT_ACTION:"abort-action"});function B(o){return Array.isArray(o)?o.flat():[]}function Ht(o){return B(o).filter(t=>t.type==="ban"&&t.completed===!0).map(t=>t.championId)}function Q(o){return Array.isArray(o)?o:[]}function _(o){const t=new Set;for(const e of o){const n=d(e);n!==null&&t.add(n)}return t}function W(o){return o.type==="pick"?0:1}function j(){return{pickConfig:b("controladoPick"),banConfig:b("controladoBan")}}function Yt(o){return o.quickAction===!0}function Qt(){const o=Gt-Y;return Y+Math.floor(Math.random()*(o+1))}class Wt{constructor(){this.session=null,this.actions=null,this.localPlayerCellId=null,this.teammateIntentChampionIds=new Set,this.localPlayerIntentChampionId=null,this.pickedChampionIds=new Set,this.bannedChampionIds=new Set,this.localPlayerAssignedPosition=null,this.watchTask=null,this.watchVersion=0,this.mounted=!1}mount(){this.mounted||(this.mounted=!0,this.watchVersion+=1,this.watchTask||(this.watchTask=this.watch()))}unmount(){this.mounted&&(this.mounted=!1,this.watchVersion+=1)}async watch(){try{for(;this.mounted;){const t=this.watchVersion;let e=!1;try{await this.refreshSessionState(),e=!0}catch(n){console.debug("auto-champion-select: Failed to update champion select",n)}if(!e||!this.mounted||t!==this.watchVersion){this.mounted&&t===this.watchVersion&&await g(H);continue}try{await this.runAutoSelectCycle()}catch(n){console.debug("auto-champion-select: Failed to run champion select cycle",n)}this.mounted&&t===this.watchVersion&&await g(H)}}finally{this.watchTask=null}}async refreshSessionState(){var s,r;const t=await w("GET",p.champSelectSession);if(!t.ok)throw new Error(`Session request failed with status ${t.status}`);this.session=await t.json(),this.actions=this.session.actions,this.localPlayerCellId=this.session.localPlayerCellId;const e=Q(this.session.myTeam),n=Q(this.session.theirTeam),i=e.find(a=>a.cellId===this.localPlayerCellId);this.localPlayerAssignedPosition=T(i==null?void 0:i.assignedPosition),this.localPlayerIntentChampionId=d(i==null?void 0:i.championPickIntent),this.pickedChampionIds=_([...e,...n].map(a=>a.championId)),this.bannedChampionIds=_([...((s=this.session.bans)==null?void 0:s.myTeamBans)||[],...((r=this.session.bans)==null?void 0:r.theirTeamBans)||[],...Ht(this.actions)]),this.teammateIntentChampionIds=_(e.map(a=>a.championPickIntent))}async runAutoSelectCycle(){const{pickConfig:t,banConfig:e}=j();if(!t.enabled&&!e.enabled)return;const n=this.getLocalPlayerActions();if(n.length===0){console.debug("auto-champion-select: No local player sub actions found, skipping..."),this.unmount();return}for(const i of n){if(!this.isActionAvailable(i))continue;const s=this.getConfigForActionType(i.type,{pickConfig:t,banConfig:e});if(!(s!=null&&s.enabled))continue;let r=i,a=s,c=this.getAvailableChampionIdsForAction(r,a);if(c.length===0)continue;const h=c.find(u=>this.isPickIntentAlreadyApplied(r,u));if(h!==void 0)c=[h];else if(!Yt(a)){const u=await this.delayAndRefreshPendingAction(r);if(!u)return;r=u.action,a=u.config,c=u.championIds}for(const u of c){const C=await this.attemptChampionForAction(r,u,a);if(C===f.APPLIED)break;if(C===f.ABORT_ACTION)return}}}getLocalPlayerActions(){return B(this.actions).filter(t=>this.isPendingLocalPlayerAction(t)).sort((t,e)=>W(t)-W(e))}isPendingLocalPlayerAction(t){return t.actorCellId===this.localPlayerCellId&&t.completed===!1}isActionAvailable(t){return t.type==="pick"||t.isInProgress===!0}getConfigForActionType(t,e){return t==="pick"?e.pickConfig:t==="ban"?e.banConfig:null}getAvailableChampionIdsForAction(t,e){return e.champions.map(n=>d(n)).filter(n=>n!==null&&!this.isChampionUnavailableForAction(t,n,e))}async delayAndRefreshPendingAction(t){const e=Qt();console.debug(`auto-champion-select: Quick action is off, waiting ${e}ms before ${t.type}...`);const n=this.watchVersion;if(await g(e),!this.mounted||n!==this.watchVersion)return null;let i=null;try{await this.refreshSessionState(),i=this.findPendingAction(t)}catch(a){return console.debug("auto-champion-select: Failed to refresh champion select after action delay",a),null}if(!i||!this.isActionAvailable(i))return null;const s=this.getConfigForActionType(t.type,j());if(!(s!=null&&s.enabled))return null;const r=this.getAvailableChampionIdsForAction(i,s);return r.length===0?null:{action:i,config:s,championIds:r}}async attemptChampionForAction(t,e,n){const i=d(e);if(i===null||this.isChampionUnavailableForAction(t,i,n))return f.TRY_NEXT_CHAMPION;if(this.isPickIntentAlreadyApplied(t,i)||(console.debug(`auto-champion-select: Trying to ${t.type} ${i}...`),(await this.selectChampion(t.id,i)).ok))return f.APPLIED;console.debug(`auto-champion-select: Failed to ${t.type} ${i}, refreshing champ select state...`);const r=await this.refreshPendingAction(t);return!r||!this.isActionAvailable(r)||!this.isChampionUnavailableForAction(r,i,n)?f.ABORT_ACTION:(console.debug(`auto-champion-select: ${i} is unavailable after refresh, trying next ${t.type}...`),f.TRY_NEXT_CHAMPION)}isPickIntentAlreadyApplied(t,e){return t.type==="pick"&&t.isInProgress!==!0&&(this.localPlayerIntentChampionId===e||d(t.championId)===e)}async refreshPendingAction(t){try{await this.refreshSessionState()}catch(e){return console.debug("auto-champion-select: Failed to refresh champion select action",e),null}return this.findPendingAction(t)}findPendingAction(t){return B(this.actions).find(e=>e.id===t.id&&e.actorCellId===t.actorCellId&&e.type===t.type&&e.completed===!1)||null}isChampionUnavailableForAction(t,e,n){if(t.type==="pick"&&!Mt(n,e,this.localPlayerAssignedPosition)){const i=ct(n,e);return this.localPlayerAssignedPosition?console.debug(`auto-champion-select: Picking ${e} but assigned position ${this.localPlayerAssignedPosition} is not in ${i.join(", ")}, skipping...`):console.debug(`auto-champion-select: Picking ${e} but no assigned position is available for ${i.join(", ")} restriction, skipping...`),!0}if(this.bannedChampionIds.has(e))return console.debug(`auto-champion-select: Banning ${e} but it's already banned, skipping...`),!0;if(t.type==="ban"&&this.teammateIntentChampionIds.has(e))if(n.force===!0)console.debug(`auto-champion-select: Banning ${e} but it's already picked, forcing...`);else return console.debug(`auto-champion-select: Banning ${e} but it's already picked, skipping...`),!0;if(t.type==="pick"&&this.pickedChampionIds.has(e))if(n.force===!0)console.debug(`auto-champion-select: Picking ${e} but it's already picked, forcing...`);else return console.debug(`auto-champion-select: Picking ${e} but it's already picked, skipping...`),!0;return!1}selectChampion(t,e){const n=p.champSelectAction(t);return w("PATCH",n,{body:{championId:e,completed:!0}})}}const m=Object.freeze({dropdown:"lol-uikit-framed-dropdown",dropdownOption:"lol-uikit-dropdown-option",radioInputOption:"lol-uikit-radio-input-option",scrollable:"lol-uikit-scrollable",socialRosterGroup:"lol-social-roster-group"}),l=Object.freeze({socialRoster:".lol-social-roster",champSelectButtons:".bottom-right-buttons",firstChampSelectSquareButton:"lol-social-chat-toggle-button, .missions-tracker-button-component, .champ-select-voice-button-wrapper",dropdownRoot:".ui-dropdown",dropdownCurrent:".ui-dropdown-current",dropdownCurrentContent:".ui-dropdown-current-content",dropdownOption:m.dropdownOption,dropdownOptionSelected:`${m.dropdownOption}[selected]`,dropdownOptionsContainer:".ui-dropdown-options-container",dropdownOptions:".ui-dropdown-options",dropdownScrollable:m.scrollable,socialRosterGroupArrow:".arrow",socialRosterGroupHeader:".group-header",socialRosterGroupLabel:"span"});function V(){return document.querySelector(l.socialRoster)}function D(){return document.querySelector(l.champSelectButtons)}const jt=50,Vt=100,L=210,X="controlado-placeholder",K="controlado-search",v="controlado-quick-action",ft="controlado-filter-icon",y="controlado-filter-icon--trash",gt="controlado-filter-input",E="controlado-quick-action-toggle",Ct="controlado-quick-action-toggle--active",wt="controlado-tag",bt="controlado-tag--search",Xt="controlado-champion-option",Kt="controlado-champion-option__content",Jt="controlado-champion-option__icon",Zt="controlado-champion-option__name",te=`url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='black' d='M13.1 2 4 13.2h6.7L9.9 22 20 9.8h-6.9L13.1 2z'/%3E%3C/svg%3E")`,J=`
11
11
  ${l.dropdownRoot} {
12
12
  position: relative;
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-champion-select",
3
- "version": "1.8.10",
3
+ "version": "1.8.11",
4
4
  "description": "Pick or ban automatically! 🐧",
5
5
  "author": "balaclava",
6
6
  "repository": {