initial commit

master
cheetah 2 years ago
parent 30c1adf506
commit f5ddd0a0a6

@ -0,0 +1,11 @@
{
"pager": {
"url": "http://127.0.0.1:3000/api/message/preset"
},
"deliveryModes": [
{
"name": "Normal Duplex",
"preset": "184cdb82b05"
}
]
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,145 @@
<!DOCTYPE html>
<html>
<head>
<title>Webform Configuration</title>
<!-- <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> -->
<link href="css/materialdesignicons.min.css" rel="stylesheet">
<link href="css/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-app-bar app>
<v-toolbar-title>Webform Configuration</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn color="success" @click="storeConfig()">Store & Restart</v-btn>
<v-checkbox label="Expert Mode" v-model="EXPERTMODE"></v-checkbox>
</v-app-bar>
<v-content>
<v-form>
<v-tabs v-model="configTab" next-icon="mdi-arrow-right-bold-box-outline" prev-icon="mdi-arrow-left-bold-box-outline" show-arrows>
<v-tab key="botSettings">Form Settings</v-tab>
<v-tab v-show="EXPERTMODE" key="notificationConfig">Notification Configuration</v-tab>
</v-tabs>
<v-tabs-items v-model="configTab">
<v-tab-item key="botSettings">
<v-container>
<p>Targets:</p>
<v-row>
<v-btn color="success" @click="addDeliveryMode()">Add</v-btn>
</v-row>
<v-row v-for="(deliveryMode, index) in configData.deliveryModes" :key="deliveryMode._id" style="border-bottom: 2px solid black;">
<pre>Index: {{ index }}</pre>
<v-col cols="12" sm="12" md="12">
<v-btn color="error" @click="configData.deliveryModes.splice(index, 1)" icon><v-icon>mdi-delete</v-icon></v-btn>
</v-col>
<v-col cols="6" sm="6" md="4">
<v-text-field v-model="deliveryMode.name" label="Name"></v-text-field>
<v-autocomplete
v-model="deliveryMode.preset"
:items="presetSearchItems"
:loading="!presetSearchItems.length > 0"
color="white"
hide-no-data
dense
label="Profile"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
></v-autocomplete>
</v-col>
</v-row>
</v-container>
</v-tab-item>
<v-tab-item key="notificationConfig">
<v-container>
<b>Routing Paramters:</b>
<v-row>
<v-col cols="12" sm="12" md="6">
<v-text-field label="Daemon Endpoint URL" v-model="configData.pager.url"></v-text-field>
</v-col>
</v-container>
</v-tab-item>
</v-tabs-items>
</v-form>
</v-content>
</v-app>
</div>
<script src="js/vue/vue.js"></script>
<script src="js/vue/vuetify.js"></script>
<script src="js/vue/vue-resource_1.5.1.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
http: { root: '/' },
data() {
return {
EXPERTMODE: false,
configTab: null,
configData: {
"bottoken": "",
"pager": {
"url": "",
},
"menuSupport": false,
"deliveryModes": []
},
presetSearchItems: [],
}
},
created() {
this.loadPresets()
this.loadConfig()
},
methods: {
loadPresets() {
this.$http.get('/api/deliveryPresets')
.then(response => {
this.presetSearchItems = response.body.map(x => { return {
text: x.name,
value: x.key,
}})
}, response => {
})
},
loadConfig() {
this.$http.get('/config').then(response => {
const newConfig = response.body
newConfig.deliveryModes = newConfig.deliveryModes.map((x) => {
x._id = btoa(JSON.stringify(x))
return x
})
this.configData = newConfig
}, response => {
})
},
storeConfig() {
const storeConfig = JSON.parse(JSON.stringify(this.configData))
storeConfig.deliveryModes = storeConfig.deliveryModes.map((x) => {
delete x._id
return x
})
this.$http.post('/config', storeConfig).then(response => {
})
.then(this.$http.post('/restart'))
.then(() => {
document.body.style = 'display:none'
setTimeout(() => window.location.reload(), 1e3)
})
},
addDeliveryMode() {
this.configData.deliveryModes.push({
name: "",
preset: null
})
},
}
})
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -0,0 +1,262 @@
<!DOCTYPE html>
<html>
<head>
<title>smartpager.network Webform</title>
<!-- <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet"> -->
<link href="css/materialdesignicons.min.css" rel="stylesheet">
<link href="css/vuetify.min.css" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
<div id="app">
<v-app>
<v-app-bar app>
<v-toolbar-title>Webform</v-toolbar-title>
<v-spacer></v-spacer>
<span>SmartPager Framework made with 🧡 by cheetah.cat - uses BOSKrypt AES256 Encryption </span>
<v-img src="ready.png" max-width="96px" max-height="48px"></v-img>
</v-app-bar>
<v-content>
<v-container> <!-- Device List-->
<v-data-table must-sort :footer-props="footerProps"
:options="options" :loading="loadingD" loading-text="Loading... Please wait"
:headers="devicesHeaders" item-key="_id" :items="deviceListData"
:single-expand="singleExpand" item-key="name" show-expand
class="elevation-1">
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>Device List</v-toolbar-title>
</v-toolbar>
</template>
<template v-slot:no-data>
No Devices to show
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<section v-if="item.deviceType == 'birdyslim'">
<pre>Device ID: {{ item.deviceID }}</pre>
<pre v-if="!!item.poweredOn">Powered On: {{ item.poweredOn }}</pre>
<pre v-if="!!item.isCharging">Charging: {{ item.isCharging }}</pre>
<pre v-if="!!item.battery">Battery: {{ item.battery }}</pre>
<pre v-if="!!item.lastSeen">Last Seen: {{ item.lastSeen }}</pre>
<pre v-if="!!item.rssi">POCSAG RSSI: -{{ item.rssi }}dBm</pre>
<pre v-if="!!item.gps">GPS Position:
Lat: {{ item.gps.latitude }}
Lng: {{ item.gps.longitude }}
Last Acquisition: {{ item.gps.lastGPSAcquisition }}m ago
</pre>
<pre v-if="!!item.lastLoRaPacket">
Last LoRaWAN Packet:
{{ item.lastLoRaPacket }}
</pre>
</section>
</td>
</template>
</v-data-table>
</v-container>
<hr>
<v-container> <!-- Recent Messages -->
<v-data-table must-sort :footer-props="footerProps"
:options="options" :loading="loadingM" loading-text="Loading... Please wait"
:headers="recentMessagesHeaders" item-key="id" :items="recentMessageListData" :items-per-page="5"
:single-expand="singleExpandRM" item-key="name" show-expand
class="elevation-1">
<template v-slot:top>
<v-toolbar flat>
<v-toolbar-title>Message History</v-toolbar-title>
<v-spacer></v-spacer>
<v-dialog v-model="dialogNewMessage" fullscreen hide-overlay transition="dialog-bottom-transition">
<template v-slot:activator="{ on, attrs }">
<v-btn color="primary" v-bind="attrs" v-on="on">
<v-icon>mdi-email</v-icon>
New Message
</v-btn>
</template>
<v-card>
<v-toolbar dark color="primary">
<v-btn icon dark @click="dialogNewMessage = false">
<v-icon>mdi-close</v-icon>
</v-btn>
<v-toolbar-title>New Message</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<v-container>
<v-row>
<v-col cols="12" sm="12" md="6">
<v-autocomplete
v-model="newMSGData.preset"
:items="presetSearchItems"
:loading="!presetSearchItems.length > 0"
color="white"
hide-no-data
dense
label="Profile"
placeholder="Start typing to Search"
prepend-icon="mdi-database-search"
></v-autocomplete>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="12" md="6">
<span>
Please leave a reference/name!
</span>
<v-textarea v-model="newMSGData.payload" label="Message"></v-select>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="12" md="6">
<v-btn color="success" dark text @click="testMsg_send()">Send</v-btn>
</v-col>
</v-row>
</v-container>
</v-card>
</v-dialog>
</v-toolbar>
</template>
<template v-slot:no-data>
No Messages to show, you can send one using the 'New Message' Button
</template>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<pre>{{ item }}</pre>
</td>
</template>
</v-data-table>
</v-container>
</v-content>
</v-app>
</div>
<script src="js/vue/vue.js"></script>
<script src="js/vue/vuetify.js"></script>
<script src="js/vue/vue-resource_1.5.1.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
http: { root: '/' },
data() {
return {
EXPERTMODE:false,
dialogNewMessage: false,
presetSearchItems: [],
loadingD: true,
loadingM: false,
search: '',
expanded: [],
singleExpand: true,
singleExpandRM: true,
options: {
itemsPerPage: -1,
},
footerProps: {
itemsPerPageOptions: [ -1, 5, 10, 20, 50, 75, 100 ]
},
devicesHeaders: [
{ text: 'ID', align: 'start', groupable: false, sortable: false, value: 'deviceID', },
{ text: 'Type', value: 'deviceType', groupable: true },
{ text: 'Last Seen', value: 'lastSeen', groupable: false },
],
newMSGData: {
pager: {
payload: "Test Message",
},
preset: null,
},
lastMSGLog: null,
messagesCheckList: [],
deviceListData: [],
recentMessageListData: [],
recentMessagesHeaders: [
{ text: 'ID', align: 'start', groupable: false, sortable: false, value: 'id', },
{ text: 'Type', value: 'type', groupable: true, sortable: false },
{ text: 'State', value: 'state', groupable: false, sortable: false },
{ text: 'Date', value: 'date', groupable: false, sortable: false },
{ text: 'Message', value: '_payload', groupable: false, sortable: false },
],
}
},
created() {
this.refreshDevices()
setInterval(this.refreshDevices, 1e3)
this.refreshMessages()
setInterval(this.refreshMessages, 1e3)
this.$http.get('/api/modes')
.then(response => {
console.log(response.body)
this.presetSearchItems = response.body.map(x => { return {
text: x.name,
value: x.preset,
//Description
}})
}, response => {
})
},
methods: {
refreshDevices() {
this.$http.get('/api/devices').then(response => {
this.deviceListData = Object.keys(response.body).map(key => {
const item = response.body[ key ]
const keyData = key.split(':')
item.deviceType = keyData[ 0 ], item.deviceID = keyData[ 1 ]
item.lastSeen = new Date(item.lastSeen).toLocaleString()
//item.validStateText = this.validStateLUT[item.validState]
return item
})
//this.accountData.sort((a,b) => a.validState - b.validState)
this.loadingD = false
}, response => {
})
},
refreshMessages() {
if (this.messagesCheckList.length == 0) return
this.$http.get('/api/message/status', { params: {
ids: this.messagesCheckList
} }).then(response => {
this.recentMessageListData = response.body.map(msg => {
const stateArray = [msg._routerData.recvAck, msg._routerData.readAck, msg._routerData.response]
// msg.state = [ msg.type, stateArray.filter(x=>x!==false).length, ...stateArray ]
msg.state = '-'
if (msg.type === 'duplex') {
switch (stateArray.filter(x=>x!==false).length) {
case 0: msg.state = '📨 Sent'; break;
case 1: msg.state = '📬 Received'; break;
case 2: msg.state = '👀 Read'; break;
case 3: msg.state = '💬 Responded'; break;
}
}
msg.date = new Date(msg.date).toLocaleString()
return msg
})
this.loadingM = false
}, response => {
})
},
testMsg_send() {
console.log(this.newMSGData.preset)
this.$http.post('/api/message/preset/',
Object.assign({ preset: this.newMSGData.preset }, { payload: this.newMSGData.payload })
).then(x=>{
console.log('response', x)
this.messagesCheckList.push(x.body)
this.dialogNewMessage = false
})
}
}
})
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

