fivelive.js 0.2.0 → 0.2.1
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.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.mts +250 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +7 -0
- package/package.json +22 -7
- package/src/client.js +0 -289
- package/src/embed.js +0 -65
- package/src/index.js +0 -7
- package/src/rest.js +0 -75
- package/src/structures.js +0 -257
- /package/{src/index.d.ts → dist/index.d.cts} +0 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var U=Object.create;var y=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var T=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var M=(r,t)=>{for(var e in t)y(r,e,{get:t[e],enumerable:!0})},$=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of S(t))!C.call(r,i)&&i!==e&&y(r,i,{get:()=>t[i],enumerable:!(s=x(t,i))||s.enumerable});return r};var E=(r,t,e)=>(e=r!=null?U(T(r)):{},$(t||!r||!r.__esModule?y(e,"default",{value:r,enumerable:!0}):e,r)),A=r=>$(y({},"__esModule",{value:!0}),r);var B={};M(B,{Channel:()=>f,Client:()=>w,EmbedBuilder:()=>I,FiveLiveAPIError:()=>g,Group:()=>l,Message:()=>h,REST:()=>d,User:()=>u,channelMention:()=>v,userMention:()=>_});module.exports=A(B);var k=require("node:events");var g=class extends Error{constructor(t,e,s,i){super(t),this.name="FiveLiveAPIError",this.status=e,this.method=s,this.path=i}},L=r=>new Promise(t=>setTimeout(t,r)),d=class{constructor(t){this.token=t.token,this.apiUrl=t.apiUrl.replace(/\/$/,""),this.retries=t.retries??3}async request(t,e,s){for(let i=0;;i++){let a=await fetch(this.apiUrl+e,{method:t,headers:{Authorization:`Bot ${this.token}`,...s!==void 0?{"Content-Type":"application/json"}:{},"User-Agent":"fivelive.js (https://live.fivenetwork.dev/developers, 0.1.0)"},body:s!==void 0?JSON.stringify(s):void 0}),o=await a.json().catch(()=>({}));if(a.status===429&&i<this.retries){let c=Number(o.retryAfter??a.headers.get("retry-after")??5);await L(Math.max(1,c)*1e3);continue}if(!a.ok)throw new g(o.error??`HTTP ${a.status}`,a.status,t,e);return o}}get(t){return this.request("GET",t)}post(t,e){return this.request("POST",t,e??{})}patch(t,e){return this.request("PATCH",t,e??{})}put(t,e){return this.request("PUT",t,e??{})}delete(t){return this.request("DELETE",t)}},n=encodeURIComponent;var _=r=>`<@${r}>`,v=r=>`<#${r}>`;function b(r){let t=typeof r=="string"?{content:r}:r??{},e=(t.embeds??[]).map(i=>typeof i?.toJSON=="function"?i.toJSON():i),s=(t.roleMentions??[]).map(i=>`@role:${i}`);return{text:t.content??"",...e.length?{embeds:e}:{},...t.mentions?.length||s.length||t.everyone?{mentions:[...t.mentions??[],...s,...t.everyone?["@everyone"]:[]]}:{},...t.reply?{replyTo:typeof t.reply=="string"?t.reply:t.reply.id}:{}}}var u=class{constructor(t,e){this.client=t,this.id=e.id,this.username=e.username??null,this.displayName=e.displayName??e.name??e.username??"",this.avatarURL=e.avatarUrl??null,this.bot=e.bot===!0,this.flags=e.flags??[]}toString(){return _(this.id)}},f=class{constructor(t,e,s){this.client=t,this.group=e,this.id=s.id,this.name=s.name,this.kind=s.kind,this.categoryId=s.categoryId??null,this.topic=s.topic??""}get isText(){return this.kind==="text"}get path(){return`/bot/groups/${n(this.group.id)}/channels/${n(this.id)}`}async send(t){let e=await this.client.rest.post(`${this.path}/messages`,b(t));return new h(this.client,e.message,this.client.user)}async sendTyping(){await this.client.rest.post(`${this.path}/typing`,{typing:!0})}async fetchMessages(t={}){let e=new URLSearchParams;return t.limit&&e.set("limit",String(t.limit)),t.after&&e.set("after",t.after),(await this.client.rest.get(`${this.path}/messages?${e}`)).messages.map(i=>new h(this.client,i))}async fetchMessage(t){let e=await this.client.rest.get(`${this.path}/messages/${n(t)}`);return new h(this.client,e.message)}toString(){return`<#${this.id}>`}},l=class{constructor(t,e){this.client=t,this._patch(e)}_patch(t){this.id=t.id,this.name=t.name,this.iconURL=t.iconUrl??null,this.ownerId=t.ownerId??null,this.memberCount=t.memberCount??0,this.roles=t.roles??[],this.permissions=t.permissions??{},this.channels=new Map((t.channels??[]).map(e=>[e.id,new f(this.client,this,e)]))}channel(t){return this.channels.get(t)??[...this.channels.values()].find(e=>e.name===t)??null}async fetchMembers(){return(await this.client.rest.get(`/bot/groups/${n(this.id)}/members`)).members.map(e=>({...e,user:this.client._cacheUser(e)}))}get path(){return`/bot/groups/${n(this.id)}`}can(t){return this.permissions.administrator===!0||this.permissions[t]===!0}async kick(t){await this.client.rest.post(`${this.path}/members/${n(t)}/kick`,{})}async ban(t,e){await this.client.rest.put(`${this.path}/bans/${n(t)}`,e?{reason:e}:{})}async unban(t){await this.client.rest.delete(`${this.path}/bans/${n(t)}`)}async fetchBans(){return(await this.client.rest.get(`${this.path}/bans`)).bans}async addRole(t,e){return(await this.client.rest.put(`${this.path}/members/${n(t)}/roles/${n(e)}`)).roleIds}async removeRole(t,e){return(await this.client.rest.delete(`${this.path}/members/${n(t)}/roles/${n(e)}`)).roleIds}async leave(){await this.client.rest.post(`${this.path}/leave`,{})}role(t){return this.roles.find(e=>e.id===t||e.name===t)??null}},h=class r{constructor(t,e,s){this.client=t,this.id=e.id,this.groupId=e.groupId,this.channelId=e.channelId,this.content=e.text??"",this.embeds=e.embeds??[],this.attachments=e.attachments??[],this.images=e.images??[],this.reactions=e.reactions??[],this.replyTo=e.replyTo??null,this.createdAt=new Date(e.ts??Date.now()),this.editedAt=e.editedAt?new Date(e.editedAt):null,this.pinned=!!e.pinnedAt,this.webhook=!!e.webhook;let i=e.mentions??[];this.mentions={users:i.filter(a=>!a.startsWith("@")),roles:i.filter(a=>a.startsWith("@role:")).map(a=>a.slice(6)),everyone:i.includes("@everyone"),raw:i},this.author=s instanceof u?s:s?t._cacheUser(s):t.users.get(e.from)??new u(t,{id:e.from,name:e.fromName})}get group(){return this.client.groups.get(this.groupId)??null}get channel(){return this.group?.channels.get(this.channelId)??null}get cleanContent(){return this.content.replace(/<@([A-Za-z0-9:_-]+)>/g,(t,e)=>{let s=this.client.users.get(e);return s?`@${s.displayName}`:t})}get mentionsMe(){return this.mentions.users.includes(this.client.user?.id)||this.mentions.everyone}get path(){return`/bot/groups/${n(this.groupId)}/channels/${n(this.channelId)}/messages`}async reply(t){let e=b(t),s=await this.client.rest.post(this.path,{...e,replyTo:this.id});return new r(this.client,s.message,this.client.user)}async edit(t){let e=b(t),s=await this.client.rest.patch(`${this.path}/${n(this.id)}`,{text:e.text,...e.embeds?{embeds:e.embeds}:{}});return new r(this.client,s.message,this.author)}async pin(){await this.client.rest.put(`${this.path}/${n(this.id)}/pin`)}async unpin(){await this.client.rest.delete(`${this.path}/${n(this.id)}/pin`)}async delete(){await this.client.rest.delete(`${this.path}/${n(this.id)}`)}async react(t){await this.client.rest.put(`${this.path}/${n(this.id)}/reactions/${n(t)}`)}async unreact(t){await this.client.rest.delete(`${this.path}/${n(this.id)}/reactions/${n(t)}`)}};var N="https://api.fivenetwork.dev",R=3e4,P=3e4;async function D(){if(typeof globalThis.WebSocket=="function")return globalThis.WebSocket;try{let r=await import("ws");return r.default??r.WebSocket}catch{throw new Error("Este Node n\xE3o tem WebSocket nativo (\xE9 do Node 22+). Rode `npm install ws` ou atualize o Node.")}}var w=class extends k.EventEmitter{constructor(t){if(super(),!t?.token||!String(t.token).startsWith("b_"))throw new Error("Passe o token do bot (come\xE7a com b_). Pegue em https://live.fivenetwork.dev/developers.");this.token=t.token,this.apiUrl=(t.apiUrl??N).replace(/\/$/,""),this.gatewayUrl=t.gatewayUrl??`${this.apiUrl.replace(/^http/,"ws")}/ws`,this.prefix=t.prefix??"!",this.ignoreBots=t.ignoreBots??!0,this.rest=new d({token:this.token,apiUrl:this.apiUrl}),this.user=null,this.groups=new Map,this.users=new Map,this.commands=new Map,this._ws=null,this._ready=!1,this._closing=!1,this._attempt=0,this._keepalive=null}command(t){this.commands.set(t.name.toLowerCase(),t);for(let e of t.aliases??[])this.commands.set(e.toLowerCase(),t);return this}async _runCommand(t){if(!this.commands.size||!t.content.startsWith(this.prefix))return;let[e,...s]=t.content.slice(this.prefix.length).trim().split(/\s+/),i=this.commands.get((e??"").toLowerCase());if(!i)return;let a={message:t,args:s,client:this,reply:o=>t.reply(o)};try{await i.run(a)}catch(o){this.listenerCount("commandError")?this.emit("commandError",o,a):this.emit("error",o)}}async login(){let t=await this.rest.get("/bot/@me");return this.user=this._cacheUser({...t.bot,bot:!0}),await this._loadGroups(),this._closing=!1,await this._connect(),this.user}destroy(){this._closing=!0,clearInterval(this._keepalive);try{this._ws?.close()}catch{}}async _loadGroups(){let t=await this.rest.get("/bot/groups"),e=new Set;for(let s of t.groups){e.add(s.id);let i=this.groups.get(s.id);i?i._patch(s):this.groups.set(s.id,new l(this,s))}for(let s of[...this.groups.keys()])e.has(s)||this.groups.delete(s)}async fetchGroup(t){let e=await this.rest.get(`/bot/groups/${encodeURIComponent(t)}`),s=this.groups.get(t);return s?s._patch(e.group):this.groups.set(t,new l(this,e.group)),this.groups.get(t)}async fetchUser(t){let e=await this.rest.get(`/bot/users/${encodeURIComponent(t)}`);return this._cacheUser(e.user)}_cacheUser(t){let e=new u(this,t);return this.users.set(e.id,e),e}async _connect(){let t=await D();return new Promise((e,s)=>{let i=new t(this.gatewayUrl);this._ws=i;let a=!1,o=c=>{i.readyState===1&&i.send(JSON.stringify(c))};i.onopen=()=>{o({type:"register",name:this.user.displayName||this.user.username,token:this.token}),clearInterval(this._keepalive),this._keepalive=setInterval(()=>o({type:"time-sync",t0:Date.now()}),R)},i.onmessage=c=>{let m;try{m=JSON.parse(typeof c.data=="string"?c.data:c.data.toString())}catch{return}if(m.type==="registered"){let p=!this._ready;this._ready=!0,this._attempt=0,a||(a=!0,e()),p?this.emit("ready",this.user):this.emit("reconnected");return}if(m.type==="register-error"){this._closing=!0;let p=new Error(m.message||"O Five Live recusou o token do bot.");a?this.emit("error",p):(a=!0,s(p));return}this._dispatch(m).catch(p=>this.emit("error",p))},i.onerror=c=>{!a&&this._attempt===0&&!this._ready&&(a=!0,s(new Error(`N\xE3o foi poss\xEDvel conectar em ${this.gatewayUrl}: ${c?.message??"erro de rede"}`)))},i.onclose=()=>{clearInterval(this._keepalive),this.emit("disconnect"),!(this._closing||!this._ready)&&this._reconnect()}})}_reconnect(){this._attempt+=1;let t=Math.min(P,1e3*2**Math.min(this._attempt-1,5))+Math.random()*500;this.emit("reconnecting",this._attempt),setTimeout(async()=>{if(!this._closing)try{await this._loadGroups(),await this._connect()}catch{this._reconnect()}},t)}async _dispatch(t){switch(t.type){case"group-message":{if(!t.message)return;let e=new h(this,t.message,t.author?{...t.author}:void 0);if(e.author.id===this.user?.id||this.ignoreBots&&e.author.bot)return;this.emit("messageCreate",e),await this._runCommand(e);return}case"group-message-updated":t.message&&this.emit("messageUpdate",new h(this,t.message));return;case"group-message-deleted":this.emit("messageDelete",{id:t.messageId,groupId:t.groupId,channelId:t.channelId});return;case"group-message-reactions":this.emit("reactionUpdate",{messageId:t.messageId,groupId:t.groupId,channelId:t.channelId,reactions:t.reactions??[]});return;case"group-typing":t.userId!==this.user?.id&&t.typing!==!1&&this.emit("typingStart",{userId:t.userId,name:t.name,groupId:t.groupId,channelId:t.channelId});return;case"group-joined":{let e=await this.fetchGroup(t.groupId);this.emit("groupJoin",e);return}case"group-updated":{if(!this.groups.has(t.groupId))return;let e=await this.fetchGroup(t.groupId).catch(()=>null);e&&this.emit("groupUpdate",e);return}case"group-member-add":{if(!t.member)return;let e=this._cacheUser(t.member),s=this.groups.get(t.groupId)??await this.fetchGroup(t.groupId).catch(()=>null);s&&(s.memberCount+=1),this.emit("memberAdd",{user:e,group:s,groupId:t.groupId,via:t.via??null});return}case"group-member-remove":{let e=this.groups.get(t.groupId)??null;e&&(e.memberCount=Math.max(0,e.memberCount-1)),this.emit("memberRemove",{userId:t.userId,user:this.users.get(t.userId)??null,group:e,groupId:t.groupId,reason:t.reason??null,by:t.by??null});return}case"group-removed":this.groups.delete(t.groupId),this.emit("groupLeave",{id:t.groupId,reason:t.reason??null});return;default:this.emit("raw",t)}}};function G(r){if(typeof r=="number")return r;let t=String(r).replace(/^#/,"");return/^[0-9a-f]{6}$/i.test(t)?parseInt(t,16):void 0}var I=class{constructor(t={}){this.data={...t}}setTitle(t){return this.data.title=t,this}setDescription(t){return this.data.description=t,this}setURL(t){return this.data.url=t,this}setColor(t){return this.data.color=G(t),this}setAuthor(t){return this.data.author={name:t.name,url:t.url,icon_url:t.iconURL},this}setFooter(t){return this.data.footer={text:t.text,icon_url:t.iconURL},this}setImage(t){return this.data.image={url:t},this}setThumbnail(t){return this.data.thumbnail={url:t},this}setTimestamp(t=new Date){return this.data.timestamp=new Date(t).toISOString(),this}addFields(...t){return this.data.fields=[...this.data.fields??[],...t.flat()],this}toJSON(){return this.data}};0&&(module.exports={Channel,Client,EmbedBuilder,FiveLiveAPIError,Group,Message,REST,User,channelMention,userMention});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.js", "../src/client.js", "../src/rest.js", "../src/structures.js", "../src/embed.js"],
|
|
4
|
+
"sourcesContent": ["// fivelive.js \u2014 SDK para bots do Five Live.\n// Documenta\u00E7\u00E3o: README.md e https://live.fivenetwork.dev/developers\n\nexport { Client } from \"./client.js\";\nexport { EmbedBuilder } from \"./embed.js\";\nexport { REST, FiveLiveAPIError } from \"./rest.js\";\nexport { Channel, Group, Message, User, channelMention, userMention } from \"./structures.js\";\n", "// O Client: conecta no WebSocket do Five Live com o token do bot, mant\u00E9m a\n// conex\u00E3o viva (e reconecta sozinho), transforma os avisos da API em eventos\n// e guarda grupos e usu\u00E1rios conhecidos.\n//\n// Eventos:\n// ready(user) conectou e carregou os grupos\n// messageCreate(message) mensagem nova num grupo do bot\n// messageUpdate(message) mensagem editada\n// messageDelete({ id, groupId, channelId })\n// reactionUpdate({ messageId, groupId, channelId, reactions })\n// typingStart({ userId, name, groupId, channelId })\n// groupJoin(group) / groupLeave({ id, reason }) / groupUpdate(group)\n// reconnecting(attempt) / reconnected() / disconnect() / error(err)\n// commandError(err, context)\n\nimport { EventEmitter } from \"node:events\";\nimport { REST } from \"./rest.js\";\nimport { Group, Message, User } from \"./structures.js\";\n\nconst DEFAULT_API = \"https://api.fivenetwork.dev\";\nconst KEEPALIVE_MS = 30_000;\nconst MAX_BACKOFF_MS = 30_000;\n\nasync function socketClass() {\n if (typeof globalThis.WebSocket === \"function\") return globalThis.WebSocket;\n try {\n const mod = await import(\"ws\");\n return mod.default ?? mod.WebSocket;\n } catch {\n throw new Error(\"Este Node n\u00E3o tem WebSocket nativo (\u00E9 do Node 22+). Rode `npm install ws` ou atualize o Node.\");\n }\n}\n\nexport class Client extends EventEmitter {\n /**\n * @param {{\n * token: string,\n * apiUrl?: string,\n * gatewayUrl?: string,\n * prefix?: string,\n * ignoreBots?: boolean,\n * }} options\n */\n constructor(options) {\n super();\n if (!options?.token || !String(options.token).startsWith(\"b_\")) {\n throw new Error(\"Passe o token do bot (come\u00E7a com b_). Pegue em https://live.fivenetwork.dev/developers.\");\n }\n this.token = options.token;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API).replace(/\\/$/, \"\");\n this.gatewayUrl = options.gatewayUrl ?? `${this.apiUrl.replace(/^http/, \"ws\")}/ws`;\n this.prefix = options.prefix ?? \"!\";\n this.ignoreBots = options.ignoreBots ?? true;\n this.rest = new REST({ token: this.token, apiUrl: this.apiUrl });\n /** @type {User | null} */\n this.user = null;\n /** @type {Map<string, Group>} */\n this.groups = new Map();\n /** @type {Map<string, User>} */\n this.users = new Map();\n /** @type {Map<string, { name: string, description?: string, aliases?: string[], run: Function }>} */\n this.commands = new Map();\n this._ws = null;\n this._ready = false;\n this._closing = false;\n this._attempt = 0;\n this._keepalive = null;\n }\n\n // \u2500\u2500\u2500 Comandos com prefixo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Registra um comando: `!nome arg1 arg2`.\n * @param {{ name: string, description?: string, aliases?: string[], run: (ctx: { message: Message, args: string[], client: Client, reply: (content: any) => Promise<Message> }) => any }} command\n */\n command(command) {\n this.commands.set(command.name.toLowerCase(), command);\n for (const alias of command.aliases ?? []) this.commands.set(alias.toLowerCase(), command);\n return this;\n }\n\n /** @param {Message} message */\n async _runCommand(message) {\n if (!this.commands.size || !message.content.startsWith(this.prefix)) return;\n const [name, ...args] = message.content.slice(this.prefix.length).trim().split(/\\s+/);\n const command = this.commands.get((name ?? \"\").toLowerCase());\n if (!command) return;\n const context = { message, args, client: this, reply: (content) => message.reply(content) };\n try {\n await command.run(context);\n } catch (err) {\n if (this.listenerCount(\"commandError\")) this.emit(\"commandError\", err, context);\n else this.emit(\"error\", err);\n }\n }\n\n // \u2500\u2500\u2500 Conex\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Conecta. Resolve quando o bot estiver pronto (evento \"ready\"). */\n async login() {\n const me = await this.rest.get(\"/bot/@me\");\n this.user = this._cacheUser({ ...me.bot, bot: true });\n await this._loadGroups();\n this._closing = false;\n await this._connect();\n return this.user;\n }\n\n /** Desconecta de vez (sem reconectar). */\n destroy() {\n this._closing = true;\n clearInterval(this._keepalive);\n try {\n this._ws?.close();\n } catch {\n // j\u00E1 fechado\n }\n }\n\n async _loadGroups() {\n const data = await this.rest.get(\"/bot/groups\");\n const seen = new Set();\n for (const raw of data.groups) {\n seen.add(raw.id);\n const held = this.groups.get(raw.id);\n if (held) held._patch(raw);\n else this.groups.set(raw.id, new Group(this, raw));\n }\n for (const id of [...this.groups.keys()]) if (!seen.has(id)) this.groups.delete(id);\n }\n\n /** Um grupo, relido da API. @param {string} id */\n async fetchGroup(id) {\n const data = await this.rest.get(`/bot/groups/${encodeURIComponent(id)}`);\n const held = this.groups.get(id);\n if (held) held._patch(data.group);\n else this.groups.set(id, new Group(this, data.group));\n return this.groups.get(id);\n }\n\n /** Uma pessoa pelo id. @param {string} id */\n async fetchUser(id) {\n const data = await this.rest.get(`/bot/users/${encodeURIComponent(id)}`);\n return this._cacheUser(data.user);\n }\n\n /** @param {Record<string, any>} data */\n _cacheUser(data) {\n const user = new User(this, data);\n this.users.set(user.id, user);\n return user;\n }\n\n async _connect() {\n const Socket = await socketClass();\n return new Promise((resolve, reject) => {\n const ws = new Socket(this.gatewayUrl);\n this._ws = ws;\n let settled = false;\n const send = (payload) => {\n if (ws.readyState === 1) ws.send(JSON.stringify(payload));\n };\n ws.onopen = () => {\n send({ type: \"register\", name: this.user.displayName || this.user.username, token: this.token });\n clearInterval(this._keepalive);\n // O proxy da hospedagem derruba conex\u00E3o parada: uma mensagem leve de tempos em tempos.\n this._keepalive = setInterval(() => send({ type: \"time-sync\", t0: Date.now() }), KEEPALIVE_MS);\n };\n ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(typeof event.data === \"string\" ? event.data : event.data.toString());\n } catch {\n return;\n }\n if (msg.type === \"registered\") {\n const first = !this._ready;\n this._ready = true;\n this._attempt = 0;\n if (!settled) {\n settled = true;\n resolve();\n }\n if (first) this.emit(\"ready\", this.user);\n else this.emit(\"reconnected\");\n return;\n }\n if (msg.type === \"register-error\") {\n this._closing = true;\n const err = new Error(msg.message || \"O Five Live recusou o token do bot.\");\n if (!settled) {\n settled = true;\n reject(err);\n } else this.emit(\"error\", err);\n return;\n }\n this._dispatch(msg).catch((err) => this.emit(\"error\", err));\n };\n ws.onerror = (event) => {\n if (!settled && this._attempt === 0 && !this._ready) {\n settled = true;\n reject(new Error(`N\u00E3o foi poss\u00EDvel conectar em ${this.gatewayUrl}: ${event?.message ?? \"erro de rede\"}`));\n }\n };\n ws.onclose = () => {\n clearInterval(this._keepalive);\n this.emit(\"disconnect\");\n if (this._closing || !this._ready) return;\n this._reconnect();\n };\n });\n }\n\n _reconnect() {\n this._attempt += 1;\n const delay = Math.min(MAX_BACKOFF_MS, 1000 * 2 ** Math.min(this._attempt - 1, 5)) + Math.random() * 500;\n this.emit(\"reconnecting\", this._attempt);\n setTimeout(async () => {\n if (this._closing) return;\n try {\n // O que mudou enquanto estava fora (grupos que entrou/saiu).\n await this._loadGroups();\n await this._connect();\n } catch {\n this._reconnect();\n }\n }, delay);\n }\n\n /** @param {Record<string, any>} msg */\n async _dispatch(msg) {\n switch (msg.type) {\n case \"group-message\": {\n if (!msg.message) return;\n const message = new Message(this, msg.message, msg.author ? { ...msg.author } : undefined);\n if (message.author.id === this.user?.id) return;\n if (this.ignoreBots && message.author.bot) return;\n this.emit(\"messageCreate\", message);\n await this._runCommand(message);\n return;\n }\n case \"group-message-updated\":\n if (msg.message) this.emit(\"messageUpdate\", new Message(this, msg.message));\n return;\n case \"group-message-deleted\":\n this.emit(\"messageDelete\", { id: msg.messageId, groupId: msg.groupId, channelId: msg.channelId });\n return;\n case \"group-message-reactions\":\n this.emit(\"reactionUpdate\", { messageId: msg.messageId, groupId: msg.groupId, channelId: msg.channelId, reactions: msg.reactions ?? [] });\n return;\n case \"group-typing\":\n if (msg.userId !== this.user?.id && msg.typing !== false) {\n this.emit(\"typingStart\", { userId: msg.userId, name: msg.name, groupId: msg.groupId, channelId: msg.channelId });\n }\n return;\n case \"group-joined\": {\n const group = await this.fetchGroup(msg.groupId);\n this.emit(\"groupJoin\", group);\n return;\n }\n case \"group-updated\": {\n if (!this.groups.has(msg.groupId)) return;\n const group = await this.fetchGroup(msg.groupId).catch(() => null);\n if (group) this.emit(\"groupUpdate\", group);\n return;\n }\n case \"group-member-add\": {\n if (!msg.member) return;\n const user = this._cacheUser(msg.member);\n const group = this.groups.get(msg.groupId) ?? (await this.fetchGroup(msg.groupId).catch(() => null));\n if (group) group.memberCount += 1;\n this.emit(\"memberAdd\", { user, group, groupId: msg.groupId, via: msg.via ?? null });\n return;\n }\n case \"group-member-remove\": {\n const group = this.groups.get(msg.groupId) ?? null;\n if (group) group.memberCount = Math.max(0, group.memberCount - 1);\n this.emit(\"memberRemove\", { userId: msg.userId, user: this.users.get(msg.userId) ?? null, group, groupId: msg.groupId, reason: msg.reason ?? null, by: msg.by ?? null });\n return;\n }\n case \"group-removed\":\n this.groups.delete(msg.groupId);\n this.emit(\"groupLeave\", { id: msg.groupId, reason: msg.reason ?? null });\n return;\n default:\n this.emit(\"raw\", msg);\n }\n }\n}\n", "// As rotas HTTP da API de bots (api/botApi.mjs), com o token no cabe\u00E7alho\n// `Authorization: Bot <token>`. Trata o limite de envio (429) esperando o\n// tempo que a API pede e tentando de novo.\n\nexport class FiveLiveAPIError extends Error {\n /**\n * @param {string} message\n * @param {number} status\n * @param {string} method\n * @param {string} path\n */\n constructor(message, status, method, path) {\n super(message);\n this.name = \"FiveLiveAPIError\";\n this.status = status;\n this.method = method;\n this.path = path;\n }\n}\n\nconst sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport class REST {\n /** @param {{ token: string, apiUrl: string, retries?: number }} options */\n constructor(options) {\n this.token = options.token;\n this.apiUrl = options.apiUrl.replace(/\\/$/, \"\");\n this.retries = options.retries ?? 3;\n }\n\n /**\n * @param {string} method\n * @param {string} path\n * @param {unknown} [body]\n */\n async request(method, path, body) {\n for (let attempt = 0; ; attempt++) {\n const res = await fetch(this.apiUrl + path, {\n method,\n headers: {\n Authorization: `Bot ${this.token}`,\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n \"User-Agent\": \"fivelive.js (https://live.fivenetwork.dev/developers, 0.1.0)\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n const data = await res.json().catch(() => ({}));\n if (res.status === 429 && attempt < this.retries) {\n const seconds = Number(data.retryAfter ?? res.headers.get(\"retry-after\") ?? 5);\n await sleep(Math.max(1, seconds) * 1000);\n continue;\n }\n if (!res.ok) throw new FiveLiveAPIError(data.error ?? `HTTP ${res.status}`, res.status, method, path);\n return data;\n }\n }\n\n get(path) {\n return this.request(\"GET\", path);\n }\n post(path, body) {\n return this.request(\"POST\", path, body ?? {});\n }\n patch(path, body) {\n return this.request(\"PATCH\", path, body ?? {});\n }\n put(path, body) {\n return this.request(\"PUT\", path, body ?? {});\n }\n delete(path) {\n return this.request(\"DELETE\", path);\n }\n}\n\nexport const enc = encodeURIComponent;\n", "// Os objetos que o bot recebe: User, Group, Channel, Message. Cada um sabe\n// agir sobre si mesmo (message.reply, channel.send, \u2026) usando o client.\n\nimport { enc } from \"./rest.js\";\n\n/** `<@id>` \u2014 como mencionar algu\u00E9m no texto. */\nexport const userMention = (id) => `<@${id}>`;\n/** `<#id>` \u2014 como apontar uma sala no texto. */\nexport const channelMention = (id) => `<#${id}>`;\n\n/**\n * Normaliza o que se manda: um texto, ou { content, embeds, mentions, reply }.\n * @param {string | Record<string, any>} input\n */\nexport function messagePayload(input) {\n const options = typeof input === \"string\" ? { content: input } : input ?? {};\n const embeds = (options.embeds ?? []).map((embed) => (typeof embed?.toJSON === \"function\" ? embed.toJSON() : embed));\n const roles = (options.roleMentions ?? []).map((id) => `@role:${id}`);\n return {\n text: options.content ?? \"\",\n ...(embeds.length ? { embeds } : {}),\n ...(options.mentions?.length || roles.length || options.everyone\n ? { mentions: [...(options.mentions ?? []), ...roles, ...(options.everyone ? [\"@everyone\"] : [])] }\n : {}),\n ...(options.reply ? { replyTo: typeof options.reply === \"string\" ? options.reply : options.reply.id } : {}),\n };\n}\n\nexport class User {\n /** @param {import(\"./client.js\").Client} client @param {Record<string, any>} data */\n constructor(client, data) {\n this.client = client;\n this.id = data.id;\n this.username = data.username ?? null;\n this.displayName = data.displayName ?? data.name ?? data.username ?? \"\";\n this.avatarURL = data.avatarUrl ?? null;\n this.bot = data.bot === true;\n this.flags = data.flags ?? [];\n }\n /** `<@id>`, para colocar no texto. */\n toString() {\n return userMention(this.id);\n }\n}\n\nexport class Channel {\n /** @param {import(\"./client.js\").Client} client @param {Group} group @param {Record<string, any>} data */\n constructor(client, group, data) {\n this.client = client;\n this.group = group;\n this.id = data.id;\n this.name = data.name;\n /** \"text\" | \"voice\" */\n this.kind = data.kind;\n this.categoryId = data.categoryId ?? null;\n this.topic = data.topic ?? \"\";\n }\n get isText() {\n return this.kind === \"text\";\n }\n get path() {\n return `/bot/groups/${enc(this.group.id)}/channels/${enc(this.id)}`;\n }\n /** Manda uma mensagem. @param {string | Record<string, any>} content */\n async send(content) {\n const data = await this.client.rest.post(`${this.path}/messages`, messagePayload(content));\n return new Message(this.client, data.message, this.client.user);\n }\n /** \"Digitando\u2026\" por alguns segundos. */\n async sendTyping() {\n await this.client.rest.post(`${this.path}/typing`, { typing: true });\n }\n /**\n * As \u00FAltimas mensagens (a mais nova por \u00FAltimo).\n * @param {{ limit?: number, after?: string }} [options]\n */\n async fetchMessages(options = {}) {\n const query = new URLSearchParams();\n if (options.limit) query.set(\"limit\", String(options.limit));\n if (options.after) query.set(\"after\", options.after);\n const data = await this.client.rest.get(`${this.path}/messages?${query}`);\n return data.messages.map((message) => new Message(this.client, message));\n }\n /** Uma mensagem pelo id. */\n async fetchMessage(id) {\n const data = await this.client.rest.get(`${this.path}/messages/${enc(id)}`);\n return new Message(this.client, data.message);\n }\n toString() {\n return `<#${this.id}>`;\n }\n}\n\nexport class Group {\n /** @param {import(\"./client.js\").Client} client @param {Record<string, any>} data */\n constructor(client, data) {\n this.client = client;\n this._patch(data);\n }\n /** @param {Record<string, any>} data */\n _patch(data) {\n this.id = data.id;\n this.name = data.name;\n this.iconURL = data.iconUrl ?? null;\n this.ownerId = data.ownerId ?? null;\n this.memberCount = data.memberCount ?? 0;\n /** @type {{ id: string, name: string, color: string | null, position: number, mentionable: boolean }[]} */\n this.roles = data.roles ?? [];\n /** O que o bot pode fazer aqui (kickMembers, banMembers, manageRoles, manageMessages\u2026). */\n this.permissions = data.permissions ?? {};\n /** @type {Map<string, Channel>} */\n this.channels = new Map((data.channels ?? []).map((channel) => [channel.id, new Channel(this.client, this, channel)]));\n }\n /** A sala pelo id ou pelo nome. @param {string} idOrName */\n channel(idOrName) {\n return this.channels.get(idOrName) ?? [...this.channels.values()].find((channel) => channel.name === idOrName) ?? null;\n }\n /** Os membros, com cargos e quem est\u00E1 online. */\n async fetchMembers() {\n const data = await this.client.rest.get(`/bot/groups/${enc(this.id)}/members`);\n return data.members.map((member) => ({ ...member, user: this.client._cacheUser(member) }));\n }\n get path() {\n return `/bot/groups/${enc(this.id)}`;\n }\n /** Se o bot tem a permiss\u00E3o (administrator vale por todas). @param {string} key */\n can(key) {\n return this.permissions.administrator === true || this.permissions[key] === true;\n }\n /** Expulsa algu\u00E9m (precisa de kickMembers e de um cargo acima do da pessoa). @param {string} userId */\n async kick(userId) {\n await this.client.rest.post(`${this.path}/members/${enc(userId)}/kick`, {});\n }\n /** Bane algu\u00E9m, mesmo que n\u00E3o esteja no grupo (banMembers). @param {string} userId @param {string} [reason] */\n async ban(userId, reason) {\n await this.client.rest.put(`${this.path}/bans/${enc(userId)}`, reason ? { reason } : {});\n }\n /** Tira o banimento (banMembers). @param {string} userId */\n async unban(userId) {\n await this.client.rest.delete(`${this.path}/bans/${enc(userId)}`);\n }\n /** Quem est\u00E1 banido (banMembers). */\n async fetchBans() {\n const data = await this.client.rest.get(`${this.path}/bans`);\n return data.bans;\n }\n /** D\u00E1 um cargo (manageRoles; s\u00F3 cargos abaixo do mais alto do bot). @param {string} userId @param {string} roleId */\n async addRole(userId, roleId) {\n const data = await this.client.rest.put(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);\n return data.roleIds;\n }\n /** Tira um cargo (manageRoles). @param {string} userId @param {string} roleId */\n async removeRole(userId, roleId) {\n const data = await this.client.rest.delete(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);\n return data.roleIds;\n }\n /** O bot sai do grupo. */\n async leave() {\n await this.client.rest.post(`${this.path}/leave`, {});\n }\n /** `<@&id>` n\u00E3o existe aqui: para mencionar um cargo, use { roleMentions: [id] } no envio. */\n role(idOrName) {\n return this.roles.find((role) => role.id === idOrName || role.name === idOrName) ?? null;\n }\n}\n\nexport class Message {\n /**\n * @param {import(\"./client.js\").Client} client\n * @param {Record<string, any>} data\n * @param {User | Record<string, any>} [author]\n */\n constructor(client, data, author) {\n this.client = client;\n this.id = data.id;\n this.groupId = data.groupId;\n this.channelId = data.channelId;\n /** O texto como est\u00E1 gravado \u2014 men\u00E7\u00F5es como `<@id>`. */\n this.content = data.text ?? \"\";\n this.embeds = data.embeds ?? [];\n this.attachments = data.attachments ?? [];\n this.images = data.images ?? [];\n this.reactions = data.reactions ?? [];\n this.replyTo = data.replyTo ?? null;\n this.createdAt = new Date(data.ts ?? Date.now());\n this.editedAt = data.editedAt ? new Date(data.editedAt) : null;\n this.pinned = Boolean(data.pinnedAt);\n this.webhook = Boolean(data.webhook);\n const mentions = data.mentions ?? [];\n this.mentions = {\n /** ids das pessoas mencionadas */\n users: mentions.filter((entry) => !entry.startsWith(\"@\")),\n /** ids dos cargos mencionados */\n roles: mentions.filter((entry) => entry.startsWith(\"@role:\")).map((entry) => entry.slice(6)),\n everyone: mentions.includes(\"@everyone\"),\n raw: mentions,\n };\n this.author =\n author instanceof User\n ? author\n : author\n ? client._cacheUser(author)\n : client.users.get(data.from) ?? new User(client, { id: data.from, name: data.fromName });\n }\n get group() {\n return this.client.groups.get(this.groupId) ?? null;\n }\n get channel() {\n return this.group?.channels.get(this.channelId) ?? null;\n }\n /** O texto com `<@id>` trocado por `@nome` (quando o nome \u00E9 conhecido). */\n get cleanContent() {\n return this.content.replace(/<@([A-Za-z0-9:_-]+)>/g, (whole, id) => {\n const user = this.client.users.get(id);\n return user ? `@${user.displayName}` : whole;\n });\n }\n /** Se a mensagem menciona o bot (pelo id, cargo n\u00E3o conta). */\n get mentionsMe() {\n return this.mentions.users.includes(this.client.user?.id) || this.mentions.everyone;\n }\n get path() {\n return `/bot/groups/${enc(this.groupId)}/channels/${enc(this.channelId)}/messages`;\n }\n /** Responde citando esta mensagem. @param {string | Record<string, any>} content */\n async reply(content) {\n const payload = messagePayload(content);\n const data = await this.client.rest.post(this.path, { ...payload, replyTo: this.id });\n return new Message(this.client, data.message, this.client.user);\n }\n /** Edita (s\u00F3 as mensagens do pr\u00F3prio bot). @param {string | Record<string, any>} content */\n async edit(content) {\n const payload = messagePayload(content);\n const data = await this.client.rest.patch(`${this.path}/${enc(this.id)}`, { text: payload.text, ...(payload.embeds ? { embeds: payload.embeds } : {}) });\n return new Message(this.client, data.message, this.author);\n }\n /** Fixa na sala (manageMessages). */\n async pin() {\n await this.client.rest.put(`${this.path}/${enc(this.id)}/pin`);\n }\n /** Desafixa (manageMessages). */\n async unpin() {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}/pin`);\n }\n /** Apaga: as do pr\u00F3prio bot sempre; as dos outros, com manageMessages. */\n async delete() {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}`);\n }\n /** Reage com um emoji. @param {string} emoji */\n async react(emoji) {\n await this.client.rest.put(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);\n }\n /** Tira a rea\u00E7\u00E3o do bot. @param {string} emoji */\n async unreact(emoji) {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);\n }\n}\n", "// Construtor de embeds no mesmo formato do discord.js (e do webhook do Five\n// Live): t\u00EDtulo, descri\u00E7\u00E3o, cor, autor, campos, imagem, miniatura, rodap\u00E9.\n\n/** @param {string | number} color */\nfunction toColor(color) {\n if (typeof color === \"number\") return color;\n const hex = String(color).replace(/^#/, \"\");\n return /^[0-9a-f]{6}$/i.test(hex) ? parseInt(hex, 16) : undefined;\n}\n\nexport class EmbedBuilder {\n /** @param {Record<string, unknown>} [data] */\n constructor(data = {}) {\n /** @type {Record<string, any>} */\n this.data = { ...data };\n }\n setTitle(title) {\n this.data.title = title;\n return this;\n }\n setDescription(description) {\n this.data.description = description;\n return this;\n }\n setURL(url) {\n this.data.url = url;\n return this;\n }\n /** @param {string | number} color \"#3b6cf6\" ou 0x3b6cf6 */\n setColor(color) {\n this.data.color = toColor(color);\n return this;\n }\n /** @param {{ name: string, url?: string, iconURL?: string }} author */\n setAuthor(author) {\n this.data.author = { name: author.name, url: author.url, icon_url: author.iconURL };\n return this;\n }\n /** @param {{ text: string, iconURL?: string }} footer */\n setFooter(footer) {\n this.data.footer = { text: footer.text, icon_url: footer.iconURL };\n return this;\n }\n setImage(url) {\n this.data.image = { url };\n return this;\n }\n setThumbnail(url) {\n this.data.thumbnail = { url };\n return this;\n }\n /** @param {Date | number | string} [timestamp] */\n setTimestamp(timestamp = new Date()) {\n this.data.timestamp = new Date(timestamp).toISOString();\n return this;\n }\n /** @param {...{ name: string, value: string, inline?: boolean }} fields */\n addFields(...fields) {\n this.data.fields = [...(this.data.fields ?? []), ...fields.flat()];\n return this;\n }\n toJSON() {\n return this.data;\n }\n}\n"],
|
|
5
|
+
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,WAAAC,EAAA,iBAAAC,EAAA,qBAAAC,EAAA,UAAAC,EAAA,YAAAC,EAAA,SAAAC,EAAA,SAAAC,EAAA,mBAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAAZ,GCeA,IAAAa,EAA6B,uBCXtB,IAAMC,EAAN,cAA+B,KAAM,CAO1C,YAAYC,EAASC,EAAQC,EAAQC,EAAM,CACzC,MAAMH,CAAO,EACb,KAAK,KAAO,mBACZ,KAAK,OAASC,EACd,KAAK,OAASC,EACd,KAAK,KAAOC,CACd,CACF,EAEMC,EAASC,GAAO,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAEzDE,EAAN,KAAW,CAEhB,YAAYC,EAAS,CACnB,KAAK,MAAQA,EAAQ,MACrB,KAAK,OAASA,EAAQ,OAAO,QAAQ,MAAO,EAAE,EAC9C,KAAK,QAAUA,EAAQ,SAAW,CACpC,CAOA,MAAM,QAAQN,EAAQC,EAAMM,EAAM,CAChC,QAASC,EAAU,GAAKA,IAAW,CACjC,IAAMC,EAAM,MAAM,MAAM,KAAK,OAASR,EAAM,CAC1C,OAAAD,EACA,QAAS,CACP,cAAe,OAAO,KAAK,KAAK,GAChC,GAAIO,IAAS,OAAY,CAAE,eAAgB,kBAAmB,EAAI,CAAC,EACnE,aAAc,8DAChB,EACA,KAAMA,IAAS,OAAY,KAAK,UAAUA,CAAI,EAAI,MACpD,CAAC,EACKG,EAAO,MAAMD,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC9C,GAAIA,EAAI,SAAW,KAAOD,EAAU,KAAK,QAAS,CAChD,IAAMG,EAAU,OAAOD,EAAK,YAAcD,EAAI,QAAQ,IAAI,aAAa,GAAK,CAAC,EAC7E,MAAMP,EAAM,KAAK,IAAI,EAAGS,CAAO,EAAI,GAAI,EACvC,QACF,CACA,GAAI,CAACF,EAAI,GAAI,MAAM,IAAIZ,EAAiBa,EAAK,OAAS,QAAQD,EAAI,MAAM,GAAIA,EAAI,OAAQT,EAAQC,CAAI,EACpG,OAAOS,CACT,CACF,CAEA,IAAIT,EAAM,CACR,OAAO,KAAK,QAAQ,MAAOA,CAAI,CACjC,CACA,KAAKA,EAAMM,EAAM,CACf,OAAO,KAAK,QAAQ,OAAQN,EAAMM,GAAQ,CAAC,CAAC,CAC9C,CACA,MAAMN,EAAMM,EAAM,CAChB,OAAO,KAAK,QAAQ,QAASN,EAAMM,GAAQ,CAAC,CAAC,CAC/C,CACA,IAAIN,EAAMM,EAAM,CACd,OAAO,KAAK,QAAQ,MAAON,EAAMM,GAAQ,CAAC,CAAC,CAC7C,CACA,OAAON,EAAM,CACX,OAAO,KAAK,QAAQ,SAAUA,CAAI,CACpC,CACF,EAEaW,EAAM,mBCpEZ,IAAMC,EAAeC,GAAO,KAAKA,CAAE,IAE7BC,EAAkBD,GAAO,KAAKA,CAAE,IAMtC,SAASE,EAAeC,EAAO,CACpC,IAAMC,EAAU,OAAOD,GAAU,SAAW,CAAE,QAASA,CAAM,EAAIA,GAAS,CAAC,EACrEE,GAAUD,EAAQ,QAAU,CAAC,GAAG,IAAKE,GAAW,OAAOA,GAAO,QAAW,WAAaA,EAAM,OAAO,EAAIA,CAAM,EAC7GC,GAASH,EAAQ,cAAgB,CAAC,GAAG,IAAKJ,GAAO,SAASA,CAAE,EAAE,EACpE,MAAO,CACL,KAAMI,EAAQ,SAAW,GACzB,GAAIC,EAAO,OAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,EAClC,GAAID,EAAQ,UAAU,QAAUG,EAAM,QAAUH,EAAQ,SACpD,CAAE,SAAU,CAAC,GAAIA,EAAQ,UAAY,CAAC,EAAI,GAAGG,EAAO,GAAIH,EAAQ,SAAW,CAAC,WAAW,EAAI,CAAC,CAAE,CAAE,EAChG,CAAC,EACL,GAAIA,EAAQ,MAAQ,CAAE,QAAS,OAAOA,EAAQ,OAAU,SAAWA,EAAQ,MAAQA,EAAQ,MAAM,EAAG,EAAI,CAAC,CAC3G,CACF,CAEO,IAAMI,EAAN,KAAW,CAEhB,YAAYC,EAAQC,EAAM,CACxB,KAAK,OAASD,EACd,KAAK,GAAKC,EAAK,GACf,KAAK,SAAWA,EAAK,UAAY,KACjC,KAAK,YAAcA,EAAK,aAAeA,EAAK,MAAQA,EAAK,UAAY,GACrE,KAAK,UAAYA,EAAK,WAAa,KACnC,KAAK,IAAMA,EAAK,MAAQ,GACxB,KAAK,MAAQA,EAAK,OAAS,CAAC,CAC9B,CAEA,UAAW,CACT,OAAOX,EAAY,KAAK,EAAE,CAC5B,CACF,EAEaY,EAAN,KAAc,CAEnB,YAAYF,EAAQG,EAAOF,EAAM,CAC/B,KAAK,OAASD,EACd,KAAK,MAAQG,EACb,KAAK,GAAKF,EAAK,GACf,KAAK,KAAOA,EAAK,KAEjB,KAAK,KAAOA,EAAK,KACjB,KAAK,WAAaA,EAAK,YAAc,KACrC,KAAK,MAAQA,EAAK,OAAS,EAC7B,CACA,IAAI,QAAS,CACX,OAAO,KAAK,OAAS,MACvB,CACA,IAAI,MAAO,CACT,MAAO,eAAeG,EAAI,KAAK,MAAM,EAAE,CAAC,aAAaA,EAAI,KAAK,EAAE,CAAC,EACnE,CAEA,MAAM,KAAKC,EAAS,CAClB,IAAMJ,EAAO,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,YAAaR,EAAeY,CAAO,CAAC,EACzF,OAAO,IAAIC,EAAQ,KAAK,OAAQL,EAAK,QAAS,KAAK,OAAO,IAAI,CAChE,CAEA,MAAM,YAAa,CACjB,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,UAAW,CAAE,OAAQ,EAAK,CAAC,CACrE,CAKA,MAAM,cAAcN,EAAU,CAAC,EAAG,CAChC,IAAMY,EAAQ,IAAI,gBAClB,OAAIZ,EAAQ,OAAOY,EAAM,IAAI,QAAS,OAAOZ,EAAQ,KAAK,CAAC,EACvDA,EAAQ,OAAOY,EAAM,IAAI,QAASZ,EAAQ,KAAK,GACtC,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,aAAaY,CAAK,EAAE,GAC5D,SAAS,IAAKC,GAAY,IAAIF,EAAQ,KAAK,OAAQE,CAAO,CAAC,CACzE,CAEA,MAAM,aAAajB,EAAI,CACrB,IAAMU,EAAO,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,aAAaG,EAAIb,CAAE,CAAC,EAAE,EAC1E,OAAO,IAAIe,EAAQ,KAAK,OAAQL,EAAK,OAAO,CAC9C,CACA,UAAW,CACT,MAAO,KAAK,KAAK,EAAE,GACrB,CACF,EAEaQ,EAAN,KAAY,CAEjB,YAAYT,EAAQC,EAAM,CACxB,KAAK,OAASD,EACd,KAAK,OAAOC,CAAI,CAClB,CAEA,OAAOA,EAAM,CACX,KAAK,GAAKA,EAAK,GACf,KAAK,KAAOA,EAAK,KACjB,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,YAAcA,EAAK,aAAe,EAEvC,KAAK,MAAQA,EAAK,OAAS,CAAC,EAE5B,KAAK,YAAcA,EAAK,aAAe,CAAC,EAExC,KAAK,SAAW,IAAI,KAAKA,EAAK,UAAY,CAAC,GAAG,IAAKS,GAAY,CAACA,EAAQ,GAAI,IAAIR,EAAQ,KAAK,OAAQ,KAAMQ,CAAO,CAAC,CAAC,CAAC,CACvH,CAEA,QAAQC,EAAU,CAChB,OAAO,KAAK,SAAS,IAAIA,CAAQ,GAAK,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,KAAMD,GAAYA,EAAQ,OAASC,CAAQ,GAAK,IACpH,CAEA,MAAM,cAAe,CAEnB,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,eAAeP,EAAI,KAAK,EAAE,CAAC,UAAU,GACjE,QAAQ,IAAKQ,IAAY,CAAE,GAAGA,EAAQ,KAAM,KAAK,OAAO,WAAWA,CAAM,CAAE,EAAE,CAC3F,CACA,IAAI,MAAO,CACT,MAAO,eAAeR,EAAI,KAAK,EAAE,CAAC,EACpC,CAEA,IAAIS,EAAK,CACP,OAAO,KAAK,YAAY,gBAAkB,IAAQ,KAAK,YAAYA,CAAG,IAAM,EAC9E,CAEA,MAAM,KAAKC,EAAQ,CACjB,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,YAAYV,EAAIU,CAAM,CAAC,QAAS,CAAC,CAAC,CAC5E,CAEA,MAAM,IAAIA,EAAQC,EAAQ,CACxB,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,SAASX,EAAIU,CAAM,CAAC,GAAIC,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAAC,CACzF,CAEA,MAAM,MAAMD,EAAQ,CAClB,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,SAASV,EAAIU,CAAM,CAAC,EAAE,CAClE,CAEA,MAAM,WAAY,CAEhB,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,GAC/C,IACd,CAEA,MAAM,QAAQA,EAAQE,EAAQ,CAE5B,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,YAAYZ,EAAIU,CAAM,CAAC,UAAUV,EAAIY,CAAM,CAAC,EAAE,GACtF,OACd,CAEA,MAAM,WAAWF,EAAQE,EAAQ,CAE/B,OADa,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,YAAYZ,EAAIU,CAAM,CAAC,UAAUV,EAAIY,CAAM,CAAC,EAAE,GACzF,OACd,CAEA,MAAM,OAAQ,CACZ,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,SAAU,CAAC,CAAC,CACtD,CAEA,KAAKL,EAAU,CACb,OAAO,KAAK,MAAM,KAAMM,GAASA,EAAK,KAAON,GAAYM,EAAK,OAASN,CAAQ,GAAK,IACtF,CACF,EAEaL,EAAN,MAAMY,CAAQ,CAMnB,YAAYlB,EAAQC,EAAMkB,EAAQ,CAChC,KAAK,OAASnB,EACd,KAAK,GAAKC,EAAK,GACf,KAAK,QAAUA,EAAK,QACpB,KAAK,UAAYA,EAAK,UAEtB,KAAK,QAAUA,EAAK,MAAQ,GAC5B,KAAK,OAASA,EAAK,QAAU,CAAC,EAC9B,KAAK,YAAcA,EAAK,aAAe,CAAC,EACxC,KAAK,OAASA,EAAK,QAAU,CAAC,EAC9B,KAAK,UAAYA,EAAK,WAAa,CAAC,EACpC,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,UAAY,IAAI,KAAKA,EAAK,IAAM,KAAK,IAAI,CAAC,EAC/C,KAAK,SAAWA,EAAK,SAAW,IAAI,KAAKA,EAAK,QAAQ,EAAI,KAC1D,KAAK,OAAS,EAAQA,EAAK,SAC3B,KAAK,QAAU,EAAQA,EAAK,QAC5B,IAAMmB,EAAWnB,EAAK,UAAY,CAAC,EACnC,KAAK,SAAW,CAEd,MAAOmB,EAAS,OAAQC,GAAU,CAACA,EAAM,WAAW,GAAG,CAAC,EAExD,MAAOD,EAAS,OAAQC,GAAUA,EAAM,WAAW,QAAQ,CAAC,EAAE,IAAKA,GAAUA,EAAM,MAAM,CAAC,CAAC,EAC3F,SAAUD,EAAS,SAAS,WAAW,EACvC,IAAKA,CACP,EACA,KAAK,OACHD,aAAkBpB,EACdoB,EACAA,EACEnB,EAAO,WAAWmB,CAAM,EACxBnB,EAAO,MAAM,IAAIC,EAAK,IAAI,GAAK,IAAIF,EAAKC,EAAQ,CAAE,GAAIC,EAAK,KAAM,KAAMA,EAAK,QAAS,CAAC,CAChG,CACA,IAAI,OAAQ,CACV,OAAO,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,GAAK,IACjD,CACA,IAAI,SAAU,CACZ,OAAO,KAAK,OAAO,SAAS,IAAI,KAAK,SAAS,GAAK,IACrD,CAEA,IAAI,cAAe,CACjB,OAAO,KAAK,QAAQ,QAAQ,wBAAyB,CAACqB,EAAO/B,IAAO,CAClE,IAAMgC,EAAO,KAAK,OAAO,MAAM,IAAIhC,CAAE,EACrC,OAAOgC,EAAO,IAAIA,EAAK,WAAW,GAAKD,CACzC,CAAC,CACH,CAEA,IAAI,YAAa,CACf,OAAO,KAAK,SAAS,MAAM,SAAS,KAAK,OAAO,MAAM,EAAE,GAAK,KAAK,SAAS,QAC7E,CACA,IAAI,MAAO,CACT,MAAO,eAAelB,EAAI,KAAK,OAAO,CAAC,aAAaA,EAAI,KAAK,SAAS,CAAC,WACzE,CAEA,MAAM,MAAMC,EAAS,CACnB,IAAMmB,EAAU/B,EAAeY,CAAO,EAChCJ,EAAO,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,KAAM,CAAE,GAAGuB,EAAS,QAAS,KAAK,EAAG,CAAC,EACpF,OAAO,IAAIN,EAAQ,KAAK,OAAQjB,EAAK,QAAS,KAAK,OAAO,IAAI,CAChE,CAEA,MAAM,KAAKI,EAAS,CAClB,IAAMmB,EAAU/B,EAAeY,CAAO,EAChCJ,EAAO,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK,IAAI,IAAIG,EAAI,KAAK,EAAE,CAAC,GAAI,CAAE,KAAMoB,EAAQ,KAAM,GAAIA,EAAQ,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CAAG,CAAC,EACvJ,OAAO,IAAIN,EAAQ,KAAK,OAAQjB,EAAK,QAAS,KAAK,MAAM,CAC3D,CAEA,MAAM,KAAM,CACV,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAIG,EAAI,KAAK,EAAE,CAAC,MAAM,CAC/D,CAEA,MAAM,OAAQ,CACZ,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIA,EAAI,KAAK,EAAE,CAAC,MAAM,CAClE,CAEA,MAAM,QAAS,CACb,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIA,EAAI,KAAK,EAAE,CAAC,EAAE,CAC9D,CAEA,MAAM,MAAMqB,EAAO,CACjB,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAIrB,EAAI,KAAK,EAAE,CAAC,cAAcA,EAAIqB,CAAK,CAAC,EAAE,CACnF,CAEA,MAAM,QAAQA,EAAO,CACnB,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIrB,EAAI,KAAK,EAAE,CAAC,cAAcA,EAAIqB,CAAK,CAAC,EAAE,CACtF,CACF,EF7OA,IAAMC,EAAc,8BACdC,EAAe,IACfC,EAAiB,IAEvB,eAAeC,GAAc,CAC3B,GAAI,OAAO,WAAW,WAAc,WAAY,OAAO,WAAW,UAClE,GAAI,CACF,IAAMC,EAAM,KAAM,QAAO,IAAI,EAC7B,OAAOA,EAAI,SAAWA,EAAI,SAC5B,MAAQ,CACN,MAAM,IAAI,MAAM,qGAA+F,CACjH,CACF,CAEO,IAAMC,EAAN,cAAqB,cAAa,CAUvC,YAAYC,EAAS,CAEnB,GADA,MAAM,EACF,CAACA,GAAS,OAAS,CAAC,OAAOA,EAAQ,KAAK,EAAE,WAAW,IAAI,EAC3D,MAAM,IAAI,MAAM,4FAAyF,EAE3G,KAAK,MAAQA,EAAQ,MACrB,KAAK,QAAUA,EAAQ,QAAUN,GAAa,QAAQ,MAAO,EAAE,EAC/D,KAAK,WAAaM,EAAQ,YAAc,GAAG,KAAK,OAAO,QAAQ,QAAS,IAAI,CAAC,MAC7E,KAAK,OAASA,EAAQ,QAAU,IAChC,KAAK,WAAaA,EAAQ,YAAc,GACxC,KAAK,KAAO,IAAIC,EAAK,CAAE,MAAO,KAAK,MAAO,OAAQ,KAAK,MAAO,CAAC,EAE/D,KAAK,KAAO,KAEZ,KAAK,OAAS,IAAI,IAElB,KAAK,MAAQ,IAAI,IAEjB,KAAK,SAAW,IAAI,IACpB,KAAK,IAAM,KACX,KAAK,OAAS,GACd,KAAK,SAAW,GAChB,KAAK,SAAW,EAChB,KAAK,WAAa,IACpB,CAQA,QAAQC,EAAS,CACf,KAAK,SAAS,IAAIA,EAAQ,KAAK,YAAY,EAAGA,CAAO,EACrD,QAAWC,KAASD,EAAQ,SAAW,CAAC,EAAG,KAAK,SAAS,IAAIC,EAAM,YAAY,EAAGD,CAAO,EACzF,OAAO,IACT,CAGA,MAAM,YAAYE,EAAS,CACzB,GAAI,CAAC,KAAK,SAAS,MAAQ,CAACA,EAAQ,QAAQ,WAAW,KAAK,MAAM,EAAG,OACrE,GAAM,CAACC,EAAM,GAAGC,CAAI,EAAIF,EAAQ,QAAQ,MAAM,KAAK,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,EAC9EF,EAAU,KAAK,SAAS,KAAKG,GAAQ,IAAI,YAAY,CAAC,EAC5D,GAAI,CAACH,EAAS,OACd,IAAMK,EAAU,CAAE,QAAAH,EAAS,KAAAE,EAAM,OAAQ,KAAM,MAAQE,GAAYJ,EAAQ,MAAMI,CAAO,CAAE,EAC1F,GAAI,CACF,MAAMN,EAAQ,IAAIK,CAAO,CAC3B,OAASE,EAAK,CACR,KAAK,cAAc,cAAc,EAAG,KAAK,KAAK,eAAgBA,EAAKF,CAAO,EACzE,KAAK,KAAK,QAASE,CAAG,CAC7B,CACF,CAKA,MAAM,OAAQ,CACZ,IAAMC,EAAK,MAAM,KAAK,KAAK,IAAI,UAAU,EACzC,YAAK,KAAO,KAAK,WAAW,CAAE,GAAGA,EAAG,IAAK,IAAK,EAAK,CAAC,EACpD,MAAM,KAAK,YAAY,EACvB,KAAK,SAAW,GAChB,MAAM,KAAK,SAAS,EACb,KAAK,IACd,CAGA,SAAU,CACR,KAAK,SAAW,GAChB,cAAc,KAAK,UAAU,EAC7B,GAAI,CACF,KAAK,KAAK,MAAM,CAClB,MAAQ,CAER,CACF,CAEA,MAAM,aAAc,CAClB,IAAMC,EAAO,MAAM,KAAK,KAAK,IAAI,aAAa,EACxCC,EAAO,IAAI,IACjB,QAAWC,KAAOF,EAAK,OAAQ,CAC7BC,EAAK,IAAIC,EAAI,EAAE,EACf,IAAMC,EAAO,KAAK,OAAO,IAAID,EAAI,EAAE,EAC/BC,EAAMA,EAAK,OAAOD,CAAG,EACpB,KAAK,OAAO,IAAIA,EAAI,GAAI,IAAIE,EAAM,KAAMF,CAAG,CAAC,CACnD,CACA,QAAWG,IAAM,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC,EAAQJ,EAAK,IAAII,CAAE,GAAG,KAAK,OAAO,OAAOA,CAAE,CACpF,CAGA,MAAM,WAAWA,EAAI,CACnB,IAAML,EAAO,MAAM,KAAK,KAAK,IAAI,eAAe,mBAAmBK,CAAE,CAAC,EAAE,EAClEF,EAAO,KAAK,OAAO,IAAIE,CAAE,EAC/B,OAAIF,EAAMA,EAAK,OAAOH,EAAK,KAAK,EAC3B,KAAK,OAAO,IAAIK,EAAI,IAAID,EAAM,KAAMJ,EAAK,KAAK,CAAC,EAC7C,KAAK,OAAO,IAAIK,CAAE,CAC3B,CAGA,MAAM,UAAUA,EAAI,CAClB,IAAML,EAAO,MAAM,KAAK,KAAK,IAAI,cAAc,mBAAmBK,CAAE,CAAC,EAAE,EACvE,OAAO,KAAK,WAAWL,EAAK,IAAI,CAClC,CAGA,WAAWA,EAAM,CACf,IAAMM,EAAO,IAAIC,EAAK,KAAMP,CAAI,EAChC,YAAK,MAAM,IAAIM,EAAK,GAAIA,CAAI,EACrBA,CACT,CAEA,MAAM,UAAW,CACf,IAAME,EAAS,MAAMtB,EAAY,EACjC,OAAO,IAAI,QAAQ,CAACuB,EAASC,IAAW,CACtC,IAAMC,EAAK,IAAIH,EAAO,KAAK,UAAU,EACrC,KAAK,IAAMG,EACX,IAAIC,EAAU,GACRC,EAAQC,GAAY,CACpBH,EAAG,aAAe,GAAGA,EAAG,KAAK,KAAK,UAAUG,CAAO,CAAC,CAC1D,EACAH,EAAG,OAAS,IAAM,CAChBE,EAAK,CAAE,KAAM,WAAY,KAAM,KAAK,KAAK,aAAe,KAAK,KAAK,SAAU,MAAO,KAAK,KAAM,CAAC,EAC/F,cAAc,KAAK,UAAU,EAE7B,KAAK,WAAa,YAAY,IAAMA,EAAK,CAAE,KAAM,YAAa,GAAI,KAAK,IAAI,CAAE,CAAC,EAAG7B,CAAY,CAC/F,EACA2B,EAAG,UAAaI,GAAU,CACxB,IAAIC,EACJ,GAAI,CACFA,EAAM,KAAK,MAAM,OAAOD,EAAM,MAAS,SAAWA,EAAM,KAAOA,EAAM,KAAK,SAAS,CAAC,CACtF,MAAQ,CACN,MACF,CACA,GAAIC,EAAI,OAAS,aAAc,CAC7B,IAAMC,EAAQ,CAAC,KAAK,OACpB,KAAK,OAAS,GACd,KAAK,SAAW,EACXL,IACHA,EAAU,GACVH,EAAQ,GAENQ,EAAO,KAAK,KAAK,QAAS,KAAK,IAAI,EAClC,KAAK,KAAK,aAAa,EAC5B,MACF,CACA,GAAID,EAAI,OAAS,iBAAkB,CACjC,KAAK,SAAW,GAChB,IAAMlB,EAAM,IAAI,MAAMkB,EAAI,SAAW,qCAAqC,EACrEJ,EAGE,KAAK,KAAK,QAASd,CAAG,GAF3Bc,EAAU,GACVF,EAAOZ,CAAG,GAEZ,MACF,CACA,KAAK,UAAUkB,CAAG,EAAE,MAAOlB,GAAQ,KAAK,KAAK,QAASA,CAAG,CAAC,CAC5D,EACAa,EAAG,QAAWI,GAAU,CAClB,CAACH,GAAW,KAAK,WAAa,GAAK,CAAC,KAAK,SAC3CA,EAAU,GACVF,EAAO,IAAI,MAAM,sCAAgC,KAAK,UAAU,KAAKK,GAAO,SAAW,cAAc,EAAE,CAAC,EAE5G,EACAJ,EAAG,QAAU,IAAM,CACjB,cAAc,KAAK,UAAU,EAC7B,KAAK,KAAK,YAAY,EAClB,OAAK,UAAY,CAAC,KAAK,SAC3B,KAAK,WAAW,CAClB,CACF,CAAC,CACH,CAEA,YAAa,CACX,KAAK,UAAY,EACjB,IAAMO,EAAQ,KAAK,IAAIjC,EAAgB,IAAO,GAAK,KAAK,IAAI,KAAK,SAAW,EAAG,CAAC,CAAC,EAAI,KAAK,OAAO,EAAI,IACrG,KAAK,KAAK,eAAgB,KAAK,QAAQ,EACvC,WAAW,SAAY,CACrB,GAAI,MAAK,SACT,GAAI,CAEF,MAAM,KAAK,YAAY,EACvB,MAAM,KAAK,SAAS,CACtB,MAAQ,CACN,KAAK,WAAW,CAClB,CACF,EAAGiC,CAAK,CACV,CAGA,MAAM,UAAUF,EAAK,CACnB,OAAQA,EAAI,KAAM,CAChB,IAAK,gBAAiB,CACpB,GAAI,CAACA,EAAI,QAAS,OAClB,IAAMvB,EAAU,IAAI0B,EAAQ,KAAMH,EAAI,QAASA,EAAI,OAAS,CAAE,GAAGA,EAAI,MAAO,EAAI,MAAS,EAEzF,GADIvB,EAAQ,OAAO,KAAO,KAAK,MAAM,IACjC,KAAK,YAAcA,EAAQ,OAAO,IAAK,OAC3C,KAAK,KAAK,gBAAiBA,CAAO,EAClC,MAAM,KAAK,YAAYA,CAAO,EAC9B,MACF,CACA,IAAK,wBACCuB,EAAI,SAAS,KAAK,KAAK,gBAAiB,IAAIG,EAAQ,KAAMH,EAAI,OAAO,CAAC,EAC1E,OACF,IAAK,wBACH,KAAK,KAAK,gBAAiB,CAAE,GAAIA,EAAI,UAAW,QAASA,EAAI,QAAS,UAAWA,EAAI,SAAU,CAAC,EAChG,OACF,IAAK,0BACH,KAAK,KAAK,iBAAkB,CAAE,UAAWA,EAAI,UAAW,QAASA,EAAI,QAAS,UAAWA,EAAI,UAAW,UAAWA,EAAI,WAAa,CAAC,CAAE,CAAC,EACxI,OACF,IAAK,eACCA,EAAI,SAAW,KAAK,MAAM,IAAMA,EAAI,SAAW,IACjD,KAAK,KAAK,cAAe,CAAE,OAAQA,EAAI,OAAQ,KAAMA,EAAI,KAAM,QAASA,EAAI,QAAS,UAAWA,EAAI,SAAU,CAAC,EAEjH,OACF,IAAK,eAAgB,CACnB,IAAMI,EAAQ,MAAM,KAAK,WAAWJ,EAAI,OAAO,EAC/C,KAAK,KAAK,YAAaI,CAAK,EAC5B,MACF,CACA,IAAK,gBAAiB,CACpB,GAAI,CAAC,KAAK,OAAO,IAAIJ,EAAI,OAAO,EAAG,OACnC,IAAMI,EAAQ,MAAM,KAAK,WAAWJ,EAAI,OAAO,EAAE,MAAM,IAAM,IAAI,EAC7DI,GAAO,KAAK,KAAK,cAAeA,CAAK,EACzC,MACF,CACA,IAAK,mBAAoB,CACvB,GAAI,CAACJ,EAAI,OAAQ,OACjB,IAAMV,EAAO,KAAK,WAAWU,EAAI,MAAM,EACjCI,EAAQ,KAAK,OAAO,IAAIJ,EAAI,OAAO,GAAM,MAAM,KAAK,WAAWA,EAAI,OAAO,EAAE,MAAM,IAAM,IAAI,EAC9FI,IAAOA,EAAM,aAAe,GAChC,KAAK,KAAK,YAAa,CAAE,KAAAd,EAAM,MAAAc,EAAO,QAASJ,EAAI,QAAS,IAAKA,EAAI,KAAO,IAAK,CAAC,EAClF,MACF,CACA,IAAK,sBAAuB,CAC1B,IAAMI,EAAQ,KAAK,OAAO,IAAIJ,EAAI,OAAO,GAAK,KAC1CI,IAAOA,EAAM,YAAc,KAAK,IAAI,EAAGA,EAAM,YAAc,CAAC,GAChE,KAAK,KAAK,eAAgB,CAAE,OAAQJ,EAAI,OAAQ,KAAM,KAAK,MAAM,IAAIA,EAAI,MAAM,GAAK,KAAM,MAAAI,EAAO,QAASJ,EAAI,QAAS,OAAQA,EAAI,QAAU,KAAM,GAAIA,EAAI,IAAM,IAAK,CAAC,EACvK,MACF,CACA,IAAK,gBACH,KAAK,OAAO,OAAOA,EAAI,OAAO,EAC9B,KAAK,KAAK,aAAc,CAAE,GAAIA,EAAI,QAAS,OAAQA,EAAI,QAAU,IAAK,CAAC,EACvE,OACF,QACE,KAAK,KAAK,MAAOA,CAAG,CACxB,CACF,CACF,EG5RA,SAASK,EAAQC,EAAO,CACtB,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,IAAMC,EAAM,OAAOD,CAAK,EAAE,QAAQ,KAAM,EAAE,EAC1C,MAAO,iBAAiB,KAAKC,CAAG,EAAI,SAASA,EAAK,EAAE,EAAI,MAC1D,CAEO,IAAMC,EAAN,KAAmB,CAExB,YAAYC,EAAO,CAAC,EAAG,CAErB,KAAK,KAAO,CAAE,GAAGA,CAAK,CACxB,CACA,SAASC,EAAO,CACd,YAAK,KAAK,MAAQA,EACX,IACT,CACA,eAAeC,EAAa,CAC1B,YAAK,KAAK,YAAcA,EACjB,IACT,CACA,OAAOC,EAAK,CACV,YAAK,KAAK,IAAMA,EACT,IACT,CAEA,SAASN,EAAO,CACd,YAAK,KAAK,MAAQD,EAAQC,CAAK,EACxB,IACT,CAEA,UAAUO,EAAQ,CAChB,YAAK,KAAK,OAAS,CAAE,KAAMA,EAAO,KAAM,IAAKA,EAAO,IAAK,SAAUA,EAAO,OAAQ,EAC3E,IACT,CAEA,UAAUC,EAAQ,CAChB,YAAK,KAAK,OAAS,CAAE,KAAMA,EAAO,KAAM,SAAUA,EAAO,OAAQ,EAC1D,IACT,CACA,SAASF,EAAK,CACZ,YAAK,KAAK,MAAQ,CAAE,IAAAA,CAAI,EACjB,IACT,CACA,aAAaA,EAAK,CAChB,YAAK,KAAK,UAAY,CAAE,IAAAA,CAAI,EACrB,IACT,CAEA,aAAaG,EAAY,IAAI,KAAQ,CACnC,YAAK,KAAK,UAAY,IAAI,KAAKA,CAAS,EAAE,YAAY,EAC/C,IACT,CAEA,aAAaC,EAAQ,CACnB,YAAK,KAAK,OAAS,CAAC,GAAI,KAAK,KAAK,QAAU,CAAC,EAAI,GAAGA,EAAO,KAAK,CAAC,EAC1D,IACT,CACA,QAAS,CACP,OAAO,KAAK,IACd,CACF",
|
|
6
|
+
"names": ["index_exports", "__export", "Channel", "Client", "EmbedBuilder", "FiveLiveAPIError", "Group", "Message", "REST", "User", "channelMention", "userMention", "__toCommonJS", "import_node_events", "FiveLiveAPIError", "message", "status", "method", "path", "sleep", "ms", "resolve", "REST", "options", "body", "attempt", "res", "data", "seconds", "enc", "userMention", "id", "channelMention", "messagePayload", "input", "options", "embeds", "embed", "roles", "User", "client", "data", "Channel", "group", "enc", "content", "Message", "query", "message", "Group", "channel", "idOrName", "member", "key", "userId", "reason", "roleId", "role", "_Message", "author", "mentions", "entry", "whole", "user", "payload", "emoji", "DEFAULT_API", "KEEPALIVE_MS", "MAX_BACKOFF_MS", "socketClass", "mod", "Client", "options", "REST", "command", "alias", "message", "name", "args", "context", "content", "err", "me", "data", "seen", "raw", "held", "Group", "id", "user", "User", "Socket", "resolve", "reject", "ws", "settled", "send", "payload", "event", "msg", "first", "delay", "Message", "group", "toColor", "color", "hex", "EmbedBuilder", "data", "title", "description", "url", "author", "footer", "timestamp", "fields"]
|
|
7
|
+
}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
|
|
3
|
+
export interface ClientOptions {
|
|
4
|
+
/** O token do bot (começa com b_), do portal do desenvolvedor. */
|
|
5
|
+
token: string;
|
|
6
|
+
/** Padrão: https://api.fivenetwork.dev */
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
/** Padrão: o apiUrl com ws(s):// e /ws. */
|
|
9
|
+
gatewayUrl?: string;
|
|
10
|
+
/** Prefixo dos comandos registrados com client.command(). Padrão: "!" */
|
|
11
|
+
prefix?: string;
|
|
12
|
+
/** Ignorar mensagens de outros bots. Padrão: true */
|
|
13
|
+
ignoreBots?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface EmbedData {
|
|
17
|
+
title?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
color?: number;
|
|
21
|
+
author?: { name: string; url?: string; icon_url?: string };
|
|
22
|
+
footer?: { text: string; icon_url?: string };
|
|
23
|
+
image?: { url: string };
|
|
24
|
+
thumbnail?: { url: string };
|
|
25
|
+
timestamp?: string;
|
|
26
|
+
fields?: { name: string; value: string; inline?: boolean }[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class EmbedBuilder {
|
|
30
|
+
constructor(data?: EmbedData);
|
|
31
|
+
data: EmbedData;
|
|
32
|
+
setTitle(title: string): this;
|
|
33
|
+
setDescription(description: string): this;
|
|
34
|
+
setURL(url: string): this;
|
|
35
|
+
/** "#3b6cf6" ou 0x3b6cf6 */
|
|
36
|
+
setColor(color: string | number): this;
|
|
37
|
+
setAuthor(author: { name: string; url?: string; iconURL?: string }): this;
|
|
38
|
+
setFooter(footer: { text: string; iconURL?: string }): this;
|
|
39
|
+
setImage(url: string): this;
|
|
40
|
+
setThumbnail(url: string): this;
|
|
41
|
+
setTimestamp(timestamp?: Date | number | string): this;
|
|
42
|
+
addFields(...fields: ({ name: string; value: string; inline?: boolean } | { name: string; value: string; inline?: boolean }[])[]): this;
|
|
43
|
+
toJSON(): EmbedData;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface MessageOptions {
|
|
47
|
+
content?: string;
|
|
48
|
+
embeds?: (EmbedBuilder | EmbedData)[];
|
|
49
|
+
/** ids de pessoas a mencionar (além dos <@id> no texto). */
|
|
50
|
+
mentions?: string[];
|
|
51
|
+
/** ids de cargos a mencionar (o cargo precisa ser mencionável, ou o bot ter "Mencionar @everyone"). */
|
|
52
|
+
roleMentions?: string[];
|
|
53
|
+
/** @everyone (precisa da permissão). */
|
|
54
|
+
everyone?: boolean;
|
|
55
|
+
/** Responder a uma mensagem (id ou a mensagem). */
|
|
56
|
+
reply?: string | Message;
|
|
57
|
+
}
|
|
58
|
+
export type MessageContent = string | MessageOptions;
|
|
59
|
+
|
|
60
|
+
export class User {
|
|
61
|
+
readonly id: string;
|
|
62
|
+
readonly username: string | null;
|
|
63
|
+
readonly displayName: string;
|
|
64
|
+
readonly avatarURL: string | null;
|
|
65
|
+
readonly bot: boolean;
|
|
66
|
+
readonly flags: string[];
|
|
67
|
+
/** "<@id>" */
|
|
68
|
+
toString(): string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class Channel {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly name: string;
|
|
74
|
+
readonly kind: "text" | "voice";
|
|
75
|
+
readonly categoryId: string | null;
|
|
76
|
+
readonly topic: string;
|
|
77
|
+
readonly group: Group;
|
|
78
|
+
readonly isText: boolean;
|
|
79
|
+
send(content: MessageContent): Promise<Message>;
|
|
80
|
+
sendTyping(): Promise<void>;
|
|
81
|
+
fetchMessages(options?: { limit?: number; after?: string }): Promise<Message[]>;
|
|
82
|
+
fetchMessage(id: string): Promise<Message>;
|
|
83
|
+
toString(): string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface GroupRole {
|
|
87
|
+
id: string;
|
|
88
|
+
name: string;
|
|
89
|
+
color: string | null;
|
|
90
|
+
position: number;
|
|
91
|
+
mentionable: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface GroupMember {
|
|
95
|
+
id: string;
|
|
96
|
+
username: string;
|
|
97
|
+
displayName: string;
|
|
98
|
+
avatarUrl: string | null;
|
|
99
|
+
bot: boolean;
|
|
100
|
+
roleIds: string[];
|
|
101
|
+
owner: boolean;
|
|
102
|
+
online: boolean;
|
|
103
|
+
user: User;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export type BotPermission =
|
|
107
|
+
| "administrator"
|
|
108
|
+
| "manageGroup"
|
|
109
|
+
| "manageChannels"
|
|
110
|
+
| "manageRoles"
|
|
111
|
+
| "kickMembers"
|
|
112
|
+
| "banMembers"
|
|
113
|
+
| "manageMessages"
|
|
114
|
+
| "manageReactions"
|
|
115
|
+
| "createInvites"
|
|
116
|
+
| "manageWebhooks";
|
|
117
|
+
|
|
118
|
+
export class Group {
|
|
119
|
+
readonly id: string;
|
|
120
|
+
readonly name: string;
|
|
121
|
+
readonly iconURL: string | null;
|
|
122
|
+
readonly ownerId: string | null;
|
|
123
|
+
readonly memberCount: number;
|
|
124
|
+
readonly roles: GroupRole[];
|
|
125
|
+
readonly channels: Map<string, Channel>;
|
|
126
|
+
/** O que o bot pode fazer neste grupo. */
|
|
127
|
+
readonly permissions: Partial<Record<BotPermission, boolean>>;
|
|
128
|
+
can(permission: BotPermission): boolean;
|
|
129
|
+
kick(userId: string): Promise<void>;
|
|
130
|
+
ban(userId: string, reason?: string): Promise<void>;
|
|
131
|
+
unban(userId: string): Promise<void>;
|
|
132
|
+
fetchBans(): Promise<{ userId: string; name: string; bannedAt: number; by: string; reason?: string }[]>;
|
|
133
|
+
addRole(userId: string, roleId: string): Promise<string[]>;
|
|
134
|
+
removeRole(userId: string, roleId: string): Promise<string[]>;
|
|
135
|
+
leave(): Promise<void>;
|
|
136
|
+
/** A sala pelo id ou pelo nome. */
|
|
137
|
+
channel(idOrName: string): Channel | null;
|
|
138
|
+
role(idOrName: string): GroupRole | null;
|
|
139
|
+
fetchMembers(): Promise<GroupMember[]>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface Reaction {
|
|
143
|
+
emoji: string;
|
|
144
|
+
users: string[];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export class Message {
|
|
148
|
+
readonly id: string;
|
|
149
|
+
readonly groupId: string;
|
|
150
|
+
readonly channelId: string;
|
|
151
|
+
/** O texto como está gravado: menções como <@id>. */
|
|
152
|
+
readonly content: string;
|
|
153
|
+
/** O texto com <@id> trocado por @nome. */
|
|
154
|
+
readonly cleanContent: string;
|
|
155
|
+
readonly embeds: EmbedData[];
|
|
156
|
+
readonly attachments: { url: string; name: string; size: number; type: string; kind: string }[];
|
|
157
|
+
readonly images: string[];
|
|
158
|
+
readonly reactions: Reaction[];
|
|
159
|
+
readonly replyTo: { id: string; from: string; fromName: string; text: string } | null;
|
|
160
|
+
readonly createdAt: Date;
|
|
161
|
+
readonly editedAt: Date | null;
|
|
162
|
+
readonly pinned: boolean;
|
|
163
|
+
readonly webhook: boolean;
|
|
164
|
+
readonly author: User;
|
|
165
|
+
readonly mentions: { users: string[]; roles: string[]; everyone: boolean; raw: string[] };
|
|
166
|
+
/** A mensagem menciona o bot (pelo id ou @everyone). */
|
|
167
|
+
readonly mentionsMe: boolean;
|
|
168
|
+
readonly group: Group | null;
|
|
169
|
+
readonly channel: Channel | null;
|
|
170
|
+
reply(content: MessageContent): Promise<Message>;
|
|
171
|
+
edit(content: MessageContent): Promise<Message>;
|
|
172
|
+
delete(): Promise<void>;
|
|
173
|
+
react(emoji: string): Promise<void>;
|
|
174
|
+
unreact(emoji: string): Promise<void>;
|
|
175
|
+
pin(): Promise<void>;
|
|
176
|
+
unpin(): Promise<void>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface CommandContext {
|
|
180
|
+
message: Message;
|
|
181
|
+
args: string[];
|
|
182
|
+
client: Client;
|
|
183
|
+
reply(content: MessageContent): Promise<Message>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface Command {
|
|
187
|
+
name: string;
|
|
188
|
+
description?: string;
|
|
189
|
+
aliases?: string[];
|
|
190
|
+
run(context: CommandContext): unknown;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface ClientEvents {
|
|
194
|
+
ready: [user: User];
|
|
195
|
+
messageCreate: [message: Message];
|
|
196
|
+
messageUpdate: [message: Message];
|
|
197
|
+
messageDelete: [info: { id: string; groupId: string; channelId: string }];
|
|
198
|
+
reactionUpdate: [info: { messageId: string; groupId: string; channelId: string; reactions: Reaction[] }];
|
|
199
|
+
typingStart: [info: { userId: string; name: string; groupId: string; channelId: string }];
|
|
200
|
+
groupJoin: [group: Group];
|
|
201
|
+
memberAdd: [info: { user: User; group: Group | null; groupId: string; via: "join" | "invite" | "profile" | null }];
|
|
202
|
+
memberRemove: [info: { userId: string; user: User | null; group: Group | null; groupId: string; reason: "left" | "kicked" | "banned" | null; by: string | null }];
|
|
203
|
+
groupLeave: [info: { id: string; reason: string | null }];
|
|
204
|
+
groupUpdate: [group: Group];
|
|
205
|
+
reconnecting: [attempt: number];
|
|
206
|
+
reconnected: [];
|
|
207
|
+
disconnect: [];
|
|
208
|
+
error: [error: Error];
|
|
209
|
+
commandError: [error: unknown, context: CommandContext];
|
|
210
|
+
raw: [payload: Record<string, unknown>];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export class Client extends EventEmitter {
|
|
214
|
+
constructor(options: ClientOptions);
|
|
215
|
+
readonly user: User | null;
|
|
216
|
+
readonly groups: Map<string, Group>;
|
|
217
|
+
readonly users: Map<string, User>;
|
|
218
|
+
readonly commands: Map<string, Command>;
|
|
219
|
+
readonly prefix: string;
|
|
220
|
+
readonly rest: REST;
|
|
221
|
+
login(): Promise<User>;
|
|
222
|
+
destroy(): void;
|
|
223
|
+
command(command: Command): this;
|
|
224
|
+
fetchGroup(id: string): Promise<Group>;
|
|
225
|
+
fetchUser(id: string): Promise<User>;
|
|
226
|
+
on<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => void): this;
|
|
227
|
+
once<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => void): this;
|
|
228
|
+
off<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => void): this;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export class REST {
|
|
232
|
+
constructor(options: { token: string; apiUrl: string; retries?: number });
|
|
233
|
+
request<T = any>(method: string, path: string, body?: unknown): Promise<T>;
|
|
234
|
+
get<T = any>(path: string): Promise<T>;
|
|
235
|
+
post<T = any>(path: string, body?: unknown): Promise<T>;
|
|
236
|
+
patch<T = any>(path: string, body?: unknown): Promise<T>;
|
|
237
|
+
put<T = any>(path: string, body?: unknown): Promise<T>;
|
|
238
|
+
delete<T = any>(path: string): Promise<T>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export class FiveLiveAPIError extends Error {
|
|
242
|
+
readonly status: number;
|
|
243
|
+
readonly method: string;
|
|
244
|
+
readonly path: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** "<@id>" */
|
|
248
|
+
export function userMention(id: string): string;
|
|
249
|
+
/** "<#id>" */
|
|
250
|
+
export function channelMention(id: string): string;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{EventEmitter as v}from"node:events";var g=class extends Error{constructor(t,e,s,r){super(t),this.name="FiveLiveAPIError",this.status=e,this.method=s,this.path=r}},_=i=>new Promise(t=>setTimeout(t,i)),m=class{constructor(t){this.token=t.token,this.apiUrl=t.apiUrl.replace(/\/$/,""),this.retries=t.retries??3}async request(t,e,s){for(let r=0;;r++){let a=await fetch(this.apiUrl+e,{method:t,headers:{Authorization:`Bot ${this.token}`,...s!==void 0?{"Content-Type":"application/json"}:{},"User-Agent":"fivelive.js (https://live.fivenetwork.dev/developers, 0.1.0)"},body:s!==void 0?JSON.stringify(s):void 0}),o=await a.json().catch(()=>({}));if(a.status===429&&r<this.retries){let h=Number(o.retryAfter??a.headers.get("retry-after")??5);await _(Math.max(1,h)*1e3);continue}if(!a.ok)throw new g(o.error??`HTTP ${a.status}`,a.status,t,e);return o}}get(t){return this.request("GET",t)}post(t,e){return this.request("POST",t,e??{})}patch(t,e){return this.request("PATCH",t,e??{})}put(t,e){return this.request("PUT",t,e??{})}delete(t){return this.request("DELETE",t)}},n=encodeURIComponent;var b=i=>`<@${i}>`,$=i=>`<#${i}>`;function y(i){let t=typeof i=="string"?{content:i}:i??{},e=(t.embeds??[]).map(r=>typeof r?.toJSON=="function"?r.toJSON():r),s=(t.roleMentions??[]).map(r=>`@role:${r}`);return{text:t.content??"",...e.length?{embeds:e}:{},...t.mentions?.length||s.length||t.everyone?{mentions:[...t.mentions??[],...s,...t.everyone?["@everyone"]:[]]}:{},...t.reply?{replyTo:typeof t.reply=="string"?t.reply:t.reply.id}:{}}}var u=class{constructor(t,e){this.client=t,this.id=e.id,this.username=e.username??null,this.displayName=e.displayName??e.name??e.username??"",this.avatarURL=e.avatarUrl??null,this.bot=e.bot===!0,this.flags=e.flags??[]}toString(){return b(this.id)}},f=class{constructor(t,e,s){this.client=t,this.group=e,this.id=s.id,this.name=s.name,this.kind=s.kind,this.categoryId=s.categoryId??null,this.topic=s.topic??""}get isText(){return this.kind==="text"}get path(){return`/bot/groups/${n(this.group.id)}/channels/${n(this.id)}`}async send(t){let e=await this.client.rest.post(`${this.path}/messages`,y(t));return new c(this.client,e.message,this.client.user)}async sendTyping(){await this.client.rest.post(`${this.path}/typing`,{typing:!0})}async fetchMessages(t={}){let e=new URLSearchParams;return t.limit&&e.set("limit",String(t.limit)),t.after&&e.set("after",t.after),(await this.client.rest.get(`${this.path}/messages?${e}`)).messages.map(r=>new c(this.client,r))}async fetchMessage(t){let e=await this.client.rest.get(`${this.path}/messages/${n(t)}`);return new c(this.client,e.message)}toString(){return`<#${this.id}>`}},p=class{constructor(t,e){this.client=t,this._patch(e)}_patch(t){this.id=t.id,this.name=t.name,this.iconURL=t.iconUrl??null,this.ownerId=t.ownerId??null,this.memberCount=t.memberCount??0,this.roles=t.roles??[],this.permissions=t.permissions??{},this.channels=new Map((t.channels??[]).map(e=>[e.id,new f(this.client,this,e)]))}channel(t){return this.channels.get(t)??[...this.channels.values()].find(e=>e.name===t)??null}async fetchMembers(){return(await this.client.rest.get(`/bot/groups/${n(this.id)}/members`)).members.map(e=>({...e,user:this.client._cacheUser(e)}))}get path(){return`/bot/groups/${n(this.id)}`}can(t){return this.permissions.administrator===!0||this.permissions[t]===!0}async kick(t){await this.client.rest.post(`${this.path}/members/${n(t)}/kick`,{})}async ban(t,e){await this.client.rest.put(`${this.path}/bans/${n(t)}`,e?{reason:e}:{})}async unban(t){await this.client.rest.delete(`${this.path}/bans/${n(t)}`)}async fetchBans(){return(await this.client.rest.get(`${this.path}/bans`)).bans}async addRole(t,e){return(await this.client.rest.put(`${this.path}/members/${n(t)}/roles/${n(e)}`)).roleIds}async removeRole(t,e){return(await this.client.rest.delete(`${this.path}/members/${n(t)}/roles/${n(e)}`)).roleIds}async leave(){await this.client.rest.post(`${this.path}/leave`,{})}role(t){return this.roles.find(e=>e.id===t||e.name===t)??null}},c=class i{constructor(t,e,s){this.client=t,this.id=e.id,this.groupId=e.groupId,this.channelId=e.channelId,this.content=e.text??"",this.embeds=e.embeds??[],this.attachments=e.attachments??[],this.images=e.images??[],this.reactions=e.reactions??[],this.replyTo=e.replyTo??null,this.createdAt=new Date(e.ts??Date.now()),this.editedAt=e.editedAt?new Date(e.editedAt):null,this.pinned=!!e.pinnedAt,this.webhook=!!e.webhook;let r=e.mentions??[];this.mentions={users:r.filter(a=>!a.startsWith("@")),roles:r.filter(a=>a.startsWith("@role:")).map(a=>a.slice(6)),everyone:r.includes("@everyone"),raw:r},this.author=s instanceof u?s:s?t._cacheUser(s):t.users.get(e.from)??new u(t,{id:e.from,name:e.fromName})}get group(){return this.client.groups.get(this.groupId)??null}get channel(){return this.group?.channels.get(this.channelId)??null}get cleanContent(){return this.content.replace(/<@([A-Za-z0-9:_-]+)>/g,(t,e)=>{let s=this.client.users.get(e);return s?`@${s.displayName}`:t})}get mentionsMe(){return this.mentions.users.includes(this.client.user?.id)||this.mentions.everyone}get path(){return`/bot/groups/${n(this.groupId)}/channels/${n(this.channelId)}/messages`}async reply(t){let e=y(t),s=await this.client.rest.post(this.path,{...e,replyTo:this.id});return new i(this.client,s.message,this.client.user)}async edit(t){let e=y(t),s=await this.client.rest.patch(`${this.path}/${n(this.id)}`,{text:e.text,...e.embeds?{embeds:e.embeds}:{}});return new i(this.client,s.message,this.author)}async pin(){await this.client.rest.put(`${this.path}/${n(this.id)}/pin`)}async unpin(){await this.client.rest.delete(`${this.path}/${n(this.id)}/pin`)}async delete(){await this.client.rest.delete(`${this.path}/${n(this.id)}`)}async react(t){await this.client.rest.put(`${this.path}/${n(this.id)}/reactions/${n(t)}`)}async unreact(t){await this.client.rest.delete(`${this.path}/${n(this.id)}/reactions/${n(t)}`)}};var k="https://api.fivenetwork.dev",U=3e4,x=3e4;async function S(){if(typeof globalThis.WebSocket=="function")return globalThis.WebSocket;try{let i=await import("ws");return i.default??i.WebSocket}catch{throw new Error("Este Node n\xE3o tem WebSocket nativo (\xE9 do Node 22+). Rode `npm install ws` ou atualize o Node.")}}var w=class extends v{constructor(t){if(super(),!t?.token||!String(t.token).startsWith("b_"))throw new Error("Passe o token do bot (come\xE7a com b_). Pegue em https://live.fivenetwork.dev/developers.");this.token=t.token,this.apiUrl=(t.apiUrl??k).replace(/\/$/,""),this.gatewayUrl=t.gatewayUrl??`${this.apiUrl.replace(/^http/,"ws")}/ws`,this.prefix=t.prefix??"!",this.ignoreBots=t.ignoreBots??!0,this.rest=new m({token:this.token,apiUrl:this.apiUrl}),this.user=null,this.groups=new Map,this.users=new Map,this.commands=new Map,this._ws=null,this._ready=!1,this._closing=!1,this._attempt=0,this._keepalive=null}command(t){this.commands.set(t.name.toLowerCase(),t);for(let e of t.aliases??[])this.commands.set(e.toLowerCase(),t);return this}async _runCommand(t){if(!this.commands.size||!t.content.startsWith(this.prefix))return;let[e,...s]=t.content.slice(this.prefix.length).trim().split(/\s+/),r=this.commands.get((e??"").toLowerCase());if(!r)return;let a={message:t,args:s,client:this,reply:o=>t.reply(o)};try{await r.run(a)}catch(o){this.listenerCount("commandError")?this.emit("commandError",o,a):this.emit("error",o)}}async login(){let t=await this.rest.get("/bot/@me");return this.user=this._cacheUser({...t.bot,bot:!0}),await this._loadGroups(),this._closing=!1,await this._connect(),this.user}destroy(){this._closing=!0,clearInterval(this._keepalive);try{this._ws?.close()}catch{}}async _loadGroups(){let t=await this.rest.get("/bot/groups"),e=new Set;for(let s of t.groups){e.add(s.id);let r=this.groups.get(s.id);r?r._patch(s):this.groups.set(s.id,new p(this,s))}for(let s of[...this.groups.keys()])e.has(s)||this.groups.delete(s)}async fetchGroup(t){let e=await this.rest.get(`/bot/groups/${encodeURIComponent(t)}`),s=this.groups.get(t);return s?s._patch(e.group):this.groups.set(t,new p(this,e.group)),this.groups.get(t)}async fetchUser(t){let e=await this.rest.get(`/bot/users/${encodeURIComponent(t)}`);return this._cacheUser(e.user)}_cacheUser(t){let e=new u(this,t);return this.users.set(e.id,e),e}async _connect(){let t=await S();return new Promise((e,s)=>{let r=new t(this.gatewayUrl);this._ws=r;let a=!1,o=h=>{r.readyState===1&&r.send(JSON.stringify(h))};r.onopen=()=>{o({type:"register",name:this.user.displayName||this.user.username,token:this.token}),clearInterval(this._keepalive),this._keepalive=setInterval(()=>o({type:"time-sync",t0:Date.now()}),U)},r.onmessage=h=>{let d;try{d=JSON.parse(typeof h.data=="string"?h.data:h.data.toString())}catch{return}if(d.type==="registered"){let l=!this._ready;this._ready=!0,this._attempt=0,a||(a=!0,e()),l?this.emit("ready",this.user):this.emit("reconnected");return}if(d.type==="register-error"){this._closing=!0;let l=new Error(d.message||"O Five Live recusou o token do bot.");a?this.emit("error",l):(a=!0,s(l));return}this._dispatch(d).catch(l=>this.emit("error",l))},r.onerror=h=>{!a&&this._attempt===0&&!this._ready&&(a=!0,s(new Error(`N\xE3o foi poss\xEDvel conectar em ${this.gatewayUrl}: ${h?.message??"erro de rede"}`)))},r.onclose=()=>{clearInterval(this._keepalive),this.emit("disconnect"),!(this._closing||!this._ready)&&this._reconnect()}})}_reconnect(){this._attempt+=1;let t=Math.min(x,1e3*2**Math.min(this._attempt-1,5))+Math.random()*500;this.emit("reconnecting",this._attempt),setTimeout(async()=>{if(!this._closing)try{await this._loadGroups(),await this._connect()}catch{this._reconnect()}},t)}async _dispatch(t){switch(t.type){case"group-message":{if(!t.message)return;let e=new c(this,t.message,t.author?{...t.author}:void 0);if(e.author.id===this.user?.id||this.ignoreBots&&e.author.bot)return;this.emit("messageCreate",e),await this._runCommand(e);return}case"group-message-updated":t.message&&this.emit("messageUpdate",new c(this,t.message));return;case"group-message-deleted":this.emit("messageDelete",{id:t.messageId,groupId:t.groupId,channelId:t.channelId});return;case"group-message-reactions":this.emit("reactionUpdate",{messageId:t.messageId,groupId:t.groupId,channelId:t.channelId,reactions:t.reactions??[]});return;case"group-typing":t.userId!==this.user?.id&&t.typing!==!1&&this.emit("typingStart",{userId:t.userId,name:t.name,groupId:t.groupId,channelId:t.channelId});return;case"group-joined":{let e=await this.fetchGroup(t.groupId);this.emit("groupJoin",e);return}case"group-updated":{if(!this.groups.has(t.groupId))return;let e=await this.fetchGroup(t.groupId).catch(()=>null);e&&this.emit("groupUpdate",e);return}case"group-member-add":{if(!t.member)return;let e=this._cacheUser(t.member),s=this.groups.get(t.groupId)??await this.fetchGroup(t.groupId).catch(()=>null);s&&(s.memberCount+=1),this.emit("memberAdd",{user:e,group:s,groupId:t.groupId,via:t.via??null});return}case"group-member-remove":{let e=this.groups.get(t.groupId)??null;e&&(e.memberCount=Math.max(0,e.memberCount-1)),this.emit("memberRemove",{userId:t.userId,user:this.users.get(t.userId)??null,group:e,groupId:t.groupId,reason:t.reason??null,by:t.by??null});return}case"group-removed":this.groups.delete(t.groupId),this.emit("groupLeave",{id:t.groupId,reason:t.reason??null});return;default:this.emit("raw",t)}}};function T(i){if(typeof i=="number")return i;let t=String(i).replace(/^#/,"");return/^[0-9a-f]{6}$/i.test(t)?parseInt(t,16):void 0}var I=class{constructor(t={}){this.data={...t}}setTitle(t){return this.data.title=t,this}setDescription(t){return this.data.description=t,this}setURL(t){return this.data.url=t,this}setColor(t){return this.data.color=T(t),this}setAuthor(t){return this.data.author={name:t.name,url:t.url,icon_url:t.iconURL},this}setFooter(t){return this.data.footer={text:t.text,icon_url:t.iconURL},this}setImage(t){return this.data.image={url:t},this}setThumbnail(t){return this.data.thumbnail={url:t},this}setTimestamp(t=new Date){return this.data.timestamp=new Date(t).toISOString(),this}addFields(...t){return this.data.fields=[...this.data.fields??[],...t.flat()],this}toJSON(){return this.data}};export{f as Channel,w as Client,I as EmbedBuilder,g as FiveLiveAPIError,p as Group,c as Message,m as REST,u as User,$ as channelMention,b as userMention};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/client.js", "../src/rest.js", "../src/structures.js", "../src/embed.js"],
|
|
4
|
+
"sourcesContent": ["// O Client: conecta no WebSocket do Five Live com o token do bot, mant\u00E9m a\n// conex\u00E3o viva (e reconecta sozinho), transforma os avisos da API em eventos\n// e guarda grupos e usu\u00E1rios conhecidos.\n//\n// Eventos:\n// ready(user) conectou e carregou os grupos\n// messageCreate(message) mensagem nova num grupo do bot\n// messageUpdate(message) mensagem editada\n// messageDelete({ id, groupId, channelId })\n// reactionUpdate({ messageId, groupId, channelId, reactions })\n// typingStart({ userId, name, groupId, channelId })\n// groupJoin(group) / groupLeave({ id, reason }) / groupUpdate(group)\n// reconnecting(attempt) / reconnected() / disconnect() / error(err)\n// commandError(err, context)\n\nimport { EventEmitter } from \"node:events\";\nimport { REST } from \"./rest.js\";\nimport { Group, Message, User } from \"./structures.js\";\n\nconst DEFAULT_API = \"https://api.fivenetwork.dev\";\nconst KEEPALIVE_MS = 30_000;\nconst MAX_BACKOFF_MS = 30_000;\n\nasync function socketClass() {\n if (typeof globalThis.WebSocket === \"function\") return globalThis.WebSocket;\n try {\n const mod = await import(\"ws\");\n return mod.default ?? mod.WebSocket;\n } catch {\n throw new Error(\"Este Node n\u00E3o tem WebSocket nativo (\u00E9 do Node 22+). Rode `npm install ws` ou atualize o Node.\");\n }\n}\n\nexport class Client extends EventEmitter {\n /**\n * @param {{\n * token: string,\n * apiUrl?: string,\n * gatewayUrl?: string,\n * prefix?: string,\n * ignoreBots?: boolean,\n * }} options\n */\n constructor(options) {\n super();\n if (!options?.token || !String(options.token).startsWith(\"b_\")) {\n throw new Error(\"Passe o token do bot (come\u00E7a com b_). Pegue em https://live.fivenetwork.dev/developers.\");\n }\n this.token = options.token;\n this.apiUrl = (options.apiUrl ?? DEFAULT_API).replace(/\\/$/, \"\");\n this.gatewayUrl = options.gatewayUrl ?? `${this.apiUrl.replace(/^http/, \"ws\")}/ws`;\n this.prefix = options.prefix ?? \"!\";\n this.ignoreBots = options.ignoreBots ?? true;\n this.rest = new REST({ token: this.token, apiUrl: this.apiUrl });\n /** @type {User | null} */\n this.user = null;\n /** @type {Map<string, Group>} */\n this.groups = new Map();\n /** @type {Map<string, User>} */\n this.users = new Map();\n /** @type {Map<string, { name: string, description?: string, aliases?: string[], run: Function }>} */\n this.commands = new Map();\n this._ws = null;\n this._ready = false;\n this._closing = false;\n this._attempt = 0;\n this._keepalive = null;\n }\n\n // \u2500\u2500\u2500 Comandos com prefixo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * Registra um comando: `!nome arg1 arg2`.\n * @param {{ name: string, description?: string, aliases?: string[], run: (ctx: { message: Message, args: string[], client: Client, reply: (content: any) => Promise<Message> }) => any }} command\n */\n command(command) {\n this.commands.set(command.name.toLowerCase(), command);\n for (const alias of command.aliases ?? []) this.commands.set(alias.toLowerCase(), command);\n return this;\n }\n\n /** @param {Message} message */\n async _runCommand(message) {\n if (!this.commands.size || !message.content.startsWith(this.prefix)) return;\n const [name, ...args] = message.content.slice(this.prefix.length).trim().split(/\\s+/);\n const command = this.commands.get((name ?? \"\").toLowerCase());\n if (!command) return;\n const context = { message, args, client: this, reply: (content) => message.reply(content) };\n try {\n await command.run(context);\n } catch (err) {\n if (this.listenerCount(\"commandError\")) this.emit(\"commandError\", err, context);\n else this.emit(\"error\", err);\n }\n }\n\n // \u2500\u2500\u2500 Conex\u00E3o \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** Conecta. Resolve quando o bot estiver pronto (evento \"ready\"). */\n async login() {\n const me = await this.rest.get(\"/bot/@me\");\n this.user = this._cacheUser({ ...me.bot, bot: true });\n await this._loadGroups();\n this._closing = false;\n await this._connect();\n return this.user;\n }\n\n /** Desconecta de vez (sem reconectar). */\n destroy() {\n this._closing = true;\n clearInterval(this._keepalive);\n try {\n this._ws?.close();\n } catch {\n // j\u00E1 fechado\n }\n }\n\n async _loadGroups() {\n const data = await this.rest.get(\"/bot/groups\");\n const seen = new Set();\n for (const raw of data.groups) {\n seen.add(raw.id);\n const held = this.groups.get(raw.id);\n if (held) held._patch(raw);\n else this.groups.set(raw.id, new Group(this, raw));\n }\n for (const id of [...this.groups.keys()]) if (!seen.has(id)) this.groups.delete(id);\n }\n\n /** Um grupo, relido da API. @param {string} id */\n async fetchGroup(id) {\n const data = await this.rest.get(`/bot/groups/${encodeURIComponent(id)}`);\n const held = this.groups.get(id);\n if (held) held._patch(data.group);\n else this.groups.set(id, new Group(this, data.group));\n return this.groups.get(id);\n }\n\n /** Uma pessoa pelo id. @param {string} id */\n async fetchUser(id) {\n const data = await this.rest.get(`/bot/users/${encodeURIComponent(id)}`);\n return this._cacheUser(data.user);\n }\n\n /** @param {Record<string, any>} data */\n _cacheUser(data) {\n const user = new User(this, data);\n this.users.set(user.id, user);\n return user;\n }\n\n async _connect() {\n const Socket = await socketClass();\n return new Promise((resolve, reject) => {\n const ws = new Socket(this.gatewayUrl);\n this._ws = ws;\n let settled = false;\n const send = (payload) => {\n if (ws.readyState === 1) ws.send(JSON.stringify(payload));\n };\n ws.onopen = () => {\n send({ type: \"register\", name: this.user.displayName || this.user.username, token: this.token });\n clearInterval(this._keepalive);\n // O proxy da hospedagem derruba conex\u00E3o parada: uma mensagem leve de tempos em tempos.\n this._keepalive = setInterval(() => send({ type: \"time-sync\", t0: Date.now() }), KEEPALIVE_MS);\n };\n ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(typeof event.data === \"string\" ? event.data : event.data.toString());\n } catch {\n return;\n }\n if (msg.type === \"registered\") {\n const first = !this._ready;\n this._ready = true;\n this._attempt = 0;\n if (!settled) {\n settled = true;\n resolve();\n }\n if (first) this.emit(\"ready\", this.user);\n else this.emit(\"reconnected\");\n return;\n }\n if (msg.type === \"register-error\") {\n this._closing = true;\n const err = new Error(msg.message || \"O Five Live recusou o token do bot.\");\n if (!settled) {\n settled = true;\n reject(err);\n } else this.emit(\"error\", err);\n return;\n }\n this._dispatch(msg).catch((err) => this.emit(\"error\", err));\n };\n ws.onerror = (event) => {\n if (!settled && this._attempt === 0 && !this._ready) {\n settled = true;\n reject(new Error(`N\u00E3o foi poss\u00EDvel conectar em ${this.gatewayUrl}: ${event?.message ?? \"erro de rede\"}`));\n }\n };\n ws.onclose = () => {\n clearInterval(this._keepalive);\n this.emit(\"disconnect\");\n if (this._closing || !this._ready) return;\n this._reconnect();\n };\n });\n }\n\n _reconnect() {\n this._attempt += 1;\n const delay = Math.min(MAX_BACKOFF_MS, 1000 * 2 ** Math.min(this._attempt - 1, 5)) + Math.random() * 500;\n this.emit(\"reconnecting\", this._attempt);\n setTimeout(async () => {\n if (this._closing) return;\n try {\n // O que mudou enquanto estava fora (grupos que entrou/saiu).\n await this._loadGroups();\n await this._connect();\n } catch {\n this._reconnect();\n }\n }, delay);\n }\n\n /** @param {Record<string, any>} msg */\n async _dispatch(msg) {\n switch (msg.type) {\n case \"group-message\": {\n if (!msg.message) return;\n const message = new Message(this, msg.message, msg.author ? { ...msg.author } : undefined);\n if (message.author.id === this.user?.id) return;\n if (this.ignoreBots && message.author.bot) return;\n this.emit(\"messageCreate\", message);\n await this._runCommand(message);\n return;\n }\n case \"group-message-updated\":\n if (msg.message) this.emit(\"messageUpdate\", new Message(this, msg.message));\n return;\n case \"group-message-deleted\":\n this.emit(\"messageDelete\", { id: msg.messageId, groupId: msg.groupId, channelId: msg.channelId });\n return;\n case \"group-message-reactions\":\n this.emit(\"reactionUpdate\", { messageId: msg.messageId, groupId: msg.groupId, channelId: msg.channelId, reactions: msg.reactions ?? [] });\n return;\n case \"group-typing\":\n if (msg.userId !== this.user?.id && msg.typing !== false) {\n this.emit(\"typingStart\", { userId: msg.userId, name: msg.name, groupId: msg.groupId, channelId: msg.channelId });\n }\n return;\n case \"group-joined\": {\n const group = await this.fetchGroup(msg.groupId);\n this.emit(\"groupJoin\", group);\n return;\n }\n case \"group-updated\": {\n if (!this.groups.has(msg.groupId)) return;\n const group = await this.fetchGroup(msg.groupId).catch(() => null);\n if (group) this.emit(\"groupUpdate\", group);\n return;\n }\n case \"group-member-add\": {\n if (!msg.member) return;\n const user = this._cacheUser(msg.member);\n const group = this.groups.get(msg.groupId) ?? (await this.fetchGroup(msg.groupId).catch(() => null));\n if (group) group.memberCount += 1;\n this.emit(\"memberAdd\", { user, group, groupId: msg.groupId, via: msg.via ?? null });\n return;\n }\n case \"group-member-remove\": {\n const group = this.groups.get(msg.groupId) ?? null;\n if (group) group.memberCount = Math.max(0, group.memberCount - 1);\n this.emit(\"memberRemove\", { userId: msg.userId, user: this.users.get(msg.userId) ?? null, group, groupId: msg.groupId, reason: msg.reason ?? null, by: msg.by ?? null });\n return;\n }\n case \"group-removed\":\n this.groups.delete(msg.groupId);\n this.emit(\"groupLeave\", { id: msg.groupId, reason: msg.reason ?? null });\n return;\n default:\n this.emit(\"raw\", msg);\n }\n }\n}\n", "// As rotas HTTP da API de bots (api/botApi.mjs), com o token no cabe\u00E7alho\n// `Authorization: Bot <token>`. Trata o limite de envio (429) esperando o\n// tempo que a API pede e tentando de novo.\n\nexport class FiveLiveAPIError extends Error {\n /**\n * @param {string} message\n * @param {number} status\n * @param {string} method\n * @param {string} path\n */\n constructor(message, status, method, path) {\n super(message);\n this.name = \"FiveLiveAPIError\";\n this.status = status;\n this.method = method;\n this.path = path;\n }\n}\n\nconst sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport class REST {\n /** @param {{ token: string, apiUrl: string, retries?: number }} options */\n constructor(options) {\n this.token = options.token;\n this.apiUrl = options.apiUrl.replace(/\\/$/, \"\");\n this.retries = options.retries ?? 3;\n }\n\n /**\n * @param {string} method\n * @param {string} path\n * @param {unknown} [body]\n */\n async request(method, path, body) {\n for (let attempt = 0; ; attempt++) {\n const res = await fetch(this.apiUrl + path, {\n method,\n headers: {\n Authorization: `Bot ${this.token}`,\n ...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n \"User-Agent\": \"fivelive.js (https://live.fivenetwork.dev/developers, 0.1.0)\",\n },\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n const data = await res.json().catch(() => ({}));\n if (res.status === 429 && attempt < this.retries) {\n const seconds = Number(data.retryAfter ?? res.headers.get(\"retry-after\") ?? 5);\n await sleep(Math.max(1, seconds) * 1000);\n continue;\n }\n if (!res.ok) throw new FiveLiveAPIError(data.error ?? `HTTP ${res.status}`, res.status, method, path);\n return data;\n }\n }\n\n get(path) {\n return this.request(\"GET\", path);\n }\n post(path, body) {\n return this.request(\"POST\", path, body ?? {});\n }\n patch(path, body) {\n return this.request(\"PATCH\", path, body ?? {});\n }\n put(path, body) {\n return this.request(\"PUT\", path, body ?? {});\n }\n delete(path) {\n return this.request(\"DELETE\", path);\n }\n}\n\nexport const enc = encodeURIComponent;\n", "// Os objetos que o bot recebe: User, Group, Channel, Message. Cada um sabe\n// agir sobre si mesmo (message.reply, channel.send, \u2026) usando o client.\n\nimport { enc } from \"./rest.js\";\n\n/** `<@id>` \u2014 como mencionar algu\u00E9m no texto. */\nexport const userMention = (id) => `<@${id}>`;\n/** `<#id>` \u2014 como apontar uma sala no texto. */\nexport const channelMention = (id) => `<#${id}>`;\n\n/**\n * Normaliza o que se manda: um texto, ou { content, embeds, mentions, reply }.\n * @param {string | Record<string, any>} input\n */\nexport function messagePayload(input) {\n const options = typeof input === \"string\" ? { content: input } : input ?? {};\n const embeds = (options.embeds ?? []).map((embed) => (typeof embed?.toJSON === \"function\" ? embed.toJSON() : embed));\n const roles = (options.roleMentions ?? []).map((id) => `@role:${id}`);\n return {\n text: options.content ?? \"\",\n ...(embeds.length ? { embeds } : {}),\n ...(options.mentions?.length || roles.length || options.everyone\n ? { mentions: [...(options.mentions ?? []), ...roles, ...(options.everyone ? [\"@everyone\"] : [])] }\n : {}),\n ...(options.reply ? { replyTo: typeof options.reply === \"string\" ? options.reply : options.reply.id } : {}),\n };\n}\n\nexport class User {\n /** @param {import(\"./client.js\").Client} client @param {Record<string, any>} data */\n constructor(client, data) {\n this.client = client;\n this.id = data.id;\n this.username = data.username ?? null;\n this.displayName = data.displayName ?? data.name ?? data.username ?? \"\";\n this.avatarURL = data.avatarUrl ?? null;\n this.bot = data.bot === true;\n this.flags = data.flags ?? [];\n }\n /** `<@id>`, para colocar no texto. */\n toString() {\n return userMention(this.id);\n }\n}\n\nexport class Channel {\n /** @param {import(\"./client.js\").Client} client @param {Group} group @param {Record<string, any>} data */\n constructor(client, group, data) {\n this.client = client;\n this.group = group;\n this.id = data.id;\n this.name = data.name;\n /** \"text\" | \"voice\" */\n this.kind = data.kind;\n this.categoryId = data.categoryId ?? null;\n this.topic = data.topic ?? \"\";\n }\n get isText() {\n return this.kind === \"text\";\n }\n get path() {\n return `/bot/groups/${enc(this.group.id)}/channels/${enc(this.id)}`;\n }\n /** Manda uma mensagem. @param {string | Record<string, any>} content */\n async send(content) {\n const data = await this.client.rest.post(`${this.path}/messages`, messagePayload(content));\n return new Message(this.client, data.message, this.client.user);\n }\n /** \"Digitando\u2026\" por alguns segundos. */\n async sendTyping() {\n await this.client.rest.post(`${this.path}/typing`, { typing: true });\n }\n /**\n * As \u00FAltimas mensagens (a mais nova por \u00FAltimo).\n * @param {{ limit?: number, after?: string }} [options]\n */\n async fetchMessages(options = {}) {\n const query = new URLSearchParams();\n if (options.limit) query.set(\"limit\", String(options.limit));\n if (options.after) query.set(\"after\", options.after);\n const data = await this.client.rest.get(`${this.path}/messages?${query}`);\n return data.messages.map((message) => new Message(this.client, message));\n }\n /** Uma mensagem pelo id. */\n async fetchMessage(id) {\n const data = await this.client.rest.get(`${this.path}/messages/${enc(id)}`);\n return new Message(this.client, data.message);\n }\n toString() {\n return `<#${this.id}>`;\n }\n}\n\nexport class Group {\n /** @param {import(\"./client.js\").Client} client @param {Record<string, any>} data */\n constructor(client, data) {\n this.client = client;\n this._patch(data);\n }\n /** @param {Record<string, any>} data */\n _patch(data) {\n this.id = data.id;\n this.name = data.name;\n this.iconURL = data.iconUrl ?? null;\n this.ownerId = data.ownerId ?? null;\n this.memberCount = data.memberCount ?? 0;\n /** @type {{ id: string, name: string, color: string | null, position: number, mentionable: boolean }[]} */\n this.roles = data.roles ?? [];\n /** O que o bot pode fazer aqui (kickMembers, banMembers, manageRoles, manageMessages\u2026). */\n this.permissions = data.permissions ?? {};\n /** @type {Map<string, Channel>} */\n this.channels = new Map((data.channels ?? []).map((channel) => [channel.id, new Channel(this.client, this, channel)]));\n }\n /** A sala pelo id ou pelo nome. @param {string} idOrName */\n channel(idOrName) {\n return this.channels.get(idOrName) ?? [...this.channels.values()].find((channel) => channel.name === idOrName) ?? null;\n }\n /** Os membros, com cargos e quem est\u00E1 online. */\n async fetchMembers() {\n const data = await this.client.rest.get(`/bot/groups/${enc(this.id)}/members`);\n return data.members.map((member) => ({ ...member, user: this.client._cacheUser(member) }));\n }\n get path() {\n return `/bot/groups/${enc(this.id)}`;\n }\n /** Se o bot tem a permiss\u00E3o (administrator vale por todas). @param {string} key */\n can(key) {\n return this.permissions.administrator === true || this.permissions[key] === true;\n }\n /** Expulsa algu\u00E9m (precisa de kickMembers e de um cargo acima do da pessoa). @param {string} userId */\n async kick(userId) {\n await this.client.rest.post(`${this.path}/members/${enc(userId)}/kick`, {});\n }\n /** Bane algu\u00E9m, mesmo que n\u00E3o esteja no grupo (banMembers). @param {string} userId @param {string} [reason] */\n async ban(userId, reason) {\n await this.client.rest.put(`${this.path}/bans/${enc(userId)}`, reason ? { reason } : {});\n }\n /** Tira o banimento (banMembers). @param {string} userId */\n async unban(userId) {\n await this.client.rest.delete(`${this.path}/bans/${enc(userId)}`);\n }\n /** Quem est\u00E1 banido (banMembers). */\n async fetchBans() {\n const data = await this.client.rest.get(`${this.path}/bans`);\n return data.bans;\n }\n /** D\u00E1 um cargo (manageRoles; s\u00F3 cargos abaixo do mais alto do bot). @param {string} userId @param {string} roleId */\n async addRole(userId, roleId) {\n const data = await this.client.rest.put(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);\n return data.roleIds;\n }\n /** Tira um cargo (manageRoles). @param {string} userId @param {string} roleId */\n async removeRole(userId, roleId) {\n const data = await this.client.rest.delete(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);\n return data.roleIds;\n }\n /** O bot sai do grupo. */\n async leave() {\n await this.client.rest.post(`${this.path}/leave`, {});\n }\n /** `<@&id>` n\u00E3o existe aqui: para mencionar um cargo, use { roleMentions: [id] } no envio. */\n role(idOrName) {\n return this.roles.find((role) => role.id === idOrName || role.name === idOrName) ?? null;\n }\n}\n\nexport class Message {\n /**\n * @param {import(\"./client.js\").Client} client\n * @param {Record<string, any>} data\n * @param {User | Record<string, any>} [author]\n */\n constructor(client, data, author) {\n this.client = client;\n this.id = data.id;\n this.groupId = data.groupId;\n this.channelId = data.channelId;\n /** O texto como est\u00E1 gravado \u2014 men\u00E7\u00F5es como `<@id>`. */\n this.content = data.text ?? \"\";\n this.embeds = data.embeds ?? [];\n this.attachments = data.attachments ?? [];\n this.images = data.images ?? [];\n this.reactions = data.reactions ?? [];\n this.replyTo = data.replyTo ?? null;\n this.createdAt = new Date(data.ts ?? Date.now());\n this.editedAt = data.editedAt ? new Date(data.editedAt) : null;\n this.pinned = Boolean(data.pinnedAt);\n this.webhook = Boolean(data.webhook);\n const mentions = data.mentions ?? [];\n this.mentions = {\n /** ids das pessoas mencionadas */\n users: mentions.filter((entry) => !entry.startsWith(\"@\")),\n /** ids dos cargos mencionados */\n roles: mentions.filter((entry) => entry.startsWith(\"@role:\")).map((entry) => entry.slice(6)),\n everyone: mentions.includes(\"@everyone\"),\n raw: mentions,\n };\n this.author =\n author instanceof User\n ? author\n : author\n ? client._cacheUser(author)\n : client.users.get(data.from) ?? new User(client, { id: data.from, name: data.fromName });\n }\n get group() {\n return this.client.groups.get(this.groupId) ?? null;\n }\n get channel() {\n return this.group?.channels.get(this.channelId) ?? null;\n }\n /** O texto com `<@id>` trocado por `@nome` (quando o nome \u00E9 conhecido). */\n get cleanContent() {\n return this.content.replace(/<@([A-Za-z0-9:_-]+)>/g, (whole, id) => {\n const user = this.client.users.get(id);\n return user ? `@${user.displayName}` : whole;\n });\n }\n /** Se a mensagem menciona o bot (pelo id, cargo n\u00E3o conta). */\n get mentionsMe() {\n return this.mentions.users.includes(this.client.user?.id) || this.mentions.everyone;\n }\n get path() {\n return `/bot/groups/${enc(this.groupId)}/channels/${enc(this.channelId)}/messages`;\n }\n /** Responde citando esta mensagem. @param {string | Record<string, any>} content */\n async reply(content) {\n const payload = messagePayload(content);\n const data = await this.client.rest.post(this.path, { ...payload, replyTo: this.id });\n return new Message(this.client, data.message, this.client.user);\n }\n /** Edita (s\u00F3 as mensagens do pr\u00F3prio bot). @param {string | Record<string, any>} content */\n async edit(content) {\n const payload = messagePayload(content);\n const data = await this.client.rest.patch(`${this.path}/${enc(this.id)}`, { text: payload.text, ...(payload.embeds ? { embeds: payload.embeds } : {}) });\n return new Message(this.client, data.message, this.author);\n }\n /** Fixa na sala (manageMessages). */\n async pin() {\n await this.client.rest.put(`${this.path}/${enc(this.id)}/pin`);\n }\n /** Desafixa (manageMessages). */\n async unpin() {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}/pin`);\n }\n /** Apaga: as do pr\u00F3prio bot sempre; as dos outros, com manageMessages. */\n async delete() {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}`);\n }\n /** Reage com um emoji. @param {string} emoji */\n async react(emoji) {\n await this.client.rest.put(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);\n }\n /** Tira a rea\u00E7\u00E3o do bot. @param {string} emoji */\n async unreact(emoji) {\n await this.client.rest.delete(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);\n }\n}\n", "// Construtor de embeds no mesmo formato do discord.js (e do webhook do Five\n// Live): t\u00EDtulo, descri\u00E7\u00E3o, cor, autor, campos, imagem, miniatura, rodap\u00E9.\n\n/** @param {string | number} color */\nfunction toColor(color) {\n if (typeof color === \"number\") return color;\n const hex = String(color).replace(/^#/, \"\");\n return /^[0-9a-f]{6}$/i.test(hex) ? parseInt(hex, 16) : undefined;\n}\n\nexport class EmbedBuilder {\n /** @param {Record<string, unknown>} [data] */\n constructor(data = {}) {\n /** @type {Record<string, any>} */\n this.data = { ...data };\n }\n setTitle(title) {\n this.data.title = title;\n return this;\n }\n setDescription(description) {\n this.data.description = description;\n return this;\n }\n setURL(url) {\n this.data.url = url;\n return this;\n }\n /** @param {string | number} color \"#3b6cf6\" ou 0x3b6cf6 */\n setColor(color) {\n this.data.color = toColor(color);\n return this;\n }\n /** @param {{ name: string, url?: string, iconURL?: string }} author */\n setAuthor(author) {\n this.data.author = { name: author.name, url: author.url, icon_url: author.iconURL };\n return this;\n }\n /** @param {{ text: string, iconURL?: string }} footer */\n setFooter(footer) {\n this.data.footer = { text: footer.text, icon_url: footer.iconURL };\n return this;\n }\n setImage(url) {\n this.data.image = { url };\n return this;\n }\n setThumbnail(url) {\n this.data.thumbnail = { url };\n return this;\n }\n /** @param {Date | number | string} [timestamp] */\n setTimestamp(timestamp = new Date()) {\n this.data.timestamp = new Date(timestamp).toISOString();\n return this;\n }\n /** @param {...{ name: string, value: string, inline?: boolean }} fields */\n addFields(...fields) {\n this.data.fields = [...(this.data.fields ?? []), ...fields.flat()];\n return this;\n }\n toJSON() {\n return this.data;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAeA,OAAS,gBAAAA,MAAoB,cCXtB,IAAMC,EAAN,cAA+B,KAAM,CAO1C,YAAYC,EAASC,EAAQC,EAAQC,EAAM,CACzC,MAAMH,CAAO,EACb,KAAK,KAAO,mBACZ,KAAK,OAASC,EACd,KAAK,OAASC,EACd,KAAK,KAAOC,CACd,CACF,EAEMC,EAASC,GAAO,IAAI,QAASC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAEzDE,EAAN,KAAW,CAEhB,YAAYC,EAAS,CACnB,KAAK,MAAQA,EAAQ,MACrB,KAAK,OAASA,EAAQ,OAAO,QAAQ,MAAO,EAAE,EAC9C,KAAK,QAAUA,EAAQ,SAAW,CACpC,CAOA,MAAM,QAAQN,EAAQC,EAAMM,EAAM,CAChC,QAASC,EAAU,GAAKA,IAAW,CACjC,IAAMC,EAAM,MAAM,MAAM,KAAK,OAASR,EAAM,CAC1C,OAAAD,EACA,QAAS,CACP,cAAe,OAAO,KAAK,KAAK,GAChC,GAAIO,IAAS,OAAY,CAAE,eAAgB,kBAAmB,EAAI,CAAC,EACnE,aAAc,8DAChB,EACA,KAAMA,IAAS,OAAY,KAAK,UAAUA,CAAI,EAAI,MACpD,CAAC,EACKG,EAAO,MAAMD,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EAC9C,GAAIA,EAAI,SAAW,KAAOD,EAAU,KAAK,QAAS,CAChD,IAAMG,EAAU,OAAOD,EAAK,YAAcD,EAAI,QAAQ,IAAI,aAAa,GAAK,CAAC,EAC7E,MAAMP,EAAM,KAAK,IAAI,EAAGS,CAAO,EAAI,GAAI,EACvC,QACF,CACA,GAAI,CAACF,EAAI,GAAI,MAAM,IAAIZ,EAAiBa,EAAK,OAAS,QAAQD,EAAI,MAAM,GAAIA,EAAI,OAAQT,EAAQC,CAAI,EACpG,OAAOS,CACT,CACF,CAEA,IAAIT,EAAM,CACR,OAAO,KAAK,QAAQ,MAAOA,CAAI,CACjC,CACA,KAAKA,EAAMM,EAAM,CACf,OAAO,KAAK,QAAQ,OAAQN,EAAMM,GAAQ,CAAC,CAAC,CAC9C,CACA,MAAMN,EAAMM,EAAM,CAChB,OAAO,KAAK,QAAQ,QAASN,EAAMM,GAAQ,CAAC,CAAC,CAC/C,CACA,IAAIN,EAAMM,EAAM,CACd,OAAO,KAAK,QAAQ,MAAON,EAAMM,GAAQ,CAAC,CAAC,CAC7C,CACA,OAAON,EAAM,CACX,OAAO,KAAK,QAAQ,SAAUA,CAAI,CACpC,CACF,EAEaW,EAAM,mBCpEZ,IAAMC,EAAeC,GAAO,KAAKA,CAAE,IAE7BC,EAAkBD,GAAO,KAAKA,CAAE,IAMtC,SAASE,EAAeC,EAAO,CACpC,IAAMC,EAAU,OAAOD,GAAU,SAAW,CAAE,QAASA,CAAM,EAAIA,GAAS,CAAC,EACrEE,GAAUD,EAAQ,QAAU,CAAC,GAAG,IAAKE,GAAW,OAAOA,GAAO,QAAW,WAAaA,EAAM,OAAO,EAAIA,CAAM,EAC7GC,GAASH,EAAQ,cAAgB,CAAC,GAAG,IAAKJ,GAAO,SAASA,CAAE,EAAE,EACpE,MAAO,CACL,KAAMI,EAAQ,SAAW,GACzB,GAAIC,EAAO,OAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,EAClC,GAAID,EAAQ,UAAU,QAAUG,EAAM,QAAUH,EAAQ,SACpD,CAAE,SAAU,CAAC,GAAIA,EAAQ,UAAY,CAAC,EAAI,GAAGG,EAAO,GAAIH,EAAQ,SAAW,CAAC,WAAW,EAAI,CAAC,CAAE,CAAE,EAChG,CAAC,EACL,GAAIA,EAAQ,MAAQ,CAAE,QAAS,OAAOA,EAAQ,OAAU,SAAWA,EAAQ,MAAQA,EAAQ,MAAM,EAAG,EAAI,CAAC,CAC3G,CACF,CAEO,IAAMI,EAAN,KAAW,CAEhB,YAAYC,EAAQC,EAAM,CACxB,KAAK,OAASD,EACd,KAAK,GAAKC,EAAK,GACf,KAAK,SAAWA,EAAK,UAAY,KACjC,KAAK,YAAcA,EAAK,aAAeA,EAAK,MAAQA,EAAK,UAAY,GACrE,KAAK,UAAYA,EAAK,WAAa,KACnC,KAAK,IAAMA,EAAK,MAAQ,GACxB,KAAK,MAAQA,EAAK,OAAS,CAAC,CAC9B,CAEA,UAAW,CACT,OAAOX,EAAY,KAAK,EAAE,CAC5B,CACF,EAEaY,EAAN,KAAc,CAEnB,YAAYF,EAAQG,EAAOF,EAAM,CAC/B,KAAK,OAASD,EACd,KAAK,MAAQG,EACb,KAAK,GAAKF,EAAK,GACf,KAAK,KAAOA,EAAK,KAEjB,KAAK,KAAOA,EAAK,KACjB,KAAK,WAAaA,EAAK,YAAc,KACrC,KAAK,MAAQA,EAAK,OAAS,EAC7B,CACA,IAAI,QAAS,CACX,OAAO,KAAK,OAAS,MACvB,CACA,IAAI,MAAO,CACT,MAAO,eAAeG,EAAI,KAAK,MAAM,EAAE,CAAC,aAAaA,EAAI,KAAK,EAAE,CAAC,EACnE,CAEA,MAAM,KAAKC,EAAS,CAClB,IAAMJ,EAAO,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,YAAaR,EAAeY,CAAO,CAAC,EACzF,OAAO,IAAIC,EAAQ,KAAK,OAAQL,EAAK,QAAS,KAAK,OAAO,IAAI,CAChE,CAEA,MAAM,YAAa,CACjB,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,UAAW,CAAE,OAAQ,EAAK,CAAC,CACrE,CAKA,MAAM,cAAcN,EAAU,CAAC,EAAG,CAChC,IAAMY,EAAQ,IAAI,gBAClB,OAAIZ,EAAQ,OAAOY,EAAM,IAAI,QAAS,OAAOZ,EAAQ,KAAK,CAAC,EACvDA,EAAQ,OAAOY,EAAM,IAAI,QAASZ,EAAQ,KAAK,GACtC,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,aAAaY,CAAK,EAAE,GAC5D,SAAS,IAAKC,GAAY,IAAIF,EAAQ,KAAK,OAAQE,CAAO,CAAC,CACzE,CAEA,MAAM,aAAajB,EAAI,CACrB,IAAMU,EAAO,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,aAAaG,EAAIb,CAAE,CAAC,EAAE,EAC1E,OAAO,IAAIe,EAAQ,KAAK,OAAQL,EAAK,OAAO,CAC9C,CACA,UAAW,CACT,MAAO,KAAK,KAAK,EAAE,GACrB,CACF,EAEaQ,EAAN,KAAY,CAEjB,YAAYT,EAAQC,EAAM,CACxB,KAAK,OAASD,EACd,KAAK,OAAOC,CAAI,CAClB,CAEA,OAAOA,EAAM,CACX,KAAK,GAAKA,EAAK,GACf,KAAK,KAAOA,EAAK,KACjB,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,YAAcA,EAAK,aAAe,EAEvC,KAAK,MAAQA,EAAK,OAAS,CAAC,EAE5B,KAAK,YAAcA,EAAK,aAAe,CAAC,EAExC,KAAK,SAAW,IAAI,KAAKA,EAAK,UAAY,CAAC,GAAG,IAAKS,GAAY,CAACA,EAAQ,GAAI,IAAIR,EAAQ,KAAK,OAAQ,KAAMQ,CAAO,CAAC,CAAC,CAAC,CACvH,CAEA,QAAQC,EAAU,CAChB,OAAO,KAAK,SAAS,IAAIA,CAAQ,GAAK,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC,EAAE,KAAMD,GAAYA,EAAQ,OAASC,CAAQ,GAAK,IACpH,CAEA,MAAM,cAAe,CAEnB,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,eAAeP,EAAI,KAAK,EAAE,CAAC,UAAU,GACjE,QAAQ,IAAKQ,IAAY,CAAE,GAAGA,EAAQ,KAAM,KAAK,OAAO,WAAWA,CAAM,CAAE,EAAE,CAC3F,CACA,IAAI,MAAO,CACT,MAAO,eAAeR,EAAI,KAAK,EAAE,CAAC,EACpC,CAEA,IAAIS,EAAK,CACP,OAAO,KAAK,YAAY,gBAAkB,IAAQ,KAAK,YAAYA,CAAG,IAAM,EAC9E,CAEA,MAAM,KAAKC,EAAQ,CACjB,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,YAAYV,EAAIU,CAAM,CAAC,QAAS,CAAC,CAAC,CAC5E,CAEA,MAAM,IAAIA,EAAQC,EAAQ,CACxB,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,SAASX,EAAIU,CAAM,CAAC,GAAIC,EAAS,CAAE,OAAAA,CAAO,EAAI,CAAC,CAAC,CACzF,CAEA,MAAM,MAAMD,EAAQ,CAClB,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,SAASV,EAAIU,CAAM,CAAC,EAAE,CAClE,CAEA,MAAM,WAAY,CAEhB,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,OAAO,GAC/C,IACd,CAEA,MAAM,QAAQA,EAAQE,EAAQ,CAE5B,OADa,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,YAAYZ,EAAIU,CAAM,CAAC,UAAUV,EAAIY,CAAM,CAAC,EAAE,GACtF,OACd,CAEA,MAAM,WAAWF,EAAQE,EAAQ,CAE/B,OADa,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,YAAYZ,EAAIU,CAAM,CAAC,UAAUV,EAAIY,CAAM,CAAC,EAAE,GACzF,OACd,CAEA,MAAM,OAAQ,CACZ,MAAM,KAAK,OAAO,KAAK,KAAK,GAAG,KAAK,IAAI,SAAU,CAAC,CAAC,CACtD,CAEA,KAAKL,EAAU,CACb,OAAO,KAAK,MAAM,KAAMM,GAASA,EAAK,KAAON,GAAYM,EAAK,OAASN,CAAQ,GAAK,IACtF,CACF,EAEaL,EAAN,MAAMY,CAAQ,CAMnB,YAAYlB,EAAQC,EAAMkB,EAAQ,CAChC,KAAK,OAASnB,EACd,KAAK,GAAKC,EAAK,GACf,KAAK,QAAUA,EAAK,QACpB,KAAK,UAAYA,EAAK,UAEtB,KAAK,QAAUA,EAAK,MAAQ,GAC5B,KAAK,OAASA,EAAK,QAAU,CAAC,EAC9B,KAAK,YAAcA,EAAK,aAAe,CAAC,EACxC,KAAK,OAASA,EAAK,QAAU,CAAC,EAC9B,KAAK,UAAYA,EAAK,WAAa,CAAC,EACpC,KAAK,QAAUA,EAAK,SAAW,KAC/B,KAAK,UAAY,IAAI,KAAKA,EAAK,IAAM,KAAK,IAAI,CAAC,EAC/C,KAAK,SAAWA,EAAK,SAAW,IAAI,KAAKA,EAAK,QAAQ,EAAI,KAC1D,KAAK,OAAS,EAAQA,EAAK,SAC3B,KAAK,QAAU,EAAQA,EAAK,QAC5B,IAAMmB,EAAWnB,EAAK,UAAY,CAAC,EACnC,KAAK,SAAW,CAEd,MAAOmB,EAAS,OAAQC,GAAU,CAACA,EAAM,WAAW,GAAG,CAAC,EAExD,MAAOD,EAAS,OAAQC,GAAUA,EAAM,WAAW,QAAQ,CAAC,EAAE,IAAKA,GAAUA,EAAM,MAAM,CAAC,CAAC,EAC3F,SAAUD,EAAS,SAAS,WAAW,EACvC,IAAKA,CACP,EACA,KAAK,OACHD,aAAkBpB,EACdoB,EACAA,EACEnB,EAAO,WAAWmB,CAAM,EACxBnB,EAAO,MAAM,IAAIC,EAAK,IAAI,GAAK,IAAIF,EAAKC,EAAQ,CAAE,GAAIC,EAAK,KAAM,KAAMA,EAAK,QAAS,CAAC,CAChG,CACA,IAAI,OAAQ,CACV,OAAO,KAAK,OAAO,OAAO,IAAI,KAAK,OAAO,GAAK,IACjD,CACA,IAAI,SAAU,CACZ,OAAO,KAAK,OAAO,SAAS,IAAI,KAAK,SAAS,GAAK,IACrD,CAEA,IAAI,cAAe,CACjB,OAAO,KAAK,QAAQ,QAAQ,wBAAyB,CAACqB,EAAO/B,IAAO,CAClE,IAAMgC,EAAO,KAAK,OAAO,MAAM,IAAIhC,CAAE,EACrC,OAAOgC,EAAO,IAAIA,EAAK,WAAW,GAAKD,CACzC,CAAC,CACH,CAEA,IAAI,YAAa,CACf,OAAO,KAAK,SAAS,MAAM,SAAS,KAAK,OAAO,MAAM,EAAE,GAAK,KAAK,SAAS,QAC7E,CACA,IAAI,MAAO,CACT,MAAO,eAAelB,EAAI,KAAK,OAAO,CAAC,aAAaA,EAAI,KAAK,SAAS,CAAC,WACzE,CAEA,MAAM,MAAMC,EAAS,CACnB,IAAMmB,EAAU/B,EAAeY,CAAO,EAChCJ,EAAO,MAAM,KAAK,OAAO,KAAK,KAAK,KAAK,KAAM,CAAE,GAAGuB,EAAS,QAAS,KAAK,EAAG,CAAC,EACpF,OAAO,IAAIN,EAAQ,KAAK,OAAQjB,EAAK,QAAS,KAAK,OAAO,IAAI,CAChE,CAEA,MAAM,KAAKI,EAAS,CAClB,IAAMmB,EAAU/B,EAAeY,CAAO,EAChCJ,EAAO,MAAM,KAAK,OAAO,KAAK,MAAM,GAAG,KAAK,IAAI,IAAIG,EAAI,KAAK,EAAE,CAAC,GAAI,CAAE,KAAMoB,EAAQ,KAAM,GAAIA,EAAQ,OAAS,CAAE,OAAQA,EAAQ,MAAO,EAAI,CAAC,CAAG,CAAC,EACvJ,OAAO,IAAIN,EAAQ,KAAK,OAAQjB,EAAK,QAAS,KAAK,MAAM,CAC3D,CAEA,MAAM,KAAM,CACV,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAIG,EAAI,KAAK,EAAE,CAAC,MAAM,CAC/D,CAEA,MAAM,OAAQ,CACZ,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIA,EAAI,KAAK,EAAE,CAAC,MAAM,CAClE,CAEA,MAAM,QAAS,CACb,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIA,EAAI,KAAK,EAAE,CAAC,EAAE,CAC9D,CAEA,MAAM,MAAMqB,EAAO,CACjB,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,IAAIrB,EAAI,KAAK,EAAE,CAAC,cAAcA,EAAIqB,CAAK,CAAC,EAAE,CACnF,CAEA,MAAM,QAAQA,EAAO,CACnB,MAAM,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,IAAI,IAAIrB,EAAI,KAAK,EAAE,CAAC,cAAcA,EAAIqB,CAAK,CAAC,EAAE,CACtF,CACF,EF7OA,IAAMC,EAAc,8BACdC,EAAe,IACfC,EAAiB,IAEvB,eAAeC,GAAc,CAC3B,GAAI,OAAO,WAAW,WAAc,WAAY,OAAO,WAAW,UAClE,GAAI,CACF,IAAMC,EAAM,KAAM,QAAO,IAAI,EAC7B,OAAOA,EAAI,SAAWA,EAAI,SAC5B,MAAQ,CACN,MAAM,IAAI,MAAM,qGAA+F,CACjH,CACF,CAEO,IAAMC,EAAN,cAAqBC,CAAa,CAUvC,YAAYC,EAAS,CAEnB,GADA,MAAM,EACF,CAACA,GAAS,OAAS,CAAC,OAAOA,EAAQ,KAAK,EAAE,WAAW,IAAI,EAC3D,MAAM,IAAI,MAAM,4FAAyF,EAE3G,KAAK,MAAQA,EAAQ,MACrB,KAAK,QAAUA,EAAQ,QAAUP,GAAa,QAAQ,MAAO,EAAE,EAC/D,KAAK,WAAaO,EAAQ,YAAc,GAAG,KAAK,OAAO,QAAQ,QAAS,IAAI,CAAC,MAC7E,KAAK,OAASA,EAAQ,QAAU,IAChC,KAAK,WAAaA,EAAQ,YAAc,GACxC,KAAK,KAAO,IAAIC,EAAK,CAAE,MAAO,KAAK,MAAO,OAAQ,KAAK,MAAO,CAAC,EAE/D,KAAK,KAAO,KAEZ,KAAK,OAAS,IAAI,IAElB,KAAK,MAAQ,IAAI,IAEjB,KAAK,SAAW,IAAI,IACpB,KAAK,IAAM,KACX,KAAK,OAAS,GACd,KAAK,SAAW,GAChB,KAAK,SAAW,EAChB,KAAK,WAAa,IACpB,CAQA,QAAQC,EAAS,CACf,KAAK,SAAS,IAAIA,EAAQ,KAAK,YAAY,EAAGA,CAAO,EACrD,QAAWC,KAASD,EAAQ,SAAW,CAAC,EAAG,KAAK,SAAS,IAAIC,EAAM,YAAY,EAAGD,CAAO,EACzF,OAAO,IACT,CAGA,MAAM,YAAYE,EAAS,CACzB,GAAI,CAAC,KAAK,SAAS,MAAQ,CAACA,EAAQ,QAAQ,WAAW,KAAK,MAAM,EAAG,OACrE,GAAM,CAACC,EAAM,GAAGC,CAAI,EAAIF,EAAQ,QAAQ,MAAM,KAAK,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,EAC9EF,EAAU,KAAK,SAAS,KAAKG,GAAQ,IAAI,YAAY,CAAC,EAC5D,GAAI,CAACH,EAAS,OACd,IAAMK,EAAU,CAAE,QAAAH,EAAS,KAAAE,EAAM,OAAQ,KAAM,MAAQE,GAAYJ,EAAQ,MAAMI,CAAO,CAAE,EAC1F,GAAI,CACF,MAAMN,EAAQ,IAAIK,CAAO,CAC3B,OAASE,EAAK,CACR,KAAK,cAAc,cAAc,EAAG,KAAK,KAAK,eAAgBA,EAAKF,CAAO,EACzE,KAAK,KAAK,QAASE,CAAG,CAC7B,CACF,CAKA,MAAM,OAAQ,CACZ,IAAMC,EAAK,MAAM,KAAK,KAAK,IAAI,UAAU,EACzC,YAAK,KAAO,KAAK,WAAW,CAAE,GAAGA,EAAG,IAAK,IAAK,EAAK,CAAC,EACpD,MAAM,KAAK,YAAY,EACvB,KAAK,SAAW,GAChB,MAAM,KAAK,SAAS,EACb,KAAK,IACd,CAGA,SAAU,CACR,KAAK,SAAW,GAChB,cAAc,KAAK,UAAU,EAC7B,GAAI,CACF,KAAK,KAAK,MAAM,CAClB,MAAQ,CAER,CACF,CAEA,MAAM,aAAc,CAClB,IAAMC,EAAO,MAAM,KAAK,KAAK,IAAI,aAAa,EACxCC,EAAO,IAAI,IACjB,QAAWC,KAAOF,EAAK,OAAQ,CAC7BC,EAAK,IAAIC,EAAI,EAAE,EACf,IAAMC,EAAO,KAAK,OAAO,IAAID,EAAI,EAAE,EAC/BC,EAAMA,EAAK,OAAOD,CAAG,EACpB,KAAK,OAAO,IAAIA,EAAI,GAAI,IAAIE,EAAM,KAAMF,CAAG,CAAC,CACnD,CACA,QAAWG,IAAM,CAAC,GAAG,KAAK,OAAO,KAAK,CAAC,EAAQJ,EAAK,IAAII,CAAE,GAAG,KAAK,OAAO,OAAOA,CAAE,CACpF,CAGA,MAAM,WAAWA,EAAI,CACnB,IAAML,EAAO,MAAM,KAAK,KAAK,IAAI,eAAe,mBAAmBK,CAAE,CAAC,EAAE,EAClEF,EAAO,KAAK,OAAO,IAAIE,CAAE,EAC/B,OAAIF,EAAMA,EAAK,OAAOH,EAAK,KAAK,EAC3B,KAAK,OAAO,IAAIK,EAAI,IAAID,EAAM,KAAMJ,EAAK,KAAK,CAAC,EAC7C,KAAK,OAAO,IAAIK,CAAE,CAC3B,CAGA,MAAM,UAAUA,EAAI,CAClB,IAAML,EAAO,MAAM,KAAK,KAAK,IAAI,cAAc,mBAAmBK,CAAE,CAAC,EAAE,EACvE,OAAO,KAAK,WAAWL,EAAK,IAAI,CAClC,CAGA,WAAWA,EAAM,CACf,IAAMM,EAAO,IAAIC,EAAK,KAAMP,CAAI,EAChC,YAAK,MAAM,IAAIM,EAAK,GAAIA,CAAI,EACrBA,CACT,CAEA,MAAM,UAAW,CACf,IAAME,EAAS,MAAMvB,EAAY,EACjC,OAAO,IAAI,QAAQ,CAACwB,EAASC,IAAW,CACtC,IAAMC,EAAK,IAAIH,EAAO,KAAK,UAAU,EACrC,KAAK,IAAMG,EACX,IAAIC,EAAU,GACRC,EAAQC,GAAY,CACpBH,EAAG,aAAe,GAAGA,EAAG,KAAK,KAAK,UAAUG,CAAO,CAAC,CAC1D,EACAH,EAAG,OAAS,IAAM,CAChBE,EAAK,CAAE,KAAM,WAAY,KAAM,KAAK,KAAK,aAAe,KAAK,KAAK,SAAU,MAAO,KAAK,KAAM,CAAC,EAC/F,cAAc,KAAK,UAAU,EAE7B,KAAK,WAAa,YAAY,IAAMA,EAAK,CAAE,KAAM,YAAa,GAAI,KAAK,IAAI,CAAE,CAAC,EAAG9B,CAAY,CAC/F,EACA4B,EAAG,UAAaI,GAAU,CACxB,IAAIC,EACJ,GAAI,CACFA,EAAM,KAAK,MAAM,OAAOD,EAAM,MAAS,SAAWA,EAAM,KAAOA,EAAM,KAAK,SAAS,CAAC,CACtF,MAAQ,CACN,MACF,CACA,GAAIC,EAAI,OAAS,aAAc,CAC7B,IAAMC,EAAQ,CAAC,KAAK,OACpB,KAAK,OAAS,GACd,KAAK,SAAW,EACXL,IACHA,EAAU,GACVH,EAAQ,GAENQ,EAAO,KAAK,KAAK,QAAS,KAAK,IAAI,EAClC,KAAK,KAAK,aAAa,EAC5B,MACF,CACA,GAAID,EAAI,OAAS,iBAAkB,CACjC,KAAK,SAAW,GAChB,IAAMlB,EAAM,IAAI,MAAMkB,EAAI,SAAW,qCAAqC,EACrEJ,EAGE,KAAK,KAAK,QAASd,CAAG,GAF3Bc,EAAU,GACVF,EAAOZ,CAAG,GAEZ,MACF,CACA,KAAK,UAAUkB,CAAG,EAAE,MAAOlB,GAAQ,KAAK,KAAK,QAASA,CAAG,CAAC,CAC5D,EACAa,EAAG,QAAWI,GAAU,CAClB,CAACH,GAAW,KAAK,WAAa,GAAK,CAAC,KAAK,SAC3CA,EAAU,GACVF,EAAO,IAAI,MAAM,sCAAgC,KAAK,UAAU,KAAKK,GAAO,SAAW,cAAc,EAAE,CAAC,EAE5G,EACAJ,EAAG,QAAU,IAAM,CACjB,cAAc,KAAK,UAAU,EAC7B,KAAK,KAAK,YAAY,EAClB,OAAK,UAAY,CAAC,KAAK,SAC3B,KAAK,WAAW,CAClB,CACF,CAAC,CACH,CAEA,YAAa,CACX,KAAK,UAAY,EACjB,IAAMO,EAAQ,KAAK,IAAIlC,EAAgB,IAAO,GAAK,KAAK,IAAI,KAAK,SAAW,EAAG,CAAC,CAAC,EAAI,KAAK,OAAO,EAAI,IACrG,KAAK,KAAK,eAAgB,KAAK,QAAQ,EACvC,WAAW,SAAY,CACrB,GAAI,MAAK,SACT,GAAI,CAEF,MAAM,KAAK,YAAY,EACvB,MAAM,KAAK,SAAS,CACtB,MAAQ,CACN,KAAK,WAAW,CAClB,CACF,EAAGkC,CAAK,CACV,CAGA,MAAM,UAAUF,EAAK,CACnB,OAAQA,EAAI,KAAM,CAChB,IAAK,gBAAiB,CACpB,GAAI,CAACA,EAAI,QAAS,OAClB,IAAMvB,EAAU,IAAI0B,EAAQ,KAAMH,EAAI,QAASA,EAAI,OAAS,CAAE,GAAGA,EAAI,MAAO,EAAI,MAAS,EAEzF,GADIvB,EAAQ,OAAO,KAAO,KAAK,MAAM,IACjC,KAAK,YAAcA,EAAQ,OAAO,IAAK,OAC3C,KAAK,KAAK,gBAAiBA,CAAO,EAClC,MAAM,KAAK,YAAYA,CAAO,EAC9B,MACF,CACA,IAAK,wBACCuB,EAAI,SAAS,KAAK,KAAK,gBAAiB,IAAIG,EAAQ,KAAMH,EAAI,OAAO,CAAC,EAC1E,OACF,IAAK,wBACH,KAAK,KAAK,gBAAiB,CAAE,GAAIA,EAAI,UAAW,QAASA,EAAI,QAAS,UAAWA,EAAI,SAAU,CAAC,EAChG,OACF,IAAK,0BACH,KAAK,KAAK,iBAAkB,CAAE,UAAWA,EAAI,UAAW,QAASA,EAAI,QAAS,UAAWA,EAAI,UAAW,UAAWA,EAAI,WAAa,CAAC,CAAE,CAAC,EACxI,OACF,IAAK,eACCA,EAAI,SAAW,KAAK,MAAM,IAAMA,EAAI,SAAW,IACjD,KAAK,KAAK,cAAe,CAAE,OAAQA,EAAI,OAAQ,KAAMA,EAAI,KAAM,QAASA,EAAI,QAAS,UAAWA,EAAI,SAAU,CAAC,EAEjH,OACF,IAAK,eAAgB,CACnB,IAAMI,EAAQ,MAAM,KAAK,WAAWJ,EAAI,OAAO,EAC/C,KAAK,KAAK,YAAaI,CAAK,EAC5B,MACF,CACA,IAAK,gBAAiB,CACpB,GAAI,CAAC,KAAK,OAAO,IAAIJ,EAAI,OAAO,EAAG,OACnC,IAAMI,EAAQ,MAAM,KAAK,WAAWJ,EAAI,OAAO,EAAE,MAAM,IAAM,IAAI,EAC7DI,GAAO,KAAK,KAAK,cAAeA,CAAK,EACzC,MACF,CACA,IAAK,mBAAoB,CACvB,GAAI,CAACJ,EAAI,OAAQ,OACjB,IAAMV,EAAO,KAAK,WAAWU,EAAI,MAAM,EACjCI,EAAQ,KAAK,OAAO,IAAIJ,EAAI,OAAO,GAAM,MAAM,KAAK,WAAWA,EAAI,OAAO,EAAE,MAAM,IAAM,IAAI,EAC9FI,IAAOA,EAAM,aAAe,GAChC,KAAK,KAAK,YAAa,CAAE,KAAAd,EAAM,MAAAc,EAAO,QAASJ,EAAI,QAAS,IAAKA,EAAI,KAAO,IAAK,CAAC,EAClF,MACF,CACA,IAAK,sBAAuB,CAC1B,IAAMI,EAAQ,KAAK,OAAO,IAAIJ,EAAI,OAAO,GAAK,KAC1CI,IAAOA,EAAM,YAAc,KAAK,IAAI,EAAGA,EAAM,YAAc,CAAC,GAChE,KAAK,KAAK,eAAgB,CAAE,OAAQJ,EAAI,OAAQ,KAAM,KAAK,MAAM,IAAIA,EAAI,MAAM,GAAK,KAAM,MAAAI,EAAO,QAASJ,EAAI,QAAS,OAAQA,EAAI,QAAU,KAAM,GAAIA,EAAI,IAAM,IAAK,CAAC,EACvK,MACF,CACA,IAAK,gBACH,KAAK,OAAO,OAAOA,EAAI,OAAO,EAC9B,KAAK,KAAK,aAAc,CAAE,GAAIA,EAAI,QAAS,OAAQA,EAAI,QAAU,IAAK,CAAC,EACvE,OACF,QACE,KAAK,KAAK,MAAOA,CAAG,CACxB,CACF,CACF,EG5RA,SAASK,EAAQC,EAAO,CACtB,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,IAAMC,EAAM,OAAOD,CAAK,EAAE,QAAQ,KAAM,EAAE,EAC1C,MAAO,iBAAiB,KAAKC,CAAG,EAAI,SAASA,EAAK,EAAE,EAAI,MAC1D,CAEO,IAAMC,EAAN,KAAmB,CAExB,YAAYC,EAAO,CAAC,EAAG,CAErB,KAAK,KAAO,CAAE,GAAGA,CAAK,CACxB,CACA,SAASC,EAAO,CACd,YAAK,KAAK,MAAQA,EACX,IACT,CACA,eAAeC,EAAa,CAC1B,YAAK,KAAK,YAAcA,EACjB,IACT,CACA,OAAOC,EAAK,CACV,YAAK,KAAK,IAAMA,EACT,IACT,CAEA,SAASN,EAAO,CACd,YAAK,KAAK,MAAQD,EAAQC,CAAK,EACxB,IACT,CAEA,UAAUO,EAAQ,CAChB,YAAK,KAAK,OAAS,CAAE,KAAMA,EAAO,KAAM,IAAKA,EAAO,IAAK,SAAUA,EAAO,OAAQ,EAC3E,IACT,CAEA,UAAUC,EAAQ,CAChB,YAAK,KAAK,OAAS,CAAE,KAAMA,EAAO,KAAM,SAAUA,EAAO,OAAQ,EAC1D,IACT,CACA,SAASF,EAAK,CACZ,YAAK,KAAK,MAAQ,CAAE,IAAAA,CAAI,EACjB,IACT,CACA,aAAaA,EAAK,CAChB,YAAK,KAAK,UAAY,CAAE,IAAAA,CAAI,EACrB,IACT,CAEA,aAAaG,EAAY,IAAI,KAAQ,CACnC,YAAK,KAAK,UAAY,IAAI,KAAKA,CAAS,EAAE,YAAY,EAC/C,IACT,CAEA,aAAaC,EAAQ,CACnB,YAAK,KAAK,OAAS,CAAC,GAAI,KAAK,KAAK,QAAU,CAAC,EAAI,GAAGA,EAAO,KAAK,CAAC,EAC1D,IACT,CACA,QAAS,CACP,OAAO,KAAK,IACd,CACF",
|
|
6
|
+
"names": ["EventEmitter", "FiveLiveAPIError", "message", "status", "method", "path", "sleep", "ms", "resolve", "REST", "options", "body", "attempt", "res", "data", "seconds", "enc", "userMention", "id", "channelMention", "messagePayload", "input", "options", "embeds", "embed", "roles", "User", "client", "data", "Channel", "group", "enc", "content", "Message", "query", "message", "Group", "channel", "idOrName", "member", "key", "userId", "reason", "roleId", "role", "_Message", "author", "mentions", "entry", "whole", "user", "payload", "emoji", "DEFAULT_API", "KEEPALIVE_MS", "MAX_BACKOFF_MS", "socketClass", "mod", "Client", "EventEmitter", "options", "REST", "command", "alias", "message", "name", "args", "context", "content", "err", "me", "data", "seen", "raw", "held", "Group", "id", "user", "User", "Socket", "resolve", "reject", "ws", "settled", "send", "payload", "event", "msg", "first", "delay", "Message", "group", "toColor", "color", "hex", "EmbedBuilder", "data", "title", "description", "url", "author", "footer", "timestamp", "fields"]
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fivelive.js",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "SDK oficial para criar bots do Five Live — eventos em tempo real, mensagens, embeds, reações e comandos.",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
7
|
-
"
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.cts",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"./package.json": "./package.json"
|
|
13
21
|
},
|
|
14
22
|
"files": [
|
|
15
|
-
"
|
|
23
|
+
"dist",
|
|
16
24
|
"README.md"
|
|
17
25
|
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "node build.mjs",
|
|
28
|
+
"prepublishOnly": "node build.mjs"
|
|
29
|
+
},
|
|
18
30
|
"engines": {
|
|
19
31
|
"node": ">=18"
|
|
20
32
|
},
|
|
21
33
|
"optionalDependencies": {
|
|
22
34
|
"ws": "^8.18.0"
|
|
23
35
|
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"esbuild": "^0.25.0"
|
|
38
|
+
},
|
|
24
39
|
"keywords": [
|
|
25
40
|
"fivelive",
|
|
26
41
|
"bot",
|
package/src/client.js
DELETED
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
// O Client: conecta no WebSocket do Five Live com o token do bot, mantém a
|
|
2
|
-
// conexão viva (e reconecta sozinho), transforma os avisos da API em eventos
|
|
3
|
-
// e guarda grupos e usuários conhecidos.
|
|
4
|
-
//
|
|
5
|
-
// Eventos:
|
|
6
|
-
// ready(user) conectou e carregou os grupos
|
|
7
|
-
// messageCreate(message) mensagem nova num grupo do bot
|
|
8
|
-
// messageUpdate(message) mensagem editada
|
|
9
|
-
// messageDelete({ id, groupId, channelId })
|
|
10
|
-
// reactionUpdate({ messageId, groupId, channelId, reactions })
|
|
11
|
-
// typingStart({ userId, name, groupId, channelId })
|
|
12
|
-
// groupJoin(group) / groupLeave({ id, reason }) / groupUpdate(group)
|
|
13
|
-
// reconnecting(attempt) / reconnected() / disconnect() / error(err)
|
|
14
|
-
// commandError(err, context)
|
|
15
|
-
|
|
16
|
-
import { EventEmitter } from "node:events";
|
|
17
|
-
import { REST } from "./rest.js";
|
|
18
|
-
import { Group, Message, User } from "./structures.js";
|
|
19
|
-
|
|
20
|
-
const DEFAULT_API = "https://api.fivenetwork.dev";
|
|
21
|
-
const KEEPALIVE_MS = 30_000;
|
|
22
|
-
const MAX_BACKOFF_MS = 30_000;
|
|
23
|
-
|
|
24
|
-
async function socketClass() {
|
|
25
|
-
if (typeof globalThis.WebSocket === "function") return globalThis.WebSocket;
|
|
26
|
-
try {
|
|
27
|
-
const mod = await import("ws");
|
|
28
|
-
return mod.default ?? mod.WebSocket;
|
|
29
|
-
} catch {
|
|
30
|
-
throw new Error("Este Node não tem WebSocket nativo (é do Node 22+). Rode `npm install ws` ou atualize o Node.");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export class Client extends EventEmitter {
|
|
35
|
-
/**
|
|
36
|
-
* @param {{
|
|
37
|
-
* token: string,
|
|
38
|
-
* apiUrl?: string,
|
|
39
|
-
* gatewayUrl?: string,
|
|
40
|
-
* prefix?: string,
|
|
41
|
-
* ignoreBots?: boolean,
|
|
42
|
-
* }} options
|
|
43
|
-
*/
|
|
44
|
-
constructor(options) {
|
|
45
|
-
super();
|
|
46
|
-
if (!options?.token || !String(options.token).startsWith("b_")) {
|
|
47
|
-
throw new Error("Passe o token do bot (começa com b_). Pegue em https://live.fivenetwork.dev/developers.");
|
|
48
|
-
}
|
|
49
|
-
this.token = options.token;
|
|
50
|
-
this.apiUrl = (options.apiUrl ?? DEFAULT_API).replace(/\/$/, "");
|
|
51
|
-
this.gatewayUrl = options.gatewayUrl ?? `${this.apiUrl.replace(/^http/, "ws")}/ws`;
|
|
52
|
-
this.prefix = options.prefix ?? "!";
|
|
53
|
-
this.ignoreBots = options.ignoreBots ?? true;
|
|
54
|
-
this.rest = new REST({ token: this.token, apiUrl: this.apiUrl });
|
|
55
|
-
/** @type {User | null} */
|
|
56
|
-
this.user = null;
|
|
57
|
-
/** @type {Map<string, Group>} */
|
|
58
|
-
this.groups = new Map();
|
|
59
|
-
/** @type {Map<string, User>} */
|
|
60
|
-
this.users = new Map();
|
|
61
|
-
/** @type {Map<string, { name: string, description?: string, aliases?: string[], run: Function }>} */
|
|
62
|
-
this.commands = new Map();
|
|
63
|
-
this._ws = null;
|
|
64
|
-
this._ready = false;
|
|
65
|
-
this._closing = false;
|
|
66
|
-
this._attempt = 0;
|
|
67
|
-
this._keepalive = null;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ─── Comandos com prefixo ─────────────────────────────────────────────────
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Registra um comando: `!nome arg1 arg2`.
|
|
74
|
-
* @param {{ name: string, description?: string, aliases?: string[], run: (ctx: { message: Message, args: string[], client: Client, reply: (content: any) => Promise<Message> }) => any }} command
|
|
75
|
-
*/
|
|
76
|
-
command(command) {
|
|
77
|
-
this.commands.set(command.name.toLowerCase(), command);
|
|
78
|
-
for (const alias of command.aliases ?? []) this.commands.set(alias.toLowerCase(), command);
|
|
79
|
-
return this;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** @param {Message} message */
|
|
83
|
-
async _runCommand(message) {
|
|
84
|
-
if (!this.commands.size || !message.content.startsWith(this.prefix)) return;
|
|
85
|
-
const [name, ...args] = message.content.slice(this.prefix.length).trim().split(/\s+/);
|
|
86
|
-
const command = this.commands.get((name ?? "").toLowerCase());
|
|
87
|
-
if (!command) return;
|
|
88
|
-
const context = { message, args, client: this, reply: (content) => message.reply(content) };
|
|
89
|
-
try {
|
|
90
|
-
await command.run(context);
|
|
91
|
-
} catch (err) {
|
|
92
|
-
if (this.listenerCount("commandError")) this.emit("commandError", err, context);
|
|
93
|
-
else this.emit("error", err);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ─── Conexão ──────────────────────────────────────────────────────────────
|
|
98
|
-
|
|
99
|
-
/** Conecta. Resolve quando o bot estiver pronto (evento "ready"). */
|
|
100
|
-
async login() {
|
|
101
|
-
const me = await this.rest.get("/bot/@me");
|
|
102
|
-
this.user = this._cacheUser({ ...me.bot, bot: true });
|
|
103
|
-
await this._loadGroups();
|
|
104
|
-
this._closing = false;
|
|
105
|
-
await this._connect();
|
|
106
|
-
return this.user;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Desconecta de vez (sem reconectar). */
|
|
110
|
-
destroy() {
|
|
111
|
-
this._closing = true;
|
|
112
|
-
clearInterval(this._keepalive);
|
|
113
|
-
try {
|
|
114
|
-
this._ws?.close();
|
|
115
|
-
} catch {
|
|
116
|
-
// já fechado
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async _loadGroups() {
|
|
121
|
-
const data = await this.rest.get("/bot/groups");
|
|
122
|
-
const seen = new Set();
|
|
123
|
-
for (const raw of data.groups) {
|
|
124
|
-
seen.add(raw.id);
|
|
125
|
-
const held = this.groups.get(raw.id);
|
|
126
|
-
if (held) held._patch(raw);
|
|
127
|
-
else this.groups.set(raw.id, new Group(this, raw));
|
|
128
|
-
}
|
|
129
|
-
for (const id of [...this.groups.keys()]) if (!seen.has(id)) this.groups.delete(id);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Um grupo, relido da API. @param {string} id */
|
|
133
|
-
async fetchGroup(id) {
|
|
134
|
-
const data = await this.rest.get(`/bot/groups/${encodeURIComponent(id)}`);
|
|
135
|
-
const held = this.groups.get(id);
|
|
136
|
-
if (held) held._patch(data.group);
|
|
137
|
-
else this.groups.set(id, new Group(this, data.group));
|
|
138
|
-
return this.groups.get(id);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Uma pessoa pelo id. @param {string} id */
|
|
142
|
-
async fetchUser(id) {
|
|
143
|
-
const data = await this.rest.get(`/bot/users/${encodeURIComponent(id)}`);
|
|
144
|
-
return this._cacheUser(data.user);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** @param {Record<string, any>} data */
|
|
148
|
-
_cacheUser(data) {
|
|
149
|
-
const user = new User(this, data);
|
|
150
|
-
this.users.set(user.id, user);
|
|
151
|
-
return user;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async _connect() {
|
|
155
|
-
const Socket = await socketClass();
|
|
156
|
-
return new Promise((resolve, reject) => {
|
|
157
|
-
const ws = new Socket(this.gatewayUrl);
|
|
158
|
-
this._ws = ws;
|
|
159
|
-
let settled = false;
|
|
160
|
-
const send = (payload) => {
|
|
161
|
-
if (ws.readyState === 1) ws.send(JSON.stringify(payload));
|
|
162
|
-
};
|
|
163
|
-
ws.onopen = () => {
|
|
164
|
-
send({ type: "register", name: this.user.displayName || this.user.username, token: this.token });
|
|
165
|
-
clearInterval(this._keepalive);
|
|
166
|
-
// O proxy da hospedagem derruba conexão parada: uma mensagem leve de tempos em tempos.
|
|
167
|
-
this._keepalive = setInterval(() => send({ type: "time-sync", t0: Date.now() }), KEEPALIVE_MS);
|
|
168
|
-
};
|
|
169
|
-
ws.onmessage = (event) => {
|
|
170
|
-
let msg;
|
|
171
|
-
try {
|
|
172
|
-
msg = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString());
|
|
173
|
-
} catch {
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (msg.type === "registered") {
|
|
177
|
-
const first = !this._ready;
|
|
178
|
-
this._ready = true;
|
|
179
|
-
this._attempt = 0;
|
|
180
|
-
if (!settled) {
|
|
181
|
-
settled = true;
|
|
182
|
-
resolve();
|
|
183
|
-
}
|
|
184
|
-
if (first) this.emit("ready", this.user);
|
|
185
|
-
else this.emit("reconnected");
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (msg.type === "register-error") {
|
|
189
|
-
this._closing = true;
|
|
190
|
-
const err = new Error(msg.message || "O Five Live recusou o token do bot.");
|
|
191
|
-
if (!settled) {
|
|
192
|
-
settled = true;
|
|
193
|
-
reject(err);
|
|
194
|
-
} else this.emit("error", err);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
this._dispatch(msg).catch((err) => this.emit("error", err));
|
|
198
|
-
};
|
|
199
|
-
ws.onerror = (event) => {
|
|
200
|
-
if (!settled && this._attempt === 0 && !this._ready) {
|
|
201
|
-
settled = true;
|
|
202
|
-
reject(new Error(`Não foi possível conectar em ${this.gatewayUrl}: ${event?.message ?? "erro de rede"}`));
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
ws.onclose = () => {
|
|
206
|
-
clearInterval(this._keepalive);
|
|
207
|
-
this.emit("disconnect");
|
|
208
|
-
if (this._closing || !this._ready) return;
|
|
209
|
-
this._reconnect();
|
|
210
|
-
};
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
_reconnect() {
|
|
215
|
-
this._attempt += 1;
|
|
216
|
-
const delay = Math.min(MAX_BACKOFF_MS, 1000 * 2 ** Math.min(this._attempt - 1, 5)) + Math.random() * 500;
|
|
217
|
-
this.emit("reconnecting", this._attempt);
|
|
218
|
-
setTimeout(async () => {
|
|
219
|
-
if (this._closing) return;
|
|
220
|
-
try {
|
|
221
|
-
// O que mudou enquanto estava fora (grupos que entrou/saiu).
|
|
222
|
-
await this._loadGroups();
|
|
223
|
-
await this._connect();
|
|
224
|
-
} catch {
|
|
225
|
-
this._reconnect();
|
|
226
|
-
}
|
|
227
|
-
}, delay);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** @param {Record<string, any>} msg */
|
|
231
|
-
async _dispatch(msg) {
|
|
232
|
-
switch (msg.type) {
|
|
233
|
-
case "group-message": {
|
|
234
|
-
if (!msg.message) return;
|
|
235
|
-
const message = new Message(this, msg.message, msg.author ? { ...msg.author } : undefined);
|
|
236
|
-
if (message.author.id === this.user?.id) return;
|
|
237
|
-
if (this.ignoreBots && message.author.bot) return;
|
|
238
|
-
this.emit("messageCreate", message);
|
|
239
|
-
await this._runCommand(message);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
case "group-message-updated":
|
|
243
|
-
if (msg.message) this.emit("messageUpdate", new Message(this, msg.message));
|
|
244
|
-
return;
|
|
245
|
-
case "group-message-deleted":
|
|
246
|
-
this.emit("messageDelete", { id: msg.messageId, groupId: msg.groupId, channelId: msg.channelId });
|
|
247
|
-
return;
|
|
248
|
-
case "group-message-reactions":
|
|
249
|
-
this.emit("reactionUpdate", { messageId: msg.messageId, groupId: msg.groupId, channelId: msg.channelId, reactions: msg.reactions ?? [] });
|
|
250
|
-
return;
|
|
251
|
-
case "group-typing":
|
|
252
|
-
if (msg.userId !== this.user?.id && msg.typing !== false) {
|
|
253
|
-
this.emit("typingStart", { userId: msg.userId, name: msg.name, groupId: msg.groupId, channelId: msg.channelId });
|
|
254
|
-
}
|
|
255
|
-
return;
|
|
256
|
-
case "group-joined": {
|
|
257
|
-
const group = await this.fetchGroup(msg.groupId);
|
|
258
|
-
this.emit("groupJoin", group);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
case "group-updated": {
|
|
262
|
-
if (!this.groups.has(msg.groupId)) return;
|
|
263
|
-
const group = await this.fetchGroup(msg.groupId).catch(() => null);
|
|
264
|
-
if (group) this.emit("groupUpdate", group);
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
case "group-member-add": {
|
|
268
|
-
if (!msg.member) return;
|
|
269
|
-
const user = this._cacheUser(msg.member);
|
|
270
|
-
const group = this.groups.get(msg.groupId) ?? (await this.fetchGroup(msg.groupId).catch(() => null));
|
|
271
|
-
if (group) group.memberCount += 1;
|
|
272
|
-
this.emit("memberAdd", { user, group, groupId: msg.groupId, via: msg.via ?? null });
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
case "group-member-remove": {
|
|
276
|
-
const group = this.groups.get(msg.groupId) ?? null;
|
|
277
|
-
if (group) group.memberCount = Math.max(0, group.memberCount - 1);
|
|
278
|
-
this.emit("memberRemove", { userId: msg.userId, user: this.users.get(msg.userId) ?? null, group, groupId: msg.groupId, reason: msg.reason ?? null, by: msg.by ?? null });
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
case "group-removed":
|
|
282
|
-
this.groups.delete(msg.groupId);
|
|
283
|
-
this.emit("groupLeave", { id: msg.groupId, reason: msg.reason ?? null });
|
|
284
|
-
return;
|
|
285
|
-
default:
|
|
286
|
-
this.emit("raw", msg);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
package/src/embed.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// Construtor de embeds no mesmo formato do discord.js (e do webhook do Five
|
|
2
|
-
// Live): título, descrição, cor, autor, campos, imagem, miniatura, rodapé.
|
|
3
|
-
|
|
4
|
-
/** @param {string | number} color */
|
|
5
|
-
function toColor(color) {
|
|
6
|
-
if (typeof color === "number") return color;
|
|
7
|
-
const hex = String(color).replace(/^#/, "");
|
|
8
|
-
return /^[0-9a-f]{6}$/i.test(hex) ? parseInt(hex, 16) : undefined;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class EmbedBuilder {
|
|
12
|
-
/** @param {Record<string, unknown>} [data] */
|
|
13
|
-
constructor(data = {}) {
|
|
14
|
-
/** @type {Record<string, any>} */
|
|
15
|
-
this.data = { ...data };
|
|
16
|
-
}
|
|
17
|
-
setTitle(title) {
|
|
18
|
-
this.data.title = title;
|
|
19
|
-
return this;
|
|
20
|
-
}
|
|
21
|
-
setDescription(description) {
|
|
22
|
-
this.data.description = description;
|
|
23
|
-
return this;
|
|
24
|
-
}
|
|
25
|
-
setURL(url) {
|
|
26
|
-
this.data.url = url;
|
|
27
|
-
return this;
|
|
28
|
-
}
|
|
29
|
-
/** @param {string | number} color "#3b6cf6" ou 0x3b6cf6 */
|
|
30
|
-
setColor(color) {
|
|
31
|
-
this.data.color = toColor(color);
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
/** @param {{ name: string, url?: string, iconURL?: string }} author */
|
|
35
|
-
setAuthor(author) {
|
|
36
|
-
this.data.author = { name: author.name, url: author.url, icon_url: author.iconURL };
|
|
37
|
-
return this;
|
|
38
|
-
}
|
|
39
|
-
/** @param {{ text: string, iconURL?: string }} footer */
|
|
40
|
-
setFooter(footer) {
|
|
41
|
-
this.data.footer = { text: footer.text, icon_url: footer.iconURL };
|
|
42
|
-
return this;
|
|
43
|
-
}
|
|
44
|
-
setImage(url) {
|
|
45
|
-
this.data.image = { url };
|
|
46
|
-
return this;
|
|
47
|
-
}
|
|
48
|
-
setThumbnail(url) {
|
|
49
|
-
this.data.thumbnail = { url };
|
|
50
|
-
return this;
|
|
51
|
-
}
|
|
52
|
-
/** @param {Date | number | string} [timestamp] */
|
|
53
|
-
setTimestamp(timestamp = new Date()) {
|
|
54
|
-
this.data.timestamp = new Date(timestamp).toISOString();
|
|
55
|
-
return this;
|
|
56
|
-
}
|
|
57
|
-
/** @param {...{ name: string, value: string, inline?: boolean }} fields */
|
|
58
|
-
addFields(...fields) {
|
|
59
|
-
this.data.fields = [...(this.data.fields ?? []), ...fields.flat()];
|
|
60
|
-
return this;
|
|
61
|
-
}
|
|
62
|
-
toJSON() {
|
|
63
|
-
return this.data;
|
|
64
|
-
}
|
|
65
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// fivelive.js — SDK para bots do Five Live.
|
|
2
|
-
// Documentação: README.md e https://live.fivenetwork.dev/developers
|
|
3
|
-
|
|
4
|
-
export { Client } from "./client.js";
|
|
5
|
-
export { EmbedBuilder } from "./embed.js";
|
|
6
|
-
export { REST, FiveLiveAPIError } from "./rest.js";
|
|
7
|
-
export { Channel, Group, Message, User, channelMention, userMention } from "./structures.js";
|
package/src/rest.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// As rotas HTTP da API de bots (api/botApi.mjs), com o token no cabeçalho
|
|
2
|
-
// `Authorization: Bot <token>`. Trata o limite de envio (429) esperando o
|
|
3
|
-
// tempo que a API pede e tentando de novo.
|
|
4
|
-
|
|
5
|
-
export class FiveLiveAPIError extends Error {
|
|
6
|
-
/**
|
|
7
|
-
* @param {string} message
|
|
8
|
-
* @param {number} status
|
|
9
|
-
* @param {string} method
|
|
10
|
-
* @param {string} path
|
|
11
|
-
*/
|
|
12
|
-
constructor(message, status, method, path) {
|
|
13
|
-
super(message);
|
|
14
|
-
this.name = "FiveLiveAPIError";
|
|
15
|
-
this.status = status;
|
|
16
|
-
this.method = method;
|
|
17
|
-
this.path = path;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
|
-
|
|
23
|
-
export class REST {
|
|
24
|
-
/** @param {{ token: string, apiUrl: string, retries?: number }} options */
|
|
25
|
-
constructor(options) {
|
|
26
|
-
this.token = options.token;
|
|
27
|
-
this.apiUrl = options.apiUrl.replace(/\/$/, "");
|
|
28
|
-
this.retries = options.retries ?? 3;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {string} method
|
|
33
|
-
* @param {string} path
|
|
34
|
-
* @param {unknown} [body]
|
|
35
|
-
*/
|
|
36
|
-
async request(method, path, body) {
|
|
37
|
-
for (let attempt = 0; ; attempt++) {
|
|
38
|
-
const res = await fetch(this.apiUrl + path, {
|
|
39
|
-
method,
|
|
40
|
-
headers: {
|
|
41
|
-
Authorization: `Bot ${this.token}`,
|
|
42
|
-
...(body !== undefined ? { "Content-Type": "application/json" } : {}),
|
|
43
|
-
"User-Agent": "fivelive.js (https://live.fivenetwork.dev/developers, 0.1.0)",
|
|
44
|
-
},
|
|
45
|
-
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
46
|
-
});
|
|
47
|
-
const data = await res.json().catch(() => ({}));
|
|
48
|
-
if (res.status === 429 && attempt < this.retries) {
|
|
49
|
-
const seconds = Number(data.retryAfter ?? res.headers.get("retry-after") ?? 5);
|
|
50
|
-
await sleep(Math.max(1, seconds) * 1000);
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (!res.ok) throw new FiveLiveAPIError(data.error ?? `HTTP ${res.status}`, res.status, method, path);
|
|
54
|
-
return data;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get(path) {
|
|
59
|
-
return this.request("GET", path);
|
|
60
|
-
}
|
|
61
|
-
post(path, body) {
|
|
62
|
-
return this.request("POST", path, body ?? {});
|
|
63
|
-
}
|
|
64
|
-
patch(path, body) {
|
|
65
|
-
return this.request("PATCH", path, body ?? {});
|
|
66
|
-
}
|
|
67
|
-
put(path, body) {
|
|
68
|
-
return this.request("PUT", path, body ?? {});
|
|
69
|
-
}
|
|
70
|
-
delete(path) {
|
|
71
|
-
return this.request("DELETE", path);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const enc = encodeURIComponent;
|
package/src/structures.js
DELETED
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
// Os objetos que o bot recebe: User, Group, Channel, Message. Cada um sabe
|
|
2
|
-
// agir sobre si mesmo (message.reply, channel.send, …) usando o client.
|
|
3
|
-
|
|
4
|
-
import { enc } from "./rest.js";
|
|
5
|
-
|
|
6
|
-
/** `<@id>` — como mencionar alguém no texto. */
|
|
7
|
-
export const userMention = (id) => `<@${id}>`;
|
|
8
|
-
/** `<#id>` — como apontar uma sala no texto. */
|
|
9
|
-
export const channelMention = (id) => `<#${id}>`;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Normaliza o que se manda: um texto, ou { content, embeds, mentions, reply }.
|
|
13
|
-
* @param {string | Record<string, any>} input
|
|
14
|
-
*/
|
|
15
|
-
export function messagePayload(input) {
|
|
16
|
-
const options = typeof input === "string" ? { content: input } : input ?? {};
|
|
17
|
-
const embeds = (options.embeds ?? []).map((embed) => (typeof embed?.toJSON === "function" ? embed.toJSON() : embed));
|
|
18
|
-
const roles = (options.roleMentions ?? []).map((id) => `@role:${id}`);
|
|
19
|
-
return {
|
|
20
|
-
text: options.content ?? "",
|
|
21
|
-
...(embeds.length ? { embeds } : {}),
|
|
22
|
-
...(options.mentions?.length || roles.length || options.everyone
|
|
23
|
-
? { mentions: [...(options.mentions ?? []), ...roles, ...(options.everyone ? ["@everyone"] : [])] }
|
|
24
|
-
: {}),
|
|
25
|
-
...(options.reply ? { replyTo: typeof options.reply === "string" ? options.reply : options.reply.id } : {}),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class User {
|
|
30
|
-
/** @param {import("./client.js").Client} client @param {Record<string, any>} data */
|
|
31
|
-
constructor(client, data) {
|
|
32
|
-
this.client = client;
|
|
33
|
-
this.id = data.id;
|
|
34
|
-
this.username = data.username ?? null;
|
|
35
|
-
this.displayName = data.displayName ?? data.name ?? data.username ?? "";
|
|
36
|
-
this.avatarURL = data.avatarUrl ?? null;
|
|
37
|
-
this.bot = data.bot === true;
|
|
38
|
-
this.flags = data.flags ?? [];
|
|
39
|
-
}
|
|
40
|
-
/** `<@id>`, para colocar no texto. */
|
|
41
|
-
toString() {
|
|
42
|
-
return userMention(this.id);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class Channel {
|
|
47
|
-
/** @param {import("./client.js").Client} client @param {Group} group @param {Record<string, any>} data */
|
|
48
|
-
constructor(client, group, data) {
|
|
49
|
-
this.client = client;
|
|
50
|
-
this.group = group;
|
|
51
|
-
this.id = data.id;
|
|
52
|
-
this.name = data.name;
|
|
53
|
-
/** "text" | "voice" */
|
|
54
|
-
this.kind = data.kind;
|
|
55
|
-
this.categoryId = data.categoryId ?? null;
|
|
56
|
-
this.topic = data.topic ?? "";
|
|
57
|
-
}
|
|
58
|
-
get isText() {
|
|
59
|
-
return this.kind === "text";
|
|
60
|
-
}
|
|
61
|
-
get path() {
|
|
62
|
-
return `/bot/groups/${enc(this.group.id)}/channels/${enc(this.id)}`;
|
|
63
|
-
}
|
|
64
|
-
/** Manda uma mensagem. @param {string | Record<string, any>} content */
|
|
65
|
-
async send(content) {
|
|
66
|
-
const data = await this.client.rest.post(`${this.path}/messages`, messagePayload(content));
|
|
67
|
-
return new Message(this.client, data.message, this.client.user);
|
|
68
|
-
}
|
|
69
|
-
/** "Digitando…" por alguns segundos. */
|
|
70
|
-
async sendTyping() {
|
|
71
|
-
await this.client.rest.post(`${this.path}/typing`, { typing: true });
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* As últimas mensagens (a mais nova por último).
|
|
75
|
-
* @param {{ limit?: number, after?: string }} [options]
|
|
76
|
-
*/
|
|
77
|
-
async fetchMessages(options = {}) {
|
|
78
|
-
const query = new URLSearchParams();
|
|
79
|
-
if (options.limit) query.set("limit", String(options.limit));
|
|
80
|
-
if (options.after) query.set("after", options.after);
|
|
81
|
-
const data = await this.client.rest.get(`${this.path}/messages?${query}`);
|
|
82
|
-
return data.messages.map((message) => new Message(this.client, message));
|
|
83
|
-
}
|
|
84
|
-
/** Uma mensagem pelo id. */
|
|
85
|
-
async fetchMessage(id) {
|
|
86
|
-
const data = await this.client.rest.get(`${this.path}/messages/${enc(id)}`);
|
|
87
|
-
return new Message(this.client, data.message);
|
|
88
|
-
}
|
|
89
|
-
toString() {
|
|
90
|
-
return `<#${this.id}>`;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export class Group {
|
|
95
|
-
/** @param {import("./client.js").Client} client @param {Record<string, any>} data */
|
|
96
|
-
constructor(client, data) {
|
|
97
|
-
this.client = client;
|
|
98
|
-
this._patch(data);
|
|
99
|
-
}
|
|
100
|
-
/** @param {Record<string, any>} data */
|
|
101
|
-
_patch(data) {
|
|
102
|
-
this.id = data.id;
|
|
103
|
-
this.name = data.name;
|
|
104
|
-
this.iconURL = data.iconUrl ?? null;
|
|
105
|
-
this.ownerId = data.ownerId ?? null;
|
|
106
|
-
this.memberCount = data.memberCount ?? 0;
|
|
107
|
-
/** @type {{ id: string, name: string, color: string | null, position: number, mentionable: boolean }[]} */
|
|
108
|
-
this.roles = data.roles ?? [];
|
|
109
|
-
/** O que o bot pode fazer aqui (kickMembers, banMembers, manageRoles, manageMessages…). */
|
|
110
|
-
this.permissions = data.permissions ?? {};
|
|
111
|
-
/** @type {Map<string, Channel>} */
|
|
112
|
-
this.channels = new Map((data.channels ?? []).map((channel) => [channel.id, new Channel(this.client, this, channel)]));
|
|
113
|
-
}
|
|
114
|
-
/** A sala pelo id ou pelo nome. @param {string} idOrName */
|
|
115
|
-
channel(idOrName) {
|
|
116
|
-
return this.channels.get(idOrName) ?? [...this.channels.values()].find((channel) => channel.name === idOrName) ?? null;
|
|
117
|
-
}
|
|
118
|
-
/** Os membros, com cargos e quem está online. */
|
|
119
|
-
async fetchMembers() {
|
|
120
|
-
const data = await this.client.rest.get(`/bot/groups/${enc(this.id)}/members`);
|
|
121
|
-
return data.members.map((member) => ({ ...member, user: this.client._cacheUser(member) }));
|
|
122
|
-
}
|
|
123
|
-
get path() {
|
|
124
|
-
return `/bot/groups/${enc(this.id)}`;
|
|
125
|
-
}
|
|
126
|
-
/** Se o bot tem a permissão (administrator vale por todas). @param {string} key */
|
|
127
|
-
can(key) {
|
|
128
|
-
return this.permissions.administrator === true || this.permissions[key] === true;
|
|
129
|
-
}
|
|
130
|
-
/** Expulsa alguém (precisa de kickMembers e de um cargo acima do da pessoa). @param {string} userId */
|
|
131
|
-
async kick(userId) {
|
|
132
|
-
await this.client.rest.post(`${this.path}/members/${enc(userId)}/kick`, {});
|
|
133
|
-
}
|
|
134
|
-
/** Bane alguém, mesmo que não esteja no grupo (banMembers). @param {string} userId @param {string} [reason] */
|
|
135
|
-
async ban(userId, reason) {
|
|
136
|
-
await this.client.rest.put(`${this.path}/bans/${enc(userId)}`, reason ? { reason } : {});
|
|
137
|
-
}
|
|
138
|
-
/** Tira o banimento (banMembers). @param {string} userId */
|
|
139
|
-
async unban(userId) {
|
|
140
|
-
await this.client.rest.delete(`${this.path}/bans/${enc(userId)}`);
|
|
141
|
-
}
|
|
142
|
-
/** Quem está banido (banMembers). */
|
|
143
|
-
async fetchBans() {
|
|
144
|
-
const data = await this.client.rest.get(`${this.path}/bans`);
|
|
145
|
-
return data.bans;
|
|
146
|
-
}
|
|
147
|
-
/** Dá um cargo (manageRoles; só cargos abaixo do mais alto do bot). @param {string} userId @param {string} roleId */
|
|
148
|
-
async addRole(userId, roleId) {
|
|
149
|
-
const data = await this.client.rest.put(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);
|
|
150
|
-
return data.roleIds;
|
|
151
|
-
}
|
|
152
|
-
/** Tira um cargo (manageRoles). @param {string} userId @param {string} roleId */
|
|
153
|
-
async removeRole(userId, roleId) {
|
|
154
|
-
const data = await this.client.rest.delete(`${this.path}/members/${enc(userId)}/roles/${enc(roleId)}`);
|
|
155
|
-
return data.roleIds;
|
|
156
|
-
}
|
|
157
|
-
/** O bot sai do grupo. */
|
|
158
|
-
async leave() {
|
|
159
|
-
await this.client.rest.post(`${this.path}/leave`, {});
|
|
160
|
-
}
|
|
161
|
-
/** `<@&id>` não existe aqui: para mencionar um cargo, use { roleMentions: [id] } no envio. */
|
|
162
|
-
role(idOrName) {
|
|
163
|
-
return this.roles.find((role) => role.id === idOrName || role.name === idOrName) ?? null;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export class Message {
|
|
168
|
-
/**
|
|
169
|
-
* @param {import("./client.js").Client} client
|
|
170
|
-
* @param {Record<string, any>} data
|
|
171
|
-
* @param {User | Record<string, any>} [author]
|
|
172
|
-
*/
|
|
173
|
-
constructor(client, data, author) {
|
|
174
|
-
this.client = client;
|
|
175
|
-
this.id = data.id;
|
|
176
|
-
this.groupId = data.groupId;
|
|
177
|
-
this.channelId = data.channelId;
|
|
178
|
-
/** O texto como está gravado — menções como `<@id>`. */
|
|
179
|
-
this.content = data.text ?? "";
|
|
180
|
-
this.embeds = data.embeds ?? [];
|
|
181
|
-
this.attachments = data.attachments ?? [];
|
|
182
|
-
this.images = data.images ?? [];
|
|
183
|
-
this.reactions = data.reactions ?? [];
|
|
184
|
-
this.replyTo = data.replyTo ?? null;
|
|
185
|
-
this.createdAt = new Date(data.ts ?? Date.now());
|
|
186
|
-
this.editedAt = data.editedAt ? new Date(data.editedAt) : null;
|
|
187
|
-
this.pinned = Boolean(data.pinnedAt);
|
|
188
|
-
this.webhook = Boolean(data.webhook);
|
|
189
|
-
const mentions = data.mentions ?? [];
|
|
190
|
-
this.mentions = {
|
|
191
|
-
/** ids das pessoas mencionadas */
|
|
192
|
-
users: mentions.filter((entry) => !entry.startsWith("@")),
|
|
193
|
-
/** ids dos cargos mencionados */
|
|
194
|
-
roles: mentions.filter((entry) => entry.startsWith("@role:")).map((entry) => entry.slice(6)),
|
|
195
|
-
everyone: mentions.includes("@everyone"),
|
|
196
|
-
raw: mentions,
|
|
197
|
-
};
|
|
198
|
-
this.author =
|
|
199
|
-
author instanceof User
|
|
200
|
-
? author
|
|
201
|
-
: author
|
|
202
|
-
? client._cacheUser(author)
|
|
203
|
-
: client.users.get(data.from) ?? new User(client, { id: data.from, name: data.fromName });
|
|
204
|
-
}
|
|
205
|
-
get group() {
|
|
206
|
-
return this.client.groups.get(this.groupId) ?? null;
|
|
207
|
-
}
|
|
208
|
-
get channel() {
|
|
209
|
-
return this.group?.channels.get(this.channelId) ?? null;
|
|
210
|
-
}
|
|
211
|
-
/** O texto com `<@id>` trocado por `@nome` (quando o nome é conhecido). */
|
|
212
|
-
get cleanContent() {
|
|
213
|
-
return this.content.replace(/<@([A-Za-z0-9:_-]+)>/g, (whole, id) => {
|
|
214
|
-
const user = this.client.users.get(id);
|
|
215
|
-
return user ? `@${user.displayName}` : whole;
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
/** Se a mensagem menciona o bot (pelo id, cargo não conta). */
|
|
219
|
-
get mentionsMe() {
|
|
220
|
-
return this.mentions.users.includes(this.client.user?.id) || this.mentions.everyone;
|
|
221
|
-
}
|
|
222
|
-
get path() {
|
|
223
|
-
return `/bot/groups/${enc(this.groupId)}/channels/${enc(this.channelId)}/messages`;
|
|
224
|
-
}
|
|
225
|
-
/** Responde citando esta mensagem. @param {string | Record<string, any>} content */
|
|
226
|
-
async reply(content) {
|
|
227
|
-
const payload = messagePayload(content);
|
|
228
|
-
const data = await this.client.rest.post(this.path, { ...payload, replyTo: this.id });
|
|
229
|
-
return new Message(this.client, data.message, this.client.user);
|
|
230
|
-
}
|
|
231
|
-
/** Edita (só as mensagens do próprio bot). @param {string | Record<string, any>} content */
|
|
232
|
-
async edit(content) {
|
|
233
|
-
const payload = messagePayload(content);
|
|
234
|
-
const data = await this.client.rest.patch(`${this.path}/${enc(this.id)}`, { text: payload.text, ...(payload.embeds ? { embeds: payload.embeds } : {}) });
|
|
235
|
-
return new Message(this.client, data.message, this.author);
|
|
236
|
-
}
|
|
237
|
-
/** Fixa na sala (manageMessages). */
|
|
238
|
-
async pin() {
|
|
239
|
-
await this.client.rest.put(`${this.path}/${enc(this.id)}/pin`);
|
|
240
|
-
}
|
|
241
|
-
/** Desafixa (manageMessages). */
|
|
242
|
-
async unpin() {
|
|
243
|
-
await this.client.rest.delete(`${this.path}/${enc(this.id)}/pin`);
|
|
244
|
-
}
|
|
245
|
-
/** Apaga: as do próprio bot sempre; as dos outros, com manageMessages. */
|
|
246
|
-
async delete() {
|
|
247
|
-
await this.client.rest.delete(`${this.path}/${enc(this.id)}`);
|
|
248
|
-
}
|
|
249
|
-
/** Reage com um emoji. @param {string} emoji */
|
|
250
|
-
async react(emoji) {
|
|
251
|
-
await this.client.rest.put(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);
|
|
252
|
-
}
|
|
253
|
-
/** Tira a reação do bot. @param {string} emoji */
|
|
254
|
-
async unreact(emoji) {
|
|
255
|
-
await this.client.rest.delete(`${this.path}/${enc(this.id)}/reactions/${enc(emoji)}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
File without changes
|