mirror of
https://github.com/recrof/map.meshcore.dev-uploader.git
synced 2026-03-28 17:42:45 +01:00
init
This commit is contained in:
20
README.md
Normal file
20
README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# MeshCore map auto uploader
|
||||||
|
|
||||||
|
## Description
|
||||||
|
This bot will upload every repeater or room server to the map when companion hears new advert
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
You will need Meshcore device with Companion USB firmware connected to the computer with internet connection
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. [Install Node.js 22 or higher](https://nodejs.org/en/download/)(most recent LTS recommended)
|
||||||
|
2. Clone this repo & install dependencies via npm
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/recrof/map.meshcore.dev-uploader
|
||||||
|
cd map.meshcore.dev-uploader
|
||||||
|
npm install .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
1. Connect working MeshCore companion usb into the computer
|
||||||
|
2. run `node index.mjs [usb_port]`
|
||||||
106
index.mjs
Normal file
106
index.mjs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import {
|
||||||
|
BufferUtils, Packet, Constants,
|
||||||
|
NodeJSSerialConnection, TCPConnection,
|
||||||
|
Advert
|
||||||
|
} from '@liamcottle/meshcore.js';
|
||||||
|
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
const device = process.argv[2] ?? '/dev/ttyACM0';
|
||||||
|
const apiURL = 'https://map.meshcore.dev/api/v1/uploader/node';
|
||||||
|
const seenAdverts = {};
|
||||||
|
let clientInfo = {};
|
||||||
|
|
||||||
|
const signData = async (connection, data) => {
|
||||||
|
const json = JSON.stringify(data);
|
||||||
|
const dataHash = new Uint8Array(
|
||||||
|
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(json))
|
||||||
|
);
|
||||||
|
|
||||||
|
return { data: json, signature: BufferUtils.bytesToHex(await connection.sign(dataHash)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
const processPacket = async (connection, rawPacket) => {
|
||||||
|
const packet = Packet.fromBytes(rawPacket);
|
||||||
|
|
||||||
|
if(packet.payload_type_string !== 'ADVERT') return;
|
||||||
|
|
||||||
|
const advert = Advert.fromBytes(packet.payload);
|
||||||
|
// console.log(advert);
|
||||||
|
if(advert.parsed.type === 'CHAT') return;
|
||||||
|
|
||||||
|
const pubKey = BufferUtils.bytesToHex(advert.publicKey);
|
||||||
|
const node = { pubKey, name: advert.parsed.name, ts: advert.timestamp, type: advert.parsed.type.toLowerCase() };
|
||||||
|
|
||||||
|
if(!advert.isVerified()) {
|
||||||
|
console.warn('ignoring: signature verification failed', node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(seenAdverts[pubKey]) {
|
||||||
|
if(seenAdverts[pubKey] >= advert.timestamp) {
|
||||||
|
console.warn('ignoring: possible replay attack', node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(advert.timestamp < seenAdverts[pubKey] + 3600) {
|
||||||
|
console.warn('ignoring: timestamp too new to reupload', node)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`uploading`, node);
|
||||||
|
const data = {
|
||||||
|
params: {
|
||||||
|
freq: clientInfo.radioFreq / 1000,
|
||||||
|
cr: clientInfo.radioCr,
|
||||||
|
sf: clientInfo.radioSf,
|
||||||
|
bw: clientInfo.radioBw / 1000
|
||||||
|
},
|
||||||
|
links: [`meshcore://${BufferUtils.bytesToHex(rawPacket)}`]
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestData = await signData(connection, data);
|
||||||
|
requestData.publicKey = BufferUtils.bytesToHex(clientInfo.publicKey);
|
||||||
|
|
||||||
|
const req = await fetch(apiURL, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(requestData)
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('sending', requestData)
|
||||||
|
console.log(await req.json());
|
||||||
|
|
||||||
|
seenAdverts[pubKey] = advert.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Connecting to ${device}...`);
|
||||||
|
|
||||||
|
let connection;
|
||||||
|
if(device.startsWith('/') || device.startsWith('COM')){
|
||||||
|
connection = new NodeJSSerialConnection(device);
|
||||||
|
} else {
|
||||||
|
const [ host, port ] = device.split(':');
|
||||||
|
connection = new TCPConnection(host, port ?? 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.on('connected', async () => {
|
||||||
|
console.log(`Connected.`);
|
||||||
|
|
||||||
|
connection.setManualAddContacts();
|
||||||
|
|
||||||
|
clientInfo = await connection.getSelfInfo();
|
||||||
|
// console.log('info', clientInfo);
|
||||||
|
|
||||||
|
console.log('Map uploader waiting for adverts...');
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.on(Constants.PushCodes.LogRxData, async (event) => {
|
||||||
|
try {
|
||||||
|
await processPacket(connection, event.raw);
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await connection.connect();
|
||||||
5
package.json
Normal file
5
package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@liamcottle/meshcore.js": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user