From d97729064654cb56907d70c2b460034278331aeb Mon Sep 17 00:00:00 2001 From: inmake Date: Tue, 17 Feb 2026 15:35:40 +0500 Subject: [PATCH] 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. --- node-session-server.zip | Bin 0 -> 38305 bytes package.json | 3 + src/index.ts | 241 ++++++++++++++++++++---------------- src/models/SessionServer.ts | 17 ++- src/routes/sendMessage.ts | 6 +- yarn.lock | 92 +++++++++++++- 6 files changed, 250 insertions(+), 109 deletions(-) create mode 100644 node-session-server.zip diff --git a/node-session-server.zip b/node-session-server.zip new file mode 100644 index 0000000000000000000000000000000000000000..227ebba4b78047629c36f36f5dafbf11f28e5130 GIT binary patch literal 38305 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 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) { 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 = {}; - 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); diff --git a/src/models/SessionServer.ts b/src/models/SessionServer.ts index 428fc18..f207b7c 100644 --- a/src/models/SessionServer.ts +++ b/src/models/SessionServer.ts @@ -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: { diff --git a/src/routes/sendMessage.ts b/src/routes/sendMessage.ts index 27892fd..872cd68 100644 --- a/src/routes/sendMessage.ts +++ b/src/routes/sendMessage.ts @@ -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); diff --git a/yarn.lock b/yarn.lock index b435781..c7ade91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"