initial commit

main
jasonfoknxu 3 years ago
commit e22c9bc87e

25
.gitignore vendored

@ -0,0 +1,25 @@
# config
.env
# Compiled script
/run
# logs
/logs
# dependencies
/node_modules
/.pnp
.pnp.js
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 nxu@nxu.biz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,69 @@
# FreeCityHK Minecraft Discord Bot
專門為 FreeCityHK Minecraft 制作的 Discord Bot
```
✨ FreeCityHK 係一個以 RP [RPG] 的 生存 Minecraft 伺服器 ✨
💫 仲記唔記得你曾經喺 Minecraft 起過嘅偉大建築,面對宏大嘅城堡有冇諗過真真正正成為一方霸主!?💫
🪙 面對霸權,你有冇一種革命情意結 ?! 或者,相比建築,更加響往冷兵器、傭兵、國戰、荒島生活!? 🪙
🏵️ 玩慣咗 GTA RP 又有冇諗過換個方式,以 Minecraft 嚟過一個另類人生RP !? 🏵️
🌅 FreeCityHK 可能就係你可以嘗試嘅選擇,你可以建立你嘅國家、城邦。🌅
🍻 你可以喺依度招兵買馬造就你嘅軍團,廣交結社成立你嘅暗部。 🍻
☀️ 每個人都有你嘅過往,而唔知喺依個世界你嘅選擇,又會造就點嘅全新篇章呢!?☀️
🥠 你嘅 RP 故事正在由你編寫中,我地期待著... 🥠
```
[FreeCityHK Minecraft 3.0介紹影片](https://www.youtube.com/watch?v=gmT4nW-8rFk)
## 功能 & 指令
- 頻道名顯示在線人數每10分鐘更新一次
- 查看玩家ID和skin `/player {玩家名}`
- 查看在線玩家 `/online`
- 查看伺服器狀態 `/server`
- 加白名單 `/whitelist {玩家名} `
- 遊戲畫面公告 `/noti {公告内容}`
- 聊天訊息 `/say {訊息内容}`
## 設定 .env
- `TOKEN` —— Discord Bot 的 Token
- `CHANNEL_ID` —— 可以使用 Bot 指令的頻道ID如多個頻道可用`,`分隔)
- `UPDATE_CHANNEL` —— 顯示在線人數的頻道ID如多個頻道可用`,`分隔)
- `SERVER` —— Minecraft Server 地址
- `QUERY_PORT` —— Minecraft Server Query Port
- `RCON_PORT` —— Minecraft Server Rcon Port
- `RCON_PW` —— Minecraft Server Rcon 密碼
- `PLAYER_API` —— 以玩家名取得玩家ID的API使用`{{NAME}}`取代玩家名
- `PROFILE_API` —— 以玩家ID取得玩家資料的API使用`{{PLAYER_ID}}`取代玩家ID
## 使用方法
> 有關建立和設定 Discord Bot 的方法,請自行了解。
1. `yarn install``npm install` 安裝所需的 dependencies
2. 把 `sample.env` 重新命名為 `.env`
3. 填寫設定
4. `yarn run build``npm run build` 進行編譯
5. `yarn start``npm start` 使用 PM2 運行
6. 如果沒有/不想用PM2可以 `yarn run test``npm run test` 使用 Node.js 直接運行
## 歡迎加入 FreeCityHK Minecraft Discord
[![FreeCityHK Minecraft Discord](.readme/discord_400.png 'Discord')](https://discord.freecityhk.city)
## Credits
- [axios](https://github.com/axios/axios)
- [Crafatar](https://github.com/crafatar/crafatar/) Minecraft Skin render 方法)
- [node-canvas](https://github.com/Automattic/node-canvas)
- [discord.js](https://github.com/discordjs/discord.js)
- [dotenv](https://github.com/motdotla/dotenv)
- [minecraft-server-util](https://github.com/PassTheMayo/minecraft-server-util)
- [node-schedule](https://github.com/node-schedule/node-schedule)

@ -0,0 +1,15 @@
module.exports = {
apps : [{
name : "freecityhk-mc-dc-bot",
script: 'run/app.js',
watch: false,
watch_delay: 1000,
ignore_watch : ["node_modules"],
//exec_mode: "cluster",
//instances: 2,
max_memory_restart: "2G",
log_date_format: "YYYY-MM-DD HH:mm:ss",
error_file: "./logs/error.log",
out_file: "./logs/out.log"
}],
};

@ -0,0 +1,25 @@
{
"name": "freecityhk-minecraft-discord-bot",
"version": "1.0.0",
"main": "run/app.js",
"license": "MIT",
"author": "jasonfoknxu <nxu@nxu.biz> (https://nxuweb.net)",
"repository": "github:jasonfoknxu/freecityhk-minecraft-discord-bot",
"scripts": {
"start": "pm2 start ecosystem.config.js --no-daemon",
"build": "tsc -p tsconfig.json",
"test": "node run/app.js"
},
"dependencies": {
"axios": "^1.3.4",
"canvas": "^2.11.0",
"discord.js": "^14.8.0",
"dotenv": "^16.0.3",
"minecraft-server-util": "^5.4.0",
"node-schedule": "^2.1.1"
},
"devDependencies": {
"@types/node-schedule": "^2.1.0",
"typescript": "^5.0.2"
}
}

@ -0,0 +1,12 @@
# Discord
TOKEN=
CHANNEL_ID=
UPDATE_CHANNEL=
# Minecraft
SERVER=
QUERY_PORT=
RCON_PORT=
RCON_PW=
PLAYER_API=https://api.mojang.com/users/profiles/minecraft/{{NAME}}
PROFILE_API=https://sessionserver.mojang.com/session/minecraft/profile/{{PLAYER_ID}}

@ -0,0 +1,144 @@
import { Client, GatewayIntentBits, VoiceChannel } from 'discord.js';
import { config } from './config';
import { getServerInfo, getOnlinePlayers, onlineCommand, serverCommand } from './commands/ServerInfo';
import { playerCommand, getPlayer } from './commands/playerInfo';
import { add2Whitelist, notiPlayer, notiPlayerCommand, say, sayCommand, whitelistCommand } from './commands/minecraft';
import { render } from './render';
import * as schedule from "node-schedule";
const client = new Client({ intents: [GatewayIntentBits.Guilds ] });
client.on('ready', async () => {
if (client.user) {
console.log(`已登入 Discord ${client.user.tag}`);
const allCommands = [playerCommand, onlineCommand, serverCommand, whitelistCommand, notiPlayerCommand, sayCommand];
allCommands.forEach(async (command) => {
await client.application?.commands.create(command.toJSON());
});
}
});
client.on('interactionCreate', async (interaction) => {
if (!interaction.isChatInputCommand()) return;
if (config.CHANNEL_ID.includes(interaction.channelId)) {
await interaction.deferReply();
switch (interaction.commandName) {
case 'player':
const playerName = interaction.options.getString('玩家名');
if (!playerName) {
await interaction.editReply('請輸入玩家名稱。');
} else {
const getPlayerInfo = await getPlayer(playerName);
if (!getPlayerInfo) {
await interaction.editReply('\:warning: 出錯了,請稍後再試一次。');
} else {
let model;
if (getPlayerInfo.textures.SKIN.url) {
model = await render(getPlayerInfo.textures.SKIN.url);
}
let replyMessage: { files?: any; content: string } = {
content: `玩家名:\`${getPlayerInfo.profileName}\`\n玩家ID\`${getPlayerInfo.profileId}\`\n`,
};
if (model) {
replyMessage = { ...replyMessage, files: [{ attachment: model }] };
}
await interaction.editReply(replyMessage);
}
}
break;
case 'online':
const onlinePlayers = await getOnlinePlayers();
if (!onlinePlayers?.list) {
await interaction.editReply('伺服器目前為離線狀態,或無法取得在線玩家數,請稍後再試一次。');
} else {
let reply = '目前有 `' + onlinePlayers.online + '` 名玩家在線';
if (onlinePlayers.online > 0) {
reply += '\n';
}
onlinePlayers.list.forEach((player) => {
reply += '\n\:bust_in_silhouette: ' + player;
});
await interaction.editReply(reply);
}
break;
case 'server':
const serverInfo = await getServerInfo();
if (!serverInfo) {
await interaction.editReply('伺服器目前為離線狀態。');
} else {
let result = `【FreeCityHK Minecraft】\n${serverInfo.motd.clean}\n版本\`${serverInfo.version}\`\n玩家人數\`${serverInfo.players.online}/${serverInfo.players.max}\``;
await interaction.editReply(result);
}
break;
case 'whitelist':
const newPlayerName = interaction.options.getString('玩家名');
if (!newPlayerName) {
await interaction.editReply('請輸入玩家名稱。');
} else {
const updateWhitelist = await add2Whitelist(newPlayerName);
if (!updateWhitelist) {
await interaction.editReply('\:warning: 出錯了,請稍後再試一次。');
} else {
await interaction.editReply(updateWhitelist);
}
}
break;
case 'noti':
const notiMessage = interaction.options.getString('公告訊息');
if (!notiMessage) {
await interaction.editReply('請輸入要發放的公告通知。');
} else {
const notiResult = await notiPlayer(notiMessage);
if (!notiResult) {
await interaction.editReply('\:warning: 出錯了,請稍後再試一次。');
} else {
await interaction.editReply(notiResult);
}
}
break;
case 'say':
const sayMessage = interaction.options.getString('訊息');
if (!sayMessage) {
await interaction.editReply('請輸入要發送的訊息。');
} else {
await say(sayMessage);
await interaction.editReply(`已發送訊息:${sayMessage}`);
}
break;
}
}
});
const updateChannelName = async () => {
if (config.UPDATE_CHANNEL) {
const serverInfo = await getServerInfo();
const onlinePlayers = serverInfo?.players.online ?? '🔴';
let title = `🎲|在線人數:${onlinePlayers}`; // 𝐅𝐫𝐞𝐞𝐂𝐢𝐭𝐲𝐇𝐊
config.UPDATE_CHANNEL.forEach(async (channelId) => {
const channel = await client.channels.fetch(channelId) as VoiceChannel;
if (channel.name !== title) {
channel.setName(title);
}
});
}
}
const start = () => {
try {
client.login(config.TOKEN);
} catch (err) {
console.error(err);
process.exit(1);
}
};
start();
const job = schedule.scheduleJob('*/10 * * * *', async () => {
await updateChannelName();
});

@ -0,0 +1,67 @@
import { config } from '../config';
import * as util from 'minecraft-server-util';
import { SlashCommandBuilder } from 'discord.js';
const add2Whitelist = async (playerName: string) => {
try {
const client = new util.RCON();
await client.connect(config.SERVER, config.RCON_PORT /* , options */);
await client.login(config.RCON_PW);
const message = await client.execute(`whitelist add ${playerName}`);
return message;
} catch (e) {
return null;
}
};
const notiPlayer = async (message: string, color: string = 'light_purple') => {
try {
const client = new util.RCON();
await client.connect(config.SERVER, config.RCON_PORT /* , options */);
await client.login(config.RCON_PW);
const result = await client.execute(
`/title @a title {"text":"${string2Unicode(message)}","bold":true,"color":"${color}"}`
);
return result;
} catch (e) {
return null;
}
};
const say = async (message: string) => {
try {
const client = new util.RCON();
await client.connect(config.SERVER, config.RCON_PORT /* , options */);
await client.login(config.RCON_PW);
const result = await client.execute(`say ${message}`);
return result;
} catch (e) {
return null;
}
};
const string2Unicode = (str: string) => {
let result = '';
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i).toString(16).toUpperCase();
result += '\\u' + '0'.repeat(4 - code.length) + code;
}
return result;
};
const whitelistCommand = new SlashCommandBuilder()
.setName('whitelist')
.setDescription('將玩家加入白名單')
.addStringOption((option) => option.setName('玩家名').setDescription('Minecraft 玩家的名稱').setRequired(true));
const notiPlayerCommand = new SlashCommandBuilder()
.setName('noti')
.setDescription('發放公告通知,將會在玩家畫面中間顯示')
.addStringOption((option) => option.setName('公告訊息').setDescription('公告内容').setRequired(true));
const sayCommand = new SlashCommandBuilder()
.setName('say')
.setDescription('透過聊天向多個玩家發送訊息')
.addStringOption((option) => option.setName('訊息').setDescription('訊息内容').setRequired(true));
export { add2Whitelist, notiPlayer, say, whitelistCommand, notiPlayerCommand, sayCommand };

@ -0,0 +1,54 @@
import { SlashCommandBuilder } from 'discord.js';
import { config } from '../config';
import axios, { AxiosResponse } from 'axios';
import { Player } from '../interface/Player';
import { PlayerProfile } from '../interface/PlayerProfile';
const getPlayerId = async (name: string): Promise<Player | null> => {
const name2IdApi = config.PLAYER_API.replace('{{NAME}}', name);
const player: AxiosResponse<Player> | null = await axios.get(name2IdApi).catch(() => {
return null;
});
if (!player?.data) {
return null;
}
return player.data;
};
const getPlayerProfile = async (playerId: string): Promise<PlayerProfile | null> => {
const profileApi = config.PROFILE_API.replace('{{PLAYER_ID}}', playerId);
const playerProfile = await axios.get(profileApi).catch(() => {
return null;
});
if (!playerProfile?.data) {
return null;
}
if (playerProfile.data.properties[0]?.value) {
const playerData: PlayerProfile = JSON.parse(
Buffer.from(playerProfile.data.properties[0].value, 'base64').toString('binary')
);
return playerData;
}
return null;
};
const getPlayer = async (name: string) => {
const playerInfo = await getPlayerId(name);
if (!playerInfo) {
return null;
}
const profile = await getPlayerProfile(playerInfo.id);
if (!profile) {
return { profileId: playerInfo.id, profileName: playerInfo.name };
}
return profile;
};
const playerCommand = new SlashCommandBuilder()
.setName('player')
.setDescription('查看 Minecraft 玩家資料')
.addStringOption((option) => option.setName('玩家名').setDescription('Minecraft 玩家的名稱').setRequired(true));
export { playerCommand, getPlayerId, getPlayerProfile, getPlayer };

@ -0,0 +1,30 @@
import { SlashCommandBuilder, Client } from 'discord.js';
import * as util from 'minecraft-server-util';
import { config } from '../config';
import { ServerInfo } from '../interface/ServerInfo';
const getServerInfo = async (): Promise<ServerInfo | null> => {
try {
const query: ServerInfo = await util.queryFull(config.SERVER, config.QUERY_PORT /* , options */);
if (!query) {
return null;
}
return query;
} catch (e) {
return null;
}
};
const getOnlinePlayers = async () => {
const serverInfo = await getServerInfo();
if (!serverInfo?.players) {
return null;
}
return serverInfo.players;
};
const onlineCommand = new SlashCommandBuilder().setName('online').setDescription('查看 FreeCityHK 在線玩家');
const serverCommand = new SlashCommandBuilder().setName('server').setDescription('FreeCityHK Minecraft 伺服器');
export { getServerInfo, getOnlinePlayers, onlineCommand, serverCommand };

@ -0,0 +1,40 @@
import * as dotenv from 'dotenv';
const loadConfig = dotenv.config();
if (loadConfig.error) {
console.error('[ERROR] Cannot load config');
throw loadConfig.error;
}
const parsedConfig = loadConfig.parsed;
if (!parsedConfig) {
console.error('[ERROR] Cannot parse config');
throw new Error();
}
interface Config {
TOKEN: string;
CHANNEL_ID: string[];
UPDATE_CHANNEL: string[];
SERVER: string;
QUERY_PORT: number;
RCON_PORT: number;
RCON_PW: string;
PLAYER_API: string;
PROFILE_API: string;
}
const config: Config = {
TOKEN: parsedConfig.TOKEN,
CHANNEL_ID: parsedConfig.CHANNEL_ID.split(','),
UPDATE_CHANNEL: parsedConfig.UPDATE_CHANNEL.split(','),
SERVER: parsedConfig.SERVER,
QUERY_PORT: parseInt(parsedConfig.QUERY_PORT ?? '25565'),
RCON_PORT: parseInt(parsedConfig.RCON_PORT ?? '25575'),
RCON_PW: parsedConfig.RCON_PW,
PLAYER_API: parsedConfig.PLAYER_API,
PROFILE_API: parsedConfig.PROFILE_API,
};
export { config };

@ -0,0 +1,7 @@
interface Player {
id: string,
name: string,
properties?: any
}
export {Player};

@ -0,0 +1,20 @@
interface PlayerProfile {
timestamp?: number | string,
profileId: string,
profileName: string,
textures?: PlayerTextures | any,
[key: string]: any
}
interface PlayerTextures {
SKIN: PlayerSkin,
[key: string]: any
}
interface PlayerSkin {
url: string,
metadata: any,
[key: string]: any
}
export {PlayerProfile, PlayerTextures, PlayerSkin}

@ -0,0 +1,25 @@
interface ServerInfo {
motd: motd,
version: string,
software?: string,
plugins?: any,
map: string,
players: playerInfo,
hostIP?: string,
hostPort?: number,
[key: string]: any
}
interface motd {
raw: string,
clean: string,
html: string
}
interface playerInfo {
online: number,
max: number,
list: string[]
}
export {ServerInfo}

@ -0,0 +1,205 @@
// Reference: https://github.com/crafatar/crafatar/
import * as cvs from 'canvas';
const removeOpacity = (canvas: cvs.Canvas) => {
let ctx = canvas.getContext('2d');
let imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
let data = imagedata.data;
for (let i = 0; i < data.length; i += 4) {
data[i + 3] = 255;
}
ctx.putImageData(imagedata, 0, 0);
return canvas;
};
const containsOpacity = (canvas: cvs.Canvas) => {
let ctx = canvas.getContext('2d');
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
for (let i = 3; i < imageData.length; i += 4) {
if (imageData[i] < 255) {
return true;
}
}
return false;
};
const resize = (src: cvs.Canvas, scale: number) => {
let dst = cvs.createCanvas(scale * src.width, scale * src.height);
let context = dst.getContext('2d');
context.patternQuality = 'fast';
context.drawImage(src, 0, 0, src.width * scale, src.height * scale);
return dst;
};
function getPart(src: cvs.Image, x: number, y: number, width: number, height: number, scale: number) {
let dst = cvs.createCanvas(scale * width, scale * height);
let context = dst.getContext('2d');
context.patternQuality = 'fast';
context.drawImage(src, x, y, width, height, 0, 0, width * scale, height * scale);
return dst;
}
function flip(src: cvs.Canvas) {
let dst = cvs.createCanvas(src.width, src.height);
let context = dst.getContext('2d');
context.scale(-1, 1);
context.drawImage(src, -src.width, 0);
return dst;
}
const skewA = 26 / 45; // 0.57777777
const skewB = skewA * 2; // 1.15555555
const render = async (
img: string,
scale: number = 6,
overlay: boolean = true,
renderBody: boolean = true,
slim: boolean = false
) => {
let canvas = cvs.createCanvas(scale * 20, scale * (renderBody ? 45.1 : 18.5));
let ctx = canvas.getContext('2d');
const skin = await cvs.loadImage(img);
let oldSkin = skin.height === 32;
let armWidth = slim ? 3 : 4;
let headTop = resize(removeOpacity(getPart(skin, 8, 0, 8, 8, 1)), scale);
let headFront = resize(removeOpacity(getPart(skin, 8, 8, 8, 8, 1)), scale);
let headRight = resize(removeOpacity(getPart(skin, 0, 8, 8, 8, 1)), scale);
let armRightTop = resize(removeOpacity(getPart(skin, 44, 16, armWidth, 4, 1)), scale);
let armRightFront = resize(removeOpacity(getPart(skin, 44, 20, armWidth, 12, 1)), scale);
let armRightSide = resize(removeOpacity(getPart(skin, 40, 20, 4, 12, 1)), scale);
let armLeftTop = oldSkin ? flip(armRightTop) : resize(removeOpacity(getPart(skin, 36, 48, armWidth, 4, 1)), scale);
let armLeftFront = oldSkin
? flip(armRightFront)
: resize(removeOpacity(getPart(skin, 36, 52, armWidth, 12, 1)), scale);
let legRightFront = resize(removeOpacity(getPart(skin, 4, 20, 4, 12, 1)), scale);
let legRightSide = resize(removeOpacity(getPart(skin, 0, 20, 4, 12, 1)), scale);
let legLeftFront = oldSkin ? flip(legRightFront) : resize(removeOpacity(getPart(skin, 20, 52, 4, 12, 1)), scale);
let bodyFront = resize(removeOpacity(getPart(skin, 20, 20, 8, 12, 1)), scale);
if (overlay) {
if (containsOpacity(getPart(skin, 32, 0, 32, 32, 1))) {
// render head overlay
headTop.getContext('2d').drawImage(getPart(skin, 40, 0, 8, 8, scale), 0, 0);
headFront.getContext('2d').drawImage(getPart(skin, 40, 8, 8, 8, scale), 0, 0);
headRight.getContext('2d').drawImage(getPart(skin, 32, 8, 8, 8, scale), 0, 0);
}
if (!oldSkin) {
let bodyArea = getPart(skin, 16, 32, 32, 16, 1);
let rightArm = getPart(skin, 48, 48, 16, 16, 1);
let leftArm = getPart(skin, 40, 32, 16, 16, 1);
let rightLeg = getPart(skin, 0, 32, 16, 16, 1);
let leftLeg = getPart(skin, 0, 48, 16, 16, 1);
if (containsOpacity(bodyArea)) {
// render body overlay
bodyFront.getContext('2d').drawImage(getPart(skin, 20, 36, 8, 12, scale), 0, 0);
}
if (containsOpacity(rightArm)) {
// render right arm overlay
armRightTop.getContext('2d').drawImage(getPart(skin, 44, 32, armWidth, 4, scale), 0, 0);
armRightFront.getContext('2d').drawImage(getPart(skin, 44, 36, armWidth, 12, scale), 0, 0);
armRightSide.getContext('2d').drawImage(getPart(skin, 40, 36, 4, 12, scale), 0, 0);
}
if (containsOpacity(leftArm)) {
// render left arm overlay
armLeftTop.getContext('2d').drawImage(getPart(skin, 36 + 16, 48, armWidth, 4, scale), 0, 0);
armLeftFront.getContext('2d').drawImage(getPart(skin, 36 + 16, 52, armWidth, 12, scale), 0, 0);
}
if (containsOpacity(rightLeg)) {
// render right leg overlay
legRightFront.getContext('2d').drawImage(getPart(skin, 4, 36, 4, 12, scale), 0, 0);
legRightSide.getContext('2d').drawImage(getPart(skin, 0, 36, 4, 12, scale), 0, 0);
}
if (containsOpacity(leftLeg)) {
// render left leg overlay
legLeftFront.getContext('2d').drawImage(getPart(skin, 4, 52, 4, 12, scale), 0, 0);
}
}
}
let x = 0;
let y = 0;
let z = 0;
let zOffset = scale * 3;
let xOffset = scale * 2;
if (renderBody) {
// pre-render front onto separate canvas
let front = cvs.createCanvas(scale * 16, scale * 24);
let frontCtx = front.getContext('2d');
frontCtx.patternQuality = 'fast';
frontCtx.drawImage(armRightFront, (4 - armWidth) * scale, 0 * scale, armWidth * scale, 12 * scale);
frontCtx.drawImage(armLeftFront, 12 * scale, 0 * scale, armWidth * scale, 12 * scale);
frontCtx.drawImage(bodyFront, 4 * scale, 0 * scale, 8 * scale, 12 * scale);
frontCtx.drawImage(legRightFront, 4 * scale, 12 * scale, 4 * scale, 12 * scale);
frontCtx.drawImage(legLeftFront, 8 * scale, 12 * scale, 4 * scale, 12 * scale);
// top
x = xOffset + scale * 2;
y = scale * -armWidth;
z = zOffset + scale * 8;
ctx.setTransform(new cvs.DOMMatrix([1, -skewA, 1, skewA, 0, 0]));
ctx.drawImage(armRightTop, y - z - 0.5, x + z, armRightTop.width + 1, armRightTop.height + 1);
y = scale * 8;
ctx.drawImage(armLeftTop, y - z, x + z, armLeftTop.width, armLeftTop.height + 1);
// right side
ctx.setTransform(new cvs.DOMMatrix([1, skewA, 0, skewB, 0, 0]));
x = xOffset + scale * 2;
y = 0;
z = zOffset + scale * 20;
ctx.drawImage(legRightSide, x + y, z - y, legRightSide.width, legRightSide.height);
x = xOffset + scale * 2;
y = scale * -armWidth;
z = zOffset + scale * 8;
ctx.drawImage(armRightSide, x + y, z - y - 0.5, armRightSide.width, armRightSide.height + 1);
// front
z = zOffset + scale * 12;
y = 0;
ctx.setTransform(new cvs.DOMMatrix([1, -skewA, 0, skewB, 0, skewA]));
ctx.drawImage(front, y + x, x + z - 0.5, front.width, front.height);
}
// head top
x = xOffset;
y = -0.5;
z = zOffset;
ctx.setTransform(new cvs.DOMMatrix([1, -skewA, 1, skewA, 0, 0]));
ctx.drawImage(headTop, y - z, x + z, headTop.width, headTop.height + 1);
// head front
x = xOffset + 8 * scale;
y = 0;
z = zOffset - 0.5;
ctx.setTransform(new cvs.DOMMatrix([1, -skewA, 0, skewB, 0, skewA]));
ctx.drawImage(headFront, y + x, x + z, headFront.width, headFront.height);
// head right
x = xOffset;
y = 0;
z = zOffset;
ctx.setTransform(new cvs.DOMMatrix([1, skewA, 0, skewB, 0, 0]));
ctx.drawImage(headRight, x + y, z - y - 0.5, headRight.width + 0.5, headRight.height + 1);
return canvas.toBuffer();
};
export { render };

@ -0,0 +1,8 @@
const number2Digit = (num: number | string) => {
const chars = num.toString().split('');
const digit = ['', '', '', '', '', '', '', '', '', ''];
const convert = chars.map((char) => digit[parseInt(char)]);
return convert.join('');
};
export { number2Digit };

@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es6", "dom", "es2017"],
"strict": true,
"outDir": "run",
"rootDir": "src",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": [
"src/*"
],
}

@ -0,0 +1,734 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@discordjs/builders@^1.5.0":
version "1.5.0"
resolved "https://registry.npmjs.org/@discordjs/builders/-/builders-1.5.0.tgz"
integrity sha512-7XxT78mnNBPigHn2y6KAXkicxIBFtZREGWaRZ249EC1l6gBUEP8IyVY5JTciIjJArxkF+tg675aZvsTNTKBpmA==
dependencies:
"@discordjs/formatters" "^0.2.0"
"@discordjs/util" "^0.2.0"
"@sapphire/shapeshift" "^3.8.1"
discord-api-types "^0.37.35"
fast-deep-equal "^3.1.3"
ts-mixer "^6.0.3"
tslib "^2.5.0"
"@discordjs/collection@^1.4.0":
version "1.4.0"
resolved "https://registry.npmjs.org/@discordjs/collection/-/collection-1.4.0.tgz"
integrity sha512-hiOJyk2CPFf1+FL3a4VKCuu1f448LlROVuu8nLz1+jCOAPokUcdFAV+l4pd3B3h6uJlJQSASoZzrdyNdjdtfzQ==
"@discordjs/formatters@^0.2.0":
version "0.2.0"
resolved "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.2.0.tgz"
integrity sha512-vn4oMSXuMZUm8ITqVOtvE7/fMMISj4cI5oLsR09PEQXHKeKDAMLltG/DWeeIs7Idfy6V8Fk3rn1e69h7NfzuNA==
dependencies:
discord-api-types "^0.37.35"
"@discordjs/rest@^1.6.0":
version "1.6.0"
resolved "https://registry.npmjs.org/@discordjs/rest/-/rest-1.6.0.tgz"
integrity sha512-HGvqNCZ5Z5j0tQHjmT1lFvE5ETO4hvomJ1r0cbnpC1zM23XhCpZ9wgTCiEmaxKz05cyf2CI9p39+9LL+6Yz1bA==
dependencies:
"@discordjs/collection" "^1.4.0"
"@discordjs/util" "^0.2.0"
"@sapphire/async-queue" "^1.5.0"
"@sapphire/snowflake" "^3.4.0"
discord-api-types "^0.37.35"
file-type "^18.2.1"
tslib "^2.5.0"
undici "^5.20.0"
"@discordjs/util@^0.2.0":
version "0.2.0"
resolved "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz"
integrity sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==
"@mapbox/node-pre-gyp@^1.0.0":
version "1.0.10"
resolved "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz"
integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==
dependencies:
detect-libc "^2.0.0"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.7"
nopt "^5.0.0"
npmlog "^5.0.1"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.11"
"@sapphire/async-queue@^1.5.0":
version "1.5.0"
resolved "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz"
integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==
"@sapphire/shapeshift@^3.8.1":
version "3.8.1"
resolved "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz"
integrity sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw==
dependencies:
fast-deep-equal "^3.1.3"
lodash "^4.17.21"
"@sapphire/snowflake@^3.4.0":
version "3.4.0"
resolved "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.0.tgz"
integrity sha512-zZxymtVO6zeXVMPds+6d7gv/OfnCc25M1Z+7ZLB0oPmeMTPeRWVPQSS16oDJy5ZsyCOLj7M6mbZml5gWXcVRNw==
"@tokenizer/token@^0.3.0":
version "0.3.0"
resolved "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz"
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@types/node-schedule@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/node-schedule/-/node-schedule-2.1.0.tgz#60375640c0509bab963573def9d1f417f438c290"
integrity sha512-NiTwl8YN3v/1YCKrDFSmCTkVxFDylueEqsOFdgF+vPsm+AlyJKGAo5yzX1FiOxPsZiN6/r8gJitYx2EaSuBmmg==
dependencies:
"@types/node" "*"
"@types/node@*":
version "18.15.3"
resolved "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz"
integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==
"@types/ws@^8.5.4":
version "8.5.4"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz"
integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==
dependencies:
"@types/node" "*"
abbrev@1:
version "1.1.1"
resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
agent-base@6:
version "6.0.2"
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
"aproba@^1.0.3 || ^2.0.0":
version "2.0.0"
resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
are-we-there-yet@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz"
integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
axios@^1.3.4:
version "1.3.4"
resolved "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz"
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
busboy@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
dependencies:
streamsearch "^1.1.0"
canvas@^2.11.0:
version "2.11.0"
resolved "https://registry.npmjs.org/canvas/-/canvas-2.11.0.tgz"
integrity sha512-bdTjFexjKJEwtIo0oRx8eD4G2yWoUOXP9lj279jmQ2zMnTQhT8C3512OKz3s+ZOaQlLbE7TuVvRDYDB3Llyy5g==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.0"
nan "^2.17.0"
simple-get "^3.0.3"
chownr@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
color-support@^1.1.2:
version "1.1.3"
resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
cron-parser@^4.2.0:
version "4.8.1"
resolved "https://registry.npmjs.org/cron-parser/-/cron-parser-4.8.1.tgz"
integrity sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==
dependencies:
luxon "^3.2.1"
debug@4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decompress-response@^4.2.0:
version "4.2.1"
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz"
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
dependencies:
mimic-response "^2.0.0"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-libc@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz"
integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
discord-api-types@^0.37.35:
version "0.37.36"
resolved "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.36.tgz"
integrity sha512-Nlxmp10UpVr/utgZ9uODQvG2Or+5w7LFrvFMswyeKC9l/+UaqGT6H0OVgEFhu9GEO4U6K7NNO5W8Carv7irnCA==
discord.js@^14.8.0:
version "14.8.0"
resolved "https://registry.npmjs.org/discord.js/-/discord.js-14.8.0.tgz"
integrity sha512-UOxYtc/YnV7jAJ2gISluJyYeBw4e+j8gWn+IoqG8unaHAVuvZ13DdYN0M1f9fbUgUvSarV798inIrYFtDNDjwQ==
dependencies:
"@discordjs/builders" "^1.5.0"
"@discordjs/collection" "^1.4.0"
"@discordjs/formatters" "^0.2.0"
"@discordjs/rest" "^1.6.0"
"@discordjs/util" "^0.2.0"
"@sapphire/snowflake" "^3.4.0"
"@types/ws" "^8.5.4"
discord-api-types "^0.37.35"
fast-deep-equal "^3.1.3"
lodash.snakecase "^4.1.1"
tslib "^2.5.0"
undici "^5.20.0"
ws "^8.12.1"
dotenv@^16.0.3:
version "16.0.3"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
file-type@^18.2.1:
version "18.2.1"
resolved "https://registry.npmjs.org/file-type/-/file-type-18.2.1.tgz"
integrity sha512-Yw5MtnMv7vgD2/6Bjmmuegc8bQEVA9GmAyaR18bMYWKqsWDG9wgYZ1j4I6gNMF5Y5JBDcUcjRQqNQx7Y8uotcg==
dependencies:
readable-web-to-node-stream "^3.0.2"
strtok3 "^7.0.0"
token-types "^5.0.1"
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
dependencies:
minipass "^3.0.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
gauge@^3.0.0:
version "3.0.2"
resolved "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz"
integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
dependencies:
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.2"
console-control-strings "^1.0.0"
has-unicode "^2.0.1"
object-assign "^4.1.1"
signal-exit "^3.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.2"
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
dependencies:
agent-base "6"
debug "4"
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
lodash.snakecase@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
long-timeout@0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz"
integrity sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
luxon@^3.2.1:
version "3.3.0"
resolved "https://registry.npmjs.org/luxon/-/luxon-3.3.0.tgz"
integrity sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==
make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
dependencies:
semver "^6.0.0"
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mimic-response@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
minecraft-motd-util@^1.1.9:
version "1.1.12"
resolved "https://registry.npmjs.org/minecraft-motd-util/-/minecraft-motd-util-1.1.12.tgz"
integrity sha512-5TuTRjrRupSTruea0nRC37r0FdhkS1O4wIJKAYfwJRCQd/X4Zyl/dVIs96h9UVW6N8jhIuz9pNkrDsqyN7VBdA==
minecraft-server-util@^5.4.0:
version "5.4.0"
resolved "https://registry.npmjs.org/minecraft-server-util/-/minecraft-server-util-5.4.0.tgz"
integrity sha512-KvwS125IxSK5vWvYp2mMuotRCZkHPkEL/iuHcfeDdBtn+GvvSdIgln/Ucy/+yyE9WN64Y64HluSHjd4W8Mlz1Q==
dependencies:
minecraft-motd-util "^1.1.9"
minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minipass@^3.0.0:
version "3.3.6"
resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^4.0.0:
version "4.2.5"
resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz"
integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==
minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
dependencies:
minipass "^3.0.0"
yallist "^4.0.0"
mkdirp@^1.0.3:
version "1.0.4"
resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nan@^2.17.0:
version "2.17.0"
resolved "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz"
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
node-fetch@^2.6.7:
version "2.6.9"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz"
integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==
dependencies:
whatwg-url "^5.0.0"
node-schedule@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-2.1.1.tgz#6958b2c5af8834954f69bb0a7a97c62b97185de3"
integrity sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==
dependencies:
cron-parser "^4.2.0"
long-timeout "0.1.1"
sorted-array-functions "^1.3.0"
nopt@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz"
integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
dependencies:
abbrev "1"
npmlog@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz"
integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
dependencies:
are-we-there-yet "^2.0.0"
console-control-strings "^1.1.0"
gauge "^3.0.0"
set-blocking "^2.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
once@^1.3.0, once@^1.3.1:
version "1.4.0"
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
peek-readable@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz"
integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
readable-stream@^3.6.0:
version "3.6.2"
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-web-to-node-stream@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz"
integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
dependencies:
readable-stream "^3.6.0"
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
semver@^6.0.0:
version "6.3.0"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.5:
version "7.3.8"
resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
signal-exit@^3.0.0:
version "3.0.7"
resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
simple-concat@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
simple-get@^3.0.3:
version "3.1.1"
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz"
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
dependencies:
decompress-response "^4.2.0"
once "^1.3.1"
simple-concat "^1.0.0"
sorted-array-functions@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz"
integrity sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==
streamsearch@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strtok3@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz"
integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==
dependencies:
"@tokenizer/token" "^0.3.0"
peek-readable "^5.0.0"
tar@^6.1.11:
version "6.1.13"
resolved "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz"
integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^4.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
token-types@^5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz"
integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==
dependencies:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
ts-mixer@^6.0.3:
version "6.0.3"
resolved "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz"
integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==
tslib@^2.5.0:
version "2.5.0"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
typescript@^5.0.2:
version "5.0.2"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz"
integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==
undici@^5.20.0:
version "5.21.0"
resolved "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz"
integrity sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==
dependencies:
busboy "^1.6.0"
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
wide-align@^1.1.2:
version "1.1.5"
resolved "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
wrappy@1:
version "1.0.2"
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
ws@^8.12.1:
version "8.13.0"
resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz"
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
Loading…
Cancel
Save