From 84b33085b9d7d80bb70920e56ab5668ec316995d Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 22 Feb 2019 17:35:19 +0100 Subject: [PATCH 01/26] Fixed environment variables --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4653a9c..451daac2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,6 @@ jobs: export GITHUB_RELEASE_TAG="This is a auto build release from travis"; export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - echo "Release commit: $TRAVIS_COMMIT"; export GITHUB_RELEASE_COMMIT="$TRAVIS_COMMIT"; - /tmp/git-release "Travis autobuild $TRAVIS_COMMIT" /tmp/build/packages/* /tmp/build/logs/*; + echo "Release commit: $TRAVIS_TAG"; export GITHUB_RELEASE_COMMIT="$TRAVIS_TAG"; + /tmp/git-release "Travis autobuild $TRAVIS_TAG" /tmp/build/packages/* /tmp/build/logs/*; if: branch = master \ No newline at end of file From cc10bdfd39bfeafaacfb6b2c98f8ea2522c47406 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Fri, 22 Feb 2019 17:49:54 +0100 Subject: [PATCH 02/26] Updated short tag parsing --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 451daac2..348b6726 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,10 @@ jobs: - "ls -lah /tmp/build/packages/" - "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" - > + export GIT_TAG_SHORT=$(git rev-parse --short HEAD) export GITHUB_RELEASE_TAG="This is a auto build release from travis"; export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - echo "Release commit: $TRAVIS_TAG"; export GITHUB_RELEASE_COMMIT="$TRAVIS_TAG"; - /tmp/git-release "Travis autobuild $TRAVIS_TAG" /tmp/build/packages/* /tmp/build/logs/*; + echo "Release commit: $GIT_TAG_SHORT"; export GITHUB_RELEASE_COMMIT="$GIT_TAG_SHORT"; + /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/*; if: branch = master \ No newline at end of file From 5c0b02057be28e46b79026f4ee7c077323d0202b Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:09:01 +0100 Subject: [PATCH 03/26] Updated travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 348b6726..d10c5c04 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,6 @@ jobs: export GITHUB_RELEASE_TAG="This is a auto build release from travis"; export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - echo "Release commit: $GIT_TAG_SHORT"; export GITHUB_RELEASE_COMMIT="$GIT_TAG_SHORT"; + echo "Release commit: $GIT_TAG_SHORT"; export GITHUB_RELEASE_COMMIT="master"; /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/*; if: branch = master \ No newline at end of file From a16ff713ea57db169454c7f10d0f06de8f55d019 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:21:31 +0100 Subject: [PATCH 04/26] Updated travis.yml --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d10c5c04..ad28a168 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,10 +22,11 @@ jobs: - "ls -lah /tmp/build/packages/" - "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" - > + export GIT_TAG=$(git rev-parse HEAD) export GIT_TAG_SHORT=$(git rev-parse --short HEAD) export GITHUB_RELEASE_TAG="This is a auto build release from travis"; export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - echo "Release commit: $GIT_TAG_SHORT"; export GITHUB_RELEASE_COMMIT="master"; + echo "Release commit: $GIT_TAG ($GIT_TAG_SHORT)"; export GITHUB_RELEASE_COMMIT="$GIT_TAG"; /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/*; if: branch = master \ No newline at end of file From 5be26aaa3a21ba55061283cfcfc6e1db8c576e67 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:35:00 +0100 Subject: [PATCH 05/26] Experimenting with git-release within travis.yml --- .travis.yml | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad28a168..8d5c0998 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,24 +4,26 @@ dist: trusty services: - docker -before_install: - - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - docker images - - docker pull $DOCKER_USERNAME/teaweb:build_new - - docker images +#before_install: + #- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + #- docker images + #- docker pull $DOCKER_USERNAME/teaweb:build_new + #- docker images jobs: include: - stage: "build" name: TeaWeb build master branch script: - - "mkdir -p /tmp/build" - - "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug" - - "ls -lah /tmp/build/" - - "ls -lah /tmp/build/logs/" - - "ls -lah /tmp/build/packages/" - - "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" + #- "mkdir -p /tmp/build" + #- "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug" + #- "ls -lah /tmp/build/" + #- "ls -lah /tmp/build/logs/" + #- "ls -lah /tmp/build/packages/" + #- "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" - > + mkdir -p /tmp/build/packages/; + echo "XXXX" > /tmp/build/packages/xxxxx; export GIT_TAG=$(git rev-parse HEAD) export GIT_TAG_SHORT=$(git rev-parse --short HEAD) export GITHUB_RELEASE_TAG="This is a auto build release from travis"; From 4e0c789dcb169a360463a71eff98911b8fe44a27 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:36:23 +0100 Subject: [PATCH 06/26] Updated travis --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d5c0998..29056118 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,11 +24,13 @@ jobs: - > mkdir -p /tmp/build/packages/; echo "XXXX" > /tmp/build/packages/xxxxx; - export GIT_TAG=$(git rev-parse HEAD) - export GIT_TAG_SHORT=$(git rev-parse --short HEAD) + export GIT_TAG=$(git rev-parse HEAD); + export GIT_TAG_SHORT=$(git rev-parse --short HEAD); + echo "Release commit: $GIT_TAG ($GIT_TAG_SHORT)"; + export GITHUB_RELEASE_TAG="This is a auto build release from travis"; export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - echo "Release commit: $GIT_TAG ($GIT_TAG_SHORT)"; export GITHUB_RELEASE_COMMIT="$GIT_TAG"; + export GITHUB_RELEASE_COMMIT="$GIT_TAG"; /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/*; if: branch = master \ No newline at end of file From 6271ab1bc1ac236a28a0005f59cf3fa8d825b078 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:41:15 +0100 Subject: [PATCH 07/26] Updates --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 29056118..eea64eb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ jobs: #- "ls -lah /tmp/build/" #- "ls -lah /tmp/build/logs/" #- "ls -lah /tmp/build/packages/" - #- "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" + - "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" - > mkdir -p /tmp/build/packages/; echo "XXXX" > /tmp/build/packages/xxxxx; From 3dbcad0b1167dfa5c6b9fb27ce4717b366a8d0fc Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:46:13 +0100 Subject: [PATCH 08/26] updated travis --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eea64eb4..9836e79d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,5 +32,7 @@ jobs: export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; export GITHUB_RELEASE_COMMIT="$GIT_TAG"; - /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/*; + /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/* \ + --commit "$GIT_TAG" \ + --tag "Hello World"; if: branch = master \ No newline at end of file From 27ba91a2304c43128d38e2b6ac44f1bc7242478f Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 10:52:07 +0100 Subject: [PATCH 09/26] updated travis --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9836e79d..7cb282a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,10 +29,10 @@ jobs: echo "Release commit: $GIT_TAG ($GIT_TAG_SHORT)"; export GITHUB_RELEASE_TAG="This is a auto build release from travis"; - export GITHUB_RELEASE_REPOSITORY="$TRAVIS_REPO_SLUG"; + export GITHUB_RELEASE_REPOSITORY="TeaSpeak/TeaWeb"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - export GITHUB_RELEASE_COMMIT="$GIT_TAG"; + export GITHUB_RELEASE_COMMIT="3dbcad0"; /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/* \ - --commit "$GIT_TAG" \ - --tag "Hello World"; + --commit "3dbcad0" \ + --tag "12312123"; if: branch = master \ No newline at end of file From 29f650fc27e0c05c64eed6f3b1a4f8d01e76121c Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 11:01:59 +0100 Subject: [PATCH 10/26] updated travis --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7cb282a7..207e57af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,9 @@ jobs: export GITHUB_RELEASE_REPOSITORY="TeaSpeak/TeaWeb"; export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; export GITHUB_RELEASE_COMMIT="3dbcad0"; - /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/* /tmp/build/logs/* \ + /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/xxxxx \ --commit "3dbcad0" \ - --tag "12312123"; + --tag "12312123" \ + --github-access-token "$GIT_AUTHTOKEN" \ + --github-repository "TeaSpeak/TeaWeb"; if: branch = master \ No newline at end of file From bd99e1bb558b1acf34628721067bbf5b3607a836 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 11:23:02 +0100 Subject: [PATCH 11/26] Release --help --- .travis.yml | 2 +- scripts/travis_deploy.sh | 70 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100755 scripts/travis_deploy.sh diff --git a/.travis.yml b/.travis.yml index 207e57af..87f2159e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ jobs: #- "ls -lah /tmp/build/" #- "ls -lah /tmp/build/logs/" #- "ls -lah /tmp/build/packages/" - - "wget https://github.com/buildkite/github-release/releases/download/v1.0/github-release-linux-amd64 -O /tmp/git-release -q; chmod +x /tmp/git-release;" + - "wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O git-release.gz -q && gunzip git-release.gz && chmod +x git-release;" - > mkdir -p /tmp/build/packages/; echo "XXXX" > /tmp/build/packages/xxxxx; diff --git a/scripts/travis_deploy.sh b/scripts/travis_deploy.sh new file mode 100755 index 00000000..f13a2cdd --- /dev/null +++ b/scripts/travis_deploy.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +if [[ -z "${GIT_AUTHTOKEN}" ]]; then + echo "Missing environment variable GIT_AUTHTOKEN. Please set it before usign this script!" + exit 1 +fi + +GIT_COMMIT_SHORT=$(git rev-parse --short HEAD) +GIT_COMMIT_LONG=$(git rev-parse HEAD) +echo "Deploying $GIT_COMMIT_SHORT ($GIT_COMMIT_LONG) to github." + +cd /tmp/ +if [[ ! -x git-release ]]; then + echo "Downloading github-release-linux (1.2.4)" + wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O git-release.gz -q; + [[ $? -eq 0 ]] || { + echo "Failed to download github-release-linux" + exit 1 + } + + gunzip git-release.gz && chmod +x git-release; + [[ $? -eq 0 ]] || { + echo "Failed to unzip github-release-linux" + exit 1 + } + + if [[ ! -x git-release ]]; then + echo "git-release isn't executable" + exit 1 + fi + echo "Download of github-release-linux (1.2.4) finished" +fi + +echo "Generating release" +./github-release release \ + --repo "TeaWeb" \ + --owner "TeaSpeak" \ + --token "${GIT_AUTHTOKEN}" \ + --title "Travis autobuild ${GIT_COMMIT_SHORT}" \ + --tag "${GIT_COMMIT_SHORT}" \ + --description "This is a autobuild release from travis" +[[ $? -eq 0 ]] || { + echo "Failed to generate git release" + exit 1 +} + +echo "Uploading release files" +folders=("/tmp/build/logs/" "/tmp/build/packages/") +for folder in "${folders[@]}"; do + echo "Scanning folder $folder" + for file in ${folder}*; do + if [[ -d ${file} ]]; then + echo " Skipping directory `basename $file` ($file)" + continue + fi + echo " Found entry $file. Uploading file."; + ./github-release upload \ + --repo "TeaWeb" \ + --owner "TeaSpeak" \ + --token "${GIT_AUTHTOKEN}" \ + --tag "${GIT_COMMIT_SHORT}" \ + --file "$file" \ + --name "`basename $file`" + + [[ $? -eq 0 ]] || { + echo "Failed to generate git release" + exit 1 + } + done +done \ No newline at end of file From daf1b03fc2ed43dd4a765ac55b284152320fb5d3 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 11:33:12 +0100 Subject: [PATCH 12/26] Updated travis and added a helper script --- .travis.yml | 15 +---------- scripts/travis_deploy.sh | 58 +++++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index 87f2159e..57681cd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,21 +20,8 @@ jobs: #- "ls -lah /tmp/build/" #- "ls -lah /tmp/build/logs/" #- "ls -lah /tmp/build/packages/" - - "wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O git-release.gz -q && gunzip git-release.gz && chmod +x git-release;" - > mkdir -p /tmp/build/packages/; echo "XXXX" > /tmp/build/packages/xxxxx; - export GIT_TAG=$(git rev-parse HEAD); - export GIT_TAG_SHORT=$(git rev-parse --short HEAD); - echo "Release commit: $GIT_TAG ($GIT_TAG_SHORT)"; - - export GITHUB_RELEASE_TAG="This is a auto build release from travis"; - export GITHUB_RELEASE_REPOSITORY="TeaSpeak/TeaWeb"; - export GITHUB_RELEASE_ACCESS_TOKEN="$GIT_AUTHTOKEN"; - export GITHUB_RELEASE_COMMIT="3dbcad0"; - /tmp/git-release "Travis autobuild $GIT_TAG_SHORT" /tmp/build/packages/xxxxx \ - --commit "3dbcad0" \ - --tag "12312123" \ - --github-access-token "$GIT_AUTHTOKEN" \ - --github-repository "TeaSpeak/TeaWeb"; + ./scripts/travis_deploy.sh if: branch = master \ No newline at end of file diff --git a/scripts/travis_deploy.sh b/scripts/travis_deploy.sh index f13a2cdd..9f30d663 100755 --- a/scripts/travis_deploy.sh +++ b/scripts/travis_deploy.sh @@ -9,30 +9,39 @@ GIT_COMMIT_SHORT=$(git rev-parse --short HEAD) GIT_COMMIT_LONG=$(git rev-parse HEAD) echo "Deploying $GIT_COMMIT_SHORT ($GIT_COMMIT_LONG) to github." -cd /tmp/ -if [[ ! -x git-release ]]; then - echo "Downloading github-release-linux (1.2.4)" - wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O git-release.gz -q; - [[ $? -eq 0 ]] || { - echo "Failed to download github-release-linux" - exit 1 - } +GIT_RELEASE_EXECUTABLE="/tmp/git-release" +if [[ ! -x ${GIT_RELEASE_EXECUTABLE} ]]; then + if [[ ! -f ${GIT_RELEASE_EXECUTABLE} ]]; then + echo "Downloading github-release-linux (1.2.4)" - gunzip git-release.gz && chmod +x git-release; - [[ $? -eq 0 ]] || { - echo "Failed to unzip github-release-linux" - exit 1 - } + if [[ -f /tmp/git-release.gz ]]; then + rm /tmp/git-release.gz + fi + wget https://github.com/tfausak/github-release/releases/download/1.2.4/github-release-linux.gz -O /tmp/git-release.gz -q; + [[ $? -eq 0 ]] || { + echo "Failed to download github-release-linux" + exit 1 + } - if [[ ! -x git-release ]]; then + gunzip /tmp/git-release.gz && chmod +x /tmp/git-release; + [[ $? -eq 0 ]] || { + echo "Failed to unzip github-release-linux" + exit 1 + } + + echo "Download of github-release-linux (1.2.4) finished" + else + chmod +x ${GIT_RELEASE_EXECUTABLE} + fi + + if [[ ! -x ${GIT_RELEASE_EXECUTABLE} ]]; then echo "git-release isn't executable" exit 1 fi - echo "Download of github-release-linux (1.2.4) finished" fi echo "Generating release" -./github-release release \ +${GIT_RELEASE_EXECUTABLE} release \ --repo "TeaWeb" \ --owner "TeaSpeak" \ --token "${GIT_AUTHTOKEN}" \ @@ -46,15 +55,22 @@ echo "Generating release" echo "Uploading release files" folders=("/tmp/build/logs/" "/tmp/build/packages/") +uploaded_files=() + for folder in "${folders[@]}"; do echo "Scanning folder $folder" + if [[ ! -d ${folder} ]]; then + continue; + fi + for file in ${folder}*; do if [[ -d ${file} ]]; then echo " Skipping directory `basename $file` ($file)" continue fi - echo " Found entry $file. Uploading file."; - ./github-release upload \ + echo " Found entry `basename $file` ($file). Uploading file."; + + ${GIT_RELEASE_EXECUTABLE} upload \ --repo "TeaWeb" \ --owner "TeaSpeak" \ --token "${GIT_AUTHTOKEN}" \ @@ -66,5 +82,9 @@ for folder in "${folders[@]}"; do echo "Failed to generate git release" exit 1 } + + uploaded_files+="$file" done -done \ No newline at end of file +done + +echo "Successfully uploaded ${#uploaded_files[@]} files." \ No newline at end of file From d07b378194137b0d84723e88c54be6eaf5ce0f9a Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 11:38:59 +0100 Subject: [PATCH 13/26] Updated travis script --- scripts/travis_deploy.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scripts/travis_deploy.sh b/scripts/travis_deploy.sh index 9f30d663..b35f0483 100755 --- a/scripts/travis_deploy.sh +++ b/scripts/travis_deploy.sh @@ -56,6 +56,7 @@ ${GIT_RELEASE_EXECUTABLE} release \ echo "Uploading release files" folders=("/tmp/build/logs/" "/tmp/build/packages/") uploaded_files=() +failed_files=() for folder in "${folders[@]}"; do echo "Scanning folder $folder" @@ -78,13 +79,15 @@ for folder in "${folders[@]}"; do --file "$file" \ --name "`basename $file`" - [[ $? -eq 0 ]] || { + [[ $? -eq 0 ]] && { + echo " Uploaded."; + uploaded_files+="$file" + } || { echo "Failed to generate git release" - exit 1 + failed_files+="$file" } - - uploaded_files+="$file" done done -echo "Successfully uploaded ${#uploaded_files[@]} files." \ No newline at end of file +echo "Successfully uploaded ${#uploaded_files[@]} files. ${#failed_files[@]} uploads failed." +exit 0 \ No newline at end of file From fbf820377c787520f9258bf71afcd751096767e9 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 11:39:29 +0100 Subject: [PATCH 14/26] Reenabled full travis build --- .travis.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 57681cd1..547787c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,22 +4,22 @@ dist: trusty services: - docker -#before_install: - #- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - #- docker images - #- docker pull $DOCKER_USERNAME/teaweb:build_new - #- docker images +before_install: + - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + - docker images + - docker pull $DOCKER_USERNAME/teaweb:build_new + - docker images jobs: include: - stage: "build" name: TeaWeb build master branch script: - #- "mkdir -p /tmp/build" - #- "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug" - #- "ls -lah /tmp/build/" - #- "ls -lah /tmp/build/logs/" - #- "ls -lah /tmp/build/packages/" + - "mkdir -p /tmp/build" + - "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug" + - "ls -lah /tmp/build/" + - "ls -lah /tmp/build/logs/" + - "ls -lah /tmp/build/packages/" - > mkdir -p /tmp/build/packages/; echo "XXXX" > /tmp/build/packages/xxxxx; From e75bbeabefa6dbe89229980fa726c437bc125667 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 14:15:22 +0100 Subject: [PATCH 15/26] Restructuring of the connection part --- .travis.yml | 5 +- shared/js/FileManager.ts | 27 +- shared/js/chat.ts | 13 +- shared/js/client.ts | 20 +- shared/js/connection.ts | 1336 ----------------- shared/js/connection/CommandHandler.ts | 649 ++++++++ shared/js/connection/CommandHelper.ts | 311 ++++ shared/js/connection/ConnectionBase.ts | 148 ++ shared/js/connection/HandshakeHandler.ts | 108 ++ shared/js/connection/ServerConnection.ts | 377 +++++ shared/js/load.ts | 12 +- shared/js/permission/GroupManager.ts | 33 +- shared/js/permission/PermissionManager.ts | 44 +- shared/js/profiles/ConnectionProfile.ts | 2 +- shared/js/profiles/Identity.ts | 29 +- shared/js/profiles/identities/NameIdentity.ts | 23 +- .../profiles/identities/TeaForumIdentity.ts | 24 +- .../profiles/identities/TeamSpeakIdentity.ts | 24 +- shared/js/ui/channel.ts | 14 +- shared/js/ui/client.ts | 58 +- shared/js/ui/frames/ControlBar.ts | 12 +- shared/js/ui/frames/SelectedItemInfo.ts | 44 +- shared/js/ui/modal/ModalBanList.ts | 10 +- shared/js/ui/modal/ModalPermissionEdit.ts | 40 +- shared/js/ui/modal/ModalPlaylistEdit.ts | 8 +- shared/js/ui/modal/ModalPlaylistList.ts | 4 +- shared/js/ui/modal/ModalQueryManage.ts | 33 +- shared/js/ui/server.ts | 4 +- shared/js/ui/view.ts | 22 +- shared/js/voice/AudioController.ts | 2 +- 30 files changed, 1917 insertions(+), 1519 deletions(-) delete mode 100644 shared/js/connection.ts create mode 100644 shared/js/connection/CommandHandler.ts create mode 100644 shared/js/connection/CommandHelper.ts create mode 100644 shared/js/connection/ConnectionBase.ts create mode 100644 shared/js/connection/HandshakeHandler.ts create mode 100644 shared/js/connection/ServerConnection.ts diff --git a/.travis.yml b/.travis.yml index 547787c2..b77c2235 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,5 @@ jobs: - "ls -lah /tmp/build/" - "ls -lah /tmp/build/logs/" - "ls -lah /tmp/build/packages/" - - > - mkdir -p /tmp/build/packages/; - echo "XXXX" > /tmp/build/packages/xxxxx; - ./scripts/travis_deploy.sh + - "./scripts/travis_deploy.sh" if: branch = master \ No newline at end of file diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index ef5b5875..4006529b 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -109,7 +109,7 @@ class DownloadFileTransfer { } } -class FileManager { +class FileManager extends connection.AbstractCommandHandler { handle: TSClient; icons: IconManager; avatars: AvatarManager; @@ -119,13 +119,28 @@ class FileManager { private downloadCounter : number = 0; constructor(client: TSClient) { + super(client.serverConnection); + this.handle = client; this.icons = new IconManager(this); this.avatars = new AvatarManager(this); - this.handle.serverConnection.commandHandler["notifyfilelist"] = this.notifyFileList.bind(this); - this.handle.serverConnection.commandHandler["notifyfilelistfinished"] = this.notifyFileListFinished.bind(this); - this.handle.serverConnection.commandHandler["notifystartdownload"] = this.notifyStartDownload.bind(this); + this.connection.command_handler_boss().register_handler(this); + } + + handle_command(command: connection.ServerCommand): boolean { + switch (command.command) { + case "notifyfilelist": + this.notifyFileList(command.arguments); + return true; + case "notifyfilelistfinished": + this.notifyFileListFinished(command.arguments); + return true; + case "notifystartdownload": + this.notifyStartDownload(command.arguments); + return true; + } + return false; } @@ -140,7 +155,7 @@ class FileManager { req.callback = accept; _this.listRequests.push(req); - _this.handle.serverConnection.sendCommand("ftgetfilelist", {"path": path, "cid": (channel ? channel.channelId : "0"), "cpw": (password ? password : "")}).then(() => {}).catch(reason => { + _this.handle.serverConnection.send_command("ftgetfilelist", {"path": path, "cid": (channel ? channel.channelId : "0"), "cpw": (password ? password : "")}).then(() => {}).catch(reason => { _this.listRequests.remove(req); if(reason instanceof CommandResult) { if(reason.id == 0x0501) { @@ -197,7 +212,7 @@ class FileManager { this.pendingDownloadTransfers.push(transfer); return new Promise((resolve, reject) => { transfer["_promiseCallback"] = resolve; - _this.handle.serverConnection.sendCommand("ftinitdownload", { + _this.handle.serverConnection.send_command("ftinitdownload", { "path": path, "name": file, "cid": (channel ? channel.channelId : "0"), diff --git a/shared/js/chat.ts b/shared/js/chat.ts index 83df61f1..bf76bf3e 100644 --- a/shared/js/chat.ts +++ b/shared/js/chat.ts @@ -354,7 +354,13 @@ class ChatBox { chat.serverChat().appendError(tr("Could not send chant message (Not connected)")); return; } - globalClient.serverConnection.sendMessage(text, ChatType.SERVER); + globalClient.serverConnection.command_helper.sendMessage(text, ChatType.SERVER).catch(error => { + if(error instanceof CommandResult) + return; + + chat.serverChat().appendMessage(tr("Failed to send text message.")); + console.error(tr("Failed to send server text message: %o"), error); + }); }; this.serverChat().name = tr("Server chat"); @@ -364,7 +370,10 @@ class ChatBox { return; } - globalClient.serverConnection.sendMessage(text, ChatType.CHANNEL, globalClient.getClient().currentChannel()); + globalClient.serverConnection.command_helper.sendMessage(text, ChatType.CHANNEL, globalClient.getClient().currentChannel()).catch(error => { + chat.channelChat().appendMessage(tr("Failed to send text message.")); + console.error(tr("Failed to send channel text message: %o"), error); + }); }; this.channelChat().name = tr("Channel chat"); diff --git a/shared/js/client.ts b/shared/js/client.ts index a529ae14..179deb91 100644 --- a/shared/js/client.ts +++ b/shared/js/client.ts @@ -2,7 +2,6 @@ /// /// /// -/// /// /// /// @@ -49,7 +48,7 @@ enum ViewReasonId { class TSClient { channelTree: ChannelTree; - serverConnection: ServerConnection; + serverConnection: connection.ServerConnection; voiceConnection: VoiceConnection; fileManager: FileManager; selectInfo: InfoBar; @@ -65,7 +64,7 @@ class TSClient { constructor() { this.selectInfo = new InfoBar(this, $("#select_info")); this.channelTree = new ChannelTree(this, $("#channelTree")); - this.serverConnection = new ServerConnection(this); + this.serverConnection = new connection.ServerConnection(this); this.fileManager = new FileManager(this); this.permissions = new PermissionManager(this); this.groups = new GroupManager(this); @@ -101,12 +100,15 @@ class TSClient { if(password && !password.hashed) { helpers.hashPassword(password.password).then(password => { - this.serverConnection.startConnection({host, port}, new HandshakeHandler(profile, name, password)); + /* errors will be already handled via the handle disconnect thing */ + this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password)); }).catch(error => { createErrorModal(tr("Error while hashing password"), tr("Failed to hash server password!
") + error).open(); }) - } else - this.serverConnection.startConnection({host, port}, new HandshakeHandler(profile, name, password ? password.password : undefined)); + } else { + /* errors will be already handled via the handle disconnect thing */ + this.serverConnection.connect({host, port}, new connection.HandshakeHandler(profile, name, password ? password.password : undefined)); + } } @@ -122,7 +124,7 @@ class TSClient { return this._clientId; } - getServerConnection() : ServerConnection { return this.serverConnection; } + getServerConnection() : connection.ServerConnection { return this.serverConnection; } /** @@ -133,7 +135,7 @@ class TSClient { this.channelTree.registerClient(this._ownEntry); settings.setServer(this.channelTree.server); this.permissions.requestPermissionList(); - this.serverConnection.sendCommand("channelsubscribeall"); + this.serverConnection.send_command("channelsubscribeall"); if(this.groups.serverGroups.length == 0) this.groups.requestGroups(); this.controlBar.updateProperties(); @@ -142,7 +144,7 @@ class TSClient { } get connected() : boolean { - return !!this.serverConnection && this.serverConnection.connected; + return this.serverConnection && this.serverConnection.connected(); } private certAcceptUrl() { diff --git a/shared/js/connection.ts b/shared/js/connection.ts deleted file mode 100644 index d9f5dbdd..00000000 --- a/shared/js/connection.ts +++ /dev/null @@ -1,1336 +0,0 @@ -/// -/// -/// -/// - -import noExitRuntime = Module.noExitRuntime; - -enum ErrorID { - PERMISSION_ERROR = 2568, - EMPTY_RESULT = 0x0501, - PLAYLIST_IS_IN_USE = 0x2103 -} - -class CommandResult { - success: boolean; - id: number; - message: string; - extra_message: string; - - json: any; - - constructor(json) { - this.json = json; - this.id = json["id"]; - this.message = json["msg"]; - - this.extra_message = ""; - if(json["extra_msg"]) this.extra_message = json["extra_msg"]; - - this.success = this.id == 0; - } -} - -class ReturnListener { - resolve: (value?: T | PromiseLike) => void; - reject: (reason?: any) => void; - code: string; - - timeout: NodeJS.Timer; -} - -class ServerConnection { - _client: TSClient; - _remote_address: ServerAddress; - _socket: WebSocket; - _connectionState: ConnectionState = ConnectionState.UNCONNECTED; - _handshakeHandler: HandshakeHandler; - commandHandler: ConnectionCommandHandler; - - readonly helper: CommandHelper; - private _connectTimeoutHandler: NodeJS.Timer = undefined; - private _connected: boolean = false; - private _retCodeIdx: number; - private _retListener: ReturnListener[]; - - constructor(client : TSClient) { - this._client = client; - - this._socket = null; - this.commandHandler = new ConnectionCommandHandler(this); - this.helper = new CommandHelper(this); - this._retCodeIdx = 0; - this._retListener = []; - } - - on_connect: () => void = () => { - console.log(tr("Socket connected")); - chat.serverChat().appendMessage(tr("Logging in...")); - this._handshakeHandler.startHandshake(); - }; - - private generateReturnCode() : string { - return (this._retCodeIdx++).toString(); - } - - startConnection(address : ServerAddress, handshake: HandshakeHandler, timeout: number = 1000) { - if(this._connectTimeoutHandler) { - clearTimeout(this._connectTimeoutHandler); - this._connectTimeoutHandler = null; - this.disconnect(); - } - this.updateConnectionState(ConnectionState.CONNECTING); - this._remote_address = address; - this._handshakeHandler = handshake; - this._handshakeHandler.setConnection(this); - this._connected = false; - chat.serverChat().appendMessage(tr("Connecting to {0}:{1}"), true, address.host, address.port); - - const self = this; - try { - this._connectTimeoutHandler = setTimeout(() => { - console.log(tr("Connect timeout triggered!")); - this.disconnect(); - this._client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); - }, timeout); - let sockCpy; - this._socket = (sockCpy = new WebSocket('wss://' + address.host + ":" + address.port)); - clearTimeout(this._connectTimeoutHandler); - this._connectTimeoutHandler = null; - if(this._socket != sockCpy) return; //Connect timeouted - - this._socket.onopen = () => { - if(this._socket != sockCpy) return; - this._connected = true; - this.on_connect(); - }; - - this._socket.onclose = event => { - if(this._socket != sockCpy) return; - this._client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, { - code: event.code, - reason: event.reason, - event: event - }); - }; - - this._socket.onerror = e => { - if(this._socket != sockCpy) return; - console.log(tr("Got error: (%s)"), self._socket.readyState); - console.log(e); - }; - - this._socket.onmessage = msg => { - if(this._socket != sockCpy) return; - self.handleWebSocketMessage(msg.data); - }; - this.updateConnectionState(ConnectionState.INITIALISING); - } catch (e) { - this.disconnect(); - this._client.handleDisconnect(DisconnectReason.CONNECT_FAILURE, e); - } - } - - updateConnectionState(state: ConnectionState) { - this._connectionState = state; - this._client.controlBar.update_connection_state(); - } - - disconnect() : boolean { - if(this._connectionState == ConnectionState.UNCONNECTED) return false; - this.updateConnectionState(ConnectionState.UNCONNECTED); - - if(this._socket) this._socket.close(3000 + 0xFF, tr("request disconnect")); - this._socket = null; - for(let future of this._retListener) - future.reject(tr("Connection closed")); - this._retListener = []; - this._retCodeIdx = 0; - this._connected = false; - return true; - } - - private handleWebSocketMessage(data) { - if(typeof(data) === "string") { - let json; - try { - json = JSON.parse(data); - } catch(e) { - console.error(tr("Could not parse message json!")); - alert(e); // error in the above string (in this case, yes)! - return; - } - if(json["type"] === undefined) { - console.log(tr("Missing data type!")); - return; - } - if(json["type"] === "command") this.handleCommand(json); - else if(json["type"] === "WebRTC") this._client.voiceConnection.handleControlPacket(json); - else { - console.log(tr("Unknown command type %o"), json["type"]); - } - } - } - - handleCommand(json) { - let group = log.group(log.LogType.DEBUG, LogCategory.NETWORKING, tr("Handling command '%s'"), json["command"]); - group.log(tr("Handling command '%s'"), json["command"]); - group.group(log.LogType.TRACE, tr("Json:")).collapsed(true).log("%o", json).end(); - - try { - let fn = this.commandHandler[json["command"]]; - if(fn === undefined) { - group.log(tr("Missing command '%s'"), json["command"]); - return; - } - fn.call(this.commandHandler, json["data"]); - } finally { - group.end(); - } - } - - sendData(data: any) { //TODO check stuff? - this._socket.send(data); - } - - private commandiefy(input: any) : string { - return JSON.stringify(input, (key, value) => { - switch (typeof value) { - case "boolean": return value == true ? "1" : "0"; - case "function": return value(); - default: - return value; - } - }); - } - - sendCommand(command: string, data: any = {}, flags: string[] = [], logResult: boolean = true) : Promise { - const _this = this; - let result = new Promise((resolve, failed) => { - let _data = $.isArray(data) ? data : [data]; - let retCode = _data[0]["return_code"] !== undefined ? _data[0].return_code : _this.generateReturnCode(); - _data[0].return_code = retCode; - - let listener = new ReturnListener(); - listener.resolve = resolve; - listener.reject = failed; - listener.code = retCode; - listener.timeout = setTimeout(() => { - _this._retListener.remove(listener); - listener.reject("timeout"); - }, 1500); - this._retListener.push(listener); - - this._socket.send(this.commandiefy({ - "type": "command", - "command": command, - "data": _data, - "flags": flags.filter(entry => entry.length != 0) - })); - }); - return new Promise((resolve, failed) => { - result.then(resolve).catch(ex => { - if(logResult) { - if(ex instanceof CommandResult) { - let res = ex; - if(!res.success) { - if(res.id == 2568) { //Permission error - res.message = tr("Insufficient client permissions. Failed on permission ") + this._client.permissions.resolveInfo(res.json["failed_permid"] as number).name; - chat.serverChat().appendError(tr("Insufficient client permissions. Failed on permission {}"), this._client.permissions.resolveInfo(res.json["failed_permid"] as number).name); - sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); - } else { - chat.serverChat().appendError(res.extra_message.length == 0 ? res.message : res.extra_message); - } - } - } else if(typeof(ex) === "string") { - chat.serverChat().appendError(tr("Command execution results in ") + ex); - } else { - console.error(tr("Invalid promise result type: %o. Result:"), typeof (ex)); - console.error(ex); - } - } - failed(ex); - }) - }); - } - - get connected() : boolean { - return this._socket && this._socket.readyState == WebSocket.OPEN; - } - - /** - * HELPER METHODS - */ - joinChannel(channel: ChannelEntry, password: string = "") : Promise { - return this.sendCommand("clientmove", [{ - "clid": this._client.getClientId(), - "cid": channel.getChannelId(), - "cpw": password - }]) - } - - sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { - if(type == ChatType.SERVER) - return this.sendCommand("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); - else if(type == ChatType.CHANNEL) - return this.sendCommand("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); - else if(type == ChatType.CLIENT) - return this.sendCommand("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); - } - - updateClient(key: string, value: string) : Promise { - let data = {}; - data[key] = value; - return this.sendCommand("clientupdate", data); - } -} - -interface HandshakeIdentityHandler { - connection: ServerConnection; - - start_handshake(); - register_callback(callback: (success: boolean, message?: string) => any); -} - -class HandshakeHandler { - private connection: ServerConnection; - private handshake_handler: HandshakeIdentityHandler; - private failed = false; - - readonly profile: profiles.ConnectionProfile; - readonly name: string; - readonly server_password: string; - - constructor(profile: profiles.ConnectionProfile, name: string, password: string) { - this.profile = profile; - this.server_password = password; - this.name = name; - } - - setConnection(con: ServerConnection) { - this.connection = con; - } - - startHandshake() { - this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection); - if(!this.handshake_handler) { - this.handshake_failed("failed to create identity handler"); - return; - } - - this.handshake_handler.register_callback((flag, message) => { - if(flag) - this.handshake_finished(); - else - this.handshake_failed(message); - }); - - this.handshake_handler.start_handshake(); - } - - private handshake_failed(message: string) { - if(this.failed) return; - - this.failed = true; - this.connection._client.handleDisconnect(DisconnectReason.HANDSHAKE_FAILED, message); - } - - private handshake_finished(version?: string) { - if(native_client && window["native"] && native.client_version && !version) { - native.client_version() - .then( this.handshake_finished.bind(this)) - .catch(error => { - console.error(tr("Failed to get version:")); - console.error(error); - this.handshake_finished("?.?.?"); - }); - return; - } - - const git_version = settings.static_global("version", "unknown"); - const browser_name = (navigator.browserSpecs || {})["name"] || " "; - let data = { - //TODO variables! - client_nickname: this.name, - client_platform: (browser_name ? browser_name + " " : "") + navigator.platform, - client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")", - - client_server_password: this.server_password, - client_browser_engine: navigator.product - }; - - if(version) { - data.client_version = "TeaClient "; - data.client_version += " " + version; - - const os = require("os"); - const arch_mapping = { - "x32": "32bit", - "x64": "64bit" - }; - - data.client_version += " " + (arch_mapping[os.arch()] || os.arch()); - - const os_mapping = { - "win32": "Windows", - "linux": "Linux" - }; - data.client_platform = (os_mapping[os.platform()] || os.platform()); - } - - this.connection.sendCommand("clientinit", data).catch(error => { - this.connection.disconnect(); - if(error instanceof CommandResult) { - if(error.id == 1028) { - this.connection._client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); - } else { - - this.connection._client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); - } - } - }); - } -} - -interface ClientNameInfo { - //cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9 - client_unique_id: string; - client_nickname: string; - client_database_id: number; -} - -interface ClientNameFromUid { - promise: LaterPromise, - keys: string[], - response: ClientNameInfo[] -} - -interface QueryListEntry { - username: string; - unique_id: string; - bounded_server: number; -} - -interface QueryList { - flag_own: boolean; - flag_all: boolean; - - queries: QueryListEntry[]; -} - -interface Playlist { - playlist_id: number; - playlist_bot_id: number; - playlist_title: string; - playlist_type: number; - playlist_owner_dbid: number; - playlist_owner_name: string; - - needed_power_modify: number; - needed_power_permission_modify: number; - needed_power_delete: number; - needed_power_song_add: number; - needed_power_song_move: number; - needed_power_song_remove: number; -} - -interface PlaylistInfo { - playlist_id: number, - playlist_title: string, - playlist_description: string, - playlist_type: number, - - playlist_owner_dbid: number, - playlist_owner_name: string, - - playlist_flag_delete_played: boolean, - playlist_flag_finished: boolean, - playlist_replay_mode: number, - playlist_current_song_id: number, -} - -interface PlaylistSong { - song_id: number; - song_previous_song_id: number; - song_invoker: string; - song_url: string; - song_url_loader: string; - song_loaded: boolean; - song_metadata: string; -} - -class CommandHelper { - readonly connection: ServerConnection; - - private _callbacks_namefromuid: ClientNameFromUid[] = []; - private _who_am_i: any; - - constructor(connection) { - this.connection = connection; - this.connection.commandHandler["notifyclientnamefromuid"] = this.handle_notifyclientnamefromuid.bind(this); - } - - info_from_uid(...uid: string[]) : Promise { - let uids = [...uid]; - for(let p of this._callbacks_namefromuid) - if(p.keys == uids) return p.promise; - - let req: ClientNameFromUid = {} as any; - req.keys = uids; - req.response = new Array(uids.length); - req.promise = new LaterPromise(); - - for(let uid of uids) { - this.connection.sendCommand("clientgetnamefromuid", { - cluid: uid - }).catch(req.promise.function_rejected()); - } - - this._callbacks_namefromuid.push(req); - return req.promise; - } - - request_query_list(server_id: number = undefined) : Promise { - return new Promise((resolve, reject) => { - this.connection.commandHandler["notifyquerylist"] = json => { - const result = {} as QueryList; - - result.flag_all = json[0]["flag_all"]; - result.flag_own = json[0]["flag_own"]; - result.queries = []; - - for(const entry of json) { - const rentry = {} as QueryListEntry; - rentry.bounded_server = entry["client_bounded_server"]; - rentry.username = entry["client_login_name"]; - rentry.unique_id = entry["client_unique_identifier"]; - - result.queries.push(rentry); - } - - resolve(result); - this.connection.commandHandler["notifyquerylist"] = undefined; - }; - - let data = {}; - if(server_id !== undefined) - data["server_id"] = server_id; - - this.connection.sendCommand("querylist", data).catch(error => { - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - resolve(undefined); - this.connection.commandHandler["notifyquerylist"] = undefined; - return; - } - } - reject(error); - }) - }); - } - - request_playlist_list() : Promise { - return new Promise((resolve, reject) => { - const notify_handler = json => { - const result: Playlist[] = []; - - for(const entry of json) { - try { - result.push({ - playlist_id: parseInt(entry["playlist_id"]), - playlist_bot_id: parseInt(entry["playlist_bot_id"]), - playlist_title: entry["playlist_title"], - playlist_type: parseInt(entry["playlist_type"]), - playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), - playlist_owner_name: entry["playlist_owner_name"], - - needed_power_modify: parseInt(entry["needed_power_modify"]), - needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), - needed_power_delete: parseInt(entry["needed_power_delete"]), - needed_power_song_add: parseInt(entry["needed_power_song_add"]), - needed_power_song_move: parseInt(entry["needed_power_song_move"]), - needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); - } - } - - this.connection.commandHandler.unset_handler("notifyplaylistlist", notify_handler); - resolve(result); - }; - - this.connection.commandHandler.set_handler("notifyplaylistlist", notify_handler); - this.connection.sendCommand("playlistlist").catch(error => { - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - this.connection.commandHandler.unset_handler("notifyplaylistlist", notify_handler); - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_songs(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const notify_handler = json => { - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); - return; - } - - const result: PlaylistSong[] = []; - - for(const entry of json) { - try { - result.push({ - song_id: parseInt(entry["song_id"]), - song_invoker: entry["song_invoker"], - song_previous_song_id: parseInt(entry["song_previous_song_id"]), - song_url: entry["song_url"], - song_url_loader: entry["song_url_loader"], - - song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", - song_metadata: entry["song_metadata"] - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); - } - } - - this.connection.commandHandler.unset_handler("notifyplaylistsonglist", notify_handler); - resolve(result); - }; - - this.connection.commandHandler.set_handler("notifyplaylistsonglist", notify_handler); - this.connection.sendCommand("playlistsonglist", {playlist_id: playlist_id}).catch(error => { - if(error instanceof CommandResult) { - if(error.id == ErrorID.EMPTY_RESULT) { - this.connection.commandHandler.unset_handler("notifyplaylistsonglist", notify_handler); - resolve([]); - return; - } - } - reject(error); - }) - }); - } - - request_playlist_info(playlist_id: number) : Promise { - return new Promise((resolve, reject) => { - const notify_handler = json => { - if(json[0]["playlist_id"] != playlist_id) { - log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); - return; - } - - json = json[0]; - - try { - //resolve - resolve({ - playlist_id: parseInt(json["playlist_id"]), - playlist_title: json["playlist_title"], - playlist_description: json["playlist_description"], - playlist_type: parseInt(json["playlist_type"]), - - playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), - playlist_owner_name: json["playlist_owner_name"], - - playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", - playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", - playlist_replay_mode: parseInt(json["playlist_replay_mode"]), - playlist_current_song_id: parseInt(json["playlist_current_song_id"]), - }); - } catch(error) { - log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); - reject("failed to parse info"); - } - - this.connection.commandHandler.unset_handler("notifyplaylistinfo", notify_handler); - }; - - this.connection.commandHandler.set_handler("notifyplaylistinfo", notify_handler); - this.connection.sendCommand("playlistinfo", {playlist_id: playlist_id}).catch(error => { - reject(error); - }) - }); - } - - /** - * @deprecated - * Its just a workaround for the query management. - * There is no garante that the whoami trick will work forever - */ - current_virtual_server_id() : Promise { - if(this._who_am_i) - return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); - - return new Promise((resolve, reject) => { - this.connection.commandHandler[""] = json => { - this._who_am_i = json[0]; - resolve(parseInt(this._who_am_i["virtualserver_id"])); - this.connection.commandHandler[""] = undefined; - }; - this.connection.sendCommand("whoami"); - }); - } - - private handle_notifyclientnamefromuid(json: any[]) { - for(let entry of json) { - let info: ClientNameInfo = {} as any; - info.client_unique_id = entry["cluid"]; - info.client_nickname = entry["clname"]; - info.client_database_id = parseInt(entry["cldbid"]); - - for(let elm of this._callbacks_namefromuid.slice(0)) { - let unset = 0; - for(let index = 0; index < elm.keys.length; index++) { - if(elm.keys[index] == info.client_unique_id) { - elm.response[index] = info; - } - if(elm.response[index] == undefined) unset++; - } - if(unset == 0) { - this._callbacks_namefromuid.remove(elm); - elm.promise.resolved(elm.response); - } - } - } - } -} - -class ConnectionCommandHandler { - readonly connection: ServerConnection; - - constructor(connection) { - this.connection = connection; - this["error"] = this.handleCommandResult; - this["channellist"] = this.handleCommandChannelList; - this["channellistfinished"] = this.handleCommandChannelListFinished; - this["notifychannelcreated"] = this.handleCommandChannelCreate; - this["notifychanneldeleted"] = this.handleCommandChannelDelete; - this["notifychannelhide"] = this.handleCommandChannelHide; - this["notifychannelshow"] = this.handleCommandChannelShow; - - this["notifycliententerview"] = this.handleCommandClientEnterView; - this["notifyclientleftview"] = this.handleCommandClientLeftView; - this["notifyclientmoved"] = this.handleNotifyClientMoved; - this["initserver"] = this.handleCommandServerInit; - this["notifychannelmoved"] = this.handleNotifyChannelMoved; - this["notifychanneledited"] = this.handleNotifyChannelEdited; - this["notifytextmessage"] = this.handleNotifyTextMessage; - this["notifyclientupdated"] = this.handleNotifyClientUpdated; - this["notifyserveredited"] = this.handleNotifyServerEdited; - this["notifyserverupdated"] = this.handleNotifyServerUpdated; - - this["notifyclientpoke"] = this.handleNotifyClientPoke; - - this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; - - this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; - this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; - this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; - } - - set_handler(command: string, handler: any) { - this[command] = handler; - } - - unset_handler(command: string, handler?: any) { - if(handler && this[command] != handler) return; - this[command] = undefined; - } - - handleCommandResult(json) { - json = json[0]; //Only one bulk - - let code : string = json["return_code"]; - if(code.length == 0) { - console.log(tr("Invalid return code! (%o)"), json); - return; - } - let retListeners = this.connection["_retListener"]; - - for(let e of retListeners) { - if(e.code != code) continue; - retListeners.remove(e); - let result = new CommandResult(json); - if(result.success) - e.resolve(result); - else - e.reject(result); - break; - } - } - - handleCommandServerInit(json){ - //We could setup the voice channel - console.log(tr("Setting up voice")); - this.connection._client.voiceConnection.createSession(); - - - json = json[0]; //Only one bulk - - this.connection._client.clientId = parseInt(json["aclid"]); - this.connection._client.getClient().updateVariables({key: "client_nickname", value: json["acn"]}); - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "aclid") continue; - if(key === "acn") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection._client.channelTree.server.updateVariables(false, ...updates); - - - chat.serverChat().name = this.connection._client.channelTree.server.properties["virtualserver_name"]; - chat.serverChat().appendMessage(tr("Connected as {0}"), true, this.connection._client.getClient().createChatTag(true)); - sound.play(Sound.CONNECTION_CONNECTED); - globalClient.onConnected(); - } - - private createChannelFromJson(json, ignoreOrder: boolean = false) { - let tree = this.connection._client.channelTree; - - let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); - tree.insertChannel(channel); - if(json["channel_order"] !== "0") { - let prev = tree.findChannel(json["channel_order"]); - if(!prev && json["channel_order"] != 0) { - if(!ignoreOrder) { - console.error(tr("Invalid channel order id!")); - return; - } - } - - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - console.error(tr("Invalid channel parent")); - return; - } - tree.moveChannel(channel, prev, parent); //TODO test if channel exists! - } - if(ignoreOrder) { - for(let ch of tree.channels) { - if(ch.properties.channel_order == channel.channelId) { - tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) - } - } - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "cpid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - } - - handleCommandChannelList(json) { - this.connection._client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ - console.log(tr("Got %d new channels"), json.length); - for(let index = 0; index < json.length; index++) - this.createChannelFromJson(json[index], true); - } - - - handleCommandChannelListFinished(json) { - this.connection._client.channelTree.show_channel_tree(); - } - - handleCommandChannelCreate(json) { - this.createChannelFromJson(json[0]); - } - - handleCommandChannelShow(json) { - this.createChannelFromJson(json[0]); //TODO may chat? - } - - handleCommandChannelDelete(json) { - let tree = this.connection._client.channelTree; - - console.log(tr("Got %d channel deletions"), json.length); - for(let index = 0; index < json.length; index++) { - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - console.error(tr("Invalid channel onDelete (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); - } - } - - handleCommandChannelHide(json) { - let tree = this.connection._client.channelTree; - - console.log(tr("Got %d channel hides"), json.length); - for(let index = 0; index < json.length; index++) { - let channel = tree.findChannel(json[index]["cid"]); - if(!channel) { - console.error(tr("Invalid channel on hide (Unknown channel)")); - continue; - } - tree.deleteChannel(channel); - } - } - - handleCommandClientEnterView(json) { - json = json[0]; //Only one bulk - let tree = this.connection._client.channelTree; - - let client: ClientEntry; - let channel = tree.findChannel(json["ctid"]); - let old_channel = tree.findChannel(json["cfid"]); - - client = tree.findClient(json["clid"]); - - if(!client) { - if(parseInt(json["client_type_exact"]) == ClientType.CLIENT_MUSIC) { - client = new MusicClientEntry(parseInt(json["clid"]), json["client_nickname"]); - } else { - client = new ClientEntry(parseInt(json["clid"]), json["client_nickname"]); - } - - client.properties.client_type = parseInt(json["client_type"]); - client = tree.insertClient(client, channel); - } else { - if(client == this.connection._client.getClient()) - chat.channelChat().name = channel.channelName(); - tree.moveClient(client, channel); - } - - if(this.connection._client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection._client.getClient().currentChannel(); - if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - if(own_channel == channel) - if(old_channel) - sound.play(Sound.USER_ENTERED); - else - sound.play(Sound.USER_ENTERED_CONNECT); - if(old_channel) { - chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.generate_tag(true), channel.generate_tag(true)); - } else { - chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.generate_tag(true)); - } - } else if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { - if(own_channel == channel) - sound.play(Sound.USER_ENTERED_MOVED); - - chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, moved by {3}"), true, - client.createChatTag(true), - old_channel ? old_channel.generate_tag(true) : undefined, - channel.generate_tag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - ); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { - if(own_channel == channel) - sound.play(Sound.USER_ENTERED_KICKED); - - chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), true, - client.createChatTag(true), - old_channel ? old_channel.generate_tag(true) : undefined, - channel.generate_tag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - json["reasonmsg"] > 0 ? " (" + json["msg"] + ")" : "" - ); - } else { - console.warn(tr("Unknown reasonid for %o"), json["reasonid"]); - } - } - - let updates: { - key: string, - value: string - }[] = []; - - for(let key in json) { - if(key == "cfid") continue; - if(key == "ctid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - - client.updateVariables(...updates); - - if(client instanceof LocalClientEntry) - this.connection._client.controlBar.updateVoice(); - } - - handleCommandClientLeftView(json) { - json = json[0]; //Only one bulk - let tree = this.connection._client.channelTree; - let client = tree.findClient(json["clid"]); - if(!client) { - console.error(tr("Unknown client left!")); - return 0; - } - if(client == this.connection._client.getClient()) { - if(json["reasonid"] == ViewReasonId.VREASON_BAN) { - this.connection._client.handleDisconnect(DisconnectReason.CLIENT_BANNED, json); - } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) { - this.connection._client.handleDisconnect(DisconnectReason.CLIENT_KICKED, json); - } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_SHUTDOWN) { - this.connection._client.handleDisconnect(DisconnectReason.SERVER_CLOSED, json); - } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_STOPPED) { - this.connection._client.handleDisconnect(DisconnectReason.SERVER_CLOSED, json); - } else - this.connection._client.handleDisconnect(DisconnectReason.UNKNOWN, json); - return; - } - - if(this.connection._client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { - const own_channel = this.connection._client.getClient().currentChannel(); - let channel_from = tree.findChannel(json["cfid"]); - let channel_to = tree.findChannel(json["ctid"]); - - if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.generate_tag(true), channel_to.generate_tag(true)); - - if(channel_from == own_channel) - sound.play(Sound.USER_LEFT); - } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_LEFT) { - chat.serverChat().appendMessage(tr("{0} left the server{1}"), true, - client.createChatTag(true), - json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" - ); - - if(channel_from == own_channel) - sound.play(Sound.USER_LEFT_DISCONNECT); - } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) { - chat.serverChat().appendError(tr("{0} was kicked from the server by {1}.{2}"), - client.createChatTag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" - ); - if(channel_from == own_channel) - sound.play(Sound.USER_LEFT_KICKED_SERVER); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { - chat.serverChat().appendError(tr("{0} was kicked from your channel by {1}.{2}"), - client.createChatTag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" - ); - - if(channel_from == own_channel) - sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else if(json["reasonid"] == ViewReasonId.VREASON_BAN) { - //"Mulus" was banned for 1 second from the server by "WolverinDEV" (Sry brauchte kurz ein opfer :P <3 (Nohomo)) - let duration = "permanently"; - if(json["bantime"]) - duration = "for " + formatDate(Number.parseInt(json["bantime"])); - - chat.serverChat().appendError(tr("{0} was banned {1} by {2}.{3}"), - client.createChatTag(true), - duration, - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" - ); - - if(channel_from == own_channel) - sound.play(Sound.USER_LEFT_BANNED); - } else { - console.error(tr("Unknown client left reason!")); - } - } - - tree.deleteClient(client); - } - - handleNotifyClientMoved(json) { - json = json[0]; //Only one bulk - let tree = this.connection._client.channelTree; - let client = tree.findClient(json["clid"]); - let channel_to = tree.findChannel(json["ctid"]); - let channel_from = tree.findChannel(json["cfid"]); - - if(!client) { - console.error(tr("Unknown client move (Client)!")); - return 0; - } - - if(!channel_to) { - console.error(tr("Unknown client move (Channel to)!")); - return 0; - } - if(!channel_from) //Not critical - console.error(tr("Unknown client move (Channel from)!")); - - let self = client instanceof LocalClientEntry; - let current_clients; - if(self) { - chat.channelChat().name = channel_to.channelName(); - current_clients = client.channelTree.clientsByChannel(client.currentChannel()) - this.connection._client.controlBar.updateVoice(channel_to); - } - - tree.moveClient(client, channel_to); - for(const entry of current_clients || []) - if(entry !== client) entry.getAudioController().stopAudio(true); - - const own_channel = this.connection._client.getClient().currentChannel(); - if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { - chat.serverChat().appendMessage(self ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), true, - client.createChatTag(true), - channel_from ? channel_from.generate_tag(true) : undefined, - channel_to.generate_tag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]) - ); - if(self) - sound.play(Sound.USER_MOVED_SELF); - else if(own_channel == channel_to) - sound.play(Sound.USER_ENTERED_MOVED); - else if(own_channel == channel_from) - sound.play(Sound.USER_LEFT_MOVED); - } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { - chat.serverChat().appendMessage(self ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), true, - client.createChatTag(true), - channel_from ? channel_from.generate_tag(true) : undefined, - channel_to.generate_tag(true) - ); - if(self) {} //If we do an action we wait for the error response - else if(own_channel == channel_to) - sound.play(Sound.USER_ENTERED); - else if(own_channel == channel_from) - sound.play(Sound.USER_LEFT); - } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { - chat.serverChat().appendMessage(self ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), true, - client.createChatTag(true), - channel_from ? channel_from.generate_tag(true) : undefined, - channel_to.generate_tag(true), - ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), - json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" - ); - if(self) { - sound.play(Sound.CHANNEL_KICKED); - } else if(own_channel == channel_to) - sound.play(Sound.USER_ENTERED_KICKED); - else if(own_channel == channel_from) - sound.play(Sound.USER_LEFT_KICKED_CHANNEL); - } else { - console.warn(tr("Unknown reason id %o"), json["reasonid"]); - } - } - - handleNotifyChannelMoved(json) { - json = json[0]; //Only one bulk - for(let key in json) - console.log("Key: " + key + " Value: " + json[key]); - - let tree = this.connection._client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - console.error(tr("Unknown channel move (Channel)!")); - return 0; - } - - let prev = tree.findChannel(json["order"]); - if(!prev && json["order"] != 0) { - console.error(tr("Unknown channel move (prev)!")); - return 0; - } - - let parent = tree.findChannel(json["cpid"]); - if(!parent && json["cpid"] != 0) { - console.error(tr("Unknown channel move (parent)!")); - return 0; - } - - tree.moveChannel(channel, prev, parent); - } - - handleNotifyChannelEdited(json) { - json = json[0]; //Only one bulk - - let tree = this.connection._client.channelTree; - let channel = tree.findChannel(json["cid"]); - if(!channel) { - console.error(tr("Unknown channel edit (Channel)!")); - return 0; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "cid") continue; - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - updates.push({key: key, value: json[key]}); - } - channel.updateVariables(...updates); - } - - handleNotifyTextMessage(json) { - json = json[0]; //Only one bulk - - //TODO chat format? - let mode = json["targetmode"]; - if(mode == 1){ - let invoker = this.connection._client.channelTree.findClient(json["invokerid"]); - let target = this.connection._client.channelTree.findClient(json["target"]); - if(!invoker) { //TODO spawn chat (Client is may invisible) - console.error(tr("Got private message from invalid client!")); - return; - } - if(!target) { //TODO spawn chat (Client is may invisible) - console.error(tr("Got private message from invalid client!")); - return; - } - if(invoker == this.connection._client.getClient()) { - sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - target.chat(true).appendMessage("{0}: {1}", true, this.connection._client.getClient().createChatTag(true), MessageHelper.bbcode_chat(json["msg"])); - } else { - sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])); - } - } else if(mode == 2) { - if(json["invokerid"] == this.connection._client.clientId) - sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); - else - sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); - chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])) - } else if(mode == 3) { - chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])); - } - } - - handleNotifyClientUpdated(json) { - json = json[0]; //Only one bulk - - let client = this.connection._client.channelTree.findClient(json["clid"]); - if(!client) { - console.error(tr("Tried to update an non existing client")); - return; - } - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key == "clid") continue; - updates.push({key: key, value: json[key]}); - } - client.updateVariables(...updates); - if(this.connection._client.selectInfo.currentSelected == client) - this.connection._client.selectInfo.update(); - } - - handleNotifyServerEdited(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection._client.channelTree.server.updateVariables(false, ...updates); - if(this.connection._client.selectInfo.currentSelected == this.connection._client.channelTree.server) - this.connection._client.selectInfo.update(); - } - - handleNotifyServerUpdated(json) { - json = json[0]; - - let updates: { - key: string, - value: string - }[] = []; - for(let key in json) { - if(key === "invokerid") continue; - if(key === "invokername") continue; - if(key === "invokeruid") continue; - if(key === "reasonid") continue; - - updates.push({key: key, value: json[key]}); - } - this.connection._client.channelTree.server.updateVariables(true, ...updates); - let info = this.connection._client.selectInfo; - if(info.currentSelected instanceof ServerEntry) - info.update(); - } - - handleNotifyMusicPlayerInfo(json) { - json = json[0]; - - let bot = this.connection._client.channelTree.find_client_by_dbid(json["bot_id"]); - if(!bot || !(bot instanceof MusicClientEntry)) { - log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); - return; - } - - bot.handlePlayerInfo(json); - } - - handleNotifyClientPoke(json) { - json = json[0]; - Modals.spawnPoke({ - id: parseInt(json["invokerid"]), - name: json["invokername"], - unique_id: json["invokeruid"] - }, json["msg"]); - - sound.play(Sound.USER_POKED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientAdd(json) { - json = json[0]; - - const self = this.connection._client.getClient(); - if(json["clid"] == self.clientId()) - sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); - } - - //TODO server chat message - handleNotifyServerGroupClientRemove(json) { - json = json[0]; - - const self = this.connection._client.getClient(); - if(json["clid"] == self.clientId()) { - sound.play(Sound.GROUP_SERVER_REVOKED_SELF); - } else { - } - } - - //TODO server chat message - handleNotifyClientChannelGroupChanged(json) { - json = json[0]; - - const self = this.connection._client.getClient(); - if(json["clid"] == self.clientId()) { - sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); - } - } -} \ No newline at end of file diff --git a/shared/js/connection/CommandHandler.ts b/shared/js/connection/CommandHandler.ts new file mode 100644 index 00000000..5fc46d82 --- /dev/null +++ b/shared/js/connection/CommandHandler.ts @@ -0,0 +1,649 @@ +namespace connection { + export class ServerConnectionCommandBoss extends AbstractCommandHandlerBoss { + constructor(connection: AbstractServerConnection) { + super(connection); + } + } + + export class ConnectionCommandHandler extends AbstractCommandHandler { + readonly connection: ServerConnection; + + constructor(connection: ServerConnection) { + super(connection); + + this["error"] = this.handleCommandResult; + this["channellist"] = this.handleCommandChannelList; + this["channellistfinished"] = this.handleCommandChannelListFinished; + this["notifychannelcreated"] = this.handleCommandChannelCreate; + this["notifychanneldeleted"] = this.handleCommandChannelDelete; + this["notifychannelhide"] = this.handleCommandChannelHide; + this["notifychannelshow"] = this.handleCommandChannelShow; + + this["notifycliententerview"] = this.handleCommandClientEnterView; + this["notifyclientleftview"] = this.handleCommandClientLeftView; + this["notifyclientmoved"] = this.handleNotifyClientMoved; + this["initserver"] = this.handleCommandServerInit; + this["notifychannelmoved"] = this.handleNotifyChannelMoved; + this["notifychanneledited"] = this.handleNotifyChannelEdited; + this["notifytextmessage"] = this.handleNotifyTextMessage; + this["notifyclientupdated"] = this.handleNotifyClientUpdated; + this["notifyserveredited"] = this.handleNotifyServerEdited; + this["notifyserverupdated"] = this.handleNotifyServerUpdated; + + this["notifyclientpoke"] = this.handleNotifyClientPoke; + + this["notifymusicplayerinfo"] = this.handleNotifyMusicPlayerInfo; + + this["notifyservergroupclientadded"] = this.handleNotifyServerGroupClientAdd; + this["notifyservergroupclientdeleted"] = this.handleNotifyServerGroupClientRemove; + this["notifyclientchannelgroupchanged"] = this.handleNotifyClientChannelGroupChanged; + } + + handle_command(command: ServerCommand) : boolean { + if(this[command.command]) { + this[command.command](command.arguments); + return true; + } + + return false; + } + + set_handler(command: string, handler: any) { + this[command] = handler; + } + + unset_handler(command: string, handler?: any) { + if(handler && this[command] != handler) return; + this[command] = undefined; + } + + handleCommandResult(json) { + json = json[0]; //Only one bulk + + let code : string = json["return_code"]; + if(code.length == 0) { + console.log(tr("Invalid return code! (%o)"), json); + return; + } + let retListeners = this.connection["_retListener"]; + + for(let e of retListeners) { + if(e.code != code) continue; + retListeners.remove(e); + let result = new CommandResult(json); + if(result.success) + e.resolve(result); + else + e.reject(result); + break; + } + } + + handleCommandServerInit(json){ + //We could setup the voice channel + console.log(tr("Setting up voice")); + this.connection.client.voiceConnection.createSession(); + + + json = json[0]; //Only one bulk + + this.connection.client.clientId = parseInt(json["aclid"]); + this.connection.client.getClient().updateVariables({key: "client_nickname", value: json["acn"]}); + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "aclid") continue; + if(key === "acn") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + + + chat.serverChat().name = this.connection.client.channelTree.server.properties["virtualserver_name"]; + chat.serverChat().appendMessage(tr("Connected as {0}"), true, this.connection.client.getClient().createChatTag(true)); + sound.play(Sound.CONNECTION_CONNECTED); + globalClient.onConnected(); + } + + private createChannelFromJson(json, ignoreOrder: boolean = false) { + let tree = this.connection.client.channelTree; + + let channel = new ChannelEntry(parseInt(json["cid"]), json["channel_name"], tree.findChannel(json["cpid"])); + tree.insertChannel(channel); + if(json["channel_order"] !== "0") { + let prev = tree.findChannel(json["channel_order"]); + if(!prev && json["channel_order"] != 0) { + if(!ignoreOrder) { + console.error(tr("Invalid channel order id!")); + return; + } + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + console.error(tr("Invalid channel parent")); + return; + } + tree.moveChannel(channel, prev, parent); //TODO test if channel exists! + } + if(ignoreOrder) { + for(let ch of tree.channels) { + if(ch.properties.channel_order == channel.channelId) { + tree.moveChannel(ch, channel, channel.parent); //Corrent the order :) + } + } + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "cpid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + } + + handleCommandChannelList(json) { + this.connection.client.channelTree.hide_channel_tree(); /* dont perform channel inserts on the dom to prevent style recalculations */ + console.log(tr("Got %d new channels"), json.length); + for(let index = 0; index < json.length; index++) + this.createChannelFromJson(json[index], true); + } + + + handleCommandChannelListFinished(json) { + this.connection.client.channelTree.show_channel_tree(); + } + + handleCommandChannelCreate(json) { + this.createChannelFromJson(json[0]); + } + + handleCommandChannelShow(json) { + this.createChannelFromJson(json[0]); //TODO may chat? + } + + handleCommandChannelDelete(json) { + let tree = this.connection.client.channelTree; + + console.log(tr("Got %d channel deletions"), json.length); + for(let index = 0; index < json.length; index++) { + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + console.error(tr("Invalid channel onDelete (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandChannelHide(json) { + let tree = this.connection.client.channelTree; + + console.log(tr("Got %d channel hides"), json.length); + for(let index = 0; index < json.length; index++) { + let channel = tree.findChannel(json[index]["cid"]); + if(!channel) { + console.error(tr("Invalid channel on hide (Unknown channel)")); + continue; + } + tree.deleteChannel(channel); + } + } + + handleCommandClientEnterView(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + + let client: ClientEntry; + let channel = tree.findChannel(json["ctid"]); + let old_channel = tree.findChannel(json["cfid"]); + + client = tree.findClient(json["clid"]); + + if(!client) { + if(parseInt(json["client_type_exact"]) == ClientType.CLIENT_MUSIC) { + client = new MusicClientEntry(parseInt(json["clid"]), json["client_nickname"]); + } else { + client = new ClientEntry(parseInt(json["clid"]), json["client_nickname"]); + } + + client.properties.client_type = parseInt(json["client_type"]); + client = tree.insertClient(client, channel); + } else { + if(client == this.connection.client.getClient()) + chat.channelChat().name = channel.channelName(); + tree.moveClient(client, channel); + } + + if(this.connection.client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + if(own_channel == channel) + if(old_channel) + sound.play(Sound.USER_ENTERED); + else + sound.play(Sound.USER_ENTERED_CONNECT); + if(old_channel) { + chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}"), true, client.createChatTag(true), old_channel.generate_tag(true), channel.generate_tag(true)); + } else { + chat.serverChat().appendMessage(tr("{0} connected to channel {1}"), true, client.createChatTag(true), channel.generate_tag(true)); + } + } else if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { + if(own_channel == channel) + sound.play(Sound.USER_ENTERED_MOVED); + + chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, moved by {3}"), true, + client.createChatTag(true), + old_channel ? old_channel.generate_tag(true) : undefined, + channel.generate_tag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + ); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + if(own_channel == channel) + sound.play(Sound.USER_ENTERED_KICKED); + + chat.serverChat().appendMessage(tr("{0} appeared from {1} to {2}, kicked by {3}{4}"), true, + client.createChatTag(true), + old_channel ? old_channel.generate_tag(true) : undefined, + channel.generate_tag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + json["reasonmsg"] > 0 ? " (" + json["msg"] + ")" : "" + ); + } else { + console.warn(tr("Unknown reasonid for %o"), json["reasonid"]); + } + } + + let updates: { + key: string, + value: string + }[] = []; + + for(let key in json) { + if(key == "cfid") continue; + if(key == "ctid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + + client.updateVariables(...updates); + + if(client instanceof LocalClientEntry) + this.connection.client.controlBar.updateVoice(); + } + + handleCommandClientLeftView(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + if(!client) { + console.error(tr("Unknown client left!")); + return 0; + } + if(client == this.connection.client.getClient()) { + if(json["reasonid"] == ViewReasonId.VREASON_BAN) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_BANNED, json); + } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) { + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, json); + } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_SHUTDOWN) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, json); + } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_STOPPED) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_CLOSED, json); + } else + this.connection.client.handleDisconnect(DisconnectReason.UNKNOWN, json); + return; + } + + if(this.connection.client.controlBar.query_visible || client.properties.client_type != ClientType.CLIENT_QUERY) { + const own_channel = this.connection.client.getClient().currentChannel(); + let channel_from = tree.findChannel(json["cfid"]); + let channel_to = tree.findChannel(json["ctid"]); + + if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + chat.serverChat().appendMessage(tr("{0} disappeared from {1} to {2}"), true, client.createChatTag(true), channel_from.generate_tag(true), channel_to.generate_tag(true)); + + if(channel_from == own_channel) + sound.play(Sound.USER_LEFT); + } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_LEFT) { + chat.serverChat().appendMessage(tr("{0} left the server{1}"), true, + client.createChatTag(true), + json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" + ); + + if(channel_from == own_channel) + sound.play(Sound.USER_LEFT_DISCONNECT); + } else if(json["reasonid"] == ViewReasonId.VREASON_SERVER_KICK) { + chat.serverChat().appendError(tr("{0} was kicked from the server by {1}.{2}"), + client.createChatTag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" + ); + if(channel_from == own_channel) + sound.play(Sound.USER_LEFT_KICKED_SERVER); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + chat.serverChat().appendError(tr("{0} was kicked from your channel by {1}.{2}"), + client.createChatTag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" + ); + + if(channel_from == own_channel) + sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else if(json["reasonid"] == ViewReasonId.VREASON_BAN) { + //"Mulus" was banned for 1 second from the server by "WolverinDEV" (Sry brauchte kurz ein opfer :P <3 (Nohomo)) + let duration = "permanently"; + if(json["bantime"]) + duration = "for " + formatDate(Number.parseInt(json["bantime"])); + + chat.serverChat().appendError(tr("{0} was banned {1} by {2}.{3}"), + client.createChatTag(true), + duration, + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" + ); + + if(channel_from == own_channel) + sound.play(Sound.USER_LEFT_BANNED); + } else { + console.error(tr("Unknown client left reason!")); + } + } + + tree.deleteClient(client); + } + + handleNotifyClientMoved(json) { + json = json[0]; //Only one bulk + let tree = this.connection.client.channelTree; + let client = tree.findClient(json["clid"]); + let channel_to = tree.findChannel(json["ctid"]); + let channel_from = tree.findChannel(json["cfid"]); + + if(!client) { + console.error(tr("Unknown client move (Client)!")); + return 0; + } + + if(!channel_to) { + console.error(tr("Unknown client move (Channel to)!")); + return 0; + } + if(!channel_from) //Not critical + console.error(tr("Unknown client move (Channel from)!")); + + let self = client instanceof LocalClientEntry; + let current_clients; + if(self) { + chat.channelChat().name = channel_to.channelName(); + current_clients = client.channelTree.clientsByChannel(client.currentChannel()) + this.connection.client.controlBar.updateVoice(channel_to); + } + + tree.moveClient(client, channel_to); + for(const entry of current_clients || []) + if(entry !== client) entry.getAudioController().stopAudio(true); + + const own_channel = this.connection.client.getClient().currentChannel(); + if(json["reasonid"] == ViewReasonId.VREASON_MOVED) { + chat.serverChat().appendMessage(self ? tr("You was moved by {3} from channel {1} to {2}") : tr("{0} was moved from channel {1} to {2} by {3}"), true, + client.createChatTag(true), + channel_from ? channel_from.generate_tag(true) : undefined, + channel_to.generate_tag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]) + ); + if(self) + sound.play(Sound.USER_MOVED_SELF); + else if(own_channel == channel_to) + sound.play(Sound.USER_ENTERED_MOVED); + else if(own_channel == channel_from) + sound.play(Sound.USER_LEFT_MOVED); + } else if(json["reasonid"] == ViewReasonId.VREASON_USER_ACTION) { + chat.serverChat().appendMessage(self ? tr("You switched from channel {1} to {2}") : tr("{0} switched from channel {1} to {2}"), true, + client.createChatTag(true), + channel_from ? channel_from.generate_tag(true) : undefined, + channel_to.generate_tag(true) + ); + if(self) {} //If we do an action we wait for the error response + else if(own_channel == channel_to) + sound.play(Sound.USER_ENTERED); + else if(own_channel == channel_from) + sound.play(Sound.USER_LEFT); + } else if(json["reasonid"] == ViewReasonId.VREASON_CHANNEL_KICK) { + chat.serverChat().appendMessage(self ? tr("You got kicked out of the channel {1} to channel {2} by {3}{4}") : tr("{0} got kicked from channel {1} to {2} by {3}{4}"), true, + client.createChatTag(true), + channel_from ? channel_from.generate_tag(true) : undefined, + channel_to.generate_tag(true), + ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"]), + json["reasonmsg"] ? " (" + json["reasonmsg"] + ")" : "" + ); + if(self) { + sound.play(Sound.CHANNEL_KICKED); + } else if(own_channel == channel_to) + sound.play(Sound.USER_ENTERED_KICKED); + else if(own_channel == channel_from) + sound.play(Sound.USER_LEFT_KICKED_CHANNEL); + } else { + console.warn(tr("Unknown reason id %o"), json["reasonid"]); + } + } + + handleNotifyChannelMoved(json) { + json = json[0]; //Only one bulk + for(let key in json) + console.log("Key: " + key + " Value: " + json[key]); + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + console.error(tr("Unknown channel move (Channel)!")); + return 0; + } + + let prev = tree.findChannel(json["order"]); + if(!prev && json["order"] != 0) { + console.error(tr("Unknown channel move (prev)!")); + return 0; + } + + let parent = tree.findChannel(json["cpid"]); + if(!parent && json["cpid"] != 0) { + console.error(tr("Unknown channel move (parent)!")); + return 0; + } + + tree.moveChannel(channel, prev, parent); + } + + handleNotifyChannelEdited(json) { + json = json[0]; //Only one bulk + + let tree = this.connection.client.channelTree; + let channel = tree.findChannel(json["cid"]); + if(!channel) { + console.error(tr("Unknown channel edit (Channel)!")); + return 0; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "cid") continue; + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + updates.push({key: key, value: json[key]}); + } + channel.updateVariables(...updates); + } + + handleNotifyTextMessage(json) { + json = json[0]; //Only one bulk + + //TODO chat format? + let mode = json["targetmode"]; + if(mode == 1){ + let invoker = this.connection.client.channelTree.findClient(json["invokerid"]); + let target = this.connection.client.channelTree.findClient(json["target"]); + if(!invoker) { //TODO spawn chat (Client is may invisible) + console.error(tr("Got private message from invalid client!")); + return; + } + if(!target) { //TODO spawn chat (Client is may invisible) + console.error(tr("Got private message from invalid client!")); + return; + } + if(invoker == this.connection.client.getClient()) { + sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + target.chat(true).appendMessage("{0}: {1}", true, this.connection.client.getClient().createChatTag(true), MessageHelper.bbcode_chat(json["msg"])); + } else { + sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + invoker.chat(true).appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])); + } + } else if(mode == 2) { + if(json["invokerid"] == this.connection.client.clientId) + sound.play(Sound.MESSAGE_SEND, {default_volume: .5}); + else + sound.play(Sound.MESSAGE_RECEIVED, {default_volume: .5}); + chat.channelChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])) + } else if(mode == 3) { + chat.serverChat().appendMessage("{0}: {1}", true, ClientEntry.chatTag(json["invokerid"], json["invokername"], json["invokeruid"], true), MessageHelper.bbcode_chat(json["msg"])); + } + } + + handleNotifyClientUpdated(json) { + json = json[0]; //Only one bulk + + let client = this.connection.client.channelTree.findClient(json["clid"]); + if(!client) { + console.error(tr("Tried to update an non existing client")); + return; + } + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key == "clid") continue; + updates.push({key: key, value: json[key]}); + } + client.updateVariables(...updates); + if(this.connection.client.selectInfo.currentSelected == client) + this.connection.client.selectInfo.update(); + } + + handleNotifyServerEdited(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(false, ...updates); + if(this.connection.client.selectInfo.currentSelected == this.connection.client.channelTree.server) + this.connection.client.selectInfo.update(); + } + + handleNotifyServerUpdated(json) { + json = json[0]; + + let updates: { + key: string, + value: string + }[] = []; + for(let key in json) { + if(key === "invokerid") continue; + if(key === "invokername") continue; + if(key === "invokeruid") continue; + if(key === "reasonid") continue; + + updates.push({key: key, value: json[key]}); + } + this.connection.client.channelTree.server.updateVariables(true, ...updates); + let info = this.connection.client.selectInfo; + if(info.currentSelected instanceof ServerEntry) + info.update(); + } + + handleNotifyMusicPlayerInfo(json) { + json = json[0]; + + let bot = this.connection.client.channelTree.find_client_by_dbid(json["bot_id"]); + if(!bot || !(bot instanceof MusicClientEntry)) { + log.warn(LogCategory.CLIENT, tr("Got music player info for unknown or invalid bot! (ID: %i, Entry: %o)"), json["bot_id"], bot); + return; + } + + bot.handlePlayerInfo(json); + } + + handleNotifyClientPoke(json) { + json = json[0]; + Modals.spawnPoke({ + id: parseInt(json["invokerid"]), + name: json["invokername"], + unique_id: json["invokeruid"] + }, json["msg"]); + + sound.play(Sound.USER_POKED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientAdd(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) + sound.play(Sound.GROUP_SERVER_ASSIGNED_SELF); + } + + //TODO server chat message + handleNotifyServerGroupClientRemove(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + sound.play(Sound.GROUP_SERVER_REVOKED_SELF); + } else { + } + } + + //TODO server chat message + handleNotifyClientChannelGroupChanged(json) { + json = json[0]; + + const self = this.connection.client.getClient(); + if(json["clid"] == self.clientId()) { + sound.play(Sound.GROUP_CHANNEL_CHANGED_SELF); + } + } + } +} \ No newline at end of file diff --git a/shared/js/connection/CommandHelper.ts b/shared/js/connection/CommandHelper.ts new file mode 100644 index 00000000..d5d53e25 --- /dev/null +++ b/shared/js/connection/CommandHelper.ts @@ -0,0 +1,311 @@ +namespace connection { + export class CommandHelper extends AbstractCommandHandler { + private _callbacks_namefromuid: ClientNameFromUid[] = []; + private _who_am_i: any; + + constructor(connection) { + super(connection); + + this.volatile_handler_boss = false; + this.ignore_consumed = true; + } + + initialize() { + this.connection.command_handler_boss().register_handler(this); + /* notifyquerylist */ + } + + handle_command(command: connection.ServerCommand): boolean { + if(command.command == "notifyclientnamefromuid") + this.handle_notifyclientnamefromuid(command.arguments); + else + return false; + return true; + } + + joinChannel(channel: ChannelEntry, password?: string) : Promise { + return this.connection.send_command("clientmove", { + "clid": this.connection.client.getClientId(), + "cid": channel.getChannelId(), + "cpw": password || "" + }); + } + + sendMessage(message: string, type: ChatType, target?: ChannelEntry | ClientEntry) : Promise { + if(type == ChatType.SERVER) + return this.connection.send_command("sendtextmessage", {"targetmode": 3, "target": 0, "msg": message}); + else if(type == ChatType.CHANNEL) + return this.connection.send_command("sendtextmessage", {"targetmode": 2, "target": (target as ChannelEntry).getChannelId(), "msg": message}); + else if(type == ChatType.CLIENT) + return this.connection.send_command("sendtextmessage", {"targetmode": 1, "target": (target as ClientEntry).clientId(), "msg": message}); + } + + updateClient(key: string, value: string) : Promise { + let data = {}; + data[key] = value; + return this.connection.send_command("clientupdate", data); + } + + info_from_uid(...uid: string[]) : Promise { + let uids = [...uid]; + for(let p of this._callbacks_namefromuid) + if(p.keys == uids) return p.promise; + + let req: ClientNameFromUid = {} as any; + req.keys = uids; + req.response = new Array(uids.length); + req.promise = new LaterPromise(); + + for(let uid of uids) { + this.connection.send_command("clientgetnamefromuid", { + cluid: uid + }).catch(req.promise.function_rejected()); + } + + this._callbacks_namefromuid.push(req); + return req.promise; + } + + request_query_list(server_id: number = undefined) : Promise { + return new Promise((resolve, reject) => { + const single_handler = { + command: "notifyquerylist", + function: command => { + const json = command.arguments; + + const result = {} as QueryList; + + result.flag_all = json[0]["flag_all"]; + result.flag_own = json[0]["flag_own"]; + result.queries = []; + + for(const entry of json) { + const rentry = {} as QueryListEntry; + rentry.bounded_server = entry["client_bounded_server"]; + rentry.username = entry["client_login_name"]; + rentry.unique_id = entry["client_unique_identifier"]; + + result.queries.push(rentry); + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + let data = {}; + if(server_id !== undefined) + data["server_id"] = server_id; + + this.connection.send_command("querylist", data).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve(undefined); + return; + } + } + reject(error); + }); + }); + } + + request_playlist_list() : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistlist", + function: command => { + const json = command.arguments; + const result: Playlist[] = []; + + for(const entry of json) { + try { + result.push({ + playlist_id: parseInt(entry["playlist_id"]), + playlist_bot_id: parseInt(entry["playlist_bot_id"]), + playlist_title: entry["playlist_title"], + playlist_type: parseInt(entry["playlist_type"]), + playlist_owner_dbid: parseInt(entry["playlist_owner_dbid"]), + playlist_owner_name: entry["playlist_owner_name"], + + needed_power_modify: parseInt(entry["needed_power_modify"]), + needed_power_permission_modify: parseInt(entry["needed_power_permission_modify"]), + needed_power_delete: parseInt(entry["needed_power_delete"]), + needed_power_song_add: parseInt(entry["needed_power_song_add"]), + needed_power_song_move: parseInt(entry["needed_power_song_move"]), + needed_power_song_remove: parseInt(entry["needed_power_song_remove"]) + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist entry: %o"), error); + } + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistlist").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }) + }); + } + + request_playlist_songs(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistsonglist", + function: command => { + const json = command.arguments; + + if(json[0]["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist songs")); + return false; + } + + const result: PlaylistSong[] = []; + + for(const entry of json) { + try { + result.push({ + song_id: parseInt(entry["song_id"]), + song_invoker: entry["song_invoker"], + song_previous_song_id: parseInt(entry["song_previous_song_id"]), + song_url: entry["song_url"], + song_url_loader: entry["song_url_loader"], + + song_loaded: entry["song_loaded"] == true || entry["song_loaded"] == "1", + song_metadata: entry["song_metadata"] + }); + } catch(error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist song entry: %o"), error); + } + } + + resolve(result); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistsonglist", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + if(error instanceof CommandResult) { + if(error.id == ErrorID.EMPTY_RESULT) { + resolve([]); + return; + } + } + reject(error); + }) + }); + } + + request_playlist_info(playlist_id: number) : Promise { + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + command: "notifyplaylistinfo", + function: command => { + const json = command.arguments[0]; + if (json["playlist_id"] != playlist_id) { + log.error(LogCategory.NETWORKING, tr("Received invalid notification for playlist info")); + return; + } + + try { + //resolve + resolve({ + playlist_id: parseInt(json["playlist_id"]), + playlist_title: json["playlist_title"], + playlist_description: json["playlist_description"], + playlist_type: parseInt(json["playlist_type"]), + + playlist_owner_dbid: parseInt(json["playlist_owner_dbid"]), + playlist_owner_name: json["playlist_owner_name"], + + playlist_flag_delete_played: json["playlist_flag_delete_played"] == true || json["playlist_flag_delete_played"] == "1", + playlist_flag_finished: json["playlist_flag_finished"] == true || json["playlist_flag_finished"] == "1", + playlist_replay_mode: parseInt(json["playlist_replay_mode"]), + playlist_current_song_id: parseInt(json["playlist_current_song_id"]), + }); + } catch (error) { + log.error(LogCategory.NETWORKING, tr("Failed to parse playlist info: %o"), error); + reject("failed to parse info"); + } + + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("playlistinfo", {playlist_id: playlist_id}).catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }) + }); + } + + /** + * @deprecated + * Its just a workaround for the query management. + * There is no garante that the whoami trick will work forever + */ + current_virtual_server_id() : Promise { + if(this._who_am_i) + return Promise.resolve(parseInt(this._who_am_i["virtualserver_id"])); + + return new Promise((resolve, reject) => { + const single_handler: SingleCommandHandler = { + function: command => { + if(command.command != "") + return false; + + this._who_am_i = command.arguments[0]; + resolve(parseInt(this._who_am_i["virtualserver_id"])); + return true; + } + }; + this.handler_boss.register_single_handler(single_handler); + + this.connection.send_command("whoami").catch(error => { + this.handler_boss.remove_single_handler(single_handler); + reject(error); + }); + }); + } + + private handle_notifyclientnamefromuid(json: any[]) { + for(let entry of json) { + let info: ClientNameInfo = {} as any; + info.client_unique_id = entry["cluid"]; + info.client_nickname = entry["clname"]; + info.client_database_id = parseInt(entry["cldbid"]); + + for(let elm of this._callbacks_namefromuid.slice(0)) { + let unset = 0; + for(let index = 0; index < elm.keys.length; index++) { + if(elm.keys[index] == info.client_unique_id) { + elm.response[index] = info; + } + if(elm.response[index] == undefined) unset++; + } + if(unset == 0) { + this._callbacks_namefromuid.remove(elm); + elm.promise.resolved(elm.response); + } + } + } + } + } +} \ No newline at end of file diff --git a/shared/js/connection/ConnectionBase.ts b/shared/js/connection/ConnectionBase.ts new file mode 100644 index 00000000..eb82a4b0 --- /dev/null +++ b/shared/js/connection/ConnectionBase.ts @@ -0,0 +1,148 @@ +namespace connection { + export interface CommandOptions { + flagset?: string[]; /* default: [] */ + process_result?: boolean; /* default: true */ + + timeout?: number /* default: 1000 */; + } + export const CommandOptionDefaults: CommandOptions = { + flagset: [], + process_result: true, + timeout: 1000 + }; + + export abstract class AbstractServerConnection { + readonly client: TSClient; + readonly command_helper: CommandHelper; + + protected constructor(client: TSClient) { + this.client = client; + + this.command_helper = new CommandHelper(this); + } + + /* resolved as soon a connection has been established. This does not means that the authentication had yet been done! */ + abstract connect(address: ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise; + + abstract connected() : boolean; + abstract disconnect(reason?: string) : Promise; + + abstract support_voice() : boolean; + abstract voice_connection() : AbstractVoiceConnection | undefined; + + abstract command_handler_boss() : AbstractCommandHandlerBoss; + abstract send_command(command: string, data?: any | any[], options?: CommandOptions) : Promise; + } + + export abstract class AbstractVoiceConnection { + readonly connection: AbstractServerConnection; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + abstract connected() : boolean; + } + + export class ServerCommand { + command: string; + arguments: any[]; + } + + export abstract class AbstractCommandHandler { + readonly connection: AbstractServerConnection; + + handler_boss: AbstractCommandHandlerBoss | undefined; + volatile_handler_boss: boolean = false; /* if true than the command handler could be registered twice to two or more handlers */ + + ignore_consumed: boolean = false; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + /** + * @return If the command should be consumed + */ + abstract handle_command(command: ServerCommand) : boolean; + } + + export interface SingleCommandHandler { + name?: string; + command?: string; + timeout?: number; + + /* if the return is true then the command handler will be removed */ + function: (command: ServerCommand) => boolean; + } + + export abstract class AbstractCommandHandlerBoss { + readonly connection: AbstractServerConnection; + protected command_handlers: AbstractCommandHandler[] = []; + /* TODO: Timeout */ + protected single_command_handler: SingleCommandHandler[] = []; + + protected constructor(connection: AbstractServerConnection) { + this.connection = connection; + } + + register_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss) + throw "handler already registered"; + + this.command_handlers.remove(handler); /* just to be sure */ + this.command_handlers.push(handler); + handler.handler_boss = this; + } + + unregister_handler(handler: AbstractCommandHandler) { + if(!handler.volatile_handler_boss && handler.handler_boss !== this) { + console.warn(tr("Tried to unregister command handler which does not belong to the handler boss")); + return; + } + + this.command_handlers.remove(handler); + handler.handler_boss = undefined; + } + + + register_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.push(handler); + } + + remove_single_handler(handler: SingleCommandHandler) { + this.single_command_handler.remove(handler); + } + + handlers() : AbstractCommandHandler[] { + return this.command_handlers; + } + + invoke_handle(command: ServerCommand) : boolean { + let flag_consumed = false; + + for(const handler of this.command_handlers) { + try { + if(!flag_consumed || handler.ignore_consumed) + flag_consumed = flag_consumed || handler.handle_command(command); + } catch(error) { + console.error(tr("Failed to invoke command handler. Invocation results in an exception: %o"), error); + } + } + + for(const handler of [...this.single_command_handler]) { + if(handler.command && handler.command != command.command) + continue; + + try { + if(handler.function(command)) + this.single_command_handler.remove(handler); + } catch(error) { + console.error(tr("Failed to invoke single command handler. Invocation results in an exception: %o"), error); + } + } + + return flag_consumed; + } + } +} \ No newline at end of file diff --git a/shared/js/connection/HandshakeHandler.ts b/shared/js/connection/HandshakeHandler.ts new file mode 100644 index 00000000..d64cbb04 --- /dev/null +++ b/shared/js/connection/HandshakeHandler.ts @@ -0,0 +1,108 @@ +namespace connection { + export interface HandshakeIdentityHandler { + connection: AbstractServerConnection; + + start_handshake(); + register_callback(callback: (success: boolean, message?: string) => any); + } + + export class HandshakeHandler { + private connection: ServerConnection; + private handshake_handler: HandshakeIdentityHandler; + private failed = false; + + readonly profile: profiles.ConnectionProfile; + readonly name: string; + readonly server_password: string; + + constructor(profile: profiles.ConnectionProfile, name: string, password: string) { + this.profile = profile; + this.server_password = password; + this.name = name; + } + + setConnection(con: ServerConnection) { + this.connection = con; + } + + startHandshake() { + this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection); + if(!this.handshake_handler) { + this.handshake_failed("failed to create identity handler"); + return; + } + + this.handshake_handler.register_callback((flag, message) => { + if(flag) + this.handshake_finished(); + else + this.handshake_failed(message); + }); + + this.handshake_handler.start_handshake(); + } + + private handshake_failed(message: string) { + if(this.failed) return; + + this.failed = true; + this.connection.client.handleDisconnect(DisconnectReason.HANDSHAKE_FAILED, message); + } + + private handshake_finished(version?: string) { + if(native_client && window["native"] && native.client_version && !version) { + native.client_version() + .then( this.handshake_finished.bind(this)) + .catch(error => { + console.error(tr("Failed to get version:")); + console.error(error); + this.handshake_finished("?.?.?"); + }); + return; + } + + const git_version = settings.static_global("version", "unknown"); + const browser_name = (navigator.browserSpecs || {})["name"] || " "; + let data = { + //TODO variables! + client_nickname: this.name, + client_platform: (browser_name ? browser_name + " " : "") + navigator.platform, + client_version: "TeaWeb " + git_version + " (" + navigator.userAgent + ")", + + client_server_password: this.server_password, + client_browser_engine: navigator.product + }; + + if(version) { + data.client_version = "TeaClient "; + data.client_version += " " + version; + + const os = require("os"); + const arch_mapping = { + "x32": "32bit", + "x64": "64bit" + }; + + data.client_version += " " + (arch_mapping[os.arch()] || os.arch()); + + const os_mapping = { + "win32": "Windows", + "linux": "Linux" + }; + data.client_platform = (os_mapping[os.platform()] || os.platform()); + } + + this.connection.send_command("clientinit", data).catch(error => { + this.connection.disconnect(); + if(error instanceof CommandResult) { + if(error.id == 1028) { + this.connection.client.handleDisconnect(DisconnectReason.SERVER_REQUIRES_PASSWORD); + } else { + + this.connection.client.handleDisconnect(DisconnectReason.CLIENT_KICKED, error); + } + } + }); + } + } +} \ No newline at end of file diff --git a/shared/js/connection/ServerConnection.ts b/shared/js/connection/ServerConnection.ts new file mode 100644 index 00000000..a28610e9 --- /dev/null +++ b/shared/js/connection/ServerConnection.ts @@ -0,0 +1,377 @@ +enum ErrorID { + PERMISSION_ERROR = 2568, + EMPTY_RESULT = 0x0501, + PLAYLIST_IS_IN_USE = 0x2103 +} + +class CommandResult { + success: boolean; + id: number; + message: string; + extra_message: string; + + json: any; + + constructor(json) { + this.json = json; + this.id = json["id"]; + this.message = json["msg"]; + + this.extra_message = ""; + if(json["extra_msg"]) this.extra_message = json["extra_msg"]; + + this.success = this.id == 0; + } +} + +class ReturnListener { + resolve: (value?: T | PromiseLike) => void; + reject: (reason?: any) => void; + code: string; + + timeout: NodeJS.Timer; +} + +namespace connection { + export class ServerConnection extends AbstractServerConnection { + _remote_address: ServerAddress; + _socket: WebSocket; + _connectionState: ConnectionState = ConnectionState.UNCONNECTED; + _handshakeHandler: HandshakeHandler; + + private _command_boss: ServerConnectionCommandBoss; + private _command_handler_default: ConnectionCommandHandler; + private _command_handler_handshake: AbstractCommandHandler; //The new handshake handler :) + + private _connect_timeout_timer: NodeJS.Timer = undefined; + private _connected: boolean = false; + private _retCodeIdx: number; + private _retListener: ReturnListener[]; + + constructor(client : TSClient) { + super(client); + + this._socket = null; + this._retCodeIdx = 0; + this._retListener = []; + + this._command_boss = new ServerConnectionCommandBoss(this); + this._command_handler_default = new ConnectionCommandHandler(this); + + this._command_boss.register_handler(this._command_handler_default); + this.command_helper.initialize(); + } + + on_connect: () => void = () => { + console.log(tr("Socket connected")); + chat.serverChat().appendMessage(tr("Logging in...")); + this._handshakeHandler.startHandshake(); + }; + + private generateReturnCode() : string { + return (this._retCodeIdx++).toString(); + } + + async connect(address : ServerAddress, handshake: HandshakeHandler, timeout?: number) : Promise { + timeout = typeof(timeout) === "number" ? timeout : 0; + + if(this._connect_timeout_timer) { + clearTimeout(this._connect_timeout_timer); + this._connect_timeout_timer = null; + try { + await this.disconnect() + } catch(error) { + console.error(tr("Failed to close old connection properly. Error: %o"), error); + throw "failed to cleanup old connection"; + } + } + this.updateConnectionState(ConnectionState.CONNECTING); + this._remote_address = address; + this._handshakeHandler = handshake; + this._handshakeHandler.setConnection(this); + this._connected = false; + chat.serverChat().appendMessage(tr("Connecting to {0}:{1}"), true, address.host, address.port); + + const self = this; + try { + let local_socket: WebSocket; + let local_timeout_timer: NodeJS.Timer; + + local_timeout_timer = setTimeout(async () => { + console.log(tr("Connect timeout triggered!")); + try { + await this.disconnect(); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to close connection after timeout had been triggered! (%o)"), error); + } + this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE); + }, timeout); + this._connect_timeout_timer = local_timeout_timer; + + this._socket = (local_socket = new WebSocket('wss://' + address.host + ":" + address.port)); /* this may hangs */ + clearTimeout(local_timeout_timer); + if(this._connect_timeout_timer == local_timeout_timer) + this._connect_timeout_timer = undefined; + + if(this._socket != local_socket) return; /* something had changed and we dont use this connection anymore! */ + local_socket.onopen = () => { + if(this._socket != local_socket) return; /* this socket isn't from interest anymore */ + + this._connected = true; + this.on_connect(); + }; + + local_socket.onclose = event => { + if(this._socket != local_socket) return; /* this socket isn't from interest anymore */ + + this.client.handleDisconnect(this._connected ? DisconnectReason.CONNECTION_CLOSED : DisconnectReason.CONNECT_FAILURE, { + code: event.code, + reason: event.reason, + event: event + }); + }; + + local_socket.onerror = e => { + if(this._socket != local_socket) return; /* this socket isn't from interest anymore */ + + console.log(tr("Received web socket error: (%o)"), e); + }; + + local_socket.onmessage = msg => { + if(this._socket != local_socket) return; /* this socket isn't from interest anymore */ + + self.handle_socket_message(msg.data); + }; + this.updateConnectionState(ConnectionState.INITIALISING); + } catch (e) { + try { + await this.disconnect(); + } catch(error) { + log.warn(LogCategory.NETWORKING, tr("Failed to close connection after connect attempt failed (%o)"), error); + } + this.client.handleDisconnect(DisconnectReason.CONNECT_FAILURE, e); + } + } + + updateConnectionState(state: ConnectionState) { + this._connectionState = state; + this.client.controlBar.update_connection_state(); + } + + async disconnect(reason?: string) : Promise { + if(typeof(reason) === "string") { + //TODO send disconnect reason + } + + if(this._connectionState == ConnectionState.UNCONNECTED) + return; + + this.updateConnectionState(ConnectionState.UNCONNECTED); + + if(this._socket) + this._socket.close(3000 + 0xFF, tr("request disconnect")); + this._socket = null; + for(let future of this._retListener) + future.reject(tr("Connection closed")); + this._retListener = []; + this._retCodeIdx = 0; + this._connected = false; + } + + private handle_socket_message(data) { + if(typeof(data) === "string") { + let json; + try { + json = JSON.parse(data); + } catch(e) { + console.error(tr("Could not parse message json!")); + alert(e); // error in the above string (in this case, yes)! + return; + } + if(json["type"] === undefined) { + console.log(tr("Missing data type!")); + return; + } + if(json["type"] === "command") { + let group = log.group(log.LogType.DEBUG, LogCategory.NETWORKING, tr("Handling command '%s'"), json["command"]); + group.log(tr("Handling command '%s'"), json["command"]); + group.group(log.LogType.TRACE, tr("Json:")).collapsed(true).log("%o", json).end(); + + this._command_boss.invoke_handle({ + command: json["command"], + arguments: json["data"] + }); + group.end(); + } else if(json["type"] === "WebRTC") this.client.voiceConnection.handleControlPacket(json); + else { + console.log(tr("Unknown command type %o"), json["type"]); + } + } else { + log.warn(LogCategory.NETWORKING, tr("Received unknown message of type %s. Dropping message"), typeof(data)); + } + } + + sendData(data: any) { + if(!this._socket || this._socket.readyState != 1) { + log.warn(LogCategory.NETWORKING, tr("Tried to send on a invalid socket (%s)"), this._socket ? "invalid state (" + this._socket.readyState + ")" : "invalid socket"); + return; + } + this._socket.send(data); + } + + private commandiefy(input: any) : string { + return JSON.stringify(input, (key, value) => { + switch (typeof value) { + case "boolean": return value == true ? "1" : "0"; + case "function": return value(); + default: + return value; + } + }); + } + + send_command(command: string, data?: any | any[], _options?: CommandOptions) : Promise { + if(!this._socket || !this.connected()) { + console.warn(tr("Tried to send a command without a valid connection.")); + return; + } + + const options: CommandOptions = {}; + Object.assign(options, CommandOptionDefaults); + Object.assign(options, _options); + + data = $.isArray(data) ? data : [data || {}]; + + const _this = this; + let result = new Promise((resolve, failed) => { + let _data = $.isArray(data) ? data : [data]; + let retCode = _data[0]["return_code"] !== undefined ? _data[0].return_code : _this.generateReturnCode(); + _data[0].return_code = retCode; + + let listener = new ReturnListener(); + listener.resolve = resolve; + listener.reject = failed; + listener.code = retCode; + listener.timeout = setTimeout(() => { + _this._retListener.remove(listener); + listener.reject("timeout"); + }, 1500); + this._retListener.push(listener); + + this._socket.send(this.commandiefy({ + "type": "command", + "command": command, + "data": _data, + "flags": options.flagset.filter(entry => entry.length != 0) + })); + }); + return new Promise((resolve, failed) => { + result.then(resolve).catch(ex => { + if(options.process_result) { + if(ex instanceof CommandResult) { + let res = ex; + if(!res.success) { + if(res.id == 2568) { //Permission error + res.message = tr("Insufficient client permissions. Failed on permission ") + this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name; + chat.serverChat().appendError(tr("Insufficient client permissions. Failed on permission {}"), this.client.permissions.resolveInfo(res.json["failed_permid"] as number).name); + sound.play(Sound.ERROR_INSUFFICIENT_PERMISSIONS); + } else { + chat.serverChat().appendError(res.extra_message.length == 0 ? res.message : res.extra_message); + } + } + } else if(typeof(ex) === "string") { + chat.serverChat().appendError(tr("Command execution results in ") + ex); + } else { + console.error(tr("Invalid promise result type: %o. Result:"), typeof (ex)); + console.error(ex); + } + } + failed(ex); + }) + }); + } + + connected() : boolean { + return this._socket && this._socket.readyState == WebSocket.OPEN; + } + + support_voice(): boolean { + return false; + } + + voice_connection(): connection.AbstractVoiceConnection | undefined { + return undefined; + } + + command_handler_boss(): connection.AbstractCommandHandlerBoss { + return this._command_boss; + } + } +} + +interface ClientNameInfo { + //cluid=tYzKUryn\/\/Y8VBMf8PHUT6B1eiE= name=Exp clname=Exp cldbid=9 + client_unique_id: string; + client_nickname: string; + client_database_id: number; +} + +interface ClientNameFromUid { + promise: LaterPromise, + keys: string[], + response: ClientNameInfo[] +} + +interface QueryListEntry { + username: string; + unique_id: string; + bounded_server: number; +} + +interface QueryList { + flag_own: boolean; + flag_all: boolean; + + queries: QueryListEntry[]; +} + +interface Playlist { + playlist_id: number; + playlist_bot_id: number; + playlist_title: string; + playlist_type: number; + playlist_owner_dbid: number; + playlist_owner_name: string; + + needed_power_modify: number; + needed_power_permission_modify: number; + needed_power_delete: number; + needed_power_song_add: number; + needed_power_song_move: number; + needed_power_song_remove: number; +} + +interface PlaylistInfo { + playlist_id: number, + playlist_title: string, + playlist_description: string, + playlist_type: number, + + playlist_owner_dbid: number, + playlist_owner_name: string, + + playlist_flag_delete_played: boolean, + playlist_flag_finished: boolean, + playlist_replay_mode: number, + playlist_current_song_id: number, +} + +interface PlaylistSong { + song_id: number; + song_previous_song_id: number; + song_invoker: string; + song_url: string; + song_url_loader: string; + song_loaded: boolean; + song_metadata: string; +} \ No newline at end of file diff --git a/shared/js/load.ts b/shared/js/load.ts index b0cff134..a51d1e1c 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -470,6 +470,11 @@ const loader_javascript = { }) } + /* load some extends classes */ + await loader.load_scripts([ + ["js/connection/ConnectionBase.js"] + ]); + /* load the main app */ await loader.load_scripts([ //Load general API's @@ -539,11 +544,16 @@ const loader_javascript = { "js/settings.js", "js/bookmarks.js", "js/contextMenu.js", - "js/connection.js", "js/FileManager.js", "js/client.js", "js/chat.js", + //Connection + "js/connection/CommandHandler.js", + "js/connection/CommandHelper.js", + "js/connection/HandshakeHandler.js", + "js/connection/ServerConnection.js", + "js/PPTListener.js", diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index da21b49d..00bc6ce3 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -58,7 +58,7 @@ class Group { } } -class GroupManager { +class GroupManager extends connection.AbstractCommandHandler { readonly handle: TSClient; serverGroups: Group[] = []; @@ -66,18 +66,29 @@ class GroupManager { private requests_group_permissions: GroupPermissionRequest[] = []; constructor(client: TSClient) { + super(client.serverConnection); + + client.serverConnection.command_handler_boss().register_handler(this); this.handle = client; + } - this.handle.serverConnection.commandHandler["notifyservergrouplist"] = this.onServerGroupList.bind(this); - this.handle.serverConnection.commandHandler["notifychannelgrouplist"] = this.onServerGroupList.bind(this); - - this.handle.serverConnection.commandHandler["notifyservergrouppermlist"] = this.onPermissionList.bind(this); - this.handle.serverConnection.commandHandler["notifychannelgrouppermlist"] = this.onPermissionList.bind(this); + handle_command(command: connection.ServerCommand): boolean { + switch (command.command) { + case "notifyservergrouplist": + case "notifychannelgrouplist": + this.handle_grouplist(command.arguments); + return true; + case "notifyservergrouppermlist": + case "notifychannelgrouppermlist": + this.handle_group_permission_list(command.arguments); + return true; + } + return false; } requestGroups(){ - this.handle.serverConnection.sendCommand("servergrouplist"); - this.handle.serverConnection.sendCommand("channelgrouplist"); + this.handle.serverConnection.send_command("servergrouplist"); + this.handle.serverConnection.send_command("channelgrouplist"); } static sorter() : (a: Group, b: Group) => number { @@ -107,7 +118,7 @@ class GroupManager { return undefined; } - private onServerGroupList(json) { + private handle_grouplist(json) { let target : GroupTarget; if(json[0]["sgid"]) target = GroupTarget.SERVER; else if(json[0]["cgid"]) target = GroupTarget.CHANNEL; @@ -167,7 +178,7 @@ class GroupManager { req.promise = new LaterPromise(); this.requests_group_permissions.push(req); - this.handle.serverConnection.sendCommand(group.target == GroupTarget.SERVER ? "servergrouppermlist" : "channelgrouppermlist", { + this.handle.serverConnection.send_command(group.target == GroupTarget.SERVER ? "servergrouppermlist" : "channelgrouppermlist", { cgid: group.id, sgid: group.id }).catch(error => { @@ -179,7 +190,7 @@ class GroupManager { return req.promise; } - private onPermissionList(json: any[]) { + private handle_group_permission_list(json: any[]) { let group = json[0]["sgid"] ? this.serverGroup(parseInt(json[0]["sgid"])) : this.channelGroup(parseInt(json[0]["cgid"])); if(!group) { log.error(LogCategory.PERMISSIONS, tr("Got group permissions for group %o/%o, but its not a registered group!"), json[0]["sgid"], json[0]["cgid"]); diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 537c8a5b..62ab15ab 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -416,7 +416,7 @@ class TeaPermissionRequest { promise: LaterPromise; } -class PermissionManager { +class PermissionManager extends connection.AbstractCommandHandler { readonly handle: TSClient; permissionList: PermissionInfo[] = []; @@ -505,14 +505,34 @@ class PermissionManager { } constructor(client: TSClient) { + super(client.serverConnection); + + //FIXME? Dont register the handler like this? + this.volatile_handler_boss = true; + client.serverConnection.command_handler_boss().register_handler(this); + this.handle = client; + } - this.handle.serverConnection.commandHandler["notifyclientneededpermissions"] = this.onNeededPermissions.bind(this); - this.handle.serverConnection.commandHandler["notifypermissionlist"] = this.onPermissionList.bind(this); - - this.handle.serverConnection.commandHandler["notifychannelpermlist"] = this.onChannelPermList.bind(this); - this.handle.serverConnection.commandHandler["notifyclientpermlist"] = this.onClientPermList.bind(this); - this.handle.serverConnection.commandHandler["notifyplaylistpermlist"] = this.onPlaylistPermList.bind(this); + handle_command(command: connection.ServerCommand): boolean { + switch (command.command) { + case "notifyclientneededpermissions": + this.onNeededPermissions(command.arguments); + return true; + case "notifypermissionlist": + this.onPermissionList(command.arguments); + return true; + case "notifychannelpermlist": + this.onChannelPermList(command.arguments); + return true; + case "notifyclientpermlist": + this.onClientPermList(command.arguments); + return true; + case "notifyplaylistpermlist": + this.onPlaylistPermList(command.arguments); + return true; + } + return false; } initialized() : boolean { @@ -520,7 +540,7 @@ class PermissionManager { } public requestPermissionList() { - this.handle.serverConnection.sendCommand("permissionlist"); + this.handle.serverConnection.send_command("permissionlist"); } private onPermissionList(json) { @@ -648,7 +668,7 @@ class PermissionManager { request = new ChannelPermissionRequest(); request.requested = Date.now(); request.channel_id = channelId; - this.handle.serverConnection.sendCommand("channelpermlist", {"cid": channelId}); + this.handle.serverConnection.send_command("channelpermlist", {"cid": channelId}); this.requests_channel_permissions.push(request); } request.callback_error.push(reject); @@ -676,7 +696,7 @@ class PermissionManager { request.client_id = client_id; request.promise = new LaterPromise(); - this.handle.serverConnection.sendCommand("clientpermlist", {cldbid: client_id}).catch(error => { + this.handle.serverConnection.send_command("clientpermlist", {cldbid: client_id}).catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) request.promise.resolved([]); else @@ -697,7 +717,7 @@ class PermissionManager { request.channel_id = channel_id; request.promise = new LaterPromise(); - this.handle.serverConnection.sendCommand("channelclientpermlist", {cldbid: client_id, cid: channel_id}).catch(error => { + this.handle.serverConnection.send_command("channelclientpermlist", {cldbid: client_id, cid: channel_id}).catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) request.promise.resolved([]); else @@ -730,7 +750,7 @@ class PermissionManager { request.playlist_id = playlist_id; request.promise = new LaterPromise(); - this.handle.serverConnection.sendCommand("playlistpermlist", {playlist_id: playlist_id}).catch(error => { + this.handle.serverConnection.send_command("playlistpermlist", {playlist_id: playlist_id}).catch(error => { if(error instanceof CommandResult && error.id == ErrorID.EMPTY_RESULT) request.promise.resolved([]); else diff --git a/shared/js/profiles/ConnectionProfile.ts b/shared/js/profiles/ConnectionProfile.ts index 1a420bfa..914fdd94 100644 --- a/shared/js/profiles/ConnectionProfile.ts +++ b/shared/js/profiles/ConnectionProfile.ts @@ -38,7 +38,7 @@ namespace profiles { this.identities[identities.IdentitifyType[type].toLowerCase()] = identity; } - spawn_identity_handshake_handler?(connection: ServerConnection) : HandshakeIdentityHandler { + spawn_identity_handshake_handler?(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { const identity = this.selected_identity(); if(!identity) return undefined; diff --git a/shared/js/profiles/Identity.ts b/shared/js/profiles/Identity.ts index 91dafb8e..f81c0b42 100644 --- a/shared/js/profiles/Identity.ts +++ b/shared/js/profiles/Identity.ts @@ -15,7 +15,7 @@ namespace profiles.identities { encode?() : string; decode(data: string) : Promise; - spawn_identity_handshake_handler(connection: ServerConnection) : HandshakeIdentityHandler; + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler; } export async function decode_identity(type: IdentitifyType, data: string) : Promise { @@ -61,12 +61,33 @@ namespace profiles.identities { return identity; } - export abstract class AbstractHandshakeIdentityHandler implements HandshakeIdentityHandler { - connection: ServerConnection; + export class HandshakeCommandHandler extends connection.AbstractCommandHandler { + readonly handle: T; + + constructor(connection: connection.AbstractServerConnection, handle: T) { + super(connection); + this.handle = handle; + } + + + handle_command(command: connection.ServerCommand): boolean { + if($.isFunction(this[command.command])) + this[command.command](command.arguments); + else if(command.command == "error") { + return false; + } else { + console.warn(tr("Received unknown command while handshaking (%o)"), command); + } + return true; + } + } + + export abstract class AbstractHandshakeIdentityHandler implements connection.HandshakeIdentityHandler { + connection: connection.AbstractServerConnection; protected callbacks: ((success: boolean, message?: string) => any)[] = []; - protected constructor(connection: ServerConnection) { + protected constructor(connection: connection.AbstractServerConnection) { this.connection = connection; } diff --git a/shared/js/profiles/identities/NameIdentity.ts b/shared/js/profiles/identities/NameIdentity.ts index 790ca7b7..236e1273 100644 --- a/shared/js/profiles/identities/NameIdentity.ts +++ b/shared/js/profiles/identities/NameIdentity.ts @@ -3,16 +3,19 @@ namespace profiles.identities { class NameHandshakeHandler extends AbstractHandshakeIdentityHandler { readonly identity: NameIdentity; + handler: HandshakeCommandHandler; - constructor(connection: ServerConnection, identity: profiles.identities.NameIdentity) { + constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.NameIdentity) { super(connection); this.identity = identity; + + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); } start_handshake() { - this.connection.commandHandler["handshakeidentityproof"] = () => this.trigger_fail("server requested unexpected proof"); - - this.connection.sendCommand("handshakebegin", { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { intention: 0, authentication_method: this.identity.type(), client_nickname: this.identity.name() @@ -24,6 +27,16 @@ namespace profiles.identities { this.trigger_fail("failed to execute begin (" + error + ")"); }).then(() => this.trigger_success()); } + + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } export class NameIdentity implements Identity { @@ -67,7 +80,7 @@ namespace profiles.identities { }); } - spawn_identity_handshake_handler(connection: ServerConnection) : HandshakeIdentityHandler { + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { return new NameHandshakeHandler(connection, this); } } diff --git a/shared/js/profiles/identities/TeaForumIdentity.ts b/shared/js/profiles/identities/TeaForumIdentity.ts index 97e9a9e1..af0914ca 100644 --- a/shared/js/profiles/identities/TeaForumIdentity.ts +++ b/shared/js/profiles/identities/TeaForumIdentity.ts @@ -3,16 +3,18 @@ namespace profiles.identities { class TeaForumHandshakeHandler extends AbstractHandshakeIdentityHandler { readonly identity: TeaForumIdentity; + handler: HandshakeCommandHandler; - constructor(connection: ServerConnection, identity: profiles.identities.TeaForumIdentity) { + constructor(connection: connection.AbstractServerConnection, identity: profiles.identities.TeaForumIdentity) { super(connection); this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } start_handshake() { - this.connection.commandHandler["handshakeidentityproof"] = this.handle_proof.bind(this); - - this.connection.sendCommand("handshakebegin", { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { intention: 0, authentication_method: this.identity.type(), data: this.identity.data_json() @@ -27,7 +29,7 @@ namespace profiles.identities { private handle_proof(json) { - this.connection.sendCommand("handshakeindentityproof", { + this.connection.send_command("handshakeindentityproof", { proof: this.identity.data_sign() }).catch(error => { console.error(tr("Failed to proof the identity. Error: %o"), error); @@ -37,6 +39,16 @@ namespace profiles.identities { this.trigger_fail("failed to execute proof (" + error + ")"); }).then(() => this.trigger_success()); } + + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } export class TeaForumIdentity implements Identity { @@ -104,7 +116,7 @@ namespace profiles.identities { }); } - spawn_identity_handshake_handler(connection: ServerConnection) : HandshakeIdentityHandler { + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection) : connection.HandshakeIdentityHandler { return new TeaForumHandshakeHandler(connection, this); } } diff --git a/shared/js/profiles/identities/TeamSpeakIdentity.ts b/shared/js/profiles/identities/TeamSpeakIdentity.ts index 7f3442a6..b83f2b1a 100644 --- a/shared/js/profiles/identities/TeamSpeakIdentity.ts +++ b/shared/js/profiles/identities/TeamSpeakIdentity.ts @@ -198,16 +198,18 @@ namespace profiles.identities { class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler { identity: TeaSpeakIdentity; + handler: HandshakeCommandHandler; - constructor(connection: ServerConnection, identity: TeaSpeakIdentity) { + constructor(connection: connection.AbstractServerConnection, identity: TeaSpeakIdentity) { super(connection); this.identity = identity; + this.handler = new HandshakeCommandHandler(connection, this); + this.handler["handshakeidentityproof"] = this.handle_proof.bind(this); } start_handshake() { - this.connection.commandHandler["handshakeidentityproof"] = this.handle_proof.bind(this); - - this.connection.sendCommand("handshakebegin", { + this.connection.command_handler_boss().register_handler(this.handler); + this.connection.send_command("handshakebegin", { intention: 0, authentication_method: this.identity.type(), publicKey: this.identity.public_key @@ -227,7 +229,7 @@ namespace profiles.identities { } this.identity.sign_message(json[0]["message"], json[0]["digest"]).then(proof => { - this.connection.sendCommand("handshakeindentityproof", {proof: proof}).catch(error => { + this.connection.send_command("handshakeindentityproof", {proof: proof}).catch(error => { console.error(tr("Failed to proof the identity. Error: %o"), error); if(error instanceof CommandResult) @@ -238,6 +240,16 @@ namespace profiles.identities { this.trigger_fail("failed to sign message"); }); } + + protected trigger_fail(message: string) { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_fail(message); + } + + protected trigger_success() { + this.connection.command_handler_boss().unregister_handler(this.handler); + super.trigger_success(); + } } class IdentityPOWWorker { @@ -831,7 +843,7 @@ namespace profiles.identities { return base64ArrayBuffer(buffer.subarray(0, index)); } - spawn_identity_handshake_handler(connection: ServerConnection): HandshakeIdentityHandler { + spawn_identity_handshake_handler(connection: connection.AbstractServerConnection): connection.HandshakeIdentityHandler { return new TeaSpeakHandshakeHandler(connection, this); } } diff --git a/shared/js/ui/channel.ts b/shared/js/ui/channel.ts index fc048ff5..edf372fd 100644 --- a/shared/js/ui/channel.ts +++ b/shared/js/ui/channel.ts @@ -94,7 +94,7 @@ class ChannelEntry { if(this._cached_channel_description) return new Promise(resolve => resolve(this._cached_channel_description)); if(this._cached_channel_description_promise) return this._cached_channel_description_promise; - this.channelTree.client.serverConnection.sendCommand("channelgetdescription", {cid: this.channelId}).catch(error => { + this.channelTree.client.serverConnection.send_command("channelgetdescription", {cid: this.channelId}).catch(error => { this._cached_channel_description_promise_reject(error); }); @@ -440,7 +440,7 @@ class ChannelEntry { Modals.createChannelModal(this, undefined, this.channelTree.client.permissions, (changes?, permissions?) => { if(changes) { changes["cid"] = this.channelId; - this.channelTree.client.serverConnection.sendCommand("channeledit", changes); + this.channelTree.client.serverConnection.send_command("channeledit", changes); log.info(LogCategory.CHANNEL, tr("Changed channel properties of channel %s: %o"), this.channelName(), changes); } @@ -456,7 +456,9 @@ class ChannelEntry { } perms[0]["cid"] = this.channelId; - this.channelTree.client.serverConnection.sendCommand("channeladdperm", perms, ["continueonerror"]).then(() => { + this.channelTree.client.serverConnection.send_command("channeladdperm", perms, { + flagset: ["continueonerror"] + }).then(() => { sound.play(Sound.CHANNEL_EDITED_SELF); }); } @@ -469,7 +471,7 @@ class ChannelEntry { name: tr("Delete channel"), invalidPermission: !flagDelete, callback: () => { - this.channelTree.client.serverConnection.sendCommand("channeldelete", {cid: this.channelId}).then(() => { + this.channelTree.client.serverConnection.send_command("channeldelete", {cid: this.channelId}).then(() => { sound.play(Sound.CHANNEL_DELETED); }) } @@ -480,7 +482,7 @@ class ChannelEntry { icon: "client-addon-collection", name: tr("Create music bot"), callback: () => { - this.channelTree.client.serverConnection.sendCommand("musicbotcreate", {cid: this.channelId}).then(() => { + this.channelTree.client.serverConnection.send_command("musicbotcreate", {cid: this.channelId}).then(() => { createInfoModal(tr("Bot successfully created"), tr("Bot has been successfully created.")).open(); }).catch(error => { if(error instanceof CommandResult) { @@ -692,7 +694,7 @@ class ChannelEntry { }); }).open(); } else if(this.channelTree.client.getClient().currentChannel() != this) - this.channelTree.client.getServerConnection().joinChannel(this, this._cachedPassword).then(() => { + this.channelTree.client.getServerConnection().command_helper.joinChannel(this, this._cachedPassword).then(() => { sound.play(Sound.CHANNEL_JOINED); }).catch(error => { if(error instanceof CommandResult) { diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts index a7579013..7d7cc31d 100644 --- a/shared/js/ui/client.ts +++ b/shared/js/ui/client.ts @@ -146,7 +146,7 @@ class ClientEntry { const source = client._channel; const self = this.channelTree.client.getClient(); - this.channelTree.client.serverConnection.sendCommand("clientmove", { + this.channelTree.client.serverConnection.send_command("clientmove", { clid: client.clientId(), cid: target.getChannelId() }).then(event => { @@ -179,7 +179,7 @@ class ClientEntry { entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; if(this.groupAssigned(group)) { entry.callback = () => { - this.channelTree.client.serverConnection.sendCommand("servergroupdelclient", { + this.channelTree.client.serverConnection.send_command("servergroupdelclient", { sgid: group.id, cldbid: this.properties.client_database_id }); @@ -187,7 +187,7 @@ class ClientEntry { entry.disabled = !this.channelTree.client.permissions.neededPermission(PermissionType.I_GROUP_MEMBER_ADD_POWER).granted(group.requiredMemberRemovePower); } else { entry.callback = () => { - this.channelTree.client.serverConnection.sendCommand("servergroupaddclient", { + this.channelTree.client.serverConnection.send_command("servergroupaddclient", { sgid: group.id, cldbid: this.properties.client_database_id }); @@ -211,7 +211,7 @@ class ClientEntry { } entry.name = group.name + " [" + (group.properties.savedb ? "perm" : "tmp") + "]"; entry.callback = () => { - this.channelTree.client.serverConnection.sendCommand("setclientchannelgroup", { + this.channelTree.client.serverConnection.send_command("setclientchannelgroup", { cldbid: this.properties.client_database_id, cgid: group.id, cid: this.currentChannel().channelId @@ -234,12 +234,12 @@ class ClientEntry { callback: () => { Modals.createServerGroupAssignmentModal(this, (group, flag) => { if(flag) { - return this.channelTree.client.serverConnection.sendCommand("servergroupaddclient", { + return this.channelTree.client.serverConnection.send_command("servergroupaddclient", { sgid: group.id, cldbid: this.properties.client_database_id }).then(result => true); } else - return this.channelTree.client.serverConnection.sendCommand("servergroupdelclient", { + return this.channelTree.client.serverConnection.send_command("servergroupdelclient", { sgid: group.id, cldbid: this.properties.client_database_id }).then(result => true); @@ -286,7 +286,7 @@ class ClientEntry { if(typeof(result) === "string") { //TODO tr console.log("Poking client " + _this.clientNickName() + " with message " + result); - _this.channelTree.client.serverConnection.sendCommand("clientpoke", { + _this.channelTree.client.serverConnection.send_command("clientpoke", { clid: _this.clientId(), msg: result }); @@ -303,7 +303,7 @@ class ClientEntry { if(typeof(result) === "string") { //TODO tr console.log("Changing " + _this.clientNickName() + "'s description to " + result); - _this.channelTree.client.serverConnection.sendCommand("clientedit", { + _this.channelTree.client.serverConnection.send_command("clientedit", { clid: _this.clientId(), client_description: result }); @@ -319,7 +319,7 @@ class ClientEntry { icon: "client-move_client_to_own_channel", name: tr("Move client to your channel"), callback: () => { - this.channelTree.client.serverConnection.sendCommand("clientmove", { + this.channelTree.client.serverConnection.send_command("clientmove", { clid: this.clientId(), cid: this.channelTree.client.getClient().currentChannel().getChannelId() }); @@ -333,7 +333,7 @@ class ClientEntry { if(result) { //TODO tr console.log("Kicking client " + _this.clientNickName() + " from channel with reason " + result); - _this.channelTree.client.serverConnection.sendCommand("clientkick", { + _this.channelTree.client.serverConnection.send_command("clientkick", { clid: _this.clientId(), reasonid: ViewReasonId.VREASON_CHANNEL_KICK, reasonmsg: result @@ -351,7 +351,7 @@ class ClientEntry { if(result) { //TODO tr console.log("Kicking client " + _this.clientNickName() + " from server with reason " + result); - _this.channelTree.client.serverConnection.sendCommand("clientkick", { + _this.channelTree.client.serverConnection.send_command("clientkick", { clid: _this.clientId(), reasonid: ViewReasonId.VREASON_SERVER_KICK, reasonmsg: result @@ -367,11 +367,13 @@ class ClientEntry { invalidPermission: !this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), callback: () => { Modals.spawnBanClient(this.properties.client_nickname, (data) => { - this.channelTree.client.serverConnection.sendCommand("banclient", { + this.channelTree.client.serverConnection.send_command("banclient", { uid: this.properties.client_unique_identifier, banreason: data.reason, time: data.length - }, [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]).then(() => { + }, { + flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""] + }).then(() => { sound.play(Sound.USER_BANNED); }); }); @@ -386,7 +388,7 @@ class ClientEntry { invalidPermission: true, //!this.channelTree.client.permissions.neededPermission(PermissionType.I_CLIENT_BAN_MAX_BANTIME).granted(1), callback: () => { Modals.spawnBanClient(this.properties.client_nickname, (duration, reason) => { - this.channelTree.client.serverConnection.sendCommand("banclient", { + this.channelTree.client.serverConnection.send_command("banclient", { uid: this.properties.client_unique_identifier, banreason: reason, time: duration @@ -653,7 +655,7 @@ class ClientEntry { updateClientVariables(){ if(this.lastVariableUpdate == 0 || new Date().getTime() - 10 * 60 * 1000 > this.lastVariableUpdate){ //Cache these only 10 min this.lastVariableUpdate = new Date().getTime(); - this.channelTree.client.serverConnection.sendCommand("clientgetvariables", {clid: this.clientId()}); + this.channelTree.client.serverConnection.send_command("clientgetvariables", {clid: this.clientId()}); } } @@ -667,12 +669,12 @@ class ClientEntry { const _this = this; c.onMessageSend = function (text: string) { - _this.channelTree.client.serverConnection.sendMessage(text, ChatType.CLIENT, _this); + _this.channelTree.client.serverConnection.command_helper.sendMessage(text, ChatType.CLIENT, _this); }; c.onClose = function () : boolean { //TODO check online? - _this.channelTree.client.serverConnection.sendCommand("clientchatclosed", {"clid": _this.clientId()}); + _this.channelTree.client.serverConnection.send_command("clientchatclosed", {"clid": _this.clientId()}); return true; } } @@ -797,7 +799,7 @@ class LocalClientEntry extends ClientEntry { createInputModal(tr("Change own description"), tr("New description:
"), text => true, result => { if(result) { console.log(tr("Changing own description to %s"), result); - _self.channelTree.client.serverConnection.sendCommand("clientedit", { + _self.channelTree.client.serverConnection.send_command("clientedit", { clid: _self.clientId(), client_description: result }); @@ -853,7 +855,7 @@ class LocalClientEntry extends ClientEntry { if(_self.clientNickName() == text) return; elm.text(_self.clientNickName()); - _self.handle.serverConnection.updateClient("client_nickname", text).then((e) => { + _self.handle.serverConnection.command_helper.updateClient("client_nickname", text).then((e) => { chat.serverChat().appendMessage(tr("Nickname successfully changed")); }).catch((e: CommandResult) => { chat.serverChat().appendError(tr("Could not change nickname ({})"), e.extra_message); @@ -915,7 +917,7 @@ class MusicClientEntry extends ClientEntry { callback: () => { createInputModal(tr("Change music bots nickname"), tr("New nickname:
"), text => text.length >= 3 && text.length <= 31, result => { if(result) { - this.channelTree.client.serverConnection.sendCommand("clientedit", { + this.channelTree.client.serverConnection.send_command("clientedit", { clid: this.clientId(), client_nickname: result }); @@ -931,7 +933,7 @@ class MusicClientEntry extends ClientEntry { callback: () => { createInputModal(tr("Change music bots description"), tr("New description:
"), text => true, result => { if(typeof(result) === 'string') { - this.channelTree.client.serverConnection.sendCommand("clientedit", { + this.channelTree.client.serverConnection.send_command("clientedit", { clid: this.clientId(), client_description: result }); @@ -955,7 +957,7 @@ class MusicClientEntry extends ClientEntry { icon: "client-edit", disabled: false, callback: () => { - this.channelTree.client.serverConnection.helper.request_playlist_list().then(lists => { + this.channelTree.client.serverConnection.command_helper.request_playlist_list().then(lists => { for(const entry of lists) { if(entry.playlist_id == this.properties.client_playlist_id) { Modals.spawnPlaylistEdit(this.channelTree.client, entry); @@ -976,7 +978,7 @@ class MusicClientEntry extends ClientEntry { callback: () => { createInputModal(tr("Please enter the URL"), tr("URL:"), text => true, result => { if(result) { - this.channelTree.client.serverConnection.sendCommand("musicbotqueueadd", { + this.channelTree.client.serverConnection.send_command("musicbotqueueadd", { bot_id: this.properties.client_database_id, type: "yt", //Its a hint not a force! url: result @@ -999,7 +1001,7 @@ class MusicClientEntry extends ClientEntry { icon: "client-move_client_to_own_channel", name: tr("Move client to your channel"), callback: () => { - this.channelTree.client.serverConnection.sendCommand("clientmove", { + this.channelTree.client.serverConnection.send_command("clientmove", { clid: this.clientId(), cid: this.channelTree.client.getClient().currentChannel().getChannelId() }); @@ -1012,7 +1014,7 @@ class MusicClientEntry extends ClientEntry { createInputModal(tr("Kick client from channel"), tr("Kick reason:
"), text => true, result => { if(result) { console.log(tr("Kicking client %o from channel with reason %o"), this.clientNickName(), result); - this.channelTree.client.serverConnection.sendCommand("clientkick", { + this.channelTree.client.serverConnection.send_command("clientkick", { clid: this.clientId(), reasonid: ViewReasonId.VREASON_CHANNEL_KICK, reasonmsg: result @@ -1045,7 +1047,7 @@ class MusicClientEntry extends ClientEntry { max_volume = 100; Modals.spawnChangeRemoteVolume(this.properties.player_volume, max_volume / 100, value => { - this.channelTree.client.serverConnection.sendCommand("clientedit", { + this.channelTree.client.serverConnection.send_command("clientedit", { clid: this.clientId(), player_volume: value, }).then(() => { @@ -1064,7 +1066,7 @@ class MusicClientEntry extends ClientEntry { const tag = $.spawn("div").append(MessageHelper.formatMessage(tr("Do you really want to delete {0}"), this.createChatTag(false))); Modals.spawnYesNo(tr("Are you sure?"), $.spawn("div").append(tag), result => { if(result) { - this.channelTree.client.serverConnection.sendCommand("musicbotdelete", { + this.channelTree.client.serverConnection.send_command("musicbotdelete", { bot_id: this.properties.client_database_id }); } @@ -1105,7 +1107,7 @@ class MusicClientEntry extends ClientEntry { this._info_promise_resolve = resolve; }); - this.channelTree.client.serverConnection.sendCommand("musicbotplayerinfo", {bot_id: this.properties.client_database_id }); + this.channelTree.client.serverConnection.send_command("musicbotplayerinfo", {bot_id: this.properties.client_database_id }); return this._info_promise; } } \ No newline at end of file diff --git a/shared/js/ui/frames/ControlBar.ts b/shared/js/ui/frames/ControlBar.ts index 6df969fe..59e5a8a9 100644 --- a/shared/js/ui/frames/ControlBar.ts +++ b/shared/js/ui/frames/ControlBar.ts @@ -137,7 +137,7 @@ class ControlBar { if(this.handle.serverConnection.connected) - this.handle.serverConnection.sendCommand("clientupdate", { + this.handle.serverConnection.send_command("clientupdate", { client_input_muted: this._muteInput }); settings.changeGlobal("mute_input", this._muteInput); @@ -162,7 +162,7 @@ class ControlBar { } if(this.handle.serverConnection.connected) - this.handle.serverConnection.sendCommand("clientupdate", { + this.handle.serverConnection.send_command("clientupdate", { client_output_muted: this._muteOutput }); settings.changeGlobal("mute_output", this._muteOutput); @@ -187,7 +187,7 @@ class ControlBar { } if(this.handle.serverConnection.connected) - this.handle.serverConnection.sendCommand("clientupdate", { + this.handle.serverConnection.send_command("clientupdate", { client_away: this._away, client_away_message: this._awayMessage }); @@ -201,7 +201,7 @@ class ControlBar { updateProperties() { if(this.handle.serverConnection.connected) - this.handle.serverConnection.sendCommand("clientupdate", { + this.handle.serverConnection.send_command("clientupdate", { client_input_muted: this._muteInput, client_output_muted: this._muteOutput, client_away: this._away, @@ -221,7 +221,7 @@ class ControlBar { this.htmlTag.find(".btn_mute_input").prop("disabled", !this.codec_supported|| !this.support_playback || !this.support_record); this.htmlTag.find(".btn_mute_output").prop("disabled", !this.codec_supported || !this.support_playback); - this.handle.serverConnection.sendCommand("clientupdate", { + this.handle.serverConnection.send_command("clientupdate", { client_input_hardware: this.codec_supported && this.support_record, client_output_hardware: this.codec_supported && this.support_playback }); @@ -273,7 +273,7 @@ class ControlBar { createInputModal(tr("Use token"), tr("Please enter your token/priviledge key"), message => message.length > 0, result => { if(!result) return; if(this.handle.serverConnection.connected) - this.handle.serverConnection.sendCommand("tokenuse", { + this.handle.serverConnection.send_command("tokenuse", { token: result }).then(() => { createInfoModal(tr("Use token"), tr("Toke successfully used!")).open(); diff --git a/shared/js/ui/frames/SelectedItemInfo.ts b/shared/js/ui/frames/SelectedItemInfo.ts index c4ef1b3c..1cd0f980 100644 --- a/shared/js/ui/frames/SelectedItemInfo.ts +++ b/shared/js/ui/frames/SelectedItemInfo.ts @@ -412,7 +412,7 @@ enum MusicPlayerState { } class MusicInfoManager extends ClientInfoManager { - notify_status: (json) => any; + single_handler: connection.SingleCommandHandler; createFrame<_>(handle: InfoBar<_>, channel: MusicClientEntry, html_tag: JQuery) { super.createFrame(handle, channel, html_tag); @@ -420,9 +420,9 @@ class MusicInfoManager extends ClientInfoManager { } updateFrame(bot: MusicClientEntry, html_tag: JQuery) { - if(this.notify_status) { - this.handle.handle.serverConnection.commandHandler.unset_handler("notifymusicstatusupdate", this.notify_status); - this.notify_status = undefined; + if(this.single_handler) { + this.handle.handle.serverConnection.command_handler_boss().remove_single_handler(this.single_handler); + this.single_handler = undefined; } this.resetIntervals(); @@ -459,7 +459,7 @@ class MusicInfoManager extends ClientInfoManager { let button_stop = frame.find('.button_stop'); button_play.click(handler => { if(!button_play.hasClass("active")) { - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: 1 }).then(updated => this.triggerUpdate()).catch(error => { @@ -472,7 +472,7 @@ class MusicInfoManager extends ClientInfoManager { }); button_pause.click(handler => { if(!button_pause.hasClass("active")) { - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: 2 }).then(updated => this.triggerUpdate()).catch(error => { @@ -484,7 +484,7 @@ class MusicInfoManager extends ClientInfoManager { button_pause.hide(); }); button_stop.click(handler => { - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: 0 }).then(updated => this.triggerUpdate()).catch(error => { @@ -507,7 +507,7 @@ class MusicInfoManager extends ClientInfoManager { { /* Button functions */ _frame.find(".btn-forward").click(() => { - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: 3 }).then(updated => this.triggerUpdate()).catch(error => { @@ -516,7 +516,7 @@ class MusicInfoManager extends ClientInfoManager { }); }); _frame.find(".btn-rewind").click(() => { - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: 4 }).then(updated => this.triggerUpdate()).catch(error => { @@ -525,7 +525,7 @@ class MusicInfoManager extends ClientInfoManager { }); }); _frame.find(".btn-settings").click(() => { - this.handle.handle.serverConnection.helper.request_playlist_list().then(lists => { + this.handle.handle.serverConnection.command_helper.request_playlist_list().then(lists => { for(const entry of lists) { if(entry.playlist_id == bot.properties.client_playlist_id) { Modals.spawnPlaylistEdit(bot.channelTree.client, entry); @@ -581,7 +581,7 @@ class MusicInfoManager extends ClientInfoManager { slider.prop("edited", true); let current_timestamp = info.player_replay_index + Date.now() - timestamp; - this.handle.handle.serverConnection.sendCommand("musicbotplayeraction", { + this.handle.handle.serverConnection.send_command("musicbotplayeraction", { bot_id: bot.properties.client_database_id, action: current_timestamp > target_timestamp ? 6 : 5, units: current_timestamp < target_timestamp ? target_timestamp - current_timestamp : current_timestamp - target_timestamp @@ -627,17 +627,23 @@ class MusicInfoManager extends ClientInfoManager { update_handler(); /* register subscription */ - this.handle.handle.serverConnection.sendCommand("musicbotsetsubscription", {bot_id: bot.properties.client_database_id}).catch(error => { + this.handle.handle.serverConnection.send_command("musicbotsetsubscription", {bot_id: bot.properties.client_database_id}).catch(error => { console.error("Failed to subscribe to displayed music bot! Using pseudo timeline"); }).then(() => { clearInterval(interval); }); - this.notify_status = json => { - json = json[0]; - update_handler(parseInt(json["player_replay_index"]), parseInt(json["player_buffered_index"])); + this.single_handler = { + command: "notifymusicstatusupdate", + function: command => { + const json = command.arguments[0]; + update_handler(parseInt(json["player_replay_index"]), parseInt(json["player_buffered_index"])); + + return false; /* do not unregister me! */ + } }; - this.handle.handle.serverConnection.commandHandler.set_handler("notifymusicstatusupdate", this.notify_status); + + this.handle.handle.serverConnection.command_handler_boss().register_single_handler(this.single_handler); } }); } @@ -718,9 +724,9 @@ class MusicInfoManager extends ClientInfoManager { } finalizeFrame(object: ClientEntry, frame: JQuery) { - if(this.notify_status) { - this.handle.handle.serverConnection.commandHandler.unset_handler("notifymusicstatusupdate", this.notify_status); - this.notify_status = undefined; + if(this.single_handler) { + this.handle.handle.serverConnection.command_handler_boss().remove_single_handler(this.single_handler); + this.single_handler = undefined; } super.finalizeFrame(object, frame); diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index 4d5f0e29..b1169c37 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -34,7 +34,7 @@ namespace Modals { if(result.server_id < 0) result.server_id = undefined; console.log(tr("Adding ban %o"), result); - client.serverConnection.sendCommand("banadd", { + client.serverConnection.send_command("banadd", { ip: result.ip, name: result.name, uid: result.unique_id, @@ -55,7 +55,7 @@ namespace Modals { console.log(tr("Apply edit changes %o"), result); if(result.server_id < 0) result.server_id = undefined; - client.serverConnection.sendCommand("banedit", { + client.serverConnection.send_command("banedit", { banid: result.banid, ip: result.ip, name: result.name, @@ -73,7 +73,7 @@ namespace Modals { }); }, ban => { console.log(tr("Deleting ban %o"), ban); - client.serverConnection.sendCommand("bandel", { + client.serverConnection.send_command("bandel", { banid: ban.banid, sid: ban.server_id }).then(() => { @@ -140,8 +140,8 @@ namespace Modals { //TODO test permission modal.clear(); - client.serverConnection.sendCommand("banlist", { sid: 0 }); //Global ban list - client.serverConnection.sendCommand("banlist").catch(error => { + client.serverConnection.send_command("banlist", { sid: 0 }); //Global ban list + client.serverConnection.send_command("banlist").catch(error => { if(error instanceof CommandResult) { } else { console.error(error); diff --git a/shared/js/ui/modal/ModalPermissionEdit.ts b/shared/js/ui/modal/ModalPermissionEdit.ts index e1f83c09..90753546 100644 --- a/shared/js/ui/modal/ModalPermissionEdit.ts +++ b/shared/js/ui/modal/ModalPermissionEdit.ts @@ -665,7 +665,7 @@ namespace Modals { permission.id, ); - return globalClient.serverConnection.sendCommand("channelclientdelperm", { + return globalClient.serverConnection.send_command("channelclientdelperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id, @@ -677,7 +677,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channelclientdelperm", { + return globalClient.serverConnection.send_command("channelclientdelperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id_grant(), @@ -694,7 +694,7 @@ namespace Modals { value.flag_negate ); - return globalClient.serverConnection.sendCommand("channelclientaddperm", { + return globalClient.serverConnection.send_command("channelclientaddperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id, @@ -709,7 +709,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channelclientaddperm", { + return globalClient.serverConnection.send_command("channelclientaddperm", { cldbid: current_cldbid, cid: current_channel.channelId, permid: permission.id_grant(), @@ -821,7 +821,7 @@ namespace Modals { permission.id, ); - return globalClient.serverConnection.sendCommand("clientaddperm", { + return globalClient.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id, }); @@ -832,7 +832,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("clientaddperm", { + return globalClient.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id_grant(), }); @@ -848,7 +848,7 @@ namespace Modals { value.flag_negate ); - return globalClient.serverConnection.sendCommand("clientaddperm", { + return globalClient.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id, permvalue: value.value, @@ -862,7 +862,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("clientaddperm", { + return globalClient.serverConnection.send_command("clientaddperm", { cldbid: current_cldbid, permid: permission.id_grant(), permvalue: value.granted, @@ -956,7 +956,7 @@ namespace Modals { permission.id, ); - return globalClient.serverConnection.sendCommand("channeldelperm", { + return globalClient.serverConnection.send_command("channeldelperm", { cid: current_channel.channelId, permid: permission.id, }); @@ -968,7 +968,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channeldelperm", { + return globalClient.serverConnection.send_command("channeldelperm", { cid: current_channel.channelId, permid: permission.id_grant(), }); @@ -984,7 +984,7 @@ namespace Modals { value.flag_negate ); - return globalClient.serverConnection.sendCommand("channeladdperm", { + return globalClient.serverConnection.send_command("channeladdperm", { cid: current_channel.channelId, permid: permission.id, permvalue: value.value, @@ -999,7 +999,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channeladdperm", { + return globalClient.serverConnection.send_command("channeladdperm", { cid: current_channel.channelId, permid: permission.id_grant(), permvalue: value.granted, @@ -1058,7 +1058,7 @@ namespace Modals { permission.id, ); - return globalClient.serverConnection.sendCommand("channelgroupdelperm", { + return globalClient.serverConnection.send_command("channelgroupdelperm", { cgid: current_group.id, permid: permission.id, }); @@ -1069,7 +1069,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channelgroupdelperm", { + return globalClient.serverConnection.send_command("channelgroupdelperm", { cgid: current_group.id, permid: permission.id_grant(), }); @@ -1085,7 +1085,7 @@ namespace Modals { value.flag_negate ); - return globalClient.serverConnection.sendCommand("channelgroupaddperm", { + return globalClient.serverConnection.send_command("channelgroupaddperm", { cgid: current_group.id, permid: permission.id, permvalue: value.value, @@ -1099,7 +1099,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("channelgroupaddperm", { + return globalClient.serverConnection.send_command("channelgroupaddperm", { cgid: current_group.id, permid: permission.id_grant(), permvalue: value.granted, @@ -1218,7 +1218,7 @@ namespace Modals { permission.id, ); - return globalClient.serverConnection.sendCommand("servergroupdelperm", { + return globalClient.serverConnection.send_command("servergroupdelperm", { sgid: current_group.id, permid: permission.id, }); @@ -1229,7 +1229,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("servergroupdelperm", { + return globalClient.serverConnection.send_command("servergroupdelperm", { sgid: current_group.id, permid: permission.id_grant(), }); @@ -1245,7 +1245,7 @@ namespace Modals { value.flag_negate ); - return globalClient.serverConnection.sendCommand("servergroupaddperm", { + return globalClient.serverConnection.send_command("servergroupaddperm", { sgid: current_group.id, permid: permission.id, permvalue: value.value, @@ -1259,7 +1259,7 @@ namespace Modals { value.granted, ); - return globalClient.serverConnection.sendCommand("servergroupaddperm", { + return globalClient.serverConnection.send_command("servergroupaddperm", { sgid: current_group.id, permid: permission.id_grant(), permvalue: value.granted, diff --git a/shared/js/ui/modal/ModalPlaylistEdit.ts b/shared/js/ui/modal/ModalPlaylistEdit.ts index 7c6723a0..35699795 100644 --- a/shared/js/ui/modal/ModalPlaylistEdit.ts +++ b/shared/js/ui/modal/ModalPlaylistEdit.ts @@ -99,7 +99,7 @@ namespace Modals { template.find(".buttons .button-save").on('click', event => { if(Object.keys(changed_properties).length != 0) { changed_properties["playlist_id"] = playlist.playlist_id; - client.serverConnection.sendCommand("playlistedit", changed_properties).then(() => { + client.serverConnection.send_command("playlistedit", changed_properties).then(() => { changed_properties = {}; update_save(); }).catch(error => { @@ -121,7 +121,7 @@ namespace Modals { } array[0]["playlist_id"] = playlist.playlist_id; - client.serverConnection.sendCommand("playlistaddperm", array).then(() => { + client.serverConnection.send_command("playlistaddperm", array).then(() => { changed_permissions = {}; update_save(); }).catch(error => { @@ -193,7 +193,7 @@ namespace Modals { button_delete.detach(); else button_delete.on('click', event => { - client.serverConnection.sendCommand("playlistsongremove", { + client.serverConnection.send_command("playlistsongremove", { playlist_id: playlist.playlist_id, song_id: song.song_id }).then(() => { @@ -236,7 +236,7 @@ namespace Modals { song_tag.find(".button-song-add").on('click', event => { spawnSongAdd(playlist, (url, loader) => { //playlist_id invoker previous url - client.serverConnection.sendCommand("playlistsongadd", { + client.serverConnection.send_command("playlistsongadd", { playlist_id: playlist.playlist_id, invoker: loader, url: url diff --git a/shared/js/ui/modal/ModalPlaylistList.ts b/shared/js/ui/modal/ModalPlaylistList.ts index 7f15cc72..9b92a837 100644 --- a/shared/js/ui/modal/ModalPlaylistList.ts +++ b/shared/js/ui/modal/ModalPlaylistList.ts @@ -108,7 +108,7 @@ namespace Modals { }); }; client.serverConnection.commandHandler.set_handler("notifyplaylistcreated", notify_handler); - client.serverConnection.sendCommand("playlistcreate").catch(error => { + client.serverConnection.send_command("playlistcreate").catch(error => { client.serverConnection.commandHandler.unset_handler("notifyplaylistcreated", notify_handler); if(error instanceof CommandResult) error = error.extra_message || error.message; @@ -126,7 +126,7 @@ namespace Modals { Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this playlist?"), result => { if(result) { - client.serverConnection.sendCommand("playlistdelete", {playlist_id: selected_playlist.playlist_id}).then(() => { + client.serverConnection.send_command("playlistdelete", {playlist_id: selected_playlist.playlist_id}).then(() => { createInfoModal(tr("Playlist deleted successful"), tr("This playlist has been deleted successfully.")).open(); update_list(); }).catch(error => { diff --git a/shared/js/ui/modal/ModalQueryManage.ts b/shared/js/ui/modal/ModalQueryManage.ts index 7b990d7c..f5f3a1f1 100644 --- a/shared/js/ui/modal/ModalQueryManage.ts +++ b/shared/js/ui/modal/ModalQueryManage.ts @@ -19,8 +19,8 @@ namespace Modals { const update_list = () => { const info_tag = modal.htmlTag.find(".footer .info a"); info_tag.text("loading..."); - client.serverConnection.helper.current_virtual_server_id().then(server_id => { - client.serverConnection.helper.request_query_list(server_id).then(result => { + client.serverConnection.command_helper.current_virtual_server_id().then(server_id => { + client.serverConnection.command_helper.request_query_list(server_id).then(result => { selected_query = undefined; const entries_tag = modal.htmlTag.find(".query-list-entries"); @@ -73,7 +73,7 @@ namespace Modals { createInputModal(tr("Change account name"), tr("Enter the new name for the login:
"), text => text.length >= 3, result => { if(result) { - client.serverConnection.sendCommand("queryrename", { + client.serverConnection.send_command("queryrename", { client_login_name: selected_query.username, client_new_login_name: result }).catch(error => { @@ -92,23 +92,28 @@ namespace Modals { createInputModal(tr("Change account's password"), tr("Enter a new password (leave blank for auto generation):
"), text => true, result => { if(result !== false) { - client.serverConnection.sendCommand("querychangepassword", { + const single_handler: connection.SingleCommandHandler = { + command: "notifyquerypasswordchanges", + function: command => { + Modals.spawnQueryCreated({ + username: command.arguments[0]["client_login_name"], + password: command.arguments[0]["client_login_password"] + }, false); + + return true; + } + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + + client.serverConnection.send_command("querychangepassword", { client_login_name: selected_query.username, client_login_password: result }).catch(error => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); if(error instanceof CommandResult) error = error.extra_message || error.message; createErrorModal(tr("Unable to change password"), tr("Failed to change password
Message: ") + error).open(); }); - - client.serverConnection.commandHandler["notifyquerypasswordchanges"] = json => { - Modals.spawnQueryCreated({ - username: json[0]["client_login_name"], - password: json[0]["client_login_password"] - }, false); - - client.serverConnection.commandHandler["notifyquerypasswordchanges"] = undefined; - }; } }).open(); }); @@ -117,7 +122,7 @@ namespace Modals { Modals.spawnYesNo(tr("Are you sure?"), tr("Do you really want to delete this account?"), result => { if(result) { - client.serverConnection.sendCommand("querydelete", { + client.serverConnection.send_command("querydelete", { client_login_name: selected_query.username }).catch(error => { if(error instanceof CommandResult) diff --git a/shared/js/ui/server.ts b/shared/js/ui/server.ts index 07ddf314..cf9d9dac 100644 --- a/shared/js/ui/server.ts +++ b/shared/js/ui/server.ts @@ -144,7 +144,7 @@ class ServerEntry { log.info(LogCategory.SERVER, tr("Changing server properties %o"), properties); console.log(tr("Changed properties: %o"), properties); if (properties) - this.channelTree.client.serverConnection.sendCommand("serveredit", properties).then(() => { + this.channelTree.client.serverConnection.send_command("serveredit", properties).then(() => { sound.play(Sound.SERVER_EDITED_SELF); }); }); @@ -200,7 +200,7 @@ class ServerEntry { if(this.info_request_promise && Date.now() - this.lastInfoRequest < 1000) return this.info_request_promise; this.lastInfoRequest = Date.now(); this.nextInfoRequest = this.lastInfoRequest + 10 * 1000; - this.channelTree.client.serverConnection.sendCommand("servergetvariables").catch(error => { + this.channelTree.client.serverConnection.send_command("servergetvariables").catch(error => { this.info_request_promise_reject(error); this.info_request_promise = undefined; this.info_request_promise_reject = undefined; diff --git a/shared/js/ui/view.ts b/shared/js/ui/view.ts index c7d1a537..bb74e0b9 100644 --- a/shared/js/ui/view.ts +++ b/shared/js/ui/view.ts @@ -450,7 +450,7 @@ class ChannelTree { createInputModal(tr("Poke clients"), tr("Poke message:
"), text => true, result => { if (typeof(result) === "string") { for (const client of this.currently_selected as ClientEntry[]) - this.client.serverConnection.sendCommand("clientpoke", { + this.client.serverConnection.send_command("clientpoke", { clid: client.clientId(), msg: result }); @@ -467,7 +467,7 @@ class ChannelTree { callback: () => { const target = this.client.getClient().currentChannel().getChannelId(); for(const client of clients) - this.client.serverConnection.sendCommand("clientmove", { + this.client.serverConnection.send_command("clientmove", { clid: client.clientId(), cid: target }); @@ -483,7 +483,7 @@ class ChannelTree { createInputModal(tr("Kick clients from channel"), tr("Kick reason:
"), text => true, result => { if (result) { for (const client of clients) - this.client.serverConnection.sendCommand("clientkick", { + this.client.serverConnection.send_command("clientkick", { clid: client.clientId(), reasonid: ViewReasonId.VREASON_CHANNEL_KICK, reasonmsg: result @@ -503,7 +503,7 @@ class ChannelTree { createInputModal(tr("Kick clients from server"), tr("Kick reason:
"), text => true, result => { if (result) { for (const client of clients) - this.client.serverConnection.sendCommand("clientkick", { + this.client.serverConnection.send_command("clientkick", { clid: client.clientId(), reasonid: ViewReasonId.VREASON_SERVER_KICK, reasonmsg: result @@ -520,11 +520,13 @@ class ChannelTree { callback: () => { Modals.spawnBanClient((clients).map(entry => entry.clientNickName()), (data) => { for (const client of clients) - this.client.serverConnection.sendCommand("banclient", { + this.client.serverConnection.send_command("banclient", { uid: client.properties.client_unique_identifier, banreason: data.reason, time: data.length - }, [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""]).then(() => { + }, { + flagset: [data.no_ip ? "no-ip" : "", data.no_hwid ? "no-hardware-id" : "", data.no_name ? "no-nickname" : ""] + }).then(() => { sound.play(Sound.USER_BANNED); }); }); @@ -545,7 +547,7 @@ class ChannelTree { Modals.spawnYesNo(tr("Are you sure?"), tag_container, result => { if(result) { for(const client of clients) - this.client.serverConnection.sendCommand("musicbotdelete", { + this.client.serverConnection.send_command("musicbotdelete", { botid: client.properties.client_database_id }); } @@ -595,7 +597,7 @@ class ChannelTree { if(!properties) return; properties["cpid"] = parent ? parent.channelId : 0; log.debug(LogCategory.CHANNEL, tr("Creating a new channel.\nProperties: %o\nPermissions: %o"), properties); - this.client.serverConnection.sendCommand("channelcreate", properties).then(() => { + this.client.serverConnection.send_command("channelcreate", properties).then(() => { let channel = this.find_channel_by_name(properties.channel_name, parent, true); if(!channel) { log.error(LogCategory.CHANNEL, tr("Failed to resolve channel after creation. Could not apply permissions!")); @@ -613,7 +615,9 @@ class ChannelTree { } perms[0]["cid"] = channel.channelId; - return this.client.serverConnection.sendCommand("channeladdperm", perms, ["continueonerror"]).then(() => new Promise(resolve => { resolve(channel); })); + return this.client.serverConnection.send_command("channeladdperm", perms, { + flagset: ["continueonerror"] + }).then(() => new Promise(resolve => { resolve(channel); })); } return new Promise(resolve => { resolve(channel); }) diff --git a/shared/js/voice/AudioController.ts b/shared/js/voice/AudioController.ts index a2ba9201..85673e00 100644 --- a/shared/js/voice/AudioController.ts +++ b/shared/js/voice/AudioController.ts @@ -151,7 +151,7 @@ class AudioController { private playQueue() { let buffer: AudioBuffer; - while(buffer = this.audioCache.pop_front()) { + while((buffer = this.audioCache.pop_front())) { if(this.playingAudioCache.length >= this._latencyBufferLength * 1.5 + 3) { console.log(tr("Dropping buffer because playing queue grows to much")); continue; /* drop the data (we're behind) */ From 9b52f0a8584e99e5aa38e97cf1392a5fa6943985 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 14:23:45 +0100 Subject: [PATCH 16/26] fixed travis file --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b77c2235..7817dc23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ jobs: name: TeaWeb build master branch script: - "mkdir -p /tmp/build" - - "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug" + - "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug || travis_terminate 1;" - "ls -lah /tmp/build/" - "ls -lah /tmp/build/logs/" - "ls -lah /tmp/build/packages/" - - "./scripts/travis_deploy.sh" + - "./scripts/travis_deploy.sh || travis_terminate 1;" if: branch = master \ No newline at end of file From e4b7ac02a694932b5fb555398ad838ea057bcca8 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 14:27:58 +0100 Subject: [PATCH 17/26] fixed some errors --- shared/js/ui/modal/ModalBanList.ts | 18 ++++++++++--- shared/js/ui/modal/ModalPermissionEdit.ts | 4 +-- shared/js/ui/modal/ModalPlaylistEdit.ts | 4 +-- shared/js/ui/modal/ModalPlaylistList.ts | 33 +++++++++++++---------- shared/js/ui/modal/ModalQuery.ts | 25 ++++++++++------- 5 files changed, 53 insertions(+), 31 deletions(-) diff --git a/shared/js/ui/modal/ModalBanList.ts b/shared/js/ui/modal/ModalBanList.ts index b1169c37..f716b102 100644 --- a/shared/js/ui/modal/ModalBanList.ts +++ b/shared/js/ui/modal/ModalBanList.ts @@ -25,6 +25,7 @@ namespace Modals { export interface BanListManager { addbans : (ban: BanEntry[]) => void; clear : (ban?: any) => void; + modal: Modal; } export function openBanList(client: TSClient) { @@ -84,8 +85,10 @@ namespace Modals { }); }); - update = () => { - client.serverConnection.commandHandler["notifybanlist"] = json => { + const single_handler: connection.SingleCommandHandler = { + command: "notifybanlist", + function: command => { + const json = command.arguments; console.log(tr("Got banlist: %o"), json); let bans: BanEntry[] = []; @@ -136,7 +139,15 @@ namespace Modals { } modal.addbans(bans); - }; + return false; + } + }; + client.serverConnection.command_handler_boss().register_single_handler(single_handler); + modal.modal.close_listener.push(() => { + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); + }); + + update = () => { //TODO test permission modal.clear(); @@ -208,6 +219,7 @@ namespace Modals { modal.htmlTag.find(".entry-container .entries").children().detach(); update_function(); }; + result.modal = modal; return result; } diff --git a/shared/js/ui/modal/ModalPermissionEdit.ts b/shared/js/ui/modal/ModalPermissionEdit.ts index 90753546..a40880fc 100644 --- a/shared/js/ui/modal/ModalPermissionEdit.ts +++ b/shared/js/ui/modal/ModalPermissionEdit.ts @@ -745,7 +745,7 @@ namespace Modals { const resolve_client = () => { let client_uid = tag_select_uid.val() as string; - globalClient.serverConnection.helper.info_from_uid(client_uid).then(result => { + globalClient.serverConnection.command_helper.info_from_uid(client_uid).then(result => { if(!result || result.length == 0) return Promise.reject("invalid data"); tag_select_uid.attr('pattern', null).removeClass('is-invalid'); @@ -888,7 +888,7 @@ namespace Modals { const resolve_client = () => { let client_uid = tag_select_uid.val() as string; - globalClient.serverConnection.helper.info_from_uid(client_uid).then(result => { + globalClient.serverConnection.command_helper.info_from_uid(client_uid).then(result => { if(!result || result.length == 0) return Promise.reject("invalid data"); tag_select_uid.attr('pattern', null).removeClass('is-invalid'); diff --git a/shared/js/ui/modal/ModalPlaylistEdit.ts b/shared/js/ui/modal/ModalPlaylistEdit.ts index 35699795..2d38192e 100644 --- a/shared/js/ui/modal/ModalPlaylistEdit.ts +++ b/shared/js/ui/modal/ModalPlaylistEdit.ts @@ -176,7 +176,7 @@ namespace Modals { const update_songs = () => { set_song_info(tr("loading song list")); - client.serverConnection.helper.request_playlist_songs(playlist.playlist_id).then(result => { + client.serverConnection.command_helper.request_playlist_songs(playlist.playlist_id).then(result => { const entries_tag = song_tag.find(".song-list-entries"); const entry_template = $("#tmpl_playlist_edit-song_entry"); entries_tag.empty(); @@ -297,7 +297,7 @@ namespace Modals { function apply_properties(tag: JQuery, client: TSClient, playlist: Playlist, change_property: (key: string, value: string) => any, callback_current_song: (id: number) => any) { const owns_playlist = playlist.playlist_owner_dbid == client.getClient().properties.client_database_id; - client.serverConnection.helper.request_playlist_info(playlist.playlist_id).then(info => { + client.serverConnection.command_helper.request_playlist_info(playlist.playlist_id).then(info => { tag.find(".property-owner input") .val(info.playlist_owner_name + " (" + info.playlist_owner_dbid + ")"); diff --git a/shared/js/ui/modal/ModalPlaylistList.ts b/shared/js/ui/modal/ModalPlaylistList.ts index 9b92a837..d30a25ab 100644 --- a/shared/js/ui/modal/ModalPlaylistList.ts +++ b/shared/js/ui/modal/ModalPlaylistList.ts @@ -43,7 +43,7 @@ namespace Modals { update_selected(); try { - available_playlists = await client.serverConnection.helper.request_playlist_list(); + available_playlists = await client.serverConnection.command_helper.request_playlist_list(); } catch(error) { info_tag.text("failed to query playlist list."); //FIXME error handling? @@ -92,24 +92,29 @@ namespace Modals { template.find(".footer .buttons .button-refresh").on('click', update_list); template.find(".button-playlist-create").on('click', event => { - const notify_handler = json => { - client.serverConnection.commandHandler.unset_handler("notifyplaylistcreated", notify_handler); - update_list().then(() => { - spawnYesNo(tr("Playlist created successful"), tr("The playlist has been successfully created.
Should we open the editor?"), result => { - if(result) { - for(const playlist of available_playlists) { - if(playlist.playlist_id == json[0]["playlist_id"]) { - spawnPlaylistEdit(client, playlist).close_listener.push(update_list); - return; + const single_handler: connection.SingleCommandHandler = { + function: command => { + const json = command.arguments; + update_list().then(() => { + spawnYesNo(tr("Playlist created successful"), tr("The playlist has been successfully created.
Should we open the editor?"), result => { + if(result) { + for(const playlist of available_playlists) { + if(playlist.playlist_id == json[0]["playlist_id"]) { + spawnPlaylistEdit(client, playlist).close_listener.push(update_list); + return; + } } } - } + }); }); - }); + + return true; + }, + command: "notifyplaylistcreated" }; - client.serverConnection.commandHandler.set_handler("notifyplaylistcreated", notify_handler); + client.serverConnection.command_handler_boss().register_single_handler(single_handler); client.serverConnection.send_command("playlistcreate").catch(error => { - client.serverConnection.commandHandler.unset_handler("notifyplaylistcreated", notify_handler); + client.serverConnection.command_handler_boss().remove_single_handler(single_handler); if(error instanceof CommandResult) error = error.extra_message || error.message; createErrorModal(tr("Unable to create playlist"), tr("Failed to create playlist
Message: ") + error).open(); diff --git a/shared/js/ui/modal/ModalQuery.ts b/shared/js/ui/modal/ModalQuery.ts index 8308d5ab..ff68bd1e 100644 --- a/shared/js/ui/modal/ModalQuery.ts +++ b/shared/js/ui/modal/ModalQuery.ts @@ -20,21 +20,26 @@ namespace Modals { } //client_login_password - globalClient.serverConnection.commandHandler["notifyquerycreated"] = json => { - json = json[0]; + const single_handler: connection.SingleCommandHandler = { + function: command => { + const json = command.arguments[0]; - spawnQueryCreated({ - username: name, - password: json.client_login_password - }, true); + spawnQueryCreated({ + username: name, + password: json.client_login_password + }, true); - if(callback_created) - callback_created(name, json.client_login_password); + if(callback_created) + callback_created(name, json.client_login_password); + return true; + } }; - - globalClient.serverConnection.sendCommand("querycreate", { + globalClient.serverConnection.command_handler_boss().register_single_handler(single_handler); + globalClient.serverConnection.send_command("querycreate", { client_login_name: name }).catch(error => { + globalClient.serverConnection.command_handler_boss().remove_single_handler(single_handler); + if(error instanceof CommandResult) error = error.extra_message || error.message; createErrorModal(tr("Unable to create account"), tr("Failed to create account
Message: ") + error).open(); From 97a4ca8a9bac6f0f135c93a03a95ad84aa171834 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 15:07:55 +0100 Subject: [PATCH 18/26] fixed missing references --- shared/js/FileManager.ts | 1 + shared/js/client.ts | 1 + shared/js/permission/GroupManager.ts | 1 + shared/js/permission/PermissionManager.ts | 1 + 4 files changed, 4 insertions(+) diff --git a/shared/js/FileManager.ts b/shared/js/FileManager.ts index 4006529b..1c0ad5e1 100644 --- a/shared/js/FileManager.ts +++ b/shared/js/FileManager.ts @@ -1,4 +1,5 @@ /// +/// class FileEntry { name: string; diff --git a/shared/js/client.ts b/shared/js/client.ts index 179deb91..ef02b4a1 100644 --- a/shared/js/client.ts +++ b/shared/js/client.ts @@ -8,6 +8,7 @@ /// /// /// +/// enum DisconnectReason { REQUESTED, diff --git a/shared/js/permission/GroupManager.ts b/shared/js/permission/GroupManager.ts index 00bc6ce3..b989e560 100644 --- a/shared/js/permission/GroupManager.ts +++ b/shared/js/permission/GroupManager.ts @@ -1,3 +1,4 @@ +/// enum GroupType { QUERY, diff --git a/shared/js/permission/PermissionManager.ts b/shared/js/permission/PermissionManager.ts index 62ab15ab..61b4a1fd 100644 --- a/shared/js/permission/PermissionManager.ts +++ b/shared/js/permission/PermissionManager.ts @@ -1,4 +1,5 @@ /// +/// enum PermissionType { B_SERVERINSTANCE_HELP_VIEW = "b_serverinstance_help_view", /* Permission ID: 1 */ From fd02eee9c147ba87ee7ff4b9611042d393ee0756 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 15:09:05 +0100 Subject: [PATCH 19/26] Fixed an possible sounds issue --- shared/js/sound/Sounds.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/js/sound/Sounds.ts b/shared/js/sound/Sounds.ts index 98c68d9e..ac40f8ca 100644 --- a/shared/js/sound/Sounds.ts +++ b/shared/js/sound/Sounds.ts @@ -240,6 +240,10 @@ namespace sound { const path = "audio/" + file.filename; const context = audio.player.context(); + if(!context) { + console.warn(tr("Tried to replay a sound without an audio context (Sound: %o). Dropping playback"), sound); + return; + } const volume = get_sound_volume(sound, options.default_volume); console.log(tr("Replaying sound %s (Sound volume: %o | Master volume %o)"), sound, volume, master_volume); From ace25a04cc5d72de773295a1f7e798d21010b4b1 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 15:19:04 +0100 Subject: [PATCH 20/26] Fixed cache check for version 000000 and removed debug --- shared/js/load.ts | 2 +- shared/js/ui/client.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/js/load.ts b/shared/js/load.ts index a51d1e1c..10e37789 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -695,7 +695,7 @@ async function check_updates() { const version = version_node.hasAttribute("value") ? version_node.getAttribute("value") : undefined; if(!version) return undefined; - if(version == "unknown" || version.replace("0", "").length == 0) + if(version == "unknown" || version.replace(/0+/, "").length == 0) return undefined; return version; diff --git a/shared/js/ui/client.ts b/shared/js/ui/client.ts index 7d7cc31d..43460497 100644 --- a/shared/js/ui/client.ts +++ b/shared/js/ui/client.ts @@ -513,7 +513,6 @@ class ClientEntry { let icon: string = ""; let clicon: string = ""; - console.error(this.properties.client_nickname + " - " + this.properties.client_type_exact + " - " + this.properties.client_type); if(this.properties.client_type_exact == ClientType.CLIENT_QUERY) { icon = "client-server_query"; console.log("Server query!"); From b82c2433711cf04067546f0d1d8d26509e945bdf Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 15:29:10 +0100 Subject: [PATCH 21/26] waiting with voice bridge connection initialisation until audio playback is supported --- shared/js/voice/VoiceHandler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shared/js/voice/VoiceHandler.ts b/shared/js/voice/VoiceHandler.ts index 52023bae..774cb86b 100644 --- a/shared/js/voice/VoiceHandler.ts +++ b/shared/js/voice/VoiceHandler.ts @@ -277,6 +277,11 @@ class VoiceConnection { createSession() { + if(!audio.player.initialized()) { + console.log(tr("Audio player isn't initialized yet. Waiting for gesture.")); + audio.player.on_ready(() => this.createSession()); + return; + } if(!this.current_encoding_supported()) return false; if(this.rtcPeerConnection) { From 6da22ca24ca0974b089063b27a8262bc36f47bdb Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Sat, 23 Feb 2019 15:44:08 +0100 Subject: [PATCH 22/26] Removed unnecessary commands --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7817dc23..c15d192d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,7 @@ services: before_install: - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - - docker images - docker pull $DOCKER_USERNAME/teaweb:build_new - - docker images jobs: include: @@ -17,8 +15,5 @@ jobs: script: - "mkdir -p /tmp/build" - "docker run --rm -v /tmp/build/logs/:/build/logs/ -v /tmp/build/packages/:/build/packages/ -v `pwd`:/build/TeaWeb $DOCKER_USERNAME/teaweb:build_new --enable-release --enable-debug || travis_terminate 1;" - - "ls -lah /tmp/build/" - - "ls -lah /tmp/build/logs/" - - "ls -lah /tmp/build/packages/" - "./scripts/travis_deploy.sh || travis_terminate 1;" if: branch = master \ No newline at end of file From 9e01c846413d599f2b0bbe26218f9cf28565a7c3 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 25 Feb 2019 15:59:42 +0100 Subject: [PATCH 23/26] Added some scripts and live statistics --- scripts/build_declarations.sh | 2 +- scripts/cleanup.sh | 25 +++++++++++++++++++++++++ shared/js/load.ts | 2 ++ shared/js/log.ts | 2 +- shared/js/main.ts | 9 +++++++++ shared/js/stats.ts | 29 ++++++++++++++++++++--------- 6 files changed, 58 insertions(+), 11 deletions(-) create mode 100755 scripts/cleanup.sh diff --git a/scripts/build_declarations.sh b/scripts/build_declarations.sh index afbc292e..ae1f35a1 100755 --- a/scripts/build_declarations.sh +++ b/scripts/build_declarations.sh @@ -21,7 +21,7 @@ function replace_tribble() { #Building the generator ./tools/build_dtsgen.sh -if [ $? -ne 0 ]; then +if [[ $? -ne 0 ]]; then echo "Failed to build typescript declaration generator" exit 1 fi diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh new file mode 100755 index 00000000..2ae764e5 --- /dev/null +++ b/scripts/cleanup.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +BASEDIR=$(dirname "$0") +cd "$BASEDIR/../" + +# This script cleanups all generated files +function remove_if_exists() { + if [[ -f "$1" ]] || [[ -d "$1" ]]; then + echo "Deleting $1" + rm -r "$1" + fi +} + +function cleanup_declarations() { + remove_if_exists shared/declarations/ + remove_if_exists web/declarations/ +} + +function cleanup_generated_files() { + remove_if_exists shared/generated + remove_if_exists web/generated +} + +cleanup_declarations +cleanup_generated_files \ No newline at end of file diff --git a/shared/js/load.ts b/shared/js/load.ts index 10e37789..193a706c 100644 --- a/shared/js/load.ts +++ b/shared/js/load.ts @@ -554,6 +554,8 @@ const loader_javascript = { "js/connection/HandshakeHandler.js", "js/connection/ServerConnection.js", + "js/stats.js", + "js/PPTListener.js", diff --git a/shared/js/log.ts b/shared/js/log.ts index 49235ce4..3e887810 100644 --- a/shared/js/log.ts +++ b/shared/js/log.ts @@ -40,7 +40,7 @@ namespace log { [LogCategory.GENERAL, true], [LogCategory.NETWORKING, true], [LogCategory.VOICE, true], - [LogCategory.I18N, true] + [LogCategory.I18N, false] ]); loader.register_task(loader.Stage.LOADED, { diff --git a/shared/js/main.ts b/shared/js/main.ts index 8a85df4f..d3d825ad 100644 --- a/shared/js/main.ts +++ b/shared/js/main.ts @@ -272,6 +272,15 @@ function main() { globalClient.channelTree.handle_resized(); }, 1000); }); + + stats.initialize({ + verbose: true, + anonymize_ip_addresses: true, + volatile_collection_only: false + }); + stats.register_user_count_listener(status => { + console.log("Received user count update: %o", status); + }); } loader.register_task(loader.Stage.LOADED, { diff --git a/shared/js/stats.ts b/shared/js/stats.ts index 49c52e80..417b54db 100644 --- a/shared/js/stats.ts +++ b/shared/js/stats.ts @@ -96,16 +96,14 @@ namespace stats { export function start_connection() { cancel_reconnect(); - - if(connection) { - const connection_copy = connection; - connection = undefined; - - connection_copy.close(3001); - } + close_connection(); connection_state = ConnectionState.CONNECTING; - connection = new WebSocket('wss://web-stats.teaspeak.de:1774'); + + connection = new WebSocket('wss://web-stats.teaspeak.de:27790'); + if(!connection) + connection = new WebSocket('wss://localhost:27788'); + { const connection_copy = connection; connection.onclose = (event: CloseEvent) => { @@ -135,7 +133,7 @@ namespace stats { console.log(LOG_PREFIX + tr("Received an error. Closing connection. Object: %o"), event); connection.close(CloseCodes.INTERNAL_ERROR); - this.invoke_reconnect(); + invoke_reconnect(); }; connection.onmessage = (event: MessageEvent) => { @@ -152,7 +150,20 @@ namespace stats { } } + export function close_connection() { + if(connection) { + const connection_copy = connection; + connection = undefined; + + try { + connection_copy.close(3001); + } catch(_) {} + } + } + function invoke_reconnect() { + close_connection(); + if(reconnect_timer) { clearTimeout(reconnect_timer); reconnect_timer = undefined; From debf76aabe77ccc30a1c66884f931ffd49c84825 Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 25 Feb 2019 16:15:06 +0100 Subject: [PATCH 24/26] Fixed relative icon path not working in release mode --- shared/css/static/ts/icons.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/css/static/ts/icons.scss b/shared/css/static/ts/icons.scss index 9eb76e8b..58c69698 100644 --- a/shared/css/static/ts/icons.scss +++ b/shared/css/static/ts/icons.scss @@ -8,7 +8,7 @@ width: 16px; height: 16px; - background: url('../../../img/client_icon_sprite.svg') no-repeat; + background: url('../../../img/client_icon_sprite.svg'), url('../../img/client_icon_sprite.svg') no-repeat; } /* Icons x16 */ @@ -612,7 +612,7 @@ .icon_x32 { display: inline-block; - background: url('../../../img/client_icon_sprite.svg') no-repeat; + background: url('../../../img/client_icon_sprite.svg'), url('../../img/client_icon_sprite.svg') no-repeat; background-size: calc(496px * 2) calc(400px * 2); height: 32px; width: 32px; From 8cf967fd804d83c463333231d77942c0e025658f Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 25 Feb 2019 16:32:29 +0100 Subject: [PATCH 25/26] fixed shared package package processing --- shared/generate_packed.sh | 13 +++++++++++++ shared/tsconfig/dtsconfig_packed.json | 10 ++++++++++ shared/tsconfig/tsconfig_packed_loader.json | 12 ++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 shared/tsconfig/dtsconfig_packed.json create mode 100644 shared/tsconfig/tsconfig_packed_loader.json diff --git a/shared/generate_packed.sh b/shared/generate_packed.sh index 324c918c..86da58ae 100755 --- a/shared/generate_packed.sh +++ b/shared/generate_packed.sh @@ -12,6 +12,7 @@ if [[ -e ${LOADER_FILE} ]]; then echo "Failed to remove loader file!\nThis could be critical later!" fi fi + npm run dtsgen -- --config $(pwd)/tsconfig/dtsconfig_loader.json -v if [[ ! -e ${LOADER_FILE} ]]; then echo "Failed to generate definitions" @@ -19,6 +20,18 @@ if [[ ! -e ${LOADER_FILE} ]]; then exit 1 fi +npm run dtsgen -- --config $(pwd)/tsconfig/dtsconfig_packed.json -v +if [[ $? -ne 0 ]]; then + echo "Failed to generate definitions for the loader" + exit 1 +fi + +execute_ttsc -p tsconfig/tsconfig_packed_loader.json +if [[ $? -ne 0 ]]; then + echo "Failed to generate packed loader file!" + exit 1 +fi + execute_ttsc -p tsconfig/tsconfig_packed.json if [[ $? -ne 0 ]]; then echo "Failed to generate packed file!" diff --git a/shared/tsconfig/dtsconfig_packed.json b/shared/tsconfig/dtsconfig_packed.json new file mode 100644 index 00000000..633c619a --- /dev/null +++ b/shared/tsconfig/dtsconfig_packed.json @@ -0,0 +1,10 @@ +{ + "source_files": [ + "../js/**/*.ts" + ], + "exclude": [ + "../js/workers/**/*.ts", + "../js/load.ts" + ], + "target_file": "../declarations/exports_packed.d.ts" +} diff --git a/shared/tsconfig/tsconfig_packed_loader.json b/shared/tsconfig/tsconfig_packed_loader.json new file mode 100644 index 00000000..edfb5f30 --- /dev/null +++ b/shared/tsconfig/tsconfig_packed_loader.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "sourceMap": true + }, + "include": [ + "../declarations/imports_*.d.ts", + "../declarations/exports_packed.d.ts", + "../js/load.ts" + ] +} \ No newline at end of file From 9818385a292b39863d0456a6b52d450a3e0d9ede Mon Sep 17 00:00:00 2001 From: WolverinDEV Date: Mon, 25 Feb 2019 16:45:03 +0100 Subject: [PATCH 26/26] Added a new test case and fixed some missing generators --- tools/dtsgen/declarator.ts | 16 ++++++++++++---- tools/dtsgen/out.d.ts | 15 ++++----------- tools/dtsgen/test/test_04.ts | 4 ++++ 3 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 tools/dtsgen/test/test_04.ts diff --git a/tools/dtsgen/declarator.ts b/tools/dtsgen/declarator.ts index f29e7f53..eb96f2a4 100644 --- a/tools/dtsgen/declarator.ts +++ b/tools/dtsgen/declarator.ts @@ -1,6 +1,8 @@ import * as ts from "typescript"; import {SyntaxKind} from "typescript"; +type TSExpression = ts.Type | ts.Node; + interface Array { last?(): T; } @@ -100,9 +102,9 @@ class StackParameters implements StackParameter { } -const generators: {[key: number]:((settings: _Settings, stack: StackParameters, node: ts.Node) => ts.Node | undefined) | undefined} = {}; +const generators: {[key: number]:((settings: _Settings, stack: StackParameters, node: ts.Node | ts.Type) => ts.Node | ts.Type | undefined) | undefined} = {}; -function _generate(settings: _Settings, stack: StackParameters, layer: ts.Node[], node: ts.Node) { +function _generate(settings: _Settings, stack: StackParameters, layer: TSExpression[], node: ts.Node) { //console.log(SyntaxKind[node.kind]); if(generators[node.kind]) { const result = generators[node.kind](settings, stack, node); @@ -322,7 +324,6 @@ generators[SyntaxKind.ClassDeclaration] = (settings, stack, node: ts.ClassDeclar }; generators[SyntaxKind.PropertySignature] = (settings, stack, node: ts.PropertySignature) => { - console.log(SyntaxKind[node.type.kind]); let type: ts.TypeNode = node.type; switch (node.type.kind) { case SyntaxKind.LiteralType: @@ -383,4 +384,11 @@ generators[SyntaxKind.EnumDeclaration] = (settings, stack, node: ts.EnumDeclarat for(const member of node.members) members.push(generators[SyntaxKind.EnumMember](settings, stack, member)); return ts.createEnumDeclaration(undefined, append_export(append_declare(node.modifiers, !stack.flag_declare), stack.flag_namespace), node.name, members); -}; \ No newline at end of file +}; + + +generators[SyntaxKind.TypeParameter] = (settings, stack, node: ts.TypeParameter) => undefined; +generators[SyntaxKind.HeritageClause] = (settings, stack, node: ts.HeritageClause) => undefined; +generators[SyntaxKind.IfStatement] = (settings, stack, node: ts.IfStatement) => undefined; +generators[SyntaxKind.ExpressionStatement] = (settings, stack, node: ts.ExpressionStatement) => undefined; +generators[SyntaxKind.SemicolonClassElement] = (settings, stack, node: ts.ExpressionStatement) => undefined; \ No newline at end of file diff --git a/tools/dtsgen/out.d.ts b/tools/dtsgen/out.d.ts index 152bd336..d2410873 100644 --- a/tools/dtsgen/out.d.ts +++ b/tools/dtsgen/out.d.ts @@ -1,13 +1,6 @@ -/* File: /home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/tools/dtsgen/test/test_03.ts */ -declare enum YY { - H, - B -} -declare interface X { - type: any; - c: YY.B; -} -declare class X { - static x(); +/* File: /home/wolverindev/TeaSpeak/TeaSpeak/Web-Client/tools/dtsgen/test/test_04.ts */ +declare class TestClass extends Promise { } +declare const lambda_function; +declare const lambda_function2; diff --git a/tools/dtsgen/test/test_04.ts b/tools/dtsgen/test/test_04.ts new file mode 100644 index 00000000..1263a5e6 --- /dev/null +++ b/tools/dtsgen/test/test_04.ts @@ -0,0 +1,4 @@ +class TestClass extends Promise { } + +const lambda_function = (str) => {}; +const lambda_function2 = (str: string) => {}; \ No newline at end of file