mirror of
https://github.com/Cyclenerd/meshcore-bot.git
synced 2026-03-28 17:42:46 +01:00
Add Repeater Telemetry (#4)
* get repeater telemetry * store voltage to csv
This commit is contained in:
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -33,5 +33,8 @@ jobs:
|
|||||||
- name: Dependencies
|
- name: Dependencies
|
||||||
run: npm ci --production
|
run: npm ci --production
|
||||||
|
|
||||||
|
- name: Help
|
||||||
|
run: node meshcore-bot.js --help
|
||||||
|
|
||||||
- name: Smoke Test
|
- name: Smoke Test
|
||||||
run: node meshcore-bot.js "gibtesnicht" | grep "cannot open gibtesnicht"
|
run: node meshcore-bot.js --port "gibtesnicht" | grep "cannot open gibtesnicht"
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
*.csv
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -1,6 +1,14 @@
|
|||||||
# 🤖 MeshCore Bot
|
# 🤖 MeshCore Bot
|
||||||
|
|
||||||
This script is a command bot that connects to a [MeshCore](https://github.com/meshcore-dev/MeshCore) companion radio device via serial connection and responds to commands received in private channels.
|
This script is a command bot that connects to a [MeshCore](https://github.com/meshcore-dev/MeshCore) companion radio device via USB serial connection and responds to commands received in private channels.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> To prevent spam in public channels, this bot only responds in private channels!
|
||||||
|
|
||||||
|
The bot is also able to fetch and log telemetry sensor (currently only voltage) data from a repeater node.
|
||||||
|
The telemetry data is logged to a Comma-Separated Values (CSV) file.
|
||||||
|
The interval at which the telemetry data is fetched can be configured.
|
||||||
|
This bot is ideal for testing MeshCore setup with repeater and distance of communication.
|
||||||
|
|
||||||
| Client | Bot |
|
| Client | Bot |
|
||||||
|--------|-----|
|
|--------|-----|
|
||||||
@@ -32,17 +40,34 @@ This script is a command bot that connects to a [MeshCore](https://github.com/me
|
|||||||
To run the bot use the following command:
|
To run the bot use the following command:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
node meshcore-bot.js [SERIAL_PORT]
|
node meshcore-bot.js --port [SERIAL_PORT] --repeater-public-key-prefix [REPEATER_PUBLIC_KEY_PREFIX] --repeater-password [REPEATER_PASSWORD] --telemetry-interval [TELEMETRY_INTERVAL_MINUTES] --csv [CSV_FILE]
|
||||||
```
|
```
|
||||||
|
|
||||||
- `[SERIAL_PORT]` is optional. If not provided, the script will default to `/dev/cu.usbmodem1101`.
|
- `--port` or `-s` (optional): The serial port of the MeshCore device. Defaults to `/dev/cu.usbmodem1101`.
|
||||||
|
- `--repeater-public-key-prefix` or `-r` (optional): The public key prefix of a repeater node to fetch telemetry from. If provided, the telemetry feature is enabled.
|
||||||
|
- `--repeater-password` or `-p` (optional): The password for the repeater. By default, this is an empty string.
|
||||||
|
- `--telemetry-interval` or `-t` (optional): The interval in minutes at which telemetry data is retrieved from the repeater. The default value is `15`.
|
||||||
|
- `--csv` or `-c` (optional): The CSV file in which the repeater's telemetry data is to be logged. If this file is specified, the telemetry data will be logged in this file.
|
||||||
|
|
||||||
### Example
|
### Examples
|
||||||
|
|
||||||
|
**Basic:**
|
||||||
```bash
|
```bash
|
||||||
node meshcore-bot.js "/dev/ttyUSB0"
|
node meshcore-bot.js --port "/dev/ttyUSB0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**With Repeater Telemetry:**
|
||||||
|
```bash
|
||||||
|
node meshcore-bot.js --port "/dev/ttyUSB0" --repeater-public-key-prefix "935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d" --repeater-password "your-password" --telemetry-interval 5
|
||||||
|
```
|
||||||
|
This will connect to the device on `/dev/ttyUSB0` and fetch telemetry from the specified repeater every 5 minutes.
|
||||||
|
|
||||||
|
**With Repeater Telemetry and CSV Logging:**
|
||||||
|
```bash
|
||||||
|
node meshcore-bot.js --port "/dev/ttyUSB0" --repeater-public-key-prefix "935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d" --repeater-password "your-password" --telemetry-interval 5 --csv "telemetry.csv"
|
||||||
|
```
|
||||||
|
This will do the same as the previous example, but it will also log the telemetry data to `telemetry.csv`.
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
- `.ping`: The bot will respond with "PONG! 🏓 (*hop count*)".
|
- `.ping`: The bot will respond with "PONG! 🏓 (*hop count*)".
|
||||||
|
|||||||
180
meshcore-bot.js
180
meshcore-bot.js
@@ -1,14 +1,91 @@
|
|||||||
import { Constants, NodeJSSerialConnection } from "@liamcottle/meshcore.js";
|
import { Constants, NodeJSSerialConnection } from "@liamcottle/meshcore.js";
|
||||||
|
import yargs from 'yargs';
|
||||||
|
import { hideBin } from 'yargs/helpers';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
class LPPDecoder {
|
||||||
|
// Decode Cayenne Low Power Payload (LPP) for LoraWan
|
||||||
|
constructor() {
|
||||||
|
this.sensors = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
decode(data) {
|
||||||
|
const buffer = Buffer.from(data);
|
||||||
|
let i = 0;
|
||||||
|
while (i < buffer.length) {
|
||||||
|
const channel = buffer[i++];
|
||||||
|
const type = buffer[i++];
|
||||||
|
switch (type) {
|
||||||
|
// Source: https://discord.com/channels/1343693475589263471/1391673743453192242/1395240557176950876
|
||||||
|
case 0x74: { // static const LPP_VOLTAGE = 116;
|
||||||
|
const name = "voltage";
|
||||||
|
this.sensors.push({ channel, type, name, value: buffer.readInt16BE(i) / 100 });
|
||||||
|
i += 2; // 2 bytes 0.01V unsigned
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
i = buffer.length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.sensors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const argv = yargs(hideBin(process.argv))
|
||||||
|
.option('port', {
|
||||||
|
alias: 's',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Serial port to connect to',
|
||||||
|
default: '/dev/cu.usbmodem1101'
|
||||||
|
})
|
||||||
|
.option('repeaterPublicKeyPrefix', {
|
||||||
|
alias: 'r',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Public key prefix of the repeater to fetch telemetry from'
|
||||||
|
})
|
||||||
|
.option('telemetryInterval', {
|
||||||
|
alias: 't',
|
||||||
|
type: 'number',
|
||||||
|
description: 'Telemetry interval in minutes',
|
||||||
|
default: 15
|
||||||
|
})
|
||||||
|
.option('repeaterPassword', {
|
||||||
|
alias: 'p',
|
||||||
|
type: 'string',
|
||||||
|
description: 'Repeater password',
|
||||||
|
default: ''
|
||||||
|
})
|
||||||
|
.option('csv', {
|
||||||
|
alias: 'c',
|
||||||
|
type: 'string',
|
||||||
|
description: 'CSV file to log telemetry to'
|
||||||
|
})
|
||||||
|
.argv;
|
||||||
|
|
||||||
// get port from cli arguments
|
// get port from cli arguments
|
||||||
/*eslint no-undef: "off"*/
|
/*eslint no-undef: "off"*/
|
||||||
const port = process.argv[2] || "/dev/cu.usbmodem1101";
|
const port = argv.port;
|
||||||
|
const repeaterPublicKeyPrefix = argv.repeaterPublicKeyPrefix;
|
||||||
|
const repeaterPassword = argv.repeaterPassword;
|
||||||
|
const telemetryIntervalMinutes = argv.telemetryInterval;
|
||||||
|
const telemetryIntervalMs = telemetryIntervalMinutes * 60 * 1000;
|
||||||
|
const csvFile = argv.csv;
|
||||||
|
|
||||||
console.log(`Connecting to ${port}`);
|
console.log(`Connecting to ${port}`);
|
||||||
|
if(repeaterPublicKeyPrefix){
|
||||||
|
console.log(`Repeater public key prefix: ${repeaterPublicKeyPrefix}`);
|
||||||
|
console.log(`Telemetry interval: ${telemetryIntervalMinutes} minutes`);
|
||||||
|
if (csvFile) {
|
||||||
|
console.log(`Logging telemetry to: ${csvFile}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create connection
|
// create connection
|
||||||
const connection = new NodeJSSerialConnection(port);
|
const connection = new NodeJSSerialConnection(port);
|
||||||
|
|
||||||
let reconnectInterval;
|
let reconnectInterval;
|
||||||
|
let telemetryInterval;
|
||||||
|
|
||||||
// wait until connected
|
// wait until connected
|
||||||
connection.on("connected", async () => {
|
connection.on("connected", async () => {
|
||||||
@@ -16,15 +93,56 @@ connection.on("connected", async () => {
|
|||||||
// we are now connected
|
// we are now connected
|
||||||
console.log("Connected");
|
console.log("Connected");
|
||||||
|
|
||||||
|
// update clock on meshcore device
|
||||||
|
console.log("Sync Clock...");
|
||||||
|
try {
|
||||||
|
await connection.syncDeviceTime();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error syncing device time", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log contacts
|
||||||
|
console.log("Get Contacts...");
|
||||||
|
try {
|
||||||
|
const contacts = await connection.getContacts();
|
||||||
|
//console.log(`Contacts:`, contacts);
|
||||||
|
for(const contact of contacts) {
|
||||||
|
const typeNames = ["None", "Contact", "Repeater", "Room"];
|
||||||
|
const typeName = typeNames[contact.type] || "Unknown";
|
||||||
|
console.log(`${typeName}: ${contact.advName}; Public Key: ${Buffer.from(contact.publicKey).toString('hex')}`);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error retrieving contacts", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log channels
|
||||||
|
console.log("Get Channels...");
|
||||||
|
try {
|
||||||
|
const channels = await connection.getChannels();
|
||||||
|
//console.log(`Channels:`, channels);
|
||||||
|
for(const channel of channels) {
|
||||||
|
if (channel.name) {
|
||||||
|
console.log(`${channel.channelIdx}: ${channel.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error retrieving channels", e);
|
||||||
|
}
|
||||||
|
|
||||||
// clear reconnect interval if it exists
|
// clear reconnect interval if it exists
|
||||||
if (reconnectInterval) {
|
if (reconnectInterval) {
|
||||||
clearInterval(reconnectInterval);
|
clearInterval(reconnectInterval);
|
||||||
reconnectInterval = null;
|
reconnectInterval = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update clock on meshcore device
|
if(repeaterPublicKeyPrefix){
|
||||||
await connection.syncDeviceTime();
|
// Start telemetry fetching interval
|
||||||
|
if (telemetryInterval) {
|
||||||
|
clearInterval(telemetryInterval);
|
||||||
|
}
|
||||||
|
telemetryInterval = setInterval(() => getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword), telemetryIntervalMs);
|
||||||
|
getRepeaterTelemetry(repeaterPublicKeyPrefix, repeaterPassword); // Also fetch immediately on connect
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// auto reconnect on disconnect
|
// auto reconnect on disconnect
|
||||||
@@ -36,6 +154,11 @@ connection.on("disconnected", () => {
|
|||||||
reconnectInterval = setInterval(async () => {
|
reconnectInterval = setInterval(async () => {
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
|
|
||||||
|
if (telemetryInterval) {
|
||||||
|
clearInterval(telemetryInterval);
|
||||||
|
telemetryInterval = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// listen for new messages
|
// listen for new messages
|
||||||
@@ -50,17 +173,17 @@ connection.on(Constants.PushCodes.MsgWaiting, async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
console.log(e);
|
console.error("Message could not be retrieved", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function onContactMessageReceived(message) {
|
async function onContactMessageReceived(message) {
|
||||||
console.log("[" + (new Date()).toISOString() + "] Contact message", message);
|
console.log(`[${new Date().toISOString()}] Contact message`, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onChannelMessageReceived(message) {
|
async function onChannelMessageReceived(message) {
|
||||||
message.senderTimestampISO = (new Date(message.senderTimestamp * 1000)).toISOString();
|
message.senderTimestampISO = (new Date(message.senderTimestamp * 1000)).toISOString();
|
||||||
console.log("[" + (new Date()).toISOString() + "] Channel message", message);
|
console.log(`[${new Date().toISOString()}] Channel message`, message);
|
||||||
// handle commands only in own channels, not in public channel with id 0
|
// handle commands only in own channels, not in public channel with id 0
|
||||||
if(message.channelIdx > 0){
|
if(message.channelIdx > 0){
|
||||||
if(message.text.includes(".ping")){
|
if(message.text.includes(".ping")){
|
||||||
@@ -74,6 +197,49 @@ async function onChannelMessageReceived(message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getRepeaterTelemetry(publicKeyPrefix, repeaterPassword) {
|
||||||
|
console.log("Fetching repeater telemetry...");
|
||||||
|
try {
|
||||||
|
const contact = await connection.findContactByPublicKeyPrefix(Buffer.from(publicKeyPrefix, "hex"));
|
||||||
|
if(!contact){
|
||||||
|
console.log("Repeater contact not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// login to repeater and get repeater telemetry
|
||||||
|
console.log("Logging in to repeater...");
|
||||||
|
await connection.login(contact.publicKey, repeaterPassword);
|
||||||
|
console.log("Fetching telemetry...");
|
||||||
|
const telemetry = await connection.getTelemetry(contact.publicKey);
|
||||||
|
//console.log("Repeater telemetry", telemetry);
|
||||||
|
if (telemetry.lppSensorData) {
|
||||||
|
try {
|
||||||
|
const lpp = new LPPDecoder();
|
||||||
|
const decoded = lpp.decode(telemetry.lppSensorData);
|
||||||
|
//console.log("Decoded repeater telemetry", decoded);
|
||||||
|
for (const sensor of decoded) {
|
||||||
|
if (sensor.name === "voltage") {
|
||||||
|
console.log(`Voltage: ${sensor.value} V`);
|
||||||
|
if (csvFile) {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const csvRow = `${timestamp},${sensor.value}\n`;
|
||||||
|
if (!fs.existsSync(csvFile)) {
|
||||||
|
fs.writeFileSync(csvFile, 'timestamp,voltage\n');
|
||||||
|
}
|
||||||
|
fs.appendFileSync(csvFile, csvRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error decoding repeater telemetry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch(e) {
|
||||||
|
console.error("Error fetching repeater telemetry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// connect to meshcore device
|
// connect to meshcore device
|
||||||
try {
|
try {
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|||||||
152
package-lock.json
generated
152
package-lock.json
generated
@@ -1,11 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "meshcore-bot",
|
"name": "meshcore-bot",
|
||||||
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@liamcottle/meshcore.js": "^1.6.0"
|
"@liamcottle/meshcore.js": "^1.6.0",
|
||||||
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.34.0",
|
"@eslint/js": "^9.34.0",
|
||||||
@@ -529,11 +532,19 @@
|
|||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
@@ -597,11 +608,24 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.1",
|
||||||
|
"wrap-ansi": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
@@ -614,7 +638,6 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
@@ -663,6 +686,21 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/escalade": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-string-regexp": {
|
"node_modules/escape-string-regexp": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||||
@@ -903,6 +941,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/glob-parent": {
|
"node_modules/glob-parent": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
@@ -986,6 +1033,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"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==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-glob": {
|
"node_modules/is-glob": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
@@ -1236,6 +1292,15 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve-from": {
|
"node_modules/resolve-from": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||||
@@ -1297,6 +1362,32 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-json-comments": {
|
"node_modules/strip-json-comments": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||||
@@ -1372,6 +1463,59 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "5.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "17.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||||
|
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^8.0.1",
|
||||||
|
"escalade": "^3.1.1",
|
||||||
|
"get-caller-file": "^2.0.5",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"string-width": "^4.2.3",
|
||||||
|
"y18n": "^5.0.5",
|
||||||
|
"yargs-parser": "^21.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "21.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||||
|
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node meshcore-bot.js"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@liamcottle/meshcore.js": "^1.6.0"
|
"@liamcottle/meshcore.js": "^1.6.0",
|
||||||
|
"yargs": "^17.7.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.34.0",
|
"@eslint/js": "^9.34.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user