Enhance server monitoring and messaging functionality. Added new dependencies for date handling and system information. Updated session server model to track GPU and CPU metrics. Improved session management with better process argument handling and local IP retrieval. Refactored sendMessage route to use login for authentication.

This commit is contained in:
2026-02-17 15:35:40 +05:00
parent c8ce49acd6
commit d977290646
6 changed files with 250 additions and 109 deletions
Binary file not shown.
+3
View File
@@ -10,12 +10,15 @@
"dependencies": {
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"date-fns": "^4.1.0",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.0",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.15",
"systeminformation": "^5.31.0",
"telegraf": "^4.16.3",
"tree-kill": "^1.2.2"
},
"devDependencies": {
+137 -104
View File
@@ -1,41 +1,76 @@
import "dotenv/config";
import connectDB from "./config/db";
import express, { json } from "express";
import { exec, execFile } from "child_process";
import cors from "cors";
import express, { json } from "express";
import os, { networkInterfaces } from "os";
import util from "util";
import { exec, execFile } from "child_process";
import treeKill from "tree-kill";
import SessionServer from "./models/SessionServer";
import ActiveSession from "./models/ActiveSession";
import { schedule } from "node-cron";
import si from "systeminformation";
import treeKill from "tree-kill";
import connectDB from "./config/db";
import ActiveSession from "./models/ActiveSession";
import SessionServer from "./models/SessionServer";
import sendMessageRoute from "./routes/sendMessage";
import ServerStatusLog from "./models/ServerStatusLog";
// Constants
const BUILDS_PATH = "C:/pixel-streaming/builds";
const CIRRUS_SERVER_PATH = "C:/pixel-streaming/signalling-server/cirrus.js";
const CIRRUS_PORTS = [14000, 14001, 14002] as const;
const PORT_OFFSET = 10;
const GPU_POLL_INTERVAL_MS = 1000;
const UE_PROCESS_ARGS = [
"-RenderOffScreen",
"-ForceRes",
"-ResX=1920",
"-ResY=1080",
"-Unattended",
"-PixelStreamingWebRTCMinBitrate=5000000",
"-PixelStreamingWebRTCMaxBitrate=20000000",
"-PixelStreamingH264Profile=HIGH",
"-PixelStreamingWebRTCDisableReceiveAudio=true",
"-PixelStreamingEncoderRateControl=VBR",
] as const;
function getUeProcessArgs(uePort: number): string[] {
return [
"-PixelStreamingIP=127.0.0.1",
`-PixelStreamingPort=${uePort}`,
...UE_PROCESS_ARGS,
];
}
connectDB();
const app = express();
const port = process.env.PORT;
const port = process.env.PORT ?? 3000;
app.use(json());
app.use(cors());
app.use("/sendMessage", sendMessageRoute);
const serverLocation = process.env.SERVER_LOCATION;
const serverName = process.env.SERVER_NAME;
const serverType = process.env.SERVER_TYPE;
const serverHostname = os.hostname();
const serverConfig = {
location: process.env.SERVER_LOCATION,
name: process.env.SERVER_NAME,
type: process.env.SERVER_TYPE,
hostname: os.hostname(),
};
const nets = networkInterfaces();
const localIP = Object.entries(nets)
.find((net) => net[0].includes("Ethernet"))?.[1]
?.find((obj) => obj.family === "IPv4")?.address;
function getLocalIP(): string | undefined {
return Object.entries(nets)
.find((net) => net[0].includes("Ethernet"))?.[1]
?.find((obj) => obj.family === "IPv4")?.address;
}
async function updateSessionServerData(data: any) {
async function updateSessionServerData(data: Record<string, unknown>) {
try {
await SessionServer.findOneAndUpdate({ hostname: serverHostname }, data);
await SessionServer.findOneAndUpdate(
{ hostname: serverConfig.hostname },
data
);
} catch (error) {
if (error instanceof Error) {
console.log("Error: ", error.message);
@@ -45,42 +80,66 @@ async function updateSessionServerData(data: any) {
const execAsync = util.promisify(exec);
async function getGpuMemoryFree() {
async function getSystemStats() {
try {
const { stdout } = await execAsync(
"nvidia-smi --query-gpu=memory.free --format=csv,noheader,nounits",
{ windowsHide: true }
);
const data: Record<string, unknown> = {};
const gpuMemoryFree = stdout.trimEnd();
// RAM (os)
const ramTotalMB = Math.round(os.totalmem() / 1024 / 1024);
const ramUsedMB = ramTotalMB - Math.round(os.freemem() / 1024 / 1024);
data.ramTotal = ramTotalMB;
data.ramUsed = ramUsedMB;
await updateSessionServerData({ gpuMemoryFree });
// CPU (systeminformation)
const cpuLoad = await si.currentLoad();
data.cpuUtilization = Math.round(cpuLoad.currentLoad);
// GPU (nvidia-smi)
try {
const { stdout } = await execAsync(
"nvidia-smi --query-gpu=memory.total,memory.used,utilization.gpu --format=csv,noheader,nounits",
{ windowsHide: true }
);
const values = stdout
.trim()
.split("\n")[0]
.split(",")
.map((v) => parseInt(v.trim(), 10));
const [gpuTotal, gpuUsed, gpuUtilization] = values;
if (!Number.isNaN(gpuTotal) && !Number.isNaN(gpuUsed)) {
data.gpuMemoryTotal = gpuTotal;
data.gpuMemoryUsed = gpuUsed;
if (!Number.isNaN(gpuUtilization)) {
data.gpuUtilization = gpuUtilization;
}
}
} catch {
// GPU unavailable, CPU/RAM still updated
}
await updateSessionServerData(data);
} catch (error) {
if (error instanceof Error) {
console.log("Error: ", error.message);
}
}
setTimeout(async () => {
await getGpuMemoryFree();
}, 1000);
setTimeout(getSystemStats, GPU_POLL_INTERVAL_MS);
}
async function getAvailablePorts() {
const cirrusPorts = [14000, 14001, 14002];
for (const cirrusPort of cirrusPorts) {
for (const cirrusPort of CIRRUS_PORTS) {
const activeSession = await ActiveSession.exists({
location: serverLocation,
name: serverName,
location: serverConfig.location,
name: serverConfig.name,
cirrusPort,
});
if (activeSession) continue;
const uePort = cirrusPort + 10;
return { cirrusPort, uePort };
return { cirrusPort, uePort: cirrusPort + PORT_OFFSET };
}
}
@@ -89,8 +148,7 @@ async function startSession(
ownerIp: string,
endAt?: string
) {
const filePath = `C:/pixel-streaming/builds/${buildName}/${buildName}.exe`;
const filePath = `${BUILDS_PATH}/${buildName}/${buildName}.exe`;
const availablePorts = await getAvailablePorts();
if (!availablePorts) {
@@ -101,11 +159,11 @@ async function startSession(
const { cirrusPort, uePort } = availablePorts;
const cirrusProcess = execFile("node", [
`C:/pixel-streaming/signalling-server/cirrus.js`,
`--StreamerPort`,
`${uePort}`,
`--HttpPort`,
`${cirrusPort}`,
CIRRUS_SERVER_PATH,
"--StreamerPort",
String(uePort),
"--HttpPort",
String(cirrusPort),
]);
const cirrusProcessId = cirrusProcess.pid;
@@ -115,38 +173,24 @@ async function startSession(
return;
}
const ueProcess = execFile(filePath, [
"-PixelStreamingIP=127.0.0.1",
`-PixelStreamingPort=${uePort}`,
"-RenderOffScreen",
"-ForceRes",
"-ResX=1920",
"-ResY=1080",
"-Unattended",
"-PixelStreamingWebRTCMinBitrate=5000000",
"-PixelStreamingWebRTCMaxBitrate=20000000",
"-PixelStreamingH264Profile=HIGH",
"-PixelStreamingWebRTCDisableReceiveAudio=true",
"-PixelStreamingEncoderRateControl=VBR",
// "-PixelStreamingHudStats=true",
// `-SessionID=${session.id}`,
]);
const ueProcess = execFile(filePath, getUeProcessArgs(uePort));
const ueProcessId = ueProcess.pid;
if (!ueProcessId) {
console.log("UE application was not started");
treeKill(cirrusProcessId);
return;
}
const type = serverType;
const localIP = getLocalIP();
try {
const activeSession = await ActiveSession.create({
location: serverLocation,
name: serverName,
location: serverConfig.location,
name: serverConfig.name,
buildName,
type,
type: serverConfig.type,
cirrusPort,
uePort,
cirrusProcessId,
@@ -187,24 +231,26 @@ async function endSession(activeSessionId: string) {
async function init() {
try {
await ServerStatusLog.create({
hostname: serverHostname,
action: "online",
});
// await ServerStatusLog.create({
// hostname: serverHostname,
// action: "online",
// });
const localIP = getLocalIP();
await SessionServer.findOneAndUpdate(
{ hostname: serverHostname },
{ hostname: serverConfig.hostname },
{
location: serverLocation,
name: serverName,
type: serverType,
hostname: serverHostname,
location: serverConfig.location,
name: serverConfig.name,
type: serverConfig.type,
hostname: serverConfig.hostname,
localIP,
},
{ upsert: true, new: true }
);
getGpuMemoryFree();
getSystemStats();
} catch (error) {
if (error instanceof Error) {
console.log("Error: ", error.message);
@@ -247,52 +293,39 @@ app.listen(port, () => {
init();
});
schedule("*/3 * * * * *", async () => {
// TODO - hostname
async function checkAndRestartUeProcesses() {
try {
const activeSessions = await ActiveSession.find({
location: serverLocation,
name: serverName,
location: serverConfig.location,
name: serverConfig.name,
});
for (const activeSession of activeSessions) {
const { ueProcessId, buildName, uePort, id } = activeSession;
if (uePort == null || !buildName) continue;
const { stdout } = await execAsync(
`wmic process where processId=${ueProcessId}`,
{ windowsHide: true }
);
const ueProcessInfo = stdout.trim();
if (ueProcessInfo) continue;
if (ueProcessInfo) return;
const filePath = `${BUILDS_PATH}/${buildName}/${buildName}.exe`;
const newUeProcess = execFile(filePath, getUeProcessArgs(uePort));
const newUeProcessId = newUeProcess.pid;
const filePath = `C:/pixel-streaming/builds/${buildName}/${buildName}.exe`;
const newUeProcess = execFile(filePath, [
"-PixelStreamingIP=127.0.0.1",
`-PixelStreamingPort=${uePort}`,
"-RenderOffScreen",
"-ForceRes",
"-ResX=1920",
"-ResY=1080",
"-Unattended",
"-PixelStreamingWebRTCMinBitrate=5000000",
"-PixelStreamingWebRTCMaxBitrate=20000000",
"-PixelStreamingH264Profile=HIGH",
"-PixelStreamingWebRTCDisableReceiveAudio=true",
"-PixelStreamingEncoderRateControl=VBR",
// "-PixelStreamingHudStats=true",
// `-SessionID=${session.id}`,
]);
const newUeProcesscessId = newUeProcess.pid;
await ActiveSession.findByIdAndUpdate(id, {
ueProcessId: newUeProcesscessId,
});
if (newUeProcessId) {
await ActiveSession.findByIdAndUpdate(id, {
ueProcessId: newUeProcessId,
});
}
}
} catch (error) {
console.log(error);
console.error("checkAndRestartUeProcesses:", error);
}
});
}
schedule("*/3 * * * * *", checkAndRestartUeProcesses);
+16 -1
View File
@@ -15,7 +15,22 @@ const sessionServerSchema = new Schema(
type: String,
unique: true,
},
gpuMemoryFree: {
gpuMemoryTotal: {
type: Number,
},
gpuMemoryUsed: {
type: Number,
},
gpuUtilization: {
type: Number,
},
cpuUtilization: {
type: Number,
},
ramTotal: {
type: Number,
},
ramUsed: {
type: Number,
},
localIP: {
+4 -2
View File
@@ -13,7 +13,7 @@ router.post("/", async (req, res) => {
port: req.body.port,
secure: true, // true for 465, false for other ports
auth: {
user: req.body.from, // generated ethereal user
user: req.body.login, // generated ethereal user
pass: req.body.password, // generated ethereal password
},
});
@@ -21,7 +21,7 @@ router.post("/", async (req, res) => {
const files = fs.readdirSync(req.body.path);
// send mail with defined transport object
await transporter.sendMail({
const result = await transporter.sendMail({
from: req.body.from, // sender address
to: req.body.to, // list of receivers
subject: req.body.subject, // Subject line
@@ -29,6 +29,8 @@ router.post("/", async (req, res) => {
attachments: files.map((file) => ({ path: `${req.body.path}/${file}` })), // attachment files
});
console.log(result);
return res.json({ ok: 1 });
} catch (error) {
console.log(error);
+90 -2
View File
@@ -49,6 +49,11 @@
dependencies:
sparse-bitfield "^3.0.3"
"@telegraf/types@^7.1.0":
version "7.1.0"
resolved "https://registry.yarnpkg.com/@telegraf/types/-/types-7.1.0.tgz#d8bd9b2f5070b4de46971416e890338cd89fc23d"
integrity sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==
"@tsconfig/node10@^1.0.7":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
@@ -203,6 +208,13 @@ abbrev@1:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
@@ -320,11 +332,29 @@ bson@^6.2.0:
resolved "https://registry.yarnpkg.com/bson/-/bson-6.4.0.tgz#99c2e37b515e6766ce8b5929e01fa79de5767d50"
integrity sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==
buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
buffer-alloc@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
dependencies:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
@@ -411,6 +441,11 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
date-fns@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -425,6 +460,13 @@ debug@4, debug@4.x, debug@^4:
dependencies:
ms "2.1.2"
debug@^4.3.4:
version "4.4.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a"
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
dependencies:
ms "^2.1.3"
define-data-property@^1.1.2:
version "1.1.4"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
@@ -508,6 +550,11 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
event-target-shim@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
express@^4.18.2:
version "4.18.3"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.3.tgz#6870746f3ff904dee1819b82e4b51509afffb0d4"
@@ -964,6 +1011,11 @@ mquery@5.0.0:
dependencies:
debug "4.x"
mri@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -974,7 +1026,7 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3, ms@^2.1.1:
ms@2.1.3, ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
@@ -996,7 +1048,7 @@ node-cron@^3.0.3:
dependencies:
uuid "8.3.2"
node-fetch@^2.6.7:
node-fetch@^2.6.7, node-fetch@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
@@ -1077,6 +1129,11 @@ once@^1.3.0:
dependencies:
wrappy "1"
p-timeout@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-4.1.0.tgz#788253c0452ab0ffecf18a62dff94ff1bd09ca0a"
integrity sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
@@ -1165,11 +1222,23 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-compare@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/safe-compare/-/safe-compare-1.1.4.tgz#5e0128538a82820e2e9250cd78e45da6786ba593"
integrity sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==
dependencies:
buffer-alloc "^1.2.0"
"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sandwich-stream@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/sandwich-stream/-/sandwich-stream-2.0.2.tgz#6d1feb6cf7e9fe9fadb41513459a72c2e84000fa"
integrity sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==
semver@^6.0.0:
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
@@ -1302,6 +1371,11 @@ supports-color@^5.5.0:
dependencies:
has-flag "^3.0.0"
systeminformation@^5.31.0:
version "5.31.0"
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.31.0.tgz#fce21969b080e6971accf2d601b78f17271d7715"
integrity sha512-z5pjzvC8UnQJ/iu34z+mo3lAeMzTGdArjPQoG5uPyV5XY4BY+M6ZcRTl4XnZqudz6sP713LhWMKv6e0kGFGCgQ==
tar@^6.1.11:
version "6.2.0"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
@@ -1314,6 +1388,20 @@ tar@^6.1.11:
mkdirp "^1.0.3"
yallist "^4.0.0"
telegraf@^4.16.3:
version "4.16.3"
resolved "https://registry.yarnpkg.com/telegraf/-/telegraf-4.16.3.tgz#f03fa30482b540a7f9895af8f13ec8f432840a66"
integrity sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==
dependencies:
"@telegraf/types" "^7.1.0"
abort-controller "^3.0.0"
debug "^4.3.4"
mri "^1.2.0"
node-fetch "^2.7.0"
p-timeout "^4.1.0"
safe-compare "^1.1.4"
sandwich-stream "^2.0.2"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"