Hallo!
Dieser Artikel gibt eine kurze Einleitung in das Thema "TeamSpeak WebQuery API". Eine mögliche Alternative für die ts3admin.class und das TS3PHPFramework.
Zitat von TeamSpeak Server CHANGELOG## Server Release 3.12.0 18 March 2020
### Important
- New feature WebQuery that allows user to access to query system using http(s) and json.
Die WebQuery API ist seit März 2020 (TeamSpeak Server Version 3.12.0 und neuer) neben den klassischen ServerQuery Schnittstellen Telnet (query_protocols=raw; Standard Port 10011/TCP; unverschlüsselt) und SSH (query_protocols=ssh, Standard Port 10022/TCP; verschlüsselt) eine weitere Schnittstelle, um ServerQuery Befehle auszuführen.
Man kann mit Hilfe der WebQuery API zum Beispiel...
- einen neuen virtuellen Server anlegen, bearbeiten, stoppen, wieder starten und löschen
- Servereinstellungen ändern
- Statistiken, wie z.B. die Anzahl der insgesamt verbundenen Clients, Traffic, Latenzen, Packetloss und vieles mehr abfragen
- Clients ent-/bannen
- Servergruppen verwalten
- und vieles mehr.
Die WebQuery API ist dabei über einfaches HTTP (query_protocols=http; Standard Port 10080/TCP; unverschlüsselt) oder auch mit etwas Aufwand (man benötigt ein SSL Zertifikat) über HTTPS (query_protocols=https; Standard Port 10443/TCP; verschlüsselt) erreichbar. Übergeben tut man der API Header, POST/GET Variablen, JSON Datensätze und erhält eine entsprechende Antwort im JSON Format.
Insgesamt soll die WebQuery API gegenüber der klassischen raw und ssh Verbindung bei API Aufrufen um einiges schneller sein, da man dazwischen z.B. keine PHP Klasse(n) mehr benötigt und somit z.B. auch weniger Overhead beim programmieren hat.
So, aber wie fängt man jetzt an? Wie kann man diese WebQuery API benutzen?
Voraussetzung hierfür ist natürlich erstmal, dass die WebQuery API serverseitig aktiviert ist. Lest hierzu entweder in der Dokumentation vom Server nach oder fragt euren Hoster/Betreiber des TeamSpeak Servers, ob diese entsprechend aktiviert ist.
Danach benötigt ihr nur noch ein paar grundlegende Programmierfähigkeiten mit (REST) APIs, sowie einen API Key für den entsprechenden TeamSpeak Server, auf welchem ihr die API Befehle entsprechend ausführen wollt.
So einen API Key könnt ihr mittels eures ServerQuery Benutzer erstellen. Loggt euch hierfür z.B. mit SSH auf die ServerQuery Schnittstelle ein:
# ssh <ServerQuery Username>@<IP oder DNS Adresse vom TeamSpeak Server>
ssh serveradmin@mein-teamspeak.de
...und generiert euch einen API Key:
# apikeyadd scope={manage|write|read} [lifetime={days}] [cldbid={clientDBID}]
apikeyadd scope=manage lifetime=180
Das "Scope" definiert in diesem Fall die Rechte, die dieser API Key erhalten soll:
- manage: Maximaler Zugriff; Administrator-Rechte
- write: Einige Schreibrechte werden vergeben, jedoch nicht alle.
- read: Nur lesende Funktionen sind aufrufbar
Um herauszufinden, welches Scope man tatsächlich benötigt, kann man in diese Liste schauen:
Alles anzeigen{ "apikeyadd", { manage_scope, } },
{ "apikeydel", { manage_scope, } },
{ "apikeylist", { manage_scope, } },
{ "banadd", { manage_scope, write_scope, } },
{ "banclient", { manage_scope, write_scope, } },
{ "bandel", { manage_scope, write_scope, } },
{ "bandelall", { manage_scope, write_scope, } },
{ "banlist", { manage_scope, write_scope, read_scope, } },
{ "bindinglist", { manage_scope, read_scope } },
{ "channeladdperm", { manage_scope, } },
{ "channelclientaddperm", { manage_scope, } },
{ "channelclientdelperm", { manage_scope, } },
{ "channelclientpermlist", { manage_scope, write_scope, read_scope, } },
{ "channelcreate", { manage_scope, write_scope, } },
{ "channeldelete", { manage_scope, write_scope, } },
{ "channeldelperm", { manage_scope, } },
{ "channeledit", { manage_scope, write_scope, } },
{ "channelfind", { manage_scope, write_scope, read_scope, } },
{ "channelgroupadd", { manage_scope, write_scope, } },
{ "channelgroupaddperm", { manage_scope, } },
{ "channelgroupclientlist", { manage_scope, write_scope, read_scope, } },
{ "channelgroupcopy", { manage_scope, } },
{ "channelgroupdel", { manage_scope, } },
{ "channelgroupdelperm", { manage_scope, } },
{ "channelgrouplist", { manage_scope, write_scope, read_scope, } },
{ "channelgrouppermlist", { manage_scope, write_scope, read_scope, } },
{ "channelgrouprename", { manage_scope, } },
{ "channelinfo", { manage_scope, write_scope, read_scope, } },
{ "channellist", { manage_scope, write_scope, read_scope, } },
{ "channelmove", { manage_scope, write_scope, } },
{ "channelpermlist", { manage_scope, write_scope, read_scope, } },
{ "clientaddperm", { manage_scope, } },
{ "clientdbdelete", { manage_scope, write_scope, } },
{ "clientdbedit", { manage_scope, write_scope, } },
{ "clientdbfind", { manage_scope, write_scope, read_scope, } },
{ "clientdbinfo", { manage_scope, write_scope, read_scope, } },
{ "clientdblist", { manage_scope, write_scope, read_scope, } },
{ "clientdelperm", { manage_scope, } },
{ "clientedit", { manage_scope, write_scope, } },
{ "clientfind", { manage_scope, write_scope, read_scope, } },
{ "clientgetdbidfromuid", { manage_scope, write_scope, read_scope, } },
{ "clientgetids", { manage_scope, write_scope, read_scope, } },
{ "clientgetnamefromdbid", { manage_scope, write_scope, read_scope, } },
{ "clientgetnamefromuid", { manage_scope, write_scope, read_scope,} },
{ "clientgetuidfromclid", { manage_scope, write_scope, read_scope, } },
{ "clientinfo", { manage_scope, write_scope, read_scope, } },
{ "clientkick", { manage_scope, write_scope, } },
{ "clientlist", { manage_scope, write_scope, read_scope, } },
{ "clientmove", { manage_scope, write_scope, } },
{ "clientpermlist", { manage_scope, write_scope, read_scope, } },
{ "clientpoke", { manage_scope, write_scope, } },
{ "clientsetserverquerylogin", { manage_scope, write_scope, } },
{ "clientupdate", { manage_scope, write_scope, } },
{ "complainadd", { manage_scope, write_scope, } },
{ "complaindel", { manage_scope, write_scope, } },
{ "complaindelall", { manage_scope, write_scope, } },
{ "complainlist", { manage_scope, write_scope, read_scope, } },
{ "custominfo", { manage_scope, write_scope, read_scope, } },
{ "customsearch", { manage_scope, write_scope, read_scope, } },
{ "customset", { manage_scope, write_scope, } },
{ "customdelete", { manage_scope, write_scope, } },
{ "ftcreatedir", not_supported },
{ "ftdeletefile", not_supported },
{ "ftgetfileinfo", not_supported },
{ "ftgetfilelist", not_supported },
{ "ftinitdownload", not_supported },
{ "ftinitupload", not_supported },
{ "ftlist", not_supported },
{ "ftrenamefile", not_supported },
{ "ftstop", not_supported },
{ "gm", { manage_scope, } },
{ "help", { manage_scope, write_scope, read_scope, } },
{ "hostinfo", { manage_scope, write_scope, read_scope, } },
{ "instanceedit", { manage_scope, } },
{ "instanceinfo", { manage_scope, write_scope, read_scope, } },
{ "logadd", { manage_scope, write_scope, } },
{ "login", not_supported },
{ "logout", not_supported },
{ "logview", { manage_scope, write_scope, read_scope, } },
{ "messageadd", { manage_scope, write_scope, } },
{ "messagedel", { manage_scope, write_scope, } },
{ "messageget", { manage_scope, write_scope, read_scope, } },
{ "messagelist", { manage_scope, write_scope, read_scope, } },
{ "messageupdateflag", { manage_scope, write_scope, } },
{ "permfind", { manage_scope, write_scope, read_scope, } },
{ "permget", { manage_scope, write_scope, read_scope, } },
{ "permidgetbyname", { manage_scope, write_scope, read_scope, } },
{ "permissionlist", { manage_scope, write_scope, read_scope, } },
{ "permoverview", { manage_scope, write_scope, read_scope, } },
{ "permreset", { manage_scope, } },
{ "privilegekeyadd", { manage_scope, write_scope, } },
{ "privilegekeydelete", { manage_scope, write_scope, } },
{ "privilegekeylist", { manage_scope, write_scope, read_scope, } },
{ "privilegekeyuse", { manage_scope, write_scope, } },
{ "queryloginadd", { manage_scope, write_scope, } },
{ "querylogindel", { manage_scope, write_scope, } },
{ "queryloginlist", { manage_scope, write_scope, read_scope } },
{ "quit", not_supported },
{ "sendtextmessage", { manage_scope, write_scope, } },
{ "servercreate", { manage_scope, } },
{ "serverdelete", { manage_scope, } },
{ "serveredit", { manage_scope, write_scope, } },
{ "servergroupadd", { manage_scope, } },
{ "servergroupaddclient", { manage_scope, } },
{ "servergroupaddperm", { manage_scope, } },
{ "servergroupautoaddperm", { manage_scope, } },
{ "servergroupautodelperm", { manage_scope, } },
{ "servergroupclientlist", { manage_scope, } },
{ "servergroupcopy", { manage_scope, } },
{ "servergroupdel", { manage_scope, } },
{ "servergroupdelclient", { manage_scope, } },
{ "servergroupdelperm", { manage_scope, } },
{ "servergrouplist", { manage_scope, } },
{ "servergrouppermlist", { manage_scope, } },
{ "servergrouprename", { manage_scope, } },
{ "servergroupsbyclientid", { manage_scope, } },
{ "serveridgetbyport", { manage_scope, } },
{ "serverinfo", { manage_scope, } },
{ "serverlist", { manage_scope, } },
{ "servernotifyregister", not_supported },
{ "servernotifyunregister", not_supported },
{ "serverprocessstop", { manage_scope, } },
{ "serverrequestconnectioninfo", { manage_scope, write_scope, read_scope, } },
{ "serversnapshotcreate", { manage_scope, } },
{ "serversnapshotdeploy", { manage_scope, } },
{ "serverstart", { manage_scope, } },
{ "serverstop", { manage_scope, } },
{ "servertemppasswordadd", { manage_scope, write_scope, } },
{ "servertemppassworddel", { manage_scope, write_scope, } },
{ "servertemppasswordlist", { manage_scope, write_scope, read_scope, } },
{ "setclientchannelgroup", { manage_scope, write_scope, } },
{ "tokenadd", { manage_scope, write_scope, } },
{ "tokendelete", { manage_scope, write_scope, } },
{ "tokenlist", { manage_scope, write_scope, read_scope, } },
{ "tokenuse", { manage_scope, write_scope, } },
{ "use", not_supported },
{ "version", { manage_scope, write_scope, read_scope, } },
{ "whoami", { manage_scope, write_scope, read_scope, } },
Die Lifetime definiert, wie lange dieser API Key in Tagen gültig ist. 0 bedeutet, dass er nie abläuft.
Die optionale cldbid definiert dann noch, wer der Eigentümer dieses API Keys ist. Standardmäßig gehört er immer dem User, der ihn auch erstellt.
Gut. Ihr solltet jetzt einen API Key haben, der wie dieser z.B. aussieht: AQB8J1sfuvq4wFAFdo6rsfFq+jAQO9asz3zGq5X
Wie gesagt: Die API kann man auf verschiedenste Art und Weise ansprechen:
- curl
- PHP
- JavaScript
- jQuery
- NodeJS
- Postman
- ...
Wir werden das ganze von einer Befehlszeile aus machen, also nutzen wir in diesem Beispiel einfach mal curl (https://linux.die.net/man/1/curl).
Dann starten wir doch mal mit einem einfachen API Request: Wer bin ich, der sich hier per API eingeloggt hat?
❯ curl -s -H 'x-api-key: AQB8J1sfuvq4wFAFdo6rsfFq+jAQO9asz3zGq5X' 'http://127.0.0.1:10080/whoami'
{"body":[{"client_channel_id":"0","client_database_id":"1","client_id":"0","client_login_name":"<internal>","client_nickname":"","client_origin_server_id":"0","client_unique_identifier":"serveradmin","virtualserver_id":"0","virtualserver_port":"0","virtualserver_status":"unknown","virtualserver_unique_identifier":""}],"status":{"code":0,"message":"ok"}}
So, das hat schon mal funktioniert. Jetzt formatieren wir das JSON aber noch schön, damit es lesbarer ist. Das geht ganz einfach mit json_pp:
❯ curl -s -H 'x-api-key: AQB8J1sfuvq4wFAFdo6rsfFq+jAQO9asz3zGq5X' 'http://127.0.0.1:10080/whoami' | json_pp
{
"status" : {
"code" : 0,
"message" : "ok"
},
"body" : [
{
"client_origin_server_id" : "0",
"client_channel_id" : "0",
"virtualserver_unique_identifier" : "",
"virtualserver_id" : "0",
"virtualserver_status" : "unknown",
"virtualserver_port" : "0",
"client_unique_identifier" : "serveradmin",
"client_login_name" : "<internal>",
"client_database_id" : "1",
"client_nickname" : "",
"client_id" : "0"
}
]
}
Alles anzeigen
Wie wir sehen, bekommen wir von der API zwei "Blöcke" zurück:
- status: Hier kann man sehen und prüfen, ob der API Request erfolgreich war oder Fehler auftraten.
- body: Hier werden immer die angeforderten Daten von der API zurück geliefert. In unserem Beispiel von whoami.
Holen wir uns mal die Liste aller virtuellen Server auf dieser TeamSpeak Instanz:
❯ curl -s -H 'x-api-key: AQB8J1sfuvq4wFAFdo6rsfFq+jAQO9asz3zGq5X' 'http://127.0.0.1:10080/serverlist' | json_pp
{
"body" : [
{
"virtualserver_autostart" : "1",
"virtualserver_queryclientsonline" : "1",
"virtualserver_uptime" : "5490154",
"virtualserver_clientsonline" : "3",
"virtualserver_id" : "2",
"virtualserver_machine_id" : "1",
"virtualserver_maxclients" : "30",
"virtualserver_port" : "9987",
"virtualserver_status" : "online",
"virtualserver_name" : "TEST TeamSpeak"
},
{
"virtualserver_uptime" : "5490153",
"virtualserver_autostart" : "1",
"virtualserver_queryclientsonline" : "1",
"virtualserver_id" : "4",
"virtualserver_clientsonline" : "1",
"virtualserver_machine_id" : "1",
"virtualserver_maxclients" : "10",
"virtualserver_status" : "online",
"virtualserver_name" : "Mein TeamSpeak",
"virtualserver_port" : "9988"
},
{
"virtualserver_autostart" : "0",
"virtualserver_queryclientsonline" : "1",
"virtualserver_uptime" : "246850",
"virtualserver_clientsonline" : "1",
"virtualserver_id" : "5",
"virtualserver_machine_id" : "1",
"virtualserver_maxclients" : "10",
"virtualserver_port" : "9989",
"virtualserver_name" : "Best TeamSpeak EU",
"virtualserver_status" : "online virtual"
}
],
"status" : {
"message" : "ok",
"code" : 0
}
}
Alles anzeigen
Und jetzt mal die clientlist vom TeamSpeak Server mit der virtualserver_id = 4:
❯ curl -s -H 'x-api-key: AQB8J1sfuvq4wFAFdo6rsfFq+jAQO9asz3zGq5X' 'http://127.0.0.1:10080/4/clientlist' | json_pp
{
"status" : {
"code" : 0,
"message" : "ok"
},
"body" : [
{
"client_nickname" : "Sebbo",
"client_database_id" : "63",
"cid" : "52",
"clid" : "26679",
"client_type" : "0"
},
{
"cid" : "59",
"client_type" : "1",
"clid" : "22469",
"client_nickname" : "serveradmin",
"client_database_id" : "1"
}
]
}
Alles anzeigen
Je nachdem, was man da jetzt alles machen möchte, muss man sich die Dokumentation der ServerQuery Befehle anschauen und seinen API Request entsprechend bauen.
Weitere Beispiele und Informationen zur WebQuery API liefert z.B. der TeamSpeak Server in der mitgelieferten doc/webquery.md Datei.
Ansonsten könnt ihr auch gerne eure persönlichen Fragen in einem neuen Thema stellen.
In diesem Sinne: Happy Coding!