initial commit
This commit is contained in:
commit
4f305b79f6
36
.eslintrc.js
Normal file
36
.eslintrc.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
module.exports = {
|
||||||
|
"env": {
|
||||||
|
"es2021": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 12,
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
"tab"
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/node_modules
|
||||||
|
/docker
|
||||||
|
/dist
|
||||||
|
*.tgz
|
35
build/build.js
Normal file
35
build/build.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const fs = require("fs")
|
||||||
|
const Mustache = require('mustache');
|
||||||
|
|
||||||
|
// Load template
|
||||||
|
const htmlTemplate = fs.readFileSync("build/template/editor.html.mustache").toString()
|
||||||
|
|
||||||
|
const nodes = fs.readdirSync("src/nodes")
|
||||||
|
|
||||||
|
nodes.forEach((e)=>{
|
||||||
|
// Load html parts
|
||||||
|
const helpHtml = fs.readFileSync(`src/nodes/${e}/ui/editor.html`)
|
||||||
|
const editorHtml = fs.readFileSync(`src/nodes/${e}/ui/editor.html`)
|
||||||
|
|
||||||
|
// Load complied editor index js/ts
|
||||||
|
let indexHtml = fs.readFileSync(`dist/nodes/${e}/ui/index.js`).toString()
|
||||||
|
// Cut first 2 lines because typescript inserts some stuff that the browser does not like
|
||||||
|
// TODO: is is ugly but it works
|
||||||
|
const tmp = indexHtml.split("\n")
|
||||||
|
tmp.splice(0,2)
|
||||||
|
indexHtml = tmp.join("\n")
|
||||||
|
|
||||||
|
// Prepare view object to be injected into render
|
||||||
|
const view = {
|
||||||
|
helpHtml,
|
||||||
|
editorHtml,
|
||||||
|
indexHtml,
|
||||||
|
nodeName: e
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = Mustache.render(htmlTemplate, view);
|
||||||
|
|
||||||
|
// Write generated html into dist
|
||||||
|
fs.writeFileSync(`dist/nodes/${e}/${e}.html`,html)
|
||||||
|
})
|
||||||
|
|
11
build/template/editor.html.mustache
Normal file
11
build/template/editor.html.mustache
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script type="text/javascript">
|
||||||
|
{{{indexHtml}}}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="{{nodeName}}">
|
||||||
|
{{{editorHtml}}}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="{{nodeName}}">
|
||||||
|
{{{helpHtml}}}
|
||||||
|
</script>
|
29
docker-compose.yml
Normal file
29
docker-compose.yml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
node-red:
|
||||||
|
image: "nodered/node-red"
|
||||||
|
container_name: "node-red"
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Berlin
|
||||||
|
volumes:
|
||||||
|
- ./docker:/data
|
||||||
|
ports:
|
||||||
|
- 1880:1880
|
||||||
|
networks:
|
||||||
|
- tsNet
|
||||||
|
ts3:
|
||||||
|
image: "teamspeak"
|
||||||
|
container_name: "ts3"
|
||||||
|
environment:
|
||||||
|
- TS3SERVER_LICENSE=accept
|
||||||
|
ports:
|
||||||
|
- "9987:9987/udp"
|
||||||
|
- "10011:10011"
|
||||||
|
- "30033:30033"
|
||||||
|
networks:
|
||||||
|
- tsNet
|
||||||
|
|
||||||
|
networks:
|
||||||
|
tsNet:
|
||||||
|
driver: bridge
|
21
docker-pack.sh
Executable file
21
docker-pack.sh
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
rm -rf dist
|
||||||
|
|
||||||
|
yarn run build
|
||||||
|
|
||||||
|
file=$(npm pack | tail -1)
|
||||||
|
|
||||||
|
sudo docker-compose stop
|
||||||
|
rm -rf docker
|
||||||
|
mkdir docker
|
||||||
|
sudo docker-compose up -d
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
cd docker
|
||||||
|
|
||||||
|
npm install "../$file"
|
||||||
|
|
||||||
|
sudo docker-compose restart
|
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "node-red-contrib-teamspeak",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Node RED teamspeak node",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build && copyfiles -u 2 './src/nodes/**/*.{png,svg}' ./dist/nodes/ && node build/build.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*"
|
||||||
|
],
|
||||||
|
"node-red": {
|
||||||
|
"nodes": {
|
||||||
|
"TS3Config": "dist/nodes/TS3Config/TS3Config.js",
|
||||||
|
"TS3Get": "dist/nodes/TS3Call/TS3Call.js",
|
||||||
|
"TS3Event": "dist/nodes/TS3Event/TS3Event.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ts3-nodejs-library": "^3.3.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^4.2.4",
|
||||||
|
"@types/node": "^14.14.41",
|
||||||
|
"@types/node-red": "^1.1.1",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||||
|
"@typescript-eslint/parser": "^4.22.0",
|
||||||
|
"copyfiles": "^2.4.1",
|
||||||
|
"eslint": "^7.25.0",
|
||||||
|
"mustache": "^4.2.0"
|
||||||
|
}
|
||||||
|
}
|
60
src/nodes/TS3Call/TS3Call.ts
Normal file
60
src/nodes/TS3Call/TS3Call.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { NodeInitializer } from "node-red"
|
||||||
|
import { TeamSpeak } from "ts3-nodejs-library"
|
||||||
|
import { Ts3ConfigNode } from "../TS3Config/types"
|
||||||
|
import { Ts3CallConfig, Ts3CallNode } from "./types"
|
||||||
|
|
||||||
|
const nodeInit: NodeInitializer = (RED): void => {
|
||||||
|
function Ts3Call(
|
||||||
|
this: Ts3CallNode,
|
||||||
|
config: Ts3CallConfig
|
||||||
|
): void {
|
||||||
|
RED.nodes.createNode(this, config)
|
||||||
|
|
||||||
|
const ts3Config = RED.nodes.getNode(config.configid) as Ts3ConfigNode
|
||||||
|
|
||||||
|
// HACK: ts3Config.on("connected") would not work because i dont know how to use typescript
|
||||||
|
ts3Config.addListener("connected",()=>{
|
||||||
|
this.status({fill:"green",shape:"dot",text:"Connected"})
|
||||||
|
})
|
||||||
|
|
||||||
|
ts3Config.addListener("disconnect",()=>{
|
||||||
|
this.status({fill:"grey",shape:"dot",text:"not conncted"})
|
||||||
|
})
|
||||||
|
|
||||||
|
ts3Config.addListener("error",()=>{
|
||||||
|
this.status({fill:"red",shape:"dot",text:"Error"})
|
||||||
|
})
|
||||||
|
|
||||||
|
this.on("input", async (msg,send,done) => {
|
||||||
|
const client = await ts3Config.getConnection()
|
||||||
|
|
||||||
|
const member: any = client[config.selection as keyof TeamSpeak]
|
||||||
|
if (typeof member === "function"){
|
||||||
|
let args: any[] = []
|
||||||
|
|
||||||
|
if (msg.payload instanceof Array){
|
||||||
|
args = msg.payload
|
||||||
|
}else{
|
||||||
|
args = [msg.payload]
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
msg.payload = await member.apply(client,args)
|
||||||
|
} catch (err) {
|
||||||
|
this.error(`Failed to call ${config.selection}: ${err}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
send(msg)
|
||||||
|
}else{
|
||||||
|
this.error(`Failed to call ${config.selection}. Not a function`)
|
||||||
|
}
|
||||||
|
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType("TS3Call", Ts3Call)
|
||||||
|
}
|
||||||
|
|
||||||
|
export = nodeInit;
|
16
src/nodes/TS3Call/types.ts
Normal file
16
src/nodes/TS3Call/types.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { NodeDef, Node, EditorNodeProperties } from "node-red"
|
||||||
|
|
||||||
|
export interface Ts3CallProps {
|
||||||
|
configid: string
|
||||||
|
selection: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface Ts3CallData {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ts3CallConfig extends NodeDef, Ts3CallProps { }
|
||||||
|
|
||||||
|
export interface Ts3CallNode extends Node, Ts3CallData {}
|
||||||
|
|
||||||
|
export interface Ts3CallEditorNodeProperties extends EditorNodeProperties,Ts3CallProps {}
|
162
src/nodes/TS3Call/ui/editor.html
Normal file
162
src/nodes/TS3Call/ui/editor.html
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-globe"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-configid"><i class="fa fa-server"></i> Connection</label>
|
||||||
|
<input type="text" id="node-input-configid">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-configid"><i class="fa fa-terminal"></i> Function</label>
|
||||||
|
<select class="form-select" id="node-input-selection">
|
||||||
|
<option value="queryLoginAdd" >queryLoginAdd</option>
|
||||||
|
<option value="queryLoginDel" >queryLoginDel</option>
|
||||||
|
<option value="queryLoginList" >queryLoginList</option>
|
||||||
|
<option value="apiKeyAdd" >apiKeyAdd</option>
|
||||||
|
<option value="apiKeyList" >apiKeyList</option>
|
||||||
|
<option value="apiKeyDel" >apiKeyDel</option>
|
||||||
|
<option value="clientSetServerQueryLogin" >clientSetServerQueryLogin</option>
|
||||||
|
<option value="clientUpdate" >clientUpdate</option>
|
||||||
|
<option value="version" >version</option>
|
||||||
|
<option value="hostInfo" >hostInfo</option>
|
||||||
|
<option value="instanceInfo" >instanceInfo</option>
|
||||||
|
<option value="instanceEdit" >instanceEdit</option>
|
||||||
|
<option value="bindingList" >bindingList</option>
|
||||||
|
<option value="useByPort" >useByPort</option>
|
||||||
|
<option value="useBySid" >useBySid</option>
|
||||||
|
<option value="whoami" >whoami</option>
|
||||||
|
<option value="serverInfo" >serverInfo</option>
|
||||||
|
<option value="serverIdGetByPort" >serverIdGetByPort</option>
|
||||||
|
<option value="serverEdit" >serverEdit</option>
|
||||||
|
<option value="serverProcessStop" >serverProcessStop</option>
|
||||||
|
<option value="connectionInfo" >connectionInfo</option>
|
||||||
|
<option value="serverCreate" >serverCreate</option>
|
||||||
|
<option value="serverDelete" >serverDelete</option>
|
||||||
|
<option value="serverStart" >serverStart</option>
|
||||||
|
<option value="serverStop" >serverStop</option>
|
||||||
|
<option value="serverGroupCreate" >serverGroupCreate</option>
|
||||||
|
<option value="serverGroupClientList" >serverGroupClientList</option>
|
||||||
|
<option value="serverGroupAddClient" >serverGroupAddClient</option>
|
||||||
|
<option value="serverGroupDelClient" >serverGroupDelClient</option>
|
||||||
|
<option value="serverGroupsByClientId" >serverGroupsByClientId</option>
|
||||||
|
<option value="clientAddServerGroup" >clientAddServerGroup</option>
|
||||||
|
<option value="clientDelServerGroup" >clientDelServerGroup</option>
|
||||||
|
<option value="serverGroupDel" >serverGroupDel</option>
|
||||||
|
<option value="serverGroupCopy" >serverGroupCopy</option>
|
||||||
|
<option value="serverGroupRename" >serverGroupRename</option>
|
||||||
|
<option value="serverGroupPermList" >serverGroupPermList</option>
|
||||||
|
<option value="serverGroupAddPerm" >serverGroupAddPerm</option>
|
||||||
|
<option value="serverGroupDelPerm" >serverGroupDelPerm</option>
|
||||||
|
<option value="serverTempPasswordAdd" >serverTempPasswordAdd</option>
|
||||||
|
<option value="serverTempPasswordDel" >serverTempPasswordDel</option>
|
||||||
|
<option value="serverTempPasswordList" >serverTempPasswordList</option>
|
||||||
|
<option value="channelCreate" >channelCreate</option>
|
||||||
|
<option value="channelGroupCreate" >channelGroupCreate</option>
|
||||||
|
<option value="getChannelById" >getChannelById</option>
|
||||||
|
<option value="getChannelByName" >getChannelByName</option>
|
||||||
|
<option value="channelFind" >channelFind</option>
|
||||||
|
<option value="channelInfo" >channelInfo</option>
|
||||||
|
<option value="channelMove" >channelMove</option>
|
||||||
|
<option value="channelDelete" >channelDelete</option>
|
||||||
|
<option value="channelEdit" >channelEdit</option>
|
||||||
|
<option value="channelPermList" >channelPermList</option>
|
||||||
|
<option value="channelSetPerm" >channelSetPerm</option>
|
||||||
|
<option value="channelSetPerms" >channelSetPerms</option>
|
||||||
|
<option value="channelDelPerm" >channelDelPerm</option>
|
||||||
|
<option value="getClientById" >getClientById</option>
|
||||||
|
<option value="getClientByDbid" >getClientByDbid</option>
|
||||||
|
<option value="getClientByUid" >getClientByUid</option>
|
||||||
|
<option value="getClientByName" >getClientByName</option>
|
||||||
|
<option value="clientInfo" >clientInfo</option>
|
||||||
|
<option value="clientDbList" >clientDbList</option>
|
||||||
|
<option value="clientDbInfo" >clientDbInfo</option>
|
||||||
|
<option value="clientKick" >clientKick</option>
|
||||||
|
<option value="clientMove" >clientMove</option>
|
||||||
|
<option value="clientPoke" >clientPoke</option>
|
||||||
|
<option value="clientPermList" >clientPermList</option>
|
||||||
|
<option value="clientAddPerm" >clientAddPerm</option>
|
||||||
|
<option value="clientDelPerm" >clientDelPerm</option>
|
||||||
|
<option value="customSearch" >customSearch</option>
|
||||||
|
<option value="customInfo" >customInfo</option>
|
||||||
|
<option value="customDelete" >customDelete</option>
|
||||||
|
<option value="customSet" >customSet</option>
|
||||||
|
<option value="sendTextMessage" >sendTextMessage</option>
|
||||||
|
<option value="sendChannelMessage" >sendChannelMessage</option>
|
||||||
|
<option value="getServerGroupById" >getServerGroupById</option>
|
||||||
|
<option value="getServerGroupByName" >getServerGroupByName</option>
|
||||||
|
<option value="getChannelGroupById" >getChannelGroupById</option>
|
||||||
|
<option value="getChannelGroupByName" >getChannelGroupByName</option>
|
||||||
|
<option value="setClientChannelGroup" >setClientChannelGroup</option>
|
||||||
|
<option value="deleteChannelGroup" >deleteChannelGroup</option>
|
||||||
|
<option value="channelGroupCopy" >channelGroupCopy</option>
|
||||||
|
<option value="channelGroupRename" >channelGroupRename</option>
|
||||||
|
<option value="channelGroupPermList" >channelGroupPermList</option>
|
||||||
|
<option value="channelGroupAddPerm" >channelGroupAddPerm</option>
|
||||||
|
<option value="channelGroupDelPerm" >channelGroupDelPerm</option>
|
||||||
|
<option value="channelGroupClientList" >channelGroupClientList</option>
|
||||||
|
<option value="permOverview" >permOverview</option>
|
||||||
|
<option value="permissionList" >permissionList</option>
|
||||||
|
<option value="permIdGetByName" >permIdGetByName</option>
|
||||||
|
<option value="permIdsGetByName" >permIdsGetByName</option>
|
||||||
|
<option value="permGet" >permGet</option>
|
||||||
|
<option value="permFind" >permFind</option>
|
||||||
|
<option value="permReset" >permReset</option>
|
||||||
|
<option value="privilegeKeyList" >privilegeKeyList</option>
|
||||||
|
<option value="privilegeKeyAdd" >privilegeKeyAdd</option>
|
||||||
|
<option value="serverGroupPrivilegeKeyAdd" >serverGroupPrivilegeKeyAdd</option>
|
||||||
|
<option value="channelGroupPrivilegeKeyAdd" >channelGroupPrivilegeKeyAdd</option>
|
||||||
|
<option value="privilegeKeyDelete" >privilegeKeyDelete</option>
|
||||||
|
<option value="privilegeKeyUse" >privilegeKeyUse</option>
|
||||||
|
<option value="messageList" >messageList</option>
|
||||||
|
<option value="messageAdd" >messageAdd</option>
|
||||||
|
<option value="messageDel" >messageDel</option>
|
||||||
|
<option value="messageGet" >messageGet</option>
|
||||||
|
<option value="messageUpdate" >messageUpdate</option>
|
||||||
|
<option value="complainList" >complainList</option>
|
||||||
|
<option value="complainAdd" >complainAdd</option>
|
||||||
|
<option value="complainDel" >complainDel</option>
|
||||||
|
<option value="banList" >banList</option>
|
||||||
|
<option value="ban" >ban</option>
|
||||||
|
<option value="banClient" >banClient</option>
|
||||||
|
<option value="banDel" >banDel</option>
|
||||||
|
<option value="logView" >logView</option>
|
||||||
|
<option value="logAdd" >logAdd</option>
|
||||||
|
<option value="gm" >gm</option>
|
||||||
|
<option value="clientFind" >clientFind</option>
|
||||||
|
<option value="clientGetIds" >clientGetIds</option>
|
||||||
|
<option value="clientGetDbidFromUid" >clientGetDbidFromUid</option>
|
||||||
|
<option value="clientGetNameFromUid" >clientGetNameFromUid</option>
|
||||||
|
<option value="clientGetUidFromClid" >clientGetUidFromClid</option>
|
||||||
|
<option value="clientGetNameFromDbid" >clientGetNameFromDbid</option>
|
||||||
|
<option value="clientEdit" >clientEdit</option>
|
||||||
|
<option value="clientDbFind" >clientDbFind</option>
|
||||||
|
<option value="clientDbEdit" >clientDbEdit</option>
|
||||||
|
<option value="clientDbDelete" >clientDbDelete</option>
|
||||||
|
<option value="serverList" >serverList</option>
|
||||||
|
<option value="channelClientPermList" >channelClientPermList</option>
|
||||||
|
<option value="channelGroupList" >channelGroupList</option>
|
||||||
|
<option value="serverGroupList" >serverGroupList</option>
|
||||||
|
<option value="channelList" >channelList</option>
|
||||||
|
<option value="clientList" >clientList</option>
|
||||||
|
<option value="ftList" >ftList</option>
|
||||||
|
<option value="ftGetFileList" >ftGetFileList</option>
|
||||||
|
<option value="ftGetFileInfo" >ftGetFileInfo</option>
|
||||||
|
<option value="ftStop" >ftStop</option>
|
||||||
|
<option value="ftDeleteFile" >ftDeleteFile</option>
|
||||||
|
<option value="ftCreateDir" >ftCreateDir</option>
|
||||||
|
<option value="ftRenameFile" >ftRenameFile</option>
|
||||||
|
<option value="ftInitUpload" >ftInitUpload</option>
|
||||||
|
<option value="ftInitDownload" >ftInitDownload</option>
|
||||||
|
<option value="uploadFile" >uploadFile</option>
|
||||||
|
<option value="downloadFile" >downloadFile</option>
|
||||||
|
<option value="downloadIcon" >downloadIcon</option>
|
||||||
|
<option value="uploadIcon" >uploadIcon</option>
|
||||||
|
<option value="getIconId" >getIconId</option>
|
||||||
|
<option value="createSnapshot" >createSnapshot</option>
|
||||||
|
<option value="deploySnapshot" >deploySnapshot</option>
|
||||||
|
</select>
|
||||||
|
<div class="form-row">
|
||||||
|
You can find documentation for all functions
|
||||||
|
<a target="_blank" href="https://multivit4min.github.io/TS3-NodeJS-Library/classes/teamspeak.teamspeak-2.html#on"> here</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
1
src/nodes/TS3Call/ui/help.html
Normal file
1
src/nodes/TS3Call/ui/help.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>Call a function on a Teamspeak 3 server.</p>
|
19
src/nodes/TS3Call/ui/index.ts
Normal file
19
src/nodes/TS3Call/ui/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { EditorRED } from "node-red"
|
||||||
|
import { Ts3CallEditorNodeProperties } from "../types"
|
||||||
|
|
||||||
|
declare const RED: EditorRED
|
||||||
|
|
||||||
|
RED.nodes.registerType<Ts3CallEditorNodeProperties>("TS3Call",{
|
||||||
|
category: "function",
|
||||||
|
color: "#a6bbcf",
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
configid: { type:"TS3Config", value: "" },
|
||||||
|
selection: { value:"version" }
|
||||||
|
},
|
||||||
|
inputs: 1,
|
||||||
|
outputs: 1,
|
||||||
|
label: function() {
|
||||||
|
return this.name || "TS3 " + this.selection
|
||||||
|
}
|
||||||
|
})
|
76
src/nodes/TS3Config/TS3Config.ts
Normal file
76
src/nodes/TS3Config/TS3Config.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { NodeInitializer } from "node-red"
|
||||||
|
import { Ts3ConfigConfig, Ts3ConfigNode } from "./types"
|
||||||
|
import { TeamSpeak, QueryProtocol } from "ts3-nodejs-library"
|
||||||
|
|
||||||
|
const nodeInit: NodeInitializer = (RED): void => {
|
||||||
|
function Ts3Config(
|
||||||
|
this: Ts3ConfigNode,
|
||||||
|
config: Ts3ConfigConfig
|
||||||
|
): void {
|
||||||
|
RED.nodes.createNode(this, config)
|
||||||
|
|
||||||
|
const host = config.host
|
||||||
|
const nickname = config.nickname
|
||||||
|
|
||||||
|
this.name = config.name
|
||||||
|
|
||||||
|
let client: TeamSpeak | null = null
|
||||||
|
let connectPromise: Promise<TeamSpeak> | null = null
|
||||||
|
let shouldDissconect = false
|
||||||
|
|
||||||
|
this.getConnection = async (): Promise<TeamSpeak>=>{
|
||||||
|
if (client){
|
||||||
|
return client
|
||||||
|
}else if (connectPromise){
|
||||||
|
return connectPromise
|
||||||
|
}else{
|
||||||
|
try{
|
||||||
|
connectPromise = TeamSpeak.connect({
|
||||||
|
host: host,
|
||||||
|
protocol: QueryProtocol.RAW,
|
||||||
|
queryport: 10011,
|
||||||
|
serverport: 9987,
|
||||||
|
username: this.credentials.username,
|
||||||
|
password: this.credentials.password,
|
||||||
|
nickname: nickname
|
||||||
|
})
|
||||||
|
client = await connectPromise
|
||||||
|
if (client == null){
|
||||||
|
throw new Error("Failed to connect to Teamspeak")
|
||||||
|
}
|
||||||
|
|
||||||
|
client.on("close", async () => {
|
||||||
|
if (!shouldDissconect){
|
||||||
|
if (client != null){
|
||||||
|
await client.reconnect(-1, 1000)
|
||||||
|
}else{
|
||||||
|
this.emit("error")
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.emit("disconnect")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.emit("connected")
|
||||||
|
return client
|
||||||
|
}catch(err){
|
||||||
|
this.emit("error")
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.on("close",async ()=>{
|
||||||
|
shouldDissconect = true
|
||||||
|
await client?.quit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType("TS3Config", Ts3Config,{
|
||||||
|
credentials: {
|
||||||
|
username: {type: "text"},
|
||||||
|
password: {type: "password"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export = nodeInit;
|
22
src/nodes/TS3Config/types.ts
Normal file
22
src/nodes/TS3Config/types.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { NodeDef, Node, EditorNodeProperties } from "node-red"
|
||||||
|
import { TeamSpeak } from "ts3-nodejs-library"
|
||||||
|
|
||||||
|
export interface Ts3ConfigProps{
|
||||||
|
host: string
|
||||||
|
nickname: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ts3ConfigCreds {
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ts3ConfigData {
|
||||||
|
getConnection(): Promise<TeamSpeak>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ts3ConfigConfig extends NodeDef, Ts3ConfigProps, Ts3ConfigCreds { }
|
||||||
|
|
||||||
|
export interface Ts3ConfigNode extends Node<Ts3ConfigCreds>, Ts3ConfigData {}
|
||||||
|
|
||||||
|
export interface Ts3ConfigEditorNodeProperties extends EditorNodeProperties,Ts3ConfigProps {}
|
20
src/nodes/TS3Config/ui/editor.html
Normal file
20
src/nodes/TS3Config/ui/editor.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-globe"></i> Name</label>
|
||||||
|
<input type="text" id="node-config-input-name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-host"><i class="fa fa-globe"></i> Hostname</label>
|
||||||
|
<input type="text" id="node-config-input-host">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-nickname"><i class="fa fa-comment"></i> Nickname</label>
|
||||||
|
<input type="text" id="node-config-input-nickname">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-username"><i class="fa fa-user"></i> Username</label>
|
||||||
|
<input type="text" id="node-config-input-username">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-password"><i class="fa fa-key"></i> Password</label>
|
||||||
|
<input type="password" id="node-config-input-password">
|
||||||
|
</div>
|
1
src/nodes/TS3Config/ui/help.html
Normal file
1
src/nodes/TS3Config/ui/help.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>Connection config for a Teamspeak 3 server.</p>
|
21
src/nodes/TS3Config/ui/index.ts
Normal file
21
src/nodes/TS3Config/ui/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { EditorRED } from "node-red"
|
||||||
|
import { Ts3ConfigCreds, Ts3ConfigEditorNodeProperties } from "../types"
|
||||||
|
|
||||||
|
declare const RED: EditorRED
|
||||||
|
|
||||||
|
RED.nodes.registerType<Ts3ConfigEditorNodeProperties,Ts3ConfigCreds>("TS3Config",{
|
||||||
|
category: "config",
|
||||||
|
color: "#a6bbcf",
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
host: { value: ""},
|
||||||
|
nickname: {value: "ServerQuery"}
|
||||||
|
},
|
||||||
|
credentials: {
|
||||||
|
username: {type: "text"},
|
||||||
|
password: {type: "password"}
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
return this.name || "TS3 Config"
|
||||||
|
}
|
||||||
|
})
|
46
src/nodes/TS3Event/TS3Event.ts
Normal file
46
src/nodes/TS3Event/TS3Event.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { NodeInitializer } from "node-red"
|
||||||
|
import { Ts3ConfigNode } from "../TS3Config/types"
|
||||||
|
import { Ts3EventConfig, Ts3EventNode } from "./types"
|
||||||
|
|
||||||
|
const nodeInit: NodeInitializer = (RED): void => {
|
||||||
|
function Ts3Event(
|
||||||
|
this: Ts3EventNode,
|
||||||
|
config: Ts3EventConfig
|
||||||
|
): void {
|
||||||
|
RED.nodes.createNode(this, config)
|
||||||
|
|
||||||
|
const ts3Config = RED.nodes.getNode(config.configid) as Ts3ConfigNode
|
||||||
|
|
||||||
|
// HACK: ts3Config.on("connected") would not work because i dont know how to use typescript
|
||||||
|
ts3Config.addListener("connected",()=>{
|
||||||
|
this.status({fill:"green",shape:"dot",text:"Connected"})
|
||||||
|
})
|
||||||
|
|
||||||
|
ts3Config.addListener("disconnect",()=>{
|
||||||
|
this.status({fill:"grey",shape:"dot",text:"not conncted"})
|
||||||
|
})
|
||||||
|
|
||||||
|
ts3Config.addListener("error",()=>{
|
||||||
|
this.status({fill:"red",shape:"dot",text:"Error"})
|
||||||
|
});
|
||||||
|
|
||||||
|
(async() =>{
|
||||||
|
const client = await ts3Config.getConnection()
|
||||||
|
|
||||||
|
// HACK: like above i dont know how to use typescript
|
||||||
|
client.addListener(config.selection,(event: any)=>{
|
||||||
|
const msg = {
|
||||||
|
payload: event
|
||||||
|
}
|
||||||
|
|
||||||
|
this.send(msg)
|
||||||
|
})
|
||||||
|
|
||||||
|
})()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.nodes.registerType("TS3Event", Ts3Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
export = nodeInit;
|
16
src/nodes/TS3Event/types.ts
Normal file
16
src/nodes/TS3Event/types.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { NodeDef, Node, EditorNodeProperties } from "node-red"
|
||||||
|
|
||||||
|
export interface Ts3EventProps {
|
||||||
|
configid: string
|
||||||
|
selection: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface Ts3EventData {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ts3EventConfig extends NodeDef, Ts3EventProps { }
|
||||||
|
|
||||||
|
export interface Ts3EventNode extends Node, Ts3EventData {}
|
||||||
|
|
||||||
|
export interface Ts3EventEditorNodeProperties extends EditorNodeProperties,Ts3EventProps {}
|
28
src/nodes/TS3Event/ui/editor.html
Normal file
28
src/nodes/TS3Event/ui/editor.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-globe"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-configid"><i class="fa fa-server"></i> Connection</label>
|
||||||
|
<input type="text" id="node-input-configid">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-configid"><i class="fa fa-terminal"></i> Event</label>
|
||||||
|
<select class="form-select" id="node-input-selection">
|
||||||
|
<option value="channelcreate" >ChannelCreate </option>
|
||||||
|
<option value="channeldelete" >ChannelDelete </option>
|
||||||
|
<option value="channeledit" >ChannelEdit </option>
|
||||||
|
<option value="channelmove" >ChannelMove </option>
|
||||||
|
<option value="clientconnect" >ClientConnect </option>
|
||||||
|
<option value="clientdisconnect" >ClientDisconnect </option>
|
||||||
|
<option value="clientmoved" >ClientMoved </option>
|
||||||
|
<option value="serveredit" >ServerEdit </option>
|
||||||
|
<option value="textmessage" >TextMessage </option>
|
||||||
|
<option value="tokenused" >TokenUsed </option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
You can find documentation for all events
|
||||||
|
<a target="_blank" href="https://multivit4min.github.io/TS3-NodeJS-Library/modules/types_events.html"> here</a>
|
||||||
|
</div>
|
1
src/nodes/TS3Event/ui/help.html
Normal file
1
src/nodes/TS3Event/ui/help.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>Listen on events on Teamspeak</p>
|
19
src/nodes/TS3Event/ui/index.ts
Normal file
19
src/nodes/TS3Event/ui/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { EditorRED } from "node-red"
|
||||||
|
import { Ts3EventEditorNodeProperties } from "../types"
|
||||||
|
|
||||||
|
declare const RED: EditorRED
|
||||||
|
|
||||||
|
RED.nodes.registerType<Ts3EventEditorNodeProperties>("TS3Event",{
|
||||||
|
category: "function",
|
||||||
|
color: "#a6bbcf",
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
configid: { type:"TS3Config", value: "" },
|
||||||
|
selection: { value:"" }
|
||||||
|
},
|
||||||
|
inputs: 0,
|
||||||
|
outputs: 1,
|
||||||
|
label: function() {
|
||||||
|
return this.name || "TS3 " + this.selection
|
||||||
|
}
|
||||||
|
})
|
71
tsconfig.json
Normal file
71
tsconfig.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
/* Basic Options */
|
||||||
|
// "incremental": true, /* Enable incremental compilation */
|
||||||
|
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||||
|
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||||
|
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||||
|
// "checkJs": true, /* Report errors in .js files. */
|
||||||
|
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||||
|
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||||
|
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||||
|
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||||
|
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||||
|
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||||
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
// "composite": true, /* Enable project compilation */
|
||||||
|
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||||
|
// "removeComments": true, /* Do not emit comments to output. */
|
||||||
|
// "noEmit": true, /* Do not emit outputs. */
|
||||||
|
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||||
|
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||||
|
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||||
|
|
||||||
|
/* Strict Type-Checking Options */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||||
|
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||||
|
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||||
|
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||||
|
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||||
|
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||||
|
|
||||||
|
/* Additional Checks */
|
||||||
|
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||||
|
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||||
|
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||||
|
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||||
|
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||||
|
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||||
|
|
||||||
|
/* Module Resolution Options */
|
||||||
|
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||||
|
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||||
|
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||||
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
|
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||||
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||||
|
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||||
|
|
||||||
|
/* Source Map Options */
|
||||||
|
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||||
|
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||||
|
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||||
|
|
||||||
|
/* Experimental Options */
|
||||||
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
|
|
||||||
|
/* Advanced Options */
|
||||||
|
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||||
|
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user