@ -0,0 +1,177 @@
const config = require('./config.json')
const io = require("socket.io-client")
const axios = require('axios')
const fs = require('fs')
function filter(txt) {
if (txt.length > 240) txt = txt.substring(0, 240)
return txt.replace(/[^\x00-\x7F]/g, "")
}
function editStatus(msgId, msg) {
if (!assoc[msgId]) return
const sPass = Math.floor( (new Date().valueOf() - assoc[msgId].date.valueOf() ) / 1e3)
msg = `+${ sPass }s> ${ msg }`
console.log(assoc[msgId])
const [tgChatId, tgMsgId] = [assoc[msgId].tgchat, assoc[msgId].tgmsg]
assoc[msgId].newText = assoc[msgId].newText + '\n' + msg
console.log(assoc[msgId])
bot.telegram.editMessageText(tgChatId, tgMsgId, null, assoc[msgId].newText).then(console.log)
}
/*
socket.on('msgmgr:event', (eventType, eventData) => {
console.log(eventType, eventData)
switch (eventType) {
case 'status': {
const [msgId, uuid, status] = eventData
editStatus(msgId, `${ uuid } is ${ status }`)
} break;
case 'read':
editStatus(eventData, 'read')
break;
case 'response': {
const [msgId, response] = eventData
editStatus(msgId, 'response ' + response)
} break;
}
})
bot.on('message', (ctx) => {
if (!ctx.update.message.text) return ctx.reply("not a textmessage")
// if (!ctx.update.message.type)
console.log(ctx.update.message.from)
const preview = `Preview: ${ filter(ctx.update.message.from.first_name) }:${ filter(ctx.update.message.text) }`
console.log(preview)
ctx.reply(preview, Markup.inlineKeyboard(
config.deliveryModes.map((deliveryModeConfig, index) =>
Markup.button.callback(deliveryModeConfig.name, 'deliveryMode-' + index)
)
))
})
for (let deliveryModeIndex in config.deliveryModes) {
bot.action('deliveryMode-' + deliveryModeIndex, async (ctx) => {
const origText = ctx.update.callback_query.message.text.substring(8+1)
await ctx.answerCbQuery()
await ctx.editMessageReplyMarkup(undefined)
const deliveryModeData = config.deliveryModes[ deliveryModeIndex ]
let msgId = (
await axios.post(
new URL(config.pager.url).origin + '/api/message/' + (!!deliveryModeData.preset ? 'preset' : 'advanced'),
Object.assign( !!deliveryModeData.preset
? { preset: deliveryModeData.preset } // backward compatibility
: { ...deliveryModeData.params }
, {
payload: origText
})
)
).data
assoc[msgId] = {
date: new Date(),
tgmsg: ctx.update.callback_query.message.message_id,
text: origText,
newText: `Preview: ${ origText }\n\n--[${ msgId }]--\n`,
tgchat: ctx.update.callback_query.message.chat.id
}
await ctx.editMessageText(assoc[msgId].newText)
})
}
*/
const express = require('express')
const { query } = require('express')
const app = express()
app.use(express.json())
app.use(express.static('html_public'))
app.use(express.static(__dirname + '/node_modules/@mdi/font'))
const allowedIDs = []
app.get('/api/modes', async (req, res) => {
return res.json(config.deliveryModes)
})
app.get('/api/devices', async (req, res) => {
const devices = (await axios.get(new URL(config.pager.url).origin + '/api/devices')).data
Object.keys(devices).map(key => {
devices[ key ].lastLoRaPacket = !!devices[ key ].lastLoRaPacket
? {
received_at: devices[ key ].lastLoRaPacket.received_at,
} : null
})
return res.json(devices)
})
app.get('/api/message/status', async (req, res) => {
if (!req.query.ids) return res.status(500).json("ERROR: no ids")
const queryIDs = req.query.ids
if (queryIDs.length > 3) queryIds = queryIDs.slice(0,3)
let results = []
for (let msgID of queryIDs) {
if (allowedIDs.indexOf(msgID) > -1)
try {
let msgStatus = (
await axios.get(new URL(config.pager.url).origin + '/api/message/status/' + msgID)
).data
if (!!msgStatus._routerData && !!msgStatus._routerData.metadata) {
msgStatus._routerData.metadata = msgStatus._routerData.metadata.map(x => {
if (x.ack == 'operational') {
try {
x.operationalData = x.metadata.uplink_message.decoded_payload.operationalData
} catch (ee) {}
}
delete x.metadata
return x
})
}
results.push(msgStatus)
} catch (e) {
}
}
return res.json(results)
})
app.post('/api/message/preset', async (req, res) => {
if (!req.body.preset) return res.status(500).json("ERROR: no msg preset")
if (!req.body.payload) return res.status(500).json("ERROR: no msg payload")
if (config.deliveryModes.filter(a=>a.preset == req.body.preset).length !== 1) return res.status(500).json("ERROR: mode does not exist")
let msgId = (
await axios.post(config.pager.url, { preset: req.body.preset, payload: req.body.payload })
).data
allowedIDs.push(msgId)
return res.json(msgId)
})
app.listen(3091, '0.0.0.0' || config.host || '127.0.0.1')
const appConfig = express()
appConfig.use(express.json())
appConfig.use(express.static('html_config'))
appConfig.use(express.static(__dirname + '/node_modules/@mdi/font'))
/** CONFIG Routes */
appConfig.get('/config', async (req, res) => {
return res.json(JSON.parse(fs.readFileSync('config.json')))
})
appConfig.get('/api/deliveryPresets', async (req, res) => {
const presets = await axios.get(new URL(config.pager.url).origin + '/api/deliveryPresets')
return res.json(presets.data)
})
appConfig.post('/config', async (req, res) => {
if (!(!!req.body.deliveryModes)) return res.status(403).json(false)
if (!(!!req.body.pager)) return res.status(403).json(false)
console.log(req.body)
fs.writeFileSync('config.json', JSON.stringify(req.body, null, "\t"))
return res.json(true)
})
appConfig.post('/restart', (req, res) => {
process.exit(1)
})
appConfig.listen(3090, '0.0.0.0' || config.host || '127.0.0.1')

@ -0,0 +1,9 @@
{
"dependencies": {
"@mdi/font": "^7.0.96",
"axios": "^0.21.1",
"express": "^4.18.2",
"moment": "^2.29.1",
"socket.io-client": "^4.0.2"
}
}
Loading…
Cancel
Save