diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 007b2e26..1b7b25bb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Report a bug with the software (this is NOT for cases of "it's not working for me") title: "[BUG] " -labels: bug +labels: bug, help wanted assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/problem-with-setup.md b/.github/ISSUE_TEMPLATE/problem-with-setup.md index feb2dc15..cd82d08e 100644 --- a/.github/ISSUE_TEMPLATE/problem-with-setup.md +++ b/.github/ISSUE_TEMPLATE/problem-with-setup.md @@ -1,8 +1,9 @@ --- name: Problem with setup -about: Please follow this template if you are having issues setting wire-pod up. -title: "[HELP]" -labels: help wanted +about: Please follow this template if you are having issues setting wire-pod up. I + (kercre123) will not be providing support for these. +title: "[HELP] " +labels: '' assignees: '' --- @@ -16,5 +17,3 @@ assignees: '' **Are you setting up a prod (regular) bot or a dev/OSKR (unlocked) bot?** **What steps have you tried?** - -(note: I will delete any issues which fit under this category but don't follow this template. I will not accept an "It's just not working!") diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 00000000..7c8c425c --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,99 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '25 22 * * *' + push: + branches: [ "main" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "main" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@v3.3.0 + with: + cosign-release: 'v2.2.2' # optional + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/.gitignore b/.gitignore index d2ed7a18..ecfea84f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,5 +37,7 @@ /chipper/windows/.aptDone /chipper/macos/libs /chipper/macos/target +/chipper/plugins/*.so /whisper.cpp -.DS_Store \ No newline at end of file +.DS_Store +/chipper/openaiChats.json diff --git a/README.md b/README.md index abfd04bf..5cba9062 100755 --- a/README.md +++ b/README.md @@ -16,11 +16,12 @@ Check out the [wiki](https://github.com/kercre123/wire-pod/wiki) for more inform If you want to :P -[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=53VQ3Q95TD2M6&no_recurring=0¤cy_code=USD) +[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoffee.com/kercre123) ## Credits -- [Digital Dream Labs](https://github.com/digital-dream-labs) for saving Vector and for open sourcing chipper which made this possible +- [Digital Dream Labs](https://github.com/digital-dream-labs) for open sourcing chipper and creating escape pod (which made this possible) +- [bliteknight](https://github.com/bliteknight) for making wire-pod more accessible with his easy-to-use pre-setup Linux boxes - [dietb](https://github.com/dietb) for rewriting chipper and giving tips - [fforchino](https://github.com/fforchino) for adding many features such as localization and multilanguage, and for helping out - [xanathon](https://github.com/xanathon) for the publicity and web interface help diff --git a/chipper/go.mod b/chipper/go.mod index b0565f6a..94005652 100644 --- a/chipper/go.mod +++ b/chipper/go.mod @@ -3,63 +3,85 @@ module github.com/kercre123/wire-pod/chipper go 1.18 require ( - github.com/Picovoice/leopard/binding/go v1.2.0 - github.com/alphacep/vosk-api/go v0.3.46 + github.com/Picovoice/leopard/binding/go/v2 v2.0.2 github.com/asticode/go-asticoqui v0.2.0 github.com/bramvdbogaerde/go-scp v1.2.1 github.com/digital-dream-labs/api v0.0.0-20210824232136-8cc90c1bb12c github.com/digital-dream-labs/hugh v0.0.0-20210210154335-f4159b9fcd5f github.com/digital-dream-labs/opus-go v0.0.0-20201230195736-934a8a9e0a1e + github.com/digital-dream-labs/vector-bluetooth v0.0.0-20210604051118-1c511122d877 github.com/fforchino/vector-go-sdk v0.0.0-20231108155304-62168f3595d6 - github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20231214200047-940de9dbe9c9 github.com/go-audio/audio v1.0.0 github.com/go-audio/wav v1.1.0 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/google/uuid v1.5.0 + github.com/kercre123/vosk-api/go v1.0.2 + github.com/kercre123/whisper.cpp/bindings/go v0.0.0-20250602164512-60cd96acff3a + github.com/kercre123/zeroconf v1.0.1 github.com/maxhawkins/go-webrtcvad v0.0.0-20210121163624-be60036f3083 github.com/ncruces/zenity v0.10.10 github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e github.com/pkg/errors v0.9.1 + github.com/sashabaranov/go-openai v1.27.1 github.com/soheilhy/cmux v0.1.5 github.com/soundhound/houndify-sdk-go v0.3.5 - golang.org/x/crypto v0.16.0 + github.com/vadv/gopher-lua-libs v0.5.0 + github.com/wlynxg/anet v0.0.1 + github.com/yuin/gopher-lua v1.1.1 + golang.org/x/crypto v0.21.0 + golang.org/x/text v0.14.0 google.golang.org/grpc v1.60.0 gopkg.in/ini.v1 v1.67.0 ) require ( - github.com/PerformLine/go-stockutil v1.9.3 // indirect + github.com/VividCortex/ewma v1.1.1 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/akavel/rsrc v0.10.2 // indirect - github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect - github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect - github.com/bregydoc/gtranslate v0.0.0-20200913051839-1bd07f6c1fc5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/aws/aws-sdk-go v1.34.28 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cbroglie/mustache v1.0.1 // indirect + github.com/cenkalti/backoff v2.2.1+incompatible // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cheggaaa/pb/v3 v3.0.5 // indirect + github.com/currantlabs/ble v0.0.0-20171229162446-c1d21c164cf8 // indirect github.com/dchest/jsmin v0.0.0-20220218165748-59f39799265f // indirect - github.com/fatih/structs v1.1.0 // indirect - github.com/fogleman/gg v1.3.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/fatih/color v1.7.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-audio/riff v1.0.0 // indirect - github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/go-sql-driver/mysql v1.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/grd/ogg v0.0.0-20130623210630-0dae53159b70 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0 // indirect - github.com/hajimehoshi/go-mp3 v0.3.3 // indirect - github.com/hajimehoshi/oto/v2 v2.2.0 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/hegedustibor/htgo-tts v0.0.0-20220821045517-04f3cda7a12f // indirect - github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 // indirect - github.com/jdkato/prose v1.2.1 // indirect + github.com/jamesruan/sodium v0.0.0-20181216154042-9620b83ffeae // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josephspurrier/goversioninfo v1.4.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/lib/pq v1.7.0 // indirect github.com/magiconair/properties v1.8.1 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-runewidth v0.0.10 // indirect + github.com/mattn/go-sqlite3 v1.14.3 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab // indirect + github.com/miekg/dns v1.1.41 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/montanaflynn/stats v0.6.3 // indirect github.com/pelletier/go-toml v1.8.0 // indirect + github.com/prometheus/client_golang v1.11.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.26.0 // indirect + github.com/prometheus/procfs v0.6.0 // indirect github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 // indirect - github.com/robertkrimen/otto v0.0.0-20221127200954-e92282a6bb0d // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/sirupsen/logrus v1.7.0 // indirect github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cast v1.3.1 // indirect @@ -67,19 +89,17 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.7.1 // indirect github.com/subosito/gotenv v1.2.0 // indirect + github.com/technoweenie/multipartstreamer v1.0.1 // indirect + github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 // indirect golang.org/x/image v0.10.0 // indirect - golang.org/x/net v0.16.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/hraban/opus.v2 v2.0.0-20201025103112-d779bb1cc5a2 // indirect - gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect - gopkg.in/sourcemap.v1 v1.0.5 // indirect + gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apimachinery v0.23.4 // indirect - sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 // indirect ) diff --git a/chipper/go.sum b/chipper/go.sum index 486ca926..74c1ef3e 100644 --- a/chipper/go.sum +++ b/chipper/go.sum @@ -40,49 +40,58 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.15-0.20200113171025-3fe6c5262873/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PerformLine/go-stockutil v1.9.3 h1:6Mty9/Zuy4LKtqn4qDJSfjnYEGsjNpIdTgqb4tHDNfI= -github.com/PerformLine/go-stockutil v1.9.3/go.mod h1:4/8PF9Dp9BKke9UuDczslbKnZS0mj5dBLvJVNxEJvPw= -github.com/Picovoice/leopard/binding/go v1.2.0 h1:NbUW+Fni5UydvcFlMx8RZtk2pccFaRJFG14kGaUa4CA= -github.com/Picovoice/leopard/binding/go v1.2.0/go.mod h1:5kaEg9ZcH2dLkrX/H1xMVF6QFM7l3vd9GKxeXSanA8s= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Picovoice/leopard/binding/go/v2 v2.0.2 h1:Knk/UV51oRuHTHd7MGtlZXwsFF5jxu6AqttB0jGMHxs= +github.com/Picovoice/leopard/binding/go/v2 v2.0.2/go.mod h1:/rYUeRDH4xBgtwBe9D8BwHIauPJ+M7czqLfyeJQJu7c= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= github.com/aalpern/go-metrics v0.0.0-20181116155206-644932c99203/go.mod h1:wHUOZ2LlAirciaWYGZM3apvZftM7aRXhLMDsdjqEFB4= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw= github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alphacep/vosk-api/go v0.3.46 h1:obkgIAUh6tBcxt2OyrV18fWTUMEHW9zv//L+8zZKRnI= -github.com/alphacep/vosk-api/go v0.3.46/go.mod h1:9X8IJsHnFk/b1xyvjlZifo+ZL5VTAx3LW+JQce/eRcA= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asticode/go-asticoqui v0.2.0 h1:Y03X7kQUf309wHvv3ys39aYwyiFXqD9gIugNCDOVSrc= github.com/asticode/go-asticoqui v0.2.0/go.mod h1:HuUAasCpAyPjOQSqoY92VeBMYLkQ/Hu7Ij3aOhQrwlk= +github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= +github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bramvdbogaerde/go-scp v1.2.1 h1:BKTqrqXiQYovrDlfuVFaEGz0r4Ou6EED8L7jCXw6Buw= github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yPXQTPshACY8vQ0= -github.com/bregydoc/gtranslate v0.0.0-20200913051839-1bd07f6c1fc5 h1:fpVDaadW68V+6vqxJHU9jrW0/z1i2MQYJZyk9w2cjpw= -github.com/bregydoc/gtranslate v0.0.0-20200913051839-1bd07f6c1fc5/go.mod h1:153ZQv0q0e2+tPGhDsQsYTRlVRTWIYMicEvriLNX2ZY= github.com/bufbuild/buf v0.37.0/go.mod h1:lQ1m2HkIaGOFba6w/aC3KYBHhKEOESP3gaAEpS3dAFM= +github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw= +github.com/cbroglie/mustache v1.0.1/go.mod h1:R/RUa+SobQ14qkP4jtx5Vke5sDytONDQXNLPY/PO69g= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/cheggaaa/pb/v3 v3.0.5 h1:lmZOti7CraK9RSjzExsY53+WWfub9Qv13B5m4ptEoPE= +github.com/cheggaaa/pb/v3 v3.0.5/go.mod h1:X1L61/+36nz9bjIsrDU52qHKOQukUQe2Ge+YvGuquCw= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -108,6 +117,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cryptix/wav v0.0.0-20180415113528-8bdace674401/go.mod h1:knK8fd+KPlGGqSUWogv1DQzGTwnfUvAi0cIoWyOG7+U= +github.com/currantlabs/ble v0.0.0-20171229162446-c1d21c164cf8 h1:eo7L0zxxFowLpF4FNlLijrAMVNlq9h8sicNwMfzauM8= +github.com/currantlabs/ble v0.0.0-20171229162446-c1d21c164cf8/go.mod h1:MGpIf7cfnYPFaMIcD8LoSgCr8Jsa4rUcV5Nb9temsYw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -122,36 +133,29 @@ github.com/digital-dream-labs/hugh v0.0.0-20210210154335-f4159b9fcd5f h1:zM1VS8f github.com/digital-dream-labs/hugh v0.0.0-20210210154335-f4159b9fcd5f/go.mod h1:ZkWUHEPG5kdhpxM9vL+5b0p207jqH2WXNWQwFraQt2A= github.com/digital-dream-labs/opus-go v0.0.0-20201230195736-934a8a9e0a1e h1:sbDWWDUdymyp3fTeQKz40a3x/frOSiWkBHD40o1/DQY= github.com/digital-dream-labs/opus-go v0.0.0-20201230195736-934a8a9e0a1e/go.mod h1:OmsiyJjTuSWs/9lgydMVi+ULpldzbMJdu3z2N2TehYk= +github.com/digital-dream-labs/vector-bluetooth v0.0.0-20210604051118-1c511122d877 h1:o0KX+EQ9kyFtCgz/rrhj70Oy9j+v8/taLNcTUqS2mLc= +github.com/digital-dream-labs/vector-bluetooth v0.0.0-20210604051118-1c511122d877/go.mod h1:Nz0BKA94LFH/klz0okyPZiqXfCqaRhs6B8BzEhXVLmQ= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v17.12.0-ce-rc1.0.20200505174321-1655290016ac+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.1-0.20180821093606-97c2040d34df/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fforchino/vector-go-sdk v0.0.0-20231108155304-62168f3595d6 h1:zJLvieaRwMPB+u1bhFMFVubWMvLR3qWuQ1H+WKw7P+0= github.com/fforchino/vector-go-sdk v0.0.0-20231108155304-62168f3595d6/go.mod h1:VhuDr1h8ilsmbbjG1Wvp66kIzWfyGFwKA54oIhpwIKQ= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.6.6/go.mod h1:3/oRIWoe7uT6bwtAayj/EmJmepBjeL4pYvt7ZxC7Rnk= -github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= -github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20231214200047-940de9dbe9c9 h1:B3bFAoLQ8y4RFNR8A4GjkCiKzuUIwblYUiIgRgWr604= -github.com/ggerganov/whisper.cpp/bindings/go v0.0.0-20231214200047-940de9dbe9c9/go.mod h1:QIjZ9OktHFG7p+/m3sMvrAJKKdWrr1fZIK0rM6HZlyo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4= github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs= @@ -165,15 +169,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= @@ -206,11 +206,8 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -239,7 +236,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -258,7 +254,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -274,8 +269,6 @@ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -292,21 +285,13 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.0-beta.5/go.mod h1:fR9dOEfEyRM7ltVH0FTpK/QA6L/5BQq8izXNRu/gyVc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0 h1:IvO4FbbQL6n3v3M1rQNobZ61SGL0gJLdvKA5KETM7Xs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.3.0/go.mod h1:d2gYTOTUQklu06xp0AJYYmRdTVU1VKrqhkYfYag2L08= -github.com/hajimehoshi/go-mp3 v0.3.3 h1:cWnfRdpye2m9ElSoVqneYRcpt/l3ijttgjMeQh+r+FE= -github.com/hajimehoshi/go-mp3 v0.3.3/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= -github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= -github.com/hajimehoshi/oto/v2 v2.2.0 h1:qhTriSacJ/2pdONRa90hjTvpEZH7xIP4W3itwYyE1Uk= -github.com/hajimehoshi/oto/v2 v2.2.0/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -321,33 +306,41 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hegedustibor/htgo-tts v0.0.0-20220821045517-04f3cda7a12f h1:Cr8e7KUf2sT3YzrBFPO+Oq+ZtIQdo/jVboRzFe9QoHs= -github.com/hegedustibor/htgo-tts v0.0.0-20220821045517-04f3cda7a12f/go.mod h1:VBNcur+xWvaQIWCaLH8w7j68zPeqQwVfjREn2S7kYbY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6 h1:4zOlv2my+vf98jT1nQt4bT/yKWUImevYPJ2H344CloE= -github.com/jbenet/go-base58 v0.0.0-20150317085156-6237cf65f3a6/go.mod h1:r/8JmuR0qjuCiEhAolkfvdZgmPiHTnJaG0UXCSeR1Zo= -github.com/jdkato/prose v1.2.1 h1:Fp3UnJmLVISmlc57BgKUzdjr0lOtjqTZicL3PaYy6cU= -github.com/jdkato/prose v1.2.1/go.mod h1:AiRHgVagnEx2JbQRQowVBKjG0bcs/vtkGCH1dYAL1rA= +github.com/jamesruan/sodium v0.0.0-20181216154042-9620b83ffeae h1:FFafUVNizQIaIHAqmDKXQlZ+8/LplHZbL11emgcto+M= +github.com/jamesruan/sodium v0.0.0-20181216154042-9620b83ffeae/go.mod h1:GZ0frHfchUw9cjP3CK1J7+M74KNo7zrhffQxTeuf5I8= github.com/jhump/protoreflect v1.8.1/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8= github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kercre123/vosk-api/go v1.0.2 h1:NDJUNv2ddw128amiVZ2xE2gKfKHeBRRhboSh+yiH6Wg= +github.com/kercre123/vosk-api/go v1.0.2/go.mod h1:oVZG/VFmg23uNDzjShcw7UhZHWYG2zXgBm5FqioE2Ao= +github.com/kercre123/whisper.cpp/bindings/go v0.0.0-20250602164512-60cd96acff3a h1:QxV7Hk6yeJ38v/NK+n0h3+LPilLHI2x8in7Z6fAH2XU= +github.com/kercre123/whisper.cpp/bindings/go v0.0.0-20250602164512-60cd96acff3a/go.mod h1:7AkTmdvF2fWNd7Z5Cnxf8b83XWIc9Lna4btMWy3SBuA= +github.com/kercre123/zeroconf v1.0.1 h1:Mbd8mN6xnNWYIqBN38x3jJjiPP2RmK4orzbGZsa1EOY= +github.com/kercre123/zeroconf v1.0.1/go.mod h1:qYGgej0DBUELb5p0zVe8yvz3NIwJs+ws9/QD6FJOCS8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -357,25 +350,42 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA= +github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxhawkins/go-webrtcvad v0.0.0-20210121163624-be60036f3083 h1:0JDcvP4R28p6+u8VIHCwYx7UwiHZ074INz3C397oc9s= github.com/maxhawkins/go-webrtcvad v0.0.0-20210121163624-be60036f3083/go.mod h1:YdrZ05xnooeP54y7m+/UvI23O1Td46PjWkLJu1VLObM= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= +github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab h1:n8cgpHzJ5+EDyDri2s/GC7a9+qK3/YEGnBsd0uS/8PY= +github.com/mgutz/logxi v0.0.0-20161027140823-aebf8a7d67ab/go.mod h1:y1pL58r5z2VvAjeG1VLGc8zOQgSOzbKN7kMHPvFXJ+8= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -386,40 +396,28 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74= github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/term v0.0.0-20200429084858-129dac9f73f6/go.mod h1:or9wGItza1sRcM4Wd3dIv8DsFHYQuFsMHEdxUIlUxms= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.6.3 h1:F8446DrvIF5V5smZfZ8K9nrmmix0AFgevPdLruGOmzk= github.com/montanaflynn/stats v0.6.3/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/zenity v0.10.10 h1:V/rtAhr5QLdDThahOkm7EYlnw4RuEsf7oN+Xb6lz1j0= github.com/ncruces/zenity v0.10.10/go.mod h1:k3k4hJ4Wt1MUbeV48y+Gbl7Fp9skfGszN/xtKmuvhZk= -github.com/neurosnap/sentences v1.0.6 h1:iBVUivNtlwGkYsJblWV8GGVFmXzZzak907Ci8aA0VTE= -github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -450,19 +448,34 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 h1:GranzK4hv1/pqTIhMTXt2X8MmMOuH3hMeUR0o9SP5yc= github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844/go.mod h1:T1TLSfyWVBRXVGzWd0o9BI4kfoO9InEgfQe4NV3mLz8= -github.com/robertkrimen/otto v0.0.0-20221127200954-e92282a6bb0d h1:G6jjiYO5GDT2e58C/v5oWfUCMZc88SjA2amv4q9ENVo= -github.com/robertkrimen/otto v0.0.0-20221127200954-e92282a6bb0d/go.mod h1:jsj99765dAh0q5pifRPqoqaJ2t+GIxl+foFmuqsfat8= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -470,8 +483,10 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sashabaranov/go-openai v1.27.1 h1:7Nx6db5NXbcoutNmAUQulEQZEpHG/SkzfexP2X5RWMk= +github.com/sashabaranov/go-openai v1.27.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= +github.com/schollz/progressbar/v3 v3.7.4/go.mod h1:1H8m5kMPW6q5fyjpDqtBHW1JT22mu2NwHQ1ApuCPh/8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shogo82148/go-shuffle v0.0.0-20180218125048-27e6095f230d/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -509,7 +524,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -518,22 +532,31 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/twitchtv/twirp v7.1.0+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/vadv/gopher-lua-libs v0.5.0 h1:m0hhWia1A1U3PIRmtdHWBj88ogzuIjm6HUBmtUa0Tz4= +github.com/vadv/gopher-lua-libs v0.5.0/go.mod h1:mlSOxmrjug7DwisiH7xBFnBellHobPbvAIhVeI/4SYY= +github.com/wlynxg/anet v0.0.1 h1:VbkEEgHxPSrRQSiyRd0pmrbcEQAEU2TTb8fb4DmSYoQ= +github.com/wlynxg/anet v0.0.1/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 h1:noHsffKZsNfU38DwcXWEPldrTjIZ8FPNKx8mYMGnqjs= +github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7/go.mod h1:bbMEM6aU1WDF1ErA5YJ0p91652pGv140gGw4Ww3RGp8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.mongodb.org/mongo-driver v1.4.2/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -564,10 +587,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -594,7 +618,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -602,7 +625,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -619,10 +641,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -635,22 +657,19 @@ golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -667,10 +686,11 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -678,13 +698,14 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -692,15 +713,15 @@ golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -712,28 +733,31 @@ golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -794,18 +818,14 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -866,7 +886,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200923140941-5646d36feee1/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210224155714-063164c882e6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= @@ -911,7 +930,6 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7 google.golang.org/protobuf v1.25.1-0.20201208041424-160c7477e0e8/go.mod h1:hFxJC2f0epmp1elRCiEGJTKAWbwxZ2nvqZdHl3FQXCY= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= @@ -927,28 +945,25 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/hraban/opus.v2 v2.0.0-20201025103112-d779bb1cc5a2 h1:sxrRNhZ+cNxxLwPw/vV8gNsz+bbqRQiZHBYBJfpyNoQ= gopkg.in/hraban/opus.v2 v2.0.0-20201025103112-d779bb1cc5a2/go.mod h1:/L5E7a21VWl8DeuCPKxQBdVG5cy+L0MRZ08B1wnqt7g= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/neurosnap/sentences.v1 v1.0.6 h1:v7ElyP020iEZQONyLld3fHILHWOPs+ntzuQTNPkul8E= -gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= -gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc h1:LMEBgNcZUqXaP7evD1PZcL6EcDVa2QOFuI+cqM3+AJM= +gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc/go.mod h1:N8UOSI6/c2yOpa/XDz3KVUiegocTziPiqNkeNTMiG1k= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= @@ -961,20 +976,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/apimachinery v0.23.4 h1:fhnuMd/xUL3Cjfl64j5ULKZ1/J9n8NuQEgNL+WXWfdM= -k8s.io/apimachinery v0.23.4/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= -k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6 h1:fD1pz4yfdADVNfFmcP2aBEtudwUQ1AlLnRBALr33v3s= -sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/chipper/intent-data/de-DE.json b/chipper/intent-data/de-DE.json index 5f157125..09d39d4d 100755 --- a/chipper/intent-data/de-DE.json +++ b/chipper/intent-data/de-DE.json @@ -29,7 +29,7 @@ }, { "name": "intent_system_sleep", - "Keyphrasen": ["Schlaf", "schlafen gehen", "ins Bett", "ins Bett gehen", "ab ins Bett", "Bett", "gute nacht", "schlaf gut"] + "keyphrases": ["Schlaf", "schlafen gehen", "ins Bett", "ins Bett gehen", "ab ins Bett", "Bett", "gute nacht", "schlaf gut"] }, { "name": "intent_greeting_goodmorning", @@ -53,11 +53,11 @@ }, { "name": "intent_amazon_signin", - "Keyphrasen": ["Trete Alexa bei", "Registriere dich bei Alexa", "Alexa aktivieren", "Schalte Alexa ein", "aktiviere Alexa"] + "keyphrases": ["Trete Alexa bei", "Registriere dich bei Alexa", "Alexa aktivieren", "Schalte Alexa ein", "aktiviere Alexa"] }, { "name": "intent_amazon_signin", - "Keyphrasen": ["Gehe aus Alexa raus", "Deaktiviere Alexa", "Schalte Alexa aus", "Beende Alexa", "Schließe Alexa"] + "keyphrases": ["Gehe aus Alexa raus", "Deaktiviere Alexa", "Schalte Alexa aus", "Beende Alexa", "Schließe Alexa"] }, { "name": "intent_imperative_forward", @@ -81,7 +81,7 @@ }, { "name": "intent_play_popawheelie", - "Keyphrasen": ["Pfeifen", "Pfeife", "Wheelie", "Handstand", "mache einen Handstand", "Kopfstand", "mache einen Kopfstand"] + "keyphrases": ["Pfeifen", "Pfeife", "Wheelie", "Handstand", "mache einen Handstand", "Kopfstand", "mache einen Kopfstand"] }, { "name": "intent_play_fistbump", @@ -149,7 +149,7 @@ }, { "name": "intent_imperative_love", - "Keyphrasen": ["Ich liebe dich", "Du bist meine Liebe", "Ich habe dich lieb"] + "keyphrases": ["Ich liebe dich", "Du bist meine Liebe", "Ich habe dich lieb"] }, { "name": "intent_knowledge_promptquestion", @@ -169,7 +169,7 @@ }, { "name": "intent_clock_time", - "Keyphrasen": ["Was ist jetzt", "Wie spät ist es", "Was ist die Zeit", "Wie viel Uhr ist es"] + "keyphrases": ["Was ist jetzt", "Wie spät ist es", "Was ist die Zeit", "Wie viel Uhr ist es"] }, { "name": "intent_imperative_quiet", @@ -177,7 +177,7 @@ }, { "name": "intent_imperative_dance", - "Keyphrasen": ["Balla", "Höre auf die Musik", "Höre der Musik zu", "Tanz", "Tanzen"] + "keyphrases": ["Balla", "Höre auf die Musik", "Höre der Musik zu", "Tanz", "Tanzen"] }, { "name": "intent_play_pickupcube", diff --git a/chipper/intent-data/en-US.json b/chipper/intent-data/en-US.json index 52354b33..6e34eb44 100755 --- a/chipper/intent-data/en-US.json +++ b/chipper/intent-data/en-US.json @@ -1,218 +1,277 @@ [ { "name" : "intent_names_username_extend", - "keyphrases": ["name is", "native is", "names", "name's", "my name is" ] + "keyphrases": ["name is", "native is", "names", "name's", "my name is" ], + "requiresexact": false }, { "name": "intent_weather_extend", - "keyphrases" : ["weather", "whether", "the other", "the water", "no other", "weather forecast", "weather tomorrow", "whats the weather" ] + "keyphrases" : ["weather", "whether", "the other", "the water", "no other", "weather forecast", "weather tomorrow", "whats the weather" ], + "requiresexact": false }, { "name": "intent_names_ask", - "keyphrases" : ["my name", "who am", "who am i"] + "keyphrases" : ["my name", "who am", "who am i"], + "requiresexact": false }, { "name": "intent_imperative_eyecolor", - "keyphrases" : ["eye color", "colo", "i call her", "i foller", "icolor", "ecce", "erior", "ichor", "agricola", "change", "oracular", "oracle", "set your eye color to"] + "keyphrases" : ["eye color", "colo", "i call her", "i foller", "icolor", "ecce", "erior", "ichor", "agricola", "change", "oracular", "oracle", "set your eye color to"], + "requiresexact": false }, { "name": "intent_character_age", - "keyphrases" : ["older", "how old", "old are you", "old or yo", "how there you"] + "keyphrases" : ["older", "how old", "old are you", "old or yo", "how there you"], + "requiresexact": false }, { "name": "intent_explore_start", - "keyphrases" : ["start", "plor", "owing", "tailoring", "oding", "oring", "pling", "start exploring"] + "keyphrases" : ["start", "plor", "owing", "tailoring", "oding", "oring", "pling", "start exploring"], + "requiresexact": false }, { "name": "intent_system_charger", - "keyphrases" : ["charge", "home", "go to your", "church", "find your ch", "charger" ] + "keyphrases" : ["charge", "home", "go to your", "church", "find your ch", "charger" ], + "requiresexact": false }, - { + { "name": "intent_system_sleep", - "keyphrases" : ["flee", "sleep", "sheep", "go to sleep" ] + "keyphrases" : ["flee", "sleep", "sheep", "go to sleep" ], + "requiresexact": false + }, + { + "name": "intent_system_noaudio", + "keyphrases" : ["[blank_audio]", "[door opens]", "[sighs]" ], + "requiresexact": true }, { "name": "intent_greeting_goodmorning", - "keyphrases" : ["morning", "mourning", "mooning", "it bore", "afternoon", "after noon", "after whom", "good morning" ] + "keyphrases" : ["morning", "mourning", "mooning", "it bore", "afternoon", "after noon", "after whom", "good morning" ], + "requiresexact": false }, { "name": "intent_greeting_goodnight", - "keyphrases" : ["night", "might", "goodnight" ] + "keyphrases" : ["night", "might", "goodnight" ], + "requiresexact": false }, { "name": "intent_greeting_goodbye", - "keyphrases" : ["good bye", "good by", "good buy", "goodbye" ] + "keyphrases" : ["good bye", "good by", "good buy", "goodbye" ], + "requiresexact": false }, { "name": "intent_seasonal_happynewyear", - "keyphrases" : ["fireworks", "new year", "happy new", "happy to", "have been", "i now you", "no year", "enee", "i never", "knew her", "hobhouse", "bennie" ] + "keyphrases" : ["fireworks", "new year", "happy new", "happy to", "have been", "i now you", "no year", "enee", "i never", "knew her", "hobhouse", "bennie" ], + "requiresexact": false }, { "name": "intent_seasonal_happyholidays", - "keyphrases" : ["he holds", "christmas", "behold", "holiday" ] + "keyphrases" : ["he holds", "christmas", "behold", "holiday" ], + "requiresexact": false }, { "name": "intent_amazon_signin", - "keyphrases" : ["in intellect", "fine in electa", "in alex", "ing alex", "in an elect", "to alex", "in angelica", "up alexa", "sign in to alexa" ] + "keyphrases" : ["in intellect", "fine in electa", "in alex", "ing alex", "in an elect", "to alex", "in angelica", "up alexa", "sign in to alexa" ], + "requiresexact": false }, { "name": "intent_amazon_signin", - "keyphrases" : ["in outlet", "i now of elea", "out alexa", "out of ale" ] + "keyphrases" : ["in outlet", "i now of elea", "out alexa", "out of ale" ], + "requiresexact": false }, { "name": "intent_imperative_forward", - "keyphrases" : ["forward", "for ward", "for word", "move forward", "forwards" ] + "keyphrases" : ["forward", "for ward", "for word", "move forward", "forwards" ], + "requiresexact": false }, { "name": "intent_imperative_turnaround", - "keyphrases" : ["around", "one eighty", "one ate he", "turn around" ] + "keyphrases" : ["around", "one eighty", "one ate he", "turn around" ], + "requiresexact": false }, { "name": "intent_imperative_turnleft", - "keyphrases" : ["rn left", "go left", "e left", "ed left", "ernest" ] + "keyphrases" : ["rn left", "go left", "e left", "ed left", "ernest" ], + "requiresexact": false }, { "name": "intent_imperative_turnright", - "keyphrases" : ["rn right", "go right", "e right", "ernie", "credit", "ed right" ] + "keyphrases" : ["rn right", "go right", "e right", "ernie", "credit", "ed right" ], + "requiresexact": false }, { "name": "intent_play_rollcube", - "keyphrases" : ["roll cu", "roll your cu", "all your cu", "roll human", "yorke", "old your he", "roll your cube" ] + "keyphrases" : ["roll cu", "roll your cu", "all your cu", "roll human", "yorke", "old your he", "roll your cube" ], + "requiresexact": false }, { "name": "intent_play_popawheelie", - "keyphrases" : ["pop a w", "polwhele", "olwen", "i wieland", "do a wheel", "doorstone", "thibetan", "powell", "welst", "a wheel", "willie", "a really", "o' billy", "pop a wheelie", "do a wheel stand" ] + "keyphrases" : ["pop a w", "polwhele", "olwen", "i wieland", "do a wheel", "doorstone", "thibetan", "powell", "welst", "a wheel", "willie", "a really", "o' billy", "pop a wheelie", "do a wheel stand" ], + "requiresexact": false }, { "name": "intent_play_fistbump", - "keyphrases" : ["this pomp", "this pump", "bump", "fistb", "fistf", "this book", "pisto", "with pomp", "fison", "first", "fifth", "were fifteen", "if bump", "wisdom", "this bu", "fist bomb", "fist ball", "this ball", "system", "fistbump" ] + "keyphrases" : ["this pomp", "this pump", "bump", "fistb", "fistf", "this book", "pisto", "with pomp", "fison", "first", "fifth", "were fifteen", "if bump", "wisdom", "this bu", "fist bomb", "fist ball", "this ball", "system", "fistbump" ], + "requiresexact": false }, { "name": "intent_play_blackjack", - "keyphrases" : ["black", "cards", "game", "play blackjack" ] + "keyphrases" : ["black", "cards", "game", "play blackjack" ], + "requiresexact": false }, { "name": "intent_imperative_affirmative", - "keyphrases" : ["yes", "correct", "sure" ] + "keyphrases" : ["yes", "correct", "sure", "yes please" ], + "requiresexact": false }, { "name": "intent_imperative_negative", - "keyphrases" : ["no", "dont" ] + "keyphrases" : ["no", "dont", "no thanks" ], + "requiresexact": true }, { "name": "intent_photo_take_extend", - "keyphrases" : ["photo", "foto", "selby", "capture", "picture", "take a photo of me" ] + "keyphrases" : ["photo", "foto", "selby", "capture", "picture", "take a photo of me" ], + "requiresexact": false }, { "name": "intent_imperative_praise", - "keyphrases" : ["good", "awesome", "also", "as some", "of them", "battle", "t rob", "the ro", "amazing", "woodcourt", "good robot" ] + "keyphrases" : ["good", "awesome", "also", "as some", "of them", "battle", "t rob", "the ro", "amazing", "woodcourt", "good robot" ], + "requiresexact": false }, { "name": "intent_imperative_abuse", - "keyphrases" : ["bad", "that ro", "ad ro", "a root", "hate", "horrible", "bad robot" ] + "keyphrases" : ["bad", "that ro", "ad ro", "a root", "hate", "horrible", "bad robot" ], + "requiresexact": false }, { "name": "intent_imperative_apologize", - "keyphrases" : ["sorry", "apologize", "apologise", "the tory", "nevermind", "never mind", "im sorry" ] + "keyphrases" : ["sorry", "apologize", "apologise", "the tory", "nevermind", "never mind", "im sorry" ], + "requiresexact": false }, { "name": "intent_imperative_backup", - "keyphrases" : ["back", "back up", "backwards" ] + "keyphrases" : ["back", "back up", "backwards","beck"], + "requiresexact": false }, { "name": "intent_imperative_volumedown", - "keyphrases" : ["all you down", "volume down", "down volume", "down the volume", "quieter", "turn the volume down" ] + "keyphrases" : ["all you down", "volume down", "down volume", "down the volume", "quieter", "turn the volume down" ], + "requiresexact": false }, { "name": "intent_imperative_volumeup", - "keyphrases" : ["all you up", "volume up", "up volume", "up the volume", "louder" ] + "keyphrases" : ["all you up", "volume up", "up volume", "up the volume", "louder" ], + "requiresexact": false }, { "name": "intent_imperative_lookatme", - "keyphrases" : ["stare", "at me" ] + "keyphrases" : ["stare", "at me" ], + "requiresexact": false }, { "name": "intent_imperative_volumelevel_extend", - "keyphrases" : ["all you", "volume", "loudness" ] + "keyphrases" : ["all you", "volume", "loudness" ], + "requiresexact": false }, { "name": "intent_imperative_shutup", - "keyphrases" : ["shut up" ] + "keyphrases" : ["shut up" ], + "requiresexact": false }, { "name": "intent_greeting_hello", - "keyphrases" : ["hello", "are you", "our you", "high", "below", "little", "follow", "for you", "far you", "how about you", "how are you", "the low", "the loo" ] + "keyphrases" : ["hello", "our you", "follow", "for you", "far you", "how about you", "how are you", "the low", "the loo", "our are you" ], + "requiresexact": false }, { "name": "intent_imperative_come", - "keyphrases" : ["come", "to me", "come here" ] + "keyphrases" : ["come to me", "come here" ], + "requiresexact": false }, { "name": "intent_imperative_love", - "keyphrases" : ["love", "dove", "i love you" ] + "keyphrases" : ["love you", "dove you", "i love you" ], + "requiresexact": false }, { "name": "intent_knowledge_promptquestion", - "keyphrases" : ["question", "weston", "i have a question" ] + "keyphrases" : ["question", "weston", "i have a question", "conversation", "lets talk", "let's talk" ], + "requiresexact": false }, { "name": "intent_clock_checktimer", - "keyphrases" : ["check timer", "check the timer", "check the time her", "check time her", "check time her", "check time of her", "checked the timer", "checked the time her", "checked the time of her" ] + "keyphrases" : ["check timer", "check the timer", "check the time her", "check time her", "check time her", "check time of her", "checked the timer", "checked the time her", "checked the time of her" ], + "requiresexact": false }, { "name": "intent_global_stop_extend", - "keyphrases" : ["up the timer", "stop timer", "cancel the", "cancel timer", "stop clock", "stop be", "stopped t", "stopped be", "stopped at", "stop the" ] + "keyphrases" : ["up the timer", "stop timer", "cancel the", "cancel timer", "stop clock", "stop be", "stopped t", "stopped be", "stopped at", "stop the" ], + "requiresexact": false }, { "name": "intent_clock_settimer_extend", - "keyphrases" : ["timer", "time for", "time of for", "time or", "time of", "set a timer for" ] + "keyphrases" : ["timer", "time for", "time of for", "time or", "time of", "set a timer for" ], + "requiresexact": false }, { "name": "intent_clock_time", - "keyphrases" : ["time is it", "the time", "what time", "clock", "what time is it" ] + "keyphrases" : ["time is it", "the time", "what time", "clock", "what time is it" ], + "requiresexact": false }, { "name": "intent_imperative_quiet", - "keyphrases" : ["quiet", "stop", "be quiet" ] + "keyphrases" : ["quiet", "stop", "be quiet" ], + "requiresexact": false }, { "name": "intent_imperative_dance", - "keyphrases" : ["dance", "dancing", "thence", "dance to the beat", "the beat", "boogie" ] + "keyphrases" : ["dance", "dancing", "thence", "dance to the beat", "the beat", "boogie", "to the music" ], + "requiresexact": false }, { "name": "intent_play_pickupcube", - "keyphrases" : ["pickup", "pick up" ] + "keyphrases" : ["pickup", "pick up" ], + "requiresexact": false }, { "name": "intent_imperative_fetchcube", - "keyphrases" : ["fetch your cu", "fetch cu", "fetch the cu", "bring to me", "bring me", "bring me your cube" ] + "keyphrases" : ["fetch your cu", "fetch cu", "fetch the cu", "bring to me", "bring me", "bring me your cube" ], + "requiresexact": false }, { "name": "intent_imperative_findcube", - "keyphrases" : ["your cu", "the cu", "find your cube" ] + "keyphrases" : ["your cu", "the cu", "find your cube" ], + "requiresexact": false }, { "name": "intent_play_anytrick", - "keyphrases" : ["trick", "something cool", "some thing cool", "do a trick" ] + "keyphrases" : ["trick", "something cool", "some thing cool", "do a trick" ], + "requiresexact": false }, { "name": "intent_message_recordmessage_extend", - "keyphrases" : ["record" ] + "keyphrases" : ["record" ], + "requiresexact": false }, { "name": "intent_message_playmessage_extend", - "keyphrases" : ["play message", "play method", "play a message", "play a method" ] + "keyphrases" : ["play message", "play method", "play a message", "play a method" ], + "requiresexact": false }, { "name": "intent_blackjack_hit", - "keyphrases" : ["hit"] + "keyphrases" : ["hit"], + "requiresexact": false }, { "name": "intent_blackjack_stand", - "keyphrases" : ["stand", "stan" ] + "keyphrases" : ["stand", "stan" ], + "requiresexact": false }, { "name": "intent_play_keepaway", - "keyphrases": ["keepaway", "keep away", "play keepaway" ] + "keyphrases": ["keepaway", "keep away", "play keepaway" ], + "requiresexact": false } ] diff --git a/chipper/intent-data/fr-FR.json b/chipper/intent-data/fr-FR.json index 2cabdf8b..fe68d95d 100755 --- a/chipper/intent-data/fr-FR.json +++ b/chipper/intent-data/fr-FR.json @@ -1,218 +1,277 @@ [ - { - "name" : "intent_names_username_extend", - "keyphrases": ["mon nom est", "mon nom est", "je suis", "voici"] - }, - { - "name": "intent_weather_extend", - "keyphrases": ["quel temps fait-il", "quel temps fera", "prévisions météorologiques", "meteo"] - }, - { - "name": "intent_names_ask", - "keyphrases": ["quel est mon nom", "comment m'appelle moi", "qui je suis"] - }, - { - "name": "intent_imperative_eyecolor", - "keyphrases": ["couleur des yeux", "couleur des yeux", "changer la couleur", "yeux"] - }, - { - "name": "intent_character_age", - "keyphrases": ["quel âge avez-vous", "quel est votre âge", "quel âge avez-vous"] - }, - { - "name": "intent_explore_start", - "keyphrases": ["allez explorer", "explore", "allez à l'exploration", "fait un tour"] - }, - { - "name": "intent_system_charger", - "keyphrases": ["rentrer à la maison", "à la maison", "rechargé", "trouver le chargeur", "aller en charge"] - }, - { - "name": "intent_system_sleep", - "keyphrases": ["va te coucher", "allez a dormir", "allez au lit", "lit", "bonne nuit"] - }, - { - "name": "intent_greeting_goodmorning", - "keyphrases": ["jour", "matin", "après-midi"] - }, - { - "name": "intent_greeting_goodnight", - "keyphrases": ["soir", "soirée"] - }, - { - "name": "intent_greeting_goodbye", - "keyphrases": ["adieu", "au revoir", "à voir"] - }, - { - "name": "intent_seasonal_happynewyear", - "keyphrases": ["fireworks", "bonne année"] - }, - { - "name": "intent_seasonal_happyholidays", - "keyphrases": ["noël", "vacance", "joyeuses fêtes"] - }, - { - "name": "intent_amazon_signin", - "keyphrases": ["entrez alexa", "inscrivez-toi sur alexa", "activez alexa"] - }, - { - "name": "intent_amazon_signin", - "keyphrases": ["sortez d'alexa", "désactivez alexa"] - }, - { - "name": "intent_imperative_forward", - "keyphrases": ["avant"] - }, - { - "name": "intent_imperative_turnaround", - "keyphrases": ["tourne"] - }, - { - "name": "intent_imperative_turnleft", - "keyphrases": ["turn à gauche", "allez à gauche"] - }, - { - "name": "intent_imperative_turnright", - "keyphrases": ["tir à droite", "allez à droite"] - }, - { - "name": "intent_play_rollcube", - "keyphrases": ["jouez avec le cube", "roulez le cube", "déplacez le cube"] - }, - { - "name": "intent_play_popawheelie", - "keyphrases": ["sifflez"] - }, - { - "name": "intent_play_fistbump", - "keyphrases": ["donnez-moi cinq", "donnez-moi les cinq"] - }, - { - "name": "intent_play_blackjack", - "keyphrases": ["vingt-un", "blackjack"] - }, - { - "name": "intent_imperative_affirmative", - "keyphrases": ["oui", "à droite", "correct", "oui", "vraie"] - }, - { - "name": "intent_imperative_negative", - "keyphrases": ["non", "pas", "mal", "false"] - }, - { - "name": "intent_photo_take_extend", - "keyphrases": ["photo", "selfie", "image"] - }, - { - "name": "intent_imperative_praise", - "keyphrases": ["bon", "gros", "mythique", "fort", "impressionnant"] - }, - { - "name": "intent_imperative_abuse", - "keyphrases": ["mal", "stupide", "ça ne va pas"] - }, - { - "name": "intent_imperative_apologize", - "keyphrases": ["je suis désolé", "désolé"] - }, - { - "name": "intent_imperative_backup", - "keyphrases": ["retournez"] - }, - { - "name": "intent_imperative_volumedown", - "keyphrases": ["abaisse le volume"] - }, - { - "name": "intent_imperative_volumeup", - "keyphrases": ["augmente le volume"] - }, - { - "name": "intent_imperative_lookatme", - "keyphrases": ["regardez-moi"] - }, - { - "name": "intent_imperative_volumelevel_extend", - "keyphrases": ["volume"] - }, - { - "name": "intent_imperative_shutup", - "keyphrases": ["tais-toi", "silence", "ferme"] - }, - { - "name": "intent_greeting_hello", - "keyphrases": ["bonjour", "comment vas-tu", "bonjour", "bonne soirée", "bon après-midi", "hey", "salut"] - }, - { - "name": "intent_imperative_come", - "keyphrases": ["viens ici", "viens à moi", "ici"] - }, - { - "name": "intent_imperative_love", - "keyphrases": ["je t'aime", "mon amour"] - }, - { - "name": "intent_knowledge_promptquestion", - "keyphrases": ["question"] - }, - { - "name": "intent_clock_checktimer", - "keyphrases": ["vérifiez le chronomètre", "vérifiez la minuterie"] - }, - { - "name": "intent_global_stop_extend", - "keyphrases": ["arrêtez le minuteur", "arrêtez le chronomètre"] - }, - { - "name": "intent_clock_settimer_extend", - "keyphrases": ["démarrez le minuteur", "démarrez le chronomètre"] - }, - { - "name": "intent_clock_time", - "keyphrases": ["quelle est l'heure", "quelle heure est il"] - }, - { - "name": "intent_imperative_quiet", - "keyphrases": ["assez", "stop", "restez bien", "restez calme"] - }, - { - "name": "intent_imperative_dance", - "keyphrases": ["danse"] - }, - { - "name": "intent_play_pickupcube", - "keyphrases": ["collect"] - }, - { - "name": "intent_imperative_fetchcube", - "keyphrases": ["apportez le cube"] - }, - { - "name": "intent_imperative_findcube", - "keyphrases": ["trouver le cube", "trouvez votre cube"] - }, - { - "name": "intent_play_anytrick", - "keyphrases": ["do something cool", "wonom me", "impression me", "inventer quelque chose"] - }, - { - "name": "intent_message_recordmessage_extend", - "keyphrases": ["enregistrer"] - }, - { - "name": "intent_message_playmessage_extend", - "keyphrases": ["reproduire le message", "lisez le message"] - }, - { - "name": "intent_blackjack_hit", - "keyphrases": ["hit"] - }, - { - "name": "intent_blackjack_stand", - "keyphrases": ["stand"] - }, - { - "name": "intent_play_keepaway", - "keyphrases": ["restez loin", "loin", "supprimé", "back"] - } -] \ No newline at end of file + { + "name" : "intent_names_username_extend", + "keyphrases": ["surnom est", "nom de naissance est ", "mes surnoms sont ", "mon surnom est", "mon pseudo", "pseudo", "mon pseudo est", "mon prénom", "mon prénom est", "prénom est" ], + "requiresexact": false + }, + { + "name": "intent_weather_extend", + "keyphrases" : ["quel temps fait-il", "quel temps fera", "prévisions météorologiques", "meteo", "quel temps fera t'il", "météo", "prévisions météo"], + "requiresexact": false + }, + { + "name": "intent_names_ask", + "keyphrases" : ["quel est mon nom", "comment je m'appelle", "qui je suis"], + "requiresexact": false + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases" : ["couleur des yeux", "change ta couleur d'yeux", "change la couleur de tes yeux", "couleur des yeux en", "change ta couleur d'yeux en", "change la couleur de tes yeux en", "change de couleur dieu"], + "requiresexact": false + }, + { + "name": "intent_character_age", + "keyphrases" : ["t'as quel âge", "quel âge as-tu", "quel est ton âge"], + "requiresexact": false + }, + { + "name": "intent_explore_start", + "keyphrases" : ["va explorer", "explore", "part en exploration", "fait un tour", "pars à l'aventure", "exploration", "en exploration", "aventure"], + "requiresexact": false + }, + { + "name": "intent_system_charger", + "keyphrases" : ["rentre à la maison", "à la maison", "recharge", "trouve le chargeur", "va te charger", "trouve ton chargeur" ], + "requiresexact": false + }, + { + "name": "intent_system_sleep", + "keyphrases" : ["cette couche", "d'accoucher", "va te coucher", "va dormir", "va au lit", "endort toi", "en dort toi", "en dortoir", "encore toi", "dors" ], + "requiresexact": false + }, + { + "name": "intent_system_noaudio", + "keyphrases" : ["[blank_audio]", "[door opens]", "[sighs]" ], + "requiresexact": true + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases" : ["bonjour", "salut", "matin", "morning", "mourning", "mooning", "it bore", "afternoon", "after noon", "after whom", "good morning" ], + "requiresexact": false + }, + { + "name": "intent_greeting_goodnight", + "keyphrases" : ["nuit","bonne nuit", "good night", "night" ], + "requiresexact": false + }, + { + "name": "intent_greeting_goodbye", + "keyphrases" : ["good bye", "good by", "good buy", "goodbye", "au revoir", "adieu", "a voir", "envoi", "paroi" ], + "requiresexact": false + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases" : ["bonne année", "feu d'artifice", "fireworks", "new year", "happy new", "happy to", "have been", "i now you", "no year", "enee", "i never", "knew her", "hobhouse", "bennie" ], + "requiresexact": false + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases" : ["he holds", "christmas", "behold", "holiday", "noël", "vacances", "joyeux noël", "joyeuses fêtes", "joyeuses Pâques" ], + "requiresexact": false + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["connecte-toi à alexa", "connecte-toi alexa", "active Alexa", "log toi", "log toi à Alexa", "active avec ça", "steve alexa"], + "requiresexact": false + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["déconnecte toi d'alexa", "désactive Alexa", "délog toi", "délog toi d'Alexa", "déconnecte alexa"], + "requiresexact": false + }, + { + "name": "intent_imperative_forward", + "keyphrases" : ["en avant", "avant", "avance", "tout droit"], + "requiresexact": false + }, + { + "name": "intent_imperative_turnaround", + "keyphrases" : ["tourne", "tourne à cent quatre vingt", "demi-tour", "cent quatre vingt" ], + "requiresexact": false + }, + { + "name": "intent_imperative_turnleft", + "keyphrases" : ["tourne à gauche", "gauche", "va à gauche" ], + "requiresexact": false + }, + { + "name": "intent_imperative_turnright", + "keyphrases" : ["tourne à droite", "droite", "va à droite" ], + "requiresexact": false + }, + { + "name": "intent_play_rollcube", + "keyphrases" : ["fais rouler ton cube", "roule tant que", "roule ton cuve", "roule ton cube", "roule cube", "deplace le cube", "deplace cube", "deplace ton cube", "bouge le cube", "bouge cube", "bouge ton cube", "bouge ton cul", "des places ton coeur" ], + "requiresexact": false + }, + { + "name": "intent_play_popawheelie", + "keyphrases" : ["fais un wheelie", "fais un huit", "fais à lui", "fais un willy", "wheelie", "wheeling", "fais un wheeling", "la ligne", "mailing", "debout", "le poirier", "poirier", "saut en arrière", "saut arrière", "ce arrière", "willy", "oui ligne", "fait une roue arrière", "roue arrière" ], + "requiresexact": false + }, + { + "name": "intent_play_fistbump", + "keyphrases" : ["fistbump", "high five", "fils veuve", "six de", "check", "tape m'en cinq", "fils de" ], + "requiresexact": false + }, + { + "name": "intent_play_blackjack", + "keyphrases" : ["black", "cards", "game", "joue au blackjack", "blackjack", "cartes", "jouons aux cartes", "joue aux cartes", "vingt et un", "vingt un" ], + "requiresexact": false + }, + { + "name": "intent_imperative_affirmative", + "keyphrases" : ["yes", "oui", "correct", "tout à fait", "oui s'il te plaît", "en effet" ], + "requiresexact": false + }, + { + "name": "intent_imperative_negative", + "keyphrases" : ["non", "ne fait pas ça", "non merci" ], + "requiresexact": true + }, + { + "name": "intent_photo_take_extend", + "keyphrases" : ["photo", "foto", "selfie", "capture", "prend une photo", "prend une photo de moi" ], + "requiresexact": false + }, + { + "name": "intent_imperative_praise", + "keyphrases" : ["bien", "génial", "fantastique", "impressionnant", "super", "good robot", "gentil", "gentil robot", "bon robot", "ton robot", "bon gros" ], + "requiresexact": false + }, + { + "name": "intent_imperative_abuse", + "keyphrases" : ["méchant", "pas gentil", "horrible", "stupide", "méchant robot" ], + "requiresexact": false + }, + { + "name": "intent_imperative_apologize", + "keyphrases" : ["désolé", "excuse-moi", "je te prie de m'excuser", "c'est pas grave", "ce n'est pas grave", "laisse tomber", "je suis désolé", "excuse", "pardon", "pardonne-moi" ], + "requiresexact": false + }, + { + "name": "intent_imperative_backup", + "keyphrases" : ["recule", "en arrière", "marche arrière"], + "requiresexact": false + }, + { + "name": "intent_imperative_volumedown", + "keyphrases" : ["moins fort", "baisse le son", "baisse son", "baisse le volume", "baisse volume" ], + "requiresexact": false + }, + { + "name": "intent_imperative_volumeup", + "keyphrases" : ["plus fort", "monte le son", "monte son", "monte le volume", "monte volume"], + "requiresexact": false + }, + { + "name": "intent_imperative_lookatme", + "keyphrases" : ["regarde-moi"], + "requiresexact": false + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases" : ["volume à fond", "fait du bruit", "fais du bruit", "du bruit" ], + "requiresexact": false + }, + { + "name": "intent_imperative_shutup", + "keyphrases" : ["tais-toi", "silence", "ferme la" ], + "requiresexact": false + }, + { + "name": "intent_greeting_hello", + "keyphrases" : ["hello", "hey", "salut", "comment vas-tu" ], + "requiresexact": false + }, + { + "name": "intent_imperative_come", + "keyphrases" : ["viens ici", "viens à moi", "ici", "viens là" ], + "requiresexact": false + }, + { + "name": "intent_imperative_love", + "keyphrases" : ["love you", "dove you", "i love you", "je t'aime", "mon amour" ], + "requiresexact": false + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases" : ["question", "weston", "i have a question", "conversation", "lets talk", "let's talk", "discutons", "parlons", "j'ai une question" ], + "requiresexact": false + }, + { + "name": "intent_clock_checktimer", + "keyphrases" : ["affiche le minuteur", "affiche un minuteur", "vérifie le minuteur", "il reste combien de temps au minuteur", "combien de temps reste t'il au minuteur", "affiche le chronomètre", "affiche un chronomètre", "vérifie le chronomètre", "il reste combien de temps au chronomètre", "combien de temps reste t'il au chronomètre", "affiche le chrono", "vérifie le chrono", "il reste combien de temps au chrono", "combien de temps reste t'il au chrono","il reste combien de temps", "combien de temps reste t'il", "il reste combien de temps"], + "requiresexact": false + }, + { + "name": "intent_global_stop_extend", + "keyphrases" : ["arrête le minuteur", "arrête le chronomètre", "stoppe le minuteur", "stoppe le chronomètre", "stop le minuteur", "stop le chronomètre"], + "requiresexact": false + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases" : ["minuteur", "chronomètre", "minuteur pour", "chronomètre pour", "met un minuteur", "met un chronomètre", "met un minuteur pour", "met un chronomètre pour", "démarre un minuteur", "démarre un chronomètre", "démarre un minuteur pour", "démarre un chronomètre pour" ], + "requiresexact": false + }, + { + "name": "intent_clock_time", + "keyphrases" : ["quelle heure est-il", "il est quelle heure", "donne moi l'heure", "affiche l'heure"], + "requiresexact": false + }, + { + "name": "intent_imperative_quiet", + "keyphrases" : ["assez", "stop", "calme-toi", "reste calme" ], + "requiresexact": false + }, + { + "name": "intent_imperative_dance", + "keyphrases" : ["danse", "écoute", "écoute la musique", "musique" ], + "requiresexact": false + }, + { + "name": "intent_play_pickupcube", + "keyphrases" : ["ramasse", "ramasse ton cu", "ramasse ton cube", "ramasse ton cuve", "soulève ton cu", "soulève ton cube", "soulève ton cuve", "ramasse cu", "ramasse cube", "ramasse cuve", "soulève cu", "soulève cube", "soulève cuve" ], + "requiresexact": false + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases" : ["apporte", "rapporte", "apporte ton cu", "apporte ton cube", "apporte ton cuve", "rapporte ton cu", "rapporte ton cube", "rapporte ton cuve", "apporte cu", "apporte cube", "apporte cuve", "rapporte cu", "rapporte cube", "rapporte cuve" ], + "requiresexact": false + }, + { + "name": "intent_imperative_findcube", + "keyphrases" : ["trouve", "cherche", "trouve ton cu", "trouve ton cube", "trouve ton cuve", "cherche ton cu", "cherche ton cube", "cherche ton cuve", "trouve cu", "trouve cube", "trouve cuve", "cherche cu", "cherche cube", "cherche cuve" ], + "requiresexact": false + }, + { + "name": "intent_play_anytrick", + "keyphrases" : ["un tour", "fais un tour", "figure", "fais une figure", "un truc", "un truc cool", "fais un truc", "fais un truc cool", "impressionne-moi", "quelque chose de cool", "fais quelque chose", "fais quelque chose de cool"], + "requiresexact": false + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases" : ["enregistre" ], + "requiresexact": false + }, + { + "name": "intent_message_playmessage_extend", + "keyphrases" : ["joue message", "joue le message", "lis message", "lis le message" ], + "requiresexact": false + }, + { + "name": "intent_blackjack_hit", + "keyphrases" : ["hit", "yes", "oui", "carte", "card"], + "requiresexact": false + }, + { + "name": "intent_blackjack_stand", + "keyphrases" : ["stand", "stan", "passe", "reste" ], + "requiresexact": false + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["va-t'en", "éloigne-toi", "reste loin", "dégage", "fou le camp", "loin" ], + "requiresexact": false + } +] diff --git a/chipper/intent-data/nt-NL.json b/chipper/intent-data/nt-NL.json new file mode 100644 index 00000000..162b1882 --- /dev/null +++ b/chipper/intent-data/nt-NL.json @@ -0,0 +1,266 @@ +[ + { + "name" : "intent_names_username_extend", + "keyphrases": ["Mijn naam is", "naam is", "bijnaam is", "mijn maan is", "Mijn baan is" ], + "requiresexact": false + }, + { + "name": "intent_weather_extend", + "keyphrases" : ["weer", "meer", "keer", "het weer", "weerbericht", "hoe is het weer", "hoe is het meer", "het keer" ], + "requiresexact": false + }, + { + "name": "intent_names_ask", + "keyphrases" : ["mijn naam", "wat is mijn naam", "watts mijn naam", "mijn maan"], + "requiresexact": false + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases" : ["ogen", "oogkleur", "boven", "maak je ogen", "boog", "boogkleur", "verander oogkleur naar", "oog meur"], + "requiresexact": false + }, + { + "name": "intent_character_age", + "keyphrases" : ["hoe oud", "hoe oud ben jij", "roe oud ben jij", "wat is je leeftijd", "wat is jouw leefijd", "hoe oud ben je"], + "requiresexact": false + }, + { + "name": "intent_explore_start", + "keyphrases" : ["ontdek", "ga ontdekken", "onttrek"], + "requiresexact": false + }, + { + "name": "intent_system_charger", + "keyphrases" : ["laad op", "huis", "ga naar", "muis", "ga naar je lader", "ga laden" ], + "requiresexact": false + }, + { + "name": "intent_system_sleep", + "keyphrases" : ["slaap", "ga slapen", "schaap", "ga schapen" ], + "requiresexact": false + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases" : ["morgen", "morge", "goede morgen", "middag", "goede middag" ], + "requiresexact": false + }, + { + "name": "intent_greeting_goodnight", + "keyphrases" : ["avond", "almond", "fijne avond","goede avond" ], + "requiresexact": false + }, + { + "name": "intent_greeting_goodbye", + "keyphrases" : ["doei", "doeidoei", "bye", "groei" ], + "requiresexact": false + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases" : ["vuurwerk", "gelukkig nieuw jaar", "gelukkig nieuw waar", "vuur sterk", "nieuwe jaar" ], + "requiresexact": false + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases" : ["feestdagen", "kerst", "fijne feestagen", "fijne kerst", "fijne kerstmis", "beestdagen" ], + "requiresexact": false + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["log in bij alexa", "bij alexa", "", "ing alex", "in bij alexa", "naar alexa", "in angelica" ], + "requiresexact": false + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["in outlet", "i now of elea", "out alexa", "out of ale" ], + "requiresexact": false + }, + { + "name": "intent_imperative_forward", + "keyphrases" : ["voren", "naar voren", "rol naar voren", "boren", "ga naar boren", "storen","ga naar storen" ], + "requiresexact": false + }, + { + "name": "intent_imperative_turnaround", + "keyphrases" : ["keer om", "slalom", "draai om", "maai om", "meer om" ], + "requiresexact": false + }, + { + "name": "intent_imperative_turnleft", + "keyphrases" : ["ga links", "ga naar links", "draai naar links", "draai links" ], + "requiresexact": false + }, + { + "name": "intent_imperative_turnright", + "keyphrases" : ["ga rechts", "rechts", "ga rechst" ], + "requiresexact": false + }, + { + "name": "intent_play_rollcube", + "keyphrases" : ["rol je kubus", "ro je kubus", "wo je kubus", "rol kubus", "wo kubus" ], + "requiresexact": false + }, + { + "name": "intent_play_popawheelie", + "keyphrases" : ["pop een wheele", "popawhele", "wele", "pop a wiele", "wielstand", "do a flip", "weerstand", "pop een wie", "poppen wie"], + "requiresexact": false + }, + { + "name": "intent_play_fistbump", + "keyphrases" : ["box", "mox", "geef een box", "boks", "stots", "roks", "cocks", "vox" ], + "requiresexact": false + }, + { + "name": "intent_play_blackjack", + "keyphrases" : ["black", "kaarten", "game", "speel blackjack", "eenentwintigen", "eendentwintigen" ], + "requiresexact": false + }, + { + "name": "intent_imperative_affirmative", + "keyphrases" : ["ja", "klopt", "tuurlijk", "graag", "ja graag" ], + "requiresexact": false + }, + { + "name": "intent_imperative_negative", + "keyphrases" : ["nee", "niet", "nee bedankt", "slee"], + "requiresexact": true + }, + { + "name": "intent_photo_take_extend", + "keyphrases" : ["photo", "foto", "selfy", "fotografeer", "grafeer", "maak een foto" ], + "requiresexact": false + }, + { + "name": "intent_imperative_praise", + "keyphrases" : ["goed", "super", "goede robot", "goed robot", "stoep robot" ], + "requiresexact": false + }, + { + "name": "intent_imperative_abuse", + "keyphrases" : ["slecht", "stom", "slechte robot", "stomme robot", "haat", "dom" ], + "requiresexact": false + }, + { + "name": "intent_imperative_apologize", + "keyphrases" : ["sorry", "spijt me", "oops", "laat maar" ], + "requiresexact": false + }, + { + "name": "intent_imperative_backup", + "keyphrases" : ["back", "back up", "backwards","beck"], + "requiresexact": false + }, + { + "name": "intent_imperative_volumedown", + "keyphrases" : ["geluid omlaag", "zachter", "zet zechter", "zet het geluid zachter", "wachter" ], + "requiresexact": false + }, + { + "name": "intent_imperative_volumeup", + "keyphrases" : ["harder", "luider", "zet harder", "zet het geluid harder", "harrer" ], + "requiresexact": false + }, + { + "name": "intent_imperative_lookatme", + "keyphrases" : ["kijk naar me", "naar me", "kijk" ], + "requiresexact": false + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases" : ["luidst", "hardst" ], + "requiresexact": false + }, + { + "name": "intent_imperative_shutup", + "keyphrases" : ["shut up", "hou op" ], + "requiresexact": false + }, + { + "name": "intent_greeting_hello", + "keyphrases" : ["hallo", "hoi", "halo" ], + "requiresexact": false + }, + { + "name": "intent_imperative_come", + "keyphrases" : ["kom hier", "kom naar mij toe", "kom mier" ], + "requiresexact": false + }, + { + "name": "intent_imperative_love", + "keyphrases" : ["hou van je", "dove you", "i love you" ], + "requiresexact": false + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases" : ["vraag", "ik heb een vraag", "waag", "ik heb een waag" ], + "requiresexact": false + }, + { + "name": "intent_clock_checktimer", + "keyphrases" : ["check timer", "check de timer" ], + "requiresexact": false + }, + { + "name": "intent_global_stop_extend", + "keyphrases" : ["stop timer", "stop de timer", "stop klok", "stoppen timer", "stop het alarm", "stop alarm" ], + "requiresexact": false + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases" : ["timer", "timer van", "alarm van", "zet alarm", "zet een timer van", "zet een alarm van" ], + "requiresexact": false + }, + { + "name": "intent_clock_time", + "keyphrases" : ["hoe laat is het", "tijd", "wat is de tijd", "klok" ], + "requiresexact": false + }, + { + "name": "intent_imperative_quiet", + "keyphrases" : ["still", "hou je bek", "doe stil" ], + "requiresexact": false + }, + { + "name": "intent_imperative_dance", + "keyphrases" : ["dans", "trans", "luister naar de muziek", "dans op de beat", "publiek", "luister naar pibliek", "naar de muziek" ], + "requiresexact": false + }, + { + "name": "intent_play_pickupcube", + "keyphrases" : ["pak op", "pak je kubus" ], + "requiresexact": false + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases" : ["breng je kubus naar mij", "haal je kubus op", "maal je kubus", "streng je kubus" ], + "requiresexact": false + }, + { + "name": "intent_imperative_findcube", + "keyphrases" : ["vind je kubus", "vind kubus", "je kubus" ], + "requiresexact": false + }, + { + "name": "intent_play_anytrick", + "keyphrases" : ["trukje", "iets cools", "doe een truk", "doe iets cools" ], + "requiresexact": false + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases" : ["neem op", "opnemen" ] + }, + { + "name": "intent_blackjack_hit", + "keyphrases" : ["hit", "raak" ], + "requiresexact": false + }, + { + "name": "intent_blackjack_stand", + "keyphrases" : ["stand", "stan", "standaard", "kraam" ], + "requiresexact": false + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["keepaway", "keep away", "play keepaway", "speel houdt weg", "houdt weg" ], + "requiresexact": false + } +] \ No newline at end of file diff --git a/chipper/intent-data/pt-BR.json b/chipper/intent-data/pt-BR.json index 8dcfb0a5..6442db5d 100644 --- a/chipper/intent-data/pt-BR.json +++ b/chipper/intent-data/pt-BR.json @@ -29,7 +29,7 @@ }, { "name": "intent_system_sleep", - "keyphrases" : ["dormir", "vai dormi" ] + "keyphrases" : ["dormir", "vai dormi", "dorme"] }, { "name": "intent_greeting_goodmorning", @@ -45,7 +45,7 @@ }, { "name": "intent_seasonal_happynewyear", - "keyphrases" : ["soltar fogos", "ano novo", "feliz ano", "feliz anivérsario" ] + "keyphrases" : ["soltar fogos", "ano novo", "feliz ano", "feliz anivérsario", "é meu anivérsario" ] }, { "name": "intent_seasonal_happyholidays", @@ -117,7 +117,7 @@ }, { "name": "intent_imperative_backup", - "keyphrases" : ["back" ] + "keyphrases" : ["para trás","vai pra trás"] }, { "name": "intent_imperative_volumedown", diff --git a/chipper/intent-data/ru-RU.json b/chipper/intent-data/ru-RU.json new file mode 100644 index 00000000..20e4a53c --- /dev/null +++ b/chipper/intent-data/ru-RU.json @@ -0,0 +1,218 @@ +[ + { + "name" : "intent_names_username_extend", + "keyphrases": ["имена", "назови имена" ] + }, + { + "name": "intent_weather_extend", + "keyphrases" : ["погода", "погода завтра", "какая погода" ] + }, + { + "name": "intent_names_ask", + "keyphrases" : ["как меня зовут", "моё имя", "кто я"] + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases" : ["глаз", "глаза", "измени цвет глаз", "поменяй цвет"] + }, + { + "name": "intent_character_age", + "keyphrases" : ["сколько тебе лет" ] + }, + { + "name": "intent_explore_start", + "keyphrases" : ["начни исследовать"] + }, + { + "name": "intent_system_charger", + "keyphrases" : ["зарядка", "дом", "база", "найди зарядку", "найди базу", "иди на зарядку", "иди на базу"] + }, + { + "name": "intent_system_sleep", + "keyphrases" : ["спать", "иди спать" ] + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases" : ["доброе утро", "утро" ] + }, + { + "name": "intent_greeting_goodnight", + "keyphrases" : ["спокойной ночи", "пора спать" ] + }, + { + "name": "intent_greeting_goodbye", + "keyphrases" : ["пока", "досвидание", "я ушёл" ] + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases" : ["фейрверк", "новый год", "салют" ] + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases" : ["праздник" ] + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["алекса" ] + }, + { + "name": "intent_amazon_signin", + "keyphrases" : ["алекса" ] + }, + { + "name": "intent_imperative_forward", + "keyphrases" : ["вперед", "едь вперёд", "двигайся вперёд" ] + }, + { + "name": "intent_imperative_turnaround", + "keyphrases" : ["повернись", "обернись", "оглянись", "развернись" ] + }, + { + "name": "intent_imperative_turnleft", + "keyphrases" : ["налево", "лево", "влево"] + }, + { + "name": "intent_imperative_turnright", + "keyphrases" : ["направо", "право", "вправо" ] + }, + { + "name": "intent_play_rollcube", + "keyphrases" : ["кати куб", "кати кубик", "покатай куб", "покатай кубик" ] + }, + { + "name": "intent_play_popawheelie", + "keyphrases" : ["встань на куб", "встань на кубик" ] + }, + { + "name": "intent_play_fistbump", + "keyphrases" : ["дай пять", "пятюня", "дай кулачок" ] + }, + { + "name": "intent_play_blackjack", + "keyphrases" : ["карты", "играть", "играем в карты", "давай играть", "давай сыграем" ] + }, + { + "name": "intent_imperative_affirmative", + "keyphrases" : ["да", "давай", "конечно" ] + }, + { + "name": "intent_imperative_negative", + "keyphrases" : ["нет" ] + }, + { + "name": "intent_photo_take_extend", + "keyphrases" : ["фото", "селфи", "сделай фото", "сфотографируй" ] + }, + { + "name": "intent_imperative_praise", + "keyphrases" : ["красавчик", "молодец", "хороший робот" ] + }, + { + "name": "intent_imperative_abuse", + "keyphrases" : ["это плохо", "отстой", "плохой робот" ] + }, + { + "name": "intent_imperative_apologize", + "keyphrases" : ["извини", "прошу прощения", "мне жаль" ] + }, + { + "name": "intent_imperative_backup", + "keyphrases" : ["назад", "двигайся назад", "едь назад" ] + }, + { + "name": "intent_imperative_volumedown", + "keyphrases" : ["убавь звук", "убавь громкость", "тише громкость" ] + }, + { + "name": "intent_imperative_volumeup", + "keyphrases" : ["прибавь звук", "прибавь громкось", "громче" ] + }, + { + "name": "intent_imperative_lookatme", + "keyphrases" : ["смотри на меня", "посмотри на меня", "взгляни на меня" ] + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases" : ["громкость", "уровень громкости" ] + }, + { + "name": "intent_imperative_shutup", + "keyphrases" : ["заткнись" ] + }, + { + "name": "intent_greeting_hello", + "keyphrases" : ["привет", "здравствуй", "рад тебя видеть" ] + }, + { + "name": "intent_imperative_come", + "keyphrases" : ["ко мне", "иди ко мне", "иди сюда" ] + }, + { + "name": "intent_imperative_love", + "keyphrases" : ["люблю тебя" ] + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases" : ["вопрос", "у меня есть вопрос" ] + }, + { + "name": "intent_clock_checktimer", + "keyphrases" : ["проверь таймер" ] + }, + { + "name": "intent_global_stop_extend", + "keyphrases" : ["останови таймер", "отмени таймер", "выключи таймер" ] + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases" : ["таймер", "поставь таймер", "установи таймер" ] + }, + { + "name": "intent_clock_time", + "keyphrases" : ["время", "который час", "сколько время", "который сейчас час" ] + }, + { + "name": "intent_imperative_quiet", + "keyphrases" : ["тихо", "прекрати", "будь тише" ] + }, + { + "name": "intent_imperative_dance", + "keyphrases" : ["танец", "танцуй", "давай танцевать" ] + }, + { + "name": "intent_play_pickupcube", + "keyphrases" : ["возьми кубик", "подбери кубик", "поднеми кубик" ] + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases" : ["дай кубик", "принеси мне", "неси мне", "принеси свой кубик" ] + }, + { + "name": "intent_imperative_findcube", + "keyphrases" : ["если кубик", "найди кубик", "ищи кубик", "где твой кубик", "найди свой кубик" ] + }, + { + "name": "intent_play_anytrick", + "keyphrases" : ["трюк", "сделай трюк" ] + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases" : ["запиши" ] + }, + { + "name": "intent_message_playmessage_extend", + "keyphrases" : ["воспроизведи сообщение" ] + }, + { + "name": "intent_blackjack_hit", + "keyphrases" : ["ещё", "ещё карту"] + }, + { + "name": "intent_blackjack_stand", + "keyphrases" : ["хватит" ] + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["расстояние", "расстоянии", "состояние" ] + } +] diff --git a/chipper/intent-data/tr-TR.json b/chipper/intent-data/tr-TR.json new file mode 100644 index 00000000..1cf0af7d --- /dev/null +++ b/chipper/intent-data/tr-TR.json @@ -0,0 +1,218 @@ +[ + { + "name": "intent_names_username_extend", + "keyphrases": ["adım", "yerliyim", "isimler", "adımın", "benim adım"] + }, + { + "name": "intent_weather_extend", + "keyphrases": ["hava durumu", "ister", "öteki", "su", "başka", "hava tahmini", "yarın hava", "hava nasıl"] + }, + { + "name": "intent_names_ask", + "keyphrases": ["benim adım", "kimim", "ben kimim"] + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases": ["göz rengi", "renk", "onu çağırıyorum", "onu takip ediyorum", "irenk", "ekse", "eriye", "ikan", "agrikola", "değiştir", "oraküler", "oracle", "göz rengini şuna ayarla"] + }, + { + "name": "intent_character_age", + "keyphrases": ["daha yaşlı", "kaç yaşında", "sen kaç yaşındasın", "yaşlı mısın", "senin yaşın kaç"] + }, + { + "name": "intent_explore_start", + "keyphrases": ["başla", "keşfet", "akıyor", "terzilik", "kodlama", "keşif", "örnek almak", "keşfetmeye başla"] + }, + { + "name": "intent_system_charger", + "keyphrases": ["şarj", "ev", "eve git", "kilise", "şarj cihazını bul", "şarj aleti"] + }, + { + "name": "intent_system_sleep", + "keyphrases": ["kaç", "uyu", "koyun", "uyumaya git"] + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases": ["sabah", "yas", "ayışığı", "sıkıldım", "öğleden sonra", "öğle sonrası", "kimi takip ettiğim", "günaydın"] + }, + { + "name": "intent_greeting_goodnight", + "keyphrases": ["gece", "güç", "iyi geceler"] + }, + { + "name": "intent_greeting_goodbye", + "keyphrases": ["güle güle", "iyi yolculuklar", "iyi alışverişler", "hoşça kal"] + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases": ["havai fişek", "yeni yıl", "mutlu yıllar", "mutlu ol", "olmuş", "şimdi seni tanıyorum", "yıl yok", "ani", "asla tanımadım", "hobhouse", "bennie"] + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases": ["o tutar", "noel", "işte bu", "tatil"] + }, + { + "name": "intent_amazon_signin", + "keyphrases": ["zekada", "elekta'da güzel", "aleks'te", "aleksi anlama", "elekt'te", "alekse", "angelica'da", "alexa'ya kaydol", "alexa'ya giriş yap"] + }, + { + "name": "intent_amazon_signin", + "keyphrases": ["çıkışta", "elea'yı şimdi biliyorum", "alexa'dan çık", "aleden çık"] + }, + { + "name": "intent_imperative_forward", + "keyphrases": ["ileri", "koru", "kelime", "ileri hareket et", "ileriye"] + }, + { + "name": "intent_imperative_turnaround", + "keyphrases": ["etrafında", "yüz seksen", "bir yediği", "etrafını dön"] + }, + { + "name": "intent_imperative_turnleft", + "keyphrases": ["sol dön", "sol taraf", "soldan git", "sol", "ernest"] + }, + { + "name": "intent_imperative_turnright", + "keyphrases": ["sağ dön", "sağa git", "sağ taraf", "ernie", "kredi", "sağdan"] + }, + { + "name": "intent_play_rollcube", + "keyphrases": ["küpü yuvarla", "küpünü yuvarla", "hepsini yuvarla", "insanı yuvarla", "yorke", "başını tut", "küpünü yuvarla"] + }, + { + "name": "intent_play_popawheelie", + "keyphrases": ["tekerlek üstünde dur", "polwhele", "olwen", "i wieland", "tekerlek yap", "kapı taşı", "tibetli", "powell", "welst", "bir tekerlek", "willie", "gerçekten", "o' billy", "tekerlek üstünde durma", "tekerlek standı yap"] + }, + { + "name": "intent_play_fistbump", + "keyphrases": ["bu pompası", "bu pompa", "yumruk", "yumrukla", "yumruksa", "bu kitap", "pisto", "pompayla", "fison", "ilk", "beşinci", "on beşte", "bump var", "bilgelik", "bu bu", "yumruk bombası", "yumruk topu", "bu top", "sistem", "yumruklama"] + }, + { + "name": "intent_play_blackjack", + "keyphrases": ["siyah", "kartlar", "oyun", "blackjack oyna"] + }, + { + "name": "intent_imperative_affirmative", + "keyphrases": ["evet", "doğru", "tabii"] + }, + { + "name": "intent_imperative_negative", + "keyphrases": ["hayır", "yapma"] + }, + { + "name": "intent_photo_take_extend", + "keyphrases": ["fotoğraf", "foto", "selby", "yakala", "resim", "bana bir fotoğraf çek"] + }, + { + "name": "intent_imperative_praise", + "keyphrases": ["iyi", "harika", "ayrıca", "bazıları", "onlardan", "savaş", "t rob", "ro", "şaşırtıcı", "woodcourt", "iyi robot"] + }, + { + "name": "intent_imperative_abuse", + "keyphrases": ["kötü", "o ro", "ad ro", "kök", "nefret", "berbat", "kötü robot"] + }, + { + "name": "intent_imperative_apologize", + "keyphrases": ["üzgünüm", "özür dilerim", "özür dile", "tory", "boşver", "aklımdan çıkmış", "ben özür dilerim"] + }, + { + "name": "intent_imperative_backup", + "keyphrases": ["geri", "geri çekil", "geriye"] + }, + { + "name": "intent_imperative_volumedown", + "keyphrases": ["sesini kıs", "ses kıs", "sesi kıs", "sesi kısın", "daha sessiz", "sesi kıs"] + }, + { + "name": "intent_imperative_volumeup", + "keyphrases": ["sesini aç", "ses aç", "sesi aç", "sesi yükselt", "daha yüksek"] + }, + { + "name": "intent_imperative_lookatme", + "keyphrases": ["bak", "bana bak"] + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases": ["sesini", "ses", "ses seviyesi"] + }, + { + "name": "intent_imperative_shutup", + "keyphrases": ["sus"] + }, + { + "name": "intent_greeting_hello", + "keyphrases": ["merhaba", "varsın", "bizim", "yüksek", "aşağı", "küçük", "takip", "senin için", "uzak sen", "sen hakkında nasıl", "nasılsın", "düşük", "tuvalet"] + }, + { + "name": "intent_imperative_come", + "keyphrases": ["gel", "bana", "buraya gel"] + }, + { + "name": "intent_imperative_love", + "keyphrases": ["sevgi", "güvercin", "seni seviyorum"] + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases": ["soru", "weston", "bir sorum var"] + }, + { + "name": "intent_clock_checktimer", + "keyphrases": ["zamanlayıcıyı kontrol et", "zamanlayıcıyı kontrol et", "zamanlayıcıyı kontrol et", "zamanı kontrol et", "zamanı kontrol et", "zamanı ondan kontrol et", "zamanlayıcıyı kontrol ettim", "zamanı ondan kontrol ettim", "zamanı ondan kontrol ettim"] + }, + { + "name": "intent_global_stop_extend", + "keyphrases": ["zamanlayıcıyı durdur", "zamanlayıcı durdur", "iptal et", "zamanlayıcıyı iptal et", "saati durdur", "dur be", "durdu t", "durdu be", "durdu", "durdur"] + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases": ["zamanlayıcı", "için zaman", "için zamanı", "ya da zaman", "zamanın", "bir zamanlayıcı ayarla"] + }, + { + "name": "intent_clock_time", + "keyphrases": ["saat kaç", "zaman", "hangi saat", "saat", "saat kaç"] + }, + { + "name": "intent_imperative_quiet", + "keyphrases": ["sessiz", "dur", "sessiz ol"] + }, + { + "name": "intent_imperative_dance", + "keyphrases": ["dans", "dans et", "dans et", "ritme dans et", "ritim", "müziğe dans et"] + }, + { + "name": "intent_play_pickupcube", + "keyphrases": ["kaldır", "küpü kaldır"] + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases": ["küpünü getir", "küpü getir", "küpü getir", "bana getir", "bana küpünü getir"] + }, + { + "name": "intent_imperative_findcube", + "keyphrases": ["küpün", "küp", "küpünü bul"] + }, + { + "name": "intent_play_anytrick", + "keyphrases": ["numara", "havalı bir şey", "havalı bir şey", "bir numara yap"] + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases": ["kaydet"] + }, + { + "name": "intent_message_playmessage_extend", + "keyphrases": ["mesajı oynat", "yöntemi oynat", "bir mesaj oynat", "bir yöntem oynat"] + }, + { + "name": "intent_blackjack_hit", + "keyphrases": ["vur"] + }, + { + "name": "intent_blackjack_stand", + "keyphrases": ["dur", "stan"] + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["uzak tut", "uzak dur", "uzak tutma oyunu oyna"] + } +] \ No newline at end of file diff --git a/chipper/intent-data/uk-UA.json b/chipper/intent-data/uk-UA.json new file mode 100644 index 00000000..5709ee64 --- /dev/null +++ b/chipper/intent-data/uk-UA.json @@ -0,0 +1,214 @@ +[ + { + "name": "intent_names_username_extend", + "keyphrases": ["імена", "назви імена"] + }, + { + "name": "intent_weather_extend", + "keyphrases": ["погода", "погода на завтра", "яка погода"] + }, + { + "name": "intent_names_ask", + "keyphrases": ["як мене звати", "моє ім'я", "хто я"] + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases": ["око", "очі", "зміни колір очей", "поміняй колір очей"] + }, + { + "name": "intent_character_age", + "keyphrases": ["скільки тобі років"] + }, + { + "name": "intent_explore_start", + "keyphrases": ["почни досліджувати", "дивись навколо"] + }, + { + "name": "intent_system_charger", + "keyphrases": ["зарядка", "дім", "база", "знайди зарядку", "знайди базу", "іди на зарядку", "іди на базу", "додому", "іди додому"] + }, + { + "name": "intent_system_sleep", + "keyphrases": ["спати", "іди спати"] + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases": ["добрий ранок", "ранок", "доброго ранку", "раночку"] + }, + { + "name": "intent_greeting_goodnight", + "keyphrases": ["спокійної ночі", "час спати", "добраніч", "на добраніч"] + }, + { + "name": "intent_greeting_goodbye", + "keyphrases": ["бувай", "прощавай", "я пішов", "я пішла", "папа"] + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases": ["феєрверк", "новий рік", "салют", "з новим роком"] + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases": ["свято", "зі святом", "сьогодні свято"] + }, + { + "name": "intent_amazon_signin", + "keyphrases": ["алекса"] + }, + { + "name": "intent_imperative_forward", + "keyphrases": ["вперед", "їдь вперед"] + }, + { + "name": "intent_imperative_turnaround", + "keyphrases": ["повернись", "обернись", "оглянься", "розвернись", "покрутись"] + }, + { + "name": "intent_imperative_turnleft", + "keyphrases": ["ліворуч", "ліво", "вліво"] + }, + { + "name": "intent_imperative_turnright", + "keyphrases": ["праворуч", "право", "вправо"] + }, + { + "name": "intent_play_rollcube", + "keyphrases": ["коти куб", "коти кубик", "поверни куб", "поверни кубик"] + }, + { + "name": "intent_play_popawheelie", + "keyphrases": ["стань на куб", "стань на кубик", "трюк", "покажи трюк"] + }, + { + "name": "intent_play_fistbump", + "keyphrases": ["дай п'ять", "п'ятюня", "дай кулачок", "давай п'ять", "давай п'ятюню"] + }, + { + "name": "intent_play_blackjack", + "keyphrases": ["карти", "грати", "граємо в карти", "давай грати", "давай зіграємо"] + }, + { + "name": "intent_imperative_affirmative", + "keyphrases": ["так", "давай", "звичайно"] + }, + { + "name": "intent_imperative_negative", + "keyphrases": ["ні"] + }, + { + "name": "intent_photo_take_extend", + "keyphrases": ["фото", "селфі", "зроби фото", "сфотографуй"] + }, + { + "name": "intent_imperative_praise", + "keyphrases": ["красень", "молодець", "гарний робот", "розумний робот"] + }, + { + "name": "intent_imperative_abuse", + "keyphrases": ["це погано", "поганий робот"] + }, + { + "name": "intent_imperative_apologize", + "keyphrases": ["вибач", "прошу пробачення", "мені шкода"] + }, + { + "name": "intent_imperative_backup", + "keyphrases": ["назад", "рухайся назад", "їдь назад"] + }, + { + "name": "intent_imperative_volumedown", + "keyphrases": ["зменш звук", "зменш гучність", "тише гучність", "тихіше"] + }, + { + "name": "intent_imperative_volumeup", + "keyphrases": ["додай звук", "додай гучність", "гучніше"] + }, + { + "name": "intent_imperative_lookatme", + "keyphrases": ["дивись на мене", "подивись на мене", "глянь на мене"] + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases": ["гучність", "рівень гучності"] + }, + { + "name": "intent_imperative_shutup", + "keyphrases": ["заткнися", "замовкни"] + }, + { + "name": "intent_greeting_hello", + "keyphrases": ["привіт", "радий тебе бачити"] + }, + { + "name": "intent_imperative_come", + "keyphrases": ["до мене", "іди до мене", "іди сюди", "їдь до мене", "їдь сюди"] + }, + { + "name": "intent_imperative_love", + "keyphrases": ["люблю тебе", "я тебе люблю", "ти такий милий", "ти смішний"] + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases": ["питання", "у мене є питання", "запитання"] + }, + { + "name": "intent_clock_checktimer", + "keyphrases": ["перевір таймер"] + }, + { + "name": "intent_global_stop_extend", + "keyphrases": ["зупини таймер", "скасуй таймер", "вимкни таймер"] + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases": ["таймер", "постав таймер", "встанови таймер"] + }, + { + "name": "intent_clock_time", + "keyphrases": ["час", "котра година", "скільки часу", "котра зараз година", "яка година", "яка зараз година"] + }, + { + "name": "intent_imperative_quiet", + "keyphrases": ["тихо", "припини", "будь тихіше", "будь тихо"] + }, + { + "name": "intent_imperative_dance", + "keyphrases": ["танець", "танцюй", "давай танцювати"] + }, + { + "name": "intent_play_pickupcube", + "keyphrases": ["візьми кубик", "підбери кубик", "підніми кубик"] + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases": ["дай кубик", "принеси мені", "неси мені", "принеси свій кубик"] + }, + { + "name": "intent_imperative_findcube", + "keyphrases": ["знайди кубик", "знайди свій кубик", "шукай кубик", "де твій кубик"] + }, + { + "name": "intent_play_anytrick", + "keyphrases": ["трюк", "зроби трюк"] + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases": ["запиши"] + }, + { + "name": "intent_message_playmessage_extend", + "keyphrases": ["відтвори повідомлення", "повтори за мною"] + }, + { + "name": "intent_blackjack_hit", + "keyphrases": ["ще", "ще карту"] + }, + { + "name": "intent_blackjack_stand", + "keyphrases": ["досить"] + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["відстань", "відвали", "йди геть", "іди звідси", "котись звідси"] + } +] \ No newline at end of file diff --git a/chipper/intent-data/vi-VN.json b/chipper/intent-data/vi-VN.json new file mode 100755 index 00000000..622afc30 --- /dev/null +++ b/chipper/intent-data/vi-VN.json @@ -0,0 +1,218 @@ +[ + { + "name": "intent_names_username_extend", + "keyphrases": ["Tên"] + }, + { + "name": "intent_weather_extend", + "keyphrases": ["Thời tiết", "Dự báo thời tiết", "Thời tiết ngày mai", "Thời tiết hôm nay"] + }, + { + "name": "intent_names_ask", + "keyphrases": ["Tên của tôi", "Tôi là", "Tôi là ai"] + }, + { + "name": "intent_imperative_eyecolor", + "keyphrases": ["Màu mắt", "Màu sắc"] + }, + { + "name": "intent_character_age", + "keyphrases": ["Bao nhiêu tuổi", "Bạn bao nhiêu tuổi"] + }, + { + "name": "intent_explore_start", + "keyphrases": ["Đi dạo", "Tự do"] + }, + { + "name": "intent_system_charger", + "keyphrases": ["Về nhà", "Sạc điện","Võ Duy Linh"] + }, + { + "name": "intent_system_sleep", + "keyphrases": ["Ngủ", "Đi ngủ", "Ngủ đi"] + }, + { + "name": "intent_greeting_goodmorning", + "keyphrases": ["Chào buổi sáng", "Chào buổi trưa", "Chào buổi chiều", "Xin chào"] + }, + { + "name": "intent_greeting_goodnight", + "keyphrases": ["Chúc ngủ ngon"] + }, + { + "name": "intent_greeting_goodbye", + "keyphrases": ["Tạm biệt", "Gặp lại sau"] + }, + { + "name": "intent_seasonal_happynewyear", + "keyphrases": ["Chúc mừng năm mới", "Pháo hoa", "Ăn mừng", "Võ Duy Linh"] + }, + { + "name": "intent_seasonal_happyholidays", + "keyphrases": ["Vui vẻ", "Ngày lễ", "Kỳ nghỉ lễ"] + }, + { + "name": "intent_amazon_signin", + "keyphrases": ["đăng nhập trợ lý ảo", "đăng ký trợ lý ảo"] + }, + { + "name": "intent_amazon_signout", + "keyphrases": ["đăng xuất trợ lý ảo", "đăng xuất trợ lý ảo"] + }, + { + "name": "intent_imperative_forward", + "keyphrases": ["Tiến lên", "Đi tới"] + }, + { + "name": "intent_imperative_turnaround", + "keyphrases": ["Quay lại", "Quay đầu", "Quay người"] + }, + { + "name": "intent_imperative_turnleft", + "keyphrases": ["Rẽ trái", "Quay trái"] + }, + { + "name": "intent_imperative_turnright", + "keyphrases": ["Rẽ phải", "Quay phải"] + }, + { + "name": "intent_play_rollcube", + "keyphrases": ["Chơi khối vuông"] + }, + { + "name": "intent_play_popawheelie", + "keyphrases": ["Giơ tay"] + }, + { + "name": "intent_play_fistbump", + "keyphrases": ["Cụng tay", "Đập tay"] + }, + { + "name": "intent_play_blackjack", + "keyphrases": ["Chơi bài", "Đánh bài"] + }, + { + "name": "intent_imperative_affirmative", + "keyphrases": ["Đúng", "Tất nhiên", "Chính xác", "Phải"] + }, + { + "name": "intent_imperative_negative", + "keyphrases": ["Không", "Sai"] + }, + { + "name": "intent_photo_take_extend", + "keyphrases": ["Chụp ảnh"] + }, + { + "name": "intent_imperative_praise", + "keyphrases": ["Tuyệt vời", "Thông minh"] + }, + { + "name": "intent_imperative_abuse", + "keyphrases": ["Ngốc", "Ngu ngốc"] + }, + { + "name": "intent_imperative_apologize", + "keyphrases": ["Xin lỗi", "Lỗi của tôi"] + }, + { + "name": "intent_imperative_backup", + "keyphrases": ["Lại đây", "Quay lại", "võ duy linh"] + }, + { + "name": "intent_imperative_volumedown", + "keyphrases": ["Giảm âm lượng", "Nhỏ tiếng"] + }, + { + "name": "intent_imperative_volumeup", + "keyphrases": ["Tăng âm lượng", "To tiếng"] + }, + { + "name": "intent_imperative_lookatme", + "keyphrases": ["Nhìn tôi", "Nhìn vào tôi"] + }, + { + "name": "intent_imperative_volumelevel_extend", + "keyphrases": ["Âm lượng"] + }, + { + "name": "intent_imperative_shutup", + "keyphrases": ["Im miệng"] + }, + { + "name": "intent_greeting_hello", + "keyphrases": ["Xin chào", "Chào bạn"] + }, + { + "name": "intent_imperative_come", + "keyphrases": ["Lại đây"] + }, + { + "name": "intent_imperative_love", + "keyphrases": ["Yêu thích", "Yêu"] + }, + { + "name": "intent_knowledge_promptquestion", + "keyphrases": ["Câu hỏi", "Trả lời"] + }, + { + "name": "intent_clock_checktimer", + "keyphrases": ["Kiểm tra báo thức", "Xem báo thức"] + }, + { + "name": "intent_global_stop_extend", + "keyphrases": ["Hủy báo thức", "Tắt báo thức"] + }, + { + "name": "intent_clock_settimer_extend", + "keyphrases": ["Đặt báo thức", "Cài báo thức"] + }, + { + "name": "intent_clock_time", + "keyphrases": ["Thời gian", "Mấy giờ rồi"] + }, + { + "name": "intent_imperative_quiet", + "keyphrases": ["Yên lặng", "Nghỉ ngơi"] + }, + { + "name": "intent_imperative_dance", + "keyphrases": ["Nhảy múa", "Nhảy đi"] + }, + { + "name": "intent_play_pickupcube", + "keyphrases": ["Nhặt khối vuông", "Đưa khối vuông cho tôi"] + }, + { + "name": "intent_imperative_fetchcube", + "keyphrases": ["Lấy khối vuông"] + }, + { + "name": "intent_imperative_findcube", + "keyphrases": ["Tìm khối vuông"] + }, + { + "name": "intent_play_anytrick", + "keyphrases": ["Kỹ năng"] + }, + { + "name": "intent_message_recordmessage_extend", + "keyphrases": ["Ghi lại"] + }, + { + "name": "intent_message_playmessage_extend", + "keyphrases": ["Phát tin nhắn"] + }, + { + "name": "intent_blackjack_hit", + "keyphrases": ["Đánh"] + }, + { + "name": "intent_blackjack_stand", + "keyphrases": ["Đứng"] + }, + { + "name": "intent_play_keepaway", + "keyphrases": ["Đi chỗ khác", "Xa ra"] + } +] diff --git a/chipper/macos/build.sh b/chipper/macos/build.sh deleted file mode 100755 index 33e0dc95..00000000 --- a/chipper/macos/build.sh +++ /dev/null @@ -1,115 +0,0 @@ -#!/bin/bash - -sudo -u $SUDO_USER brew install autoconf automake libtool create-dmg - -export ORIGDIR="$(pwd)" -export PODLIBS="${ORIGDIR}/libs" - -if [[ ! -d ${PODLIBS}/opus ]]; then - echo "opus directory doesn't exist. cloning and building" - mkdir -p ${PODLIBS} - cd ${PODLIBS} - git clone https://github.com/xiph/opus --depth=1 - cd opus - ./autogen.sh - ./configure --prefix="${PODLIBS}/opus" - make -j - make install - cd ${ORIGDIR} -fi - -if [[ ! -d ${PODLIBS}/vosk ]]; then - echo "getting vosk from alphacep releases page" - cd ${PODLIBS} - wget https://github.com/alphacep/vosk-api/releases/download/v0.3.42/vosk-osx-0.3.42.zip - unzip vosk-osx-0.3.42.zip - mv vosk-osx-0.3.42 vosk - cd ${ORIGDIR} -fi - -export GOOS=darwin -export GOARCH=arm64 - -export CGO_ENABLED=1 -export CGO_LDFLAGS="-L${PODLIBS}/opus/lib -L${PODLIBS}/vosk" -export CGO_CFLAGS="-I${PODLIBS}/opus/include -I${PODLIBS}/vosk" - -rm -rf target -cd .. - -go build \ --tags nolibopusfile \ --o macos/target/app/wire-pod.app/Contents/MacOS/wire-pod \ -./cmd/macos - -cd macos - -APPDIR=target/app/wire-pod.app/Contents -PLISTFILE=${APPDIR}/Info.plist -RESOURCES=${APPDIR}/Resources -FRAMEWORKS=${APPDIR}/Frameworks -CHIPPER=${APPDIR}/Frameworks/chipper -VECTOR_CLOUD=${APPDIR}/Frameworks/vector-cloud - -echo "" > $PLISTFILE -echo "" >> $PLISTFILE -echo "" >> $PLISTFILE -echo "" >> $PLISTFILE -echo " CFBundleGetInfoString" >> $PLISTFILE -echo " wire-pod" >> $PLISTFILE -echo " CFBundleExecutable" >> $PLISTFILE -echo " wire-pod" >> $PLISTFILE -echo " CFBundleIdentifier" >> $PLISTFILE -echo " io.github.kercre123" >> $PLISTFILE -echo " CFBundleName" >> $PLISTFILE -echo " wire-pod" >> $PLISTFILE -echo " CFBundleIconFile" >> $PLISTFILE -echo " icon.icns" >> $PLISTFILE -echo " CFBundleVersion" >> $PLISTFILE -echo " 0.0.1" >> $PLISTFILE -echo " CFBundleInfoDictionaryVersion" >> $PLISTFILE -echo " 6.0" >> $PLISTFILE -echo " CFBundlePackageType" >> $PLISTFILE -echo " APPL" >> $PLISTFILE -echo " NSHighResolutionCapable" >> $PLISTFILE -echo " NSSupportsAutomaticGraphicsSwitching" >> $PLISTFILE -echo " LSUIElement" >> $PLISTFILE -echo "" >> $PLISTFILE -echo "" >> $PLISTFILE - -mkdir -p ${RESOURCES} -mkdir -p ${FRAMEWORKS} -mkdir -p ${CHIPPER} -mkdir -p ${VECTOR_CLOUD}/build - -cp -r icons/ ${RESOURCES} -cp ${PODLIBS}/opus/lib/libopus.0.dylib ${FRAMEWORKS} -cp ${PODLIBS}/vosk/libvosk.dylib ${FRAMEWORKS} -cp ../weather-map.json ${CHIPPER} -cp -r ../intent-data ${CHIPPER} -cp -r ../webroot ${CHIPPER} -cp -r ../epod ${CHIPPER} -cp ../../vector-cloud/build/vic-cloud ${VECTOR_CLOUD}/build/ -cp ../../vector-cloud/pod-bot-install.sh ${VECTOR_CLOUD} - -sudo install_name_tool \ --change ${PODLIBS}/opus/lib/libopus.0.dylib \ -@executable_path/../Frameworks/libopus.0.dylib \ -${APPDIR}/MacOS/wire-pod - -sudo install_name_tool \ --change libvosk.dylib \ -@executable_path/../Frameworks/libvosk.dylib \ -${APPDIR}/MacOS/wire-pod - -mkdir target/installer -sudo create-dmg \ ---volname "wire-pod Installer" \ ---window-size 800 450 \ ---icon-size 100 \ ---icon "wire-pod.app" 200 200 \ ---hide-extension "wire-pod.app" \ ---app-drop-link 600 200 \ ---hdiutil-quiet \ -target/installer/wire-pod-${GOOS}-${GOARCH}.dmg \ -target/app/ \ No newline at end of file diff --git a/chipper/macos/icons/icon.icns b/chipper/macos/icons/icon.icns deleted file mode 100644 index cf8a06d1..00000000 Binary files a/chipper/macos/icons/icon.icns and /dev/null differ diff --git a/chipper/macos/icons/start-up-24x24.ico b/chipper/macos/icons/start-up-24x24.ico deleted file mode 100644 index 61366797..00000000 Binary files a/chipper/macos/icons/start-up-24x24.ico and /dev/null differ diff --git a/chipper/macos/icons/start-up-full.png b/chipper/macos/icons/start-up-full.png deleted file mode 100644 index 9bf0c922..00000000 Binary files a/chipper/macos/icons/start-up-full.png and /dev/null differ diff --git a/chipper/pkg/initwirepod/startserver.go b/chipper/pkg/initwirepod/startserver.go index 0478d0a5..a9226f04 100644 --- a/chipper/pkg/initwirepod/startserver.go +++ b/chipper/pkg/initwirepod/startserver.go @@ -13,6 +13,7 @@ import ( "github.com/digital-dream-labs/api/go/tokenpb" "github.com/digital-dream-labs/hugh/log" "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/mdnshandler" chipperserver "github.com/kercre123/wire-pod/chipper/pkg/servers/chipper" jdocsserver "github.com/kercre123/wire-pod/chipper/pkg/servers/jdocs" tokenserver "github.com/kercre123/wire-pod/chipper/pkg/servers/token" @@ -27,6 +28,8 @@ import ( grpcserver "github.com/digital-dream-labs/hugh/grpc/server" ) +var PostingmDNS bool + var serverOne cmux.CMux var serverTwo cmux.CMux var listenerOne net.Listener @@ -94,7 +97,7 @@ func BeginWirepodSpecific(sttInitFunc func() error, sttHandlerFunc interface{}, } func StartFromProgramInit(sttInitFunc func() error, sttHandlerFunc interface{}, voiceProcessorName string) { - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { os.Setenv("DEBUG_LOGGING", "true") os.Setenv("STT_SERVICE", "vosk") } @@ -108,48 +111,39 @@ func StartFromProgramInit(sttInitFunc func() error, sttHandlerFunc interface{}, logger.Println("\033[33m\033[1mWire-pod is not setup. Use the webserver at port 8080 to set up wire-pod.\033[0m") vars.APIConfig.PastInitialSetup = false } else { - //go PostmDNS() go StartChipper() } // main thread is configuration ws wpweb.StartWebServer() } -// func PostmDNS() { -// logger.Println("Registering escapepod.local on network (every minute)") -// mdnsport, _ := freeport.GetFreePort() -// for { -// ipAddr := botsetup.GetOutboundIP().String() -// server, _ := zeroconf.RegisterProxy("escapepod", "_app-proto._tcp", "local.", mdnsport, "escapepod", []string{ipAddr}, []string{"txtv=0", "lo=1", "la=2"}, nil) -// time.Sleep(time.Second * 60) -// server.Shutdown() -// server = nil -// } -// } - -func CheckHostname() { - hostname, _ := os.Hostname() - if hostname != "escapepod" && vars.APIConfig.Server.EPConfig { - logger.Println("\033[31m\033[1mWARNING: You have chosen the Escape Pod config, but the system hostname is not 'escapepod'. This means your robot will not be able to communicate with wire-pod unless you have a custom network configuration.") - logger.Println("Actual reported hostname: " + hostname + "\033[0m") +func RestartServer() { + if chipperServing { + serverOne.Close() + serverTwo.Close() + listenerOne.Close() + listenerTwo.Close() } + go StartChipper() } -func RestartServer() { +func StopServer() { if chipperServing { serverOne.Close() serverTwo.Close() listenerOne.Close() listenerTwo.Close() } - go StartChipper() } func StartChipper() { // load certs + if vars.APIConfig.Server.EPConfig && runtime.GOOS != "android" { + go mdnshandler.PostmDNS() + } var certPub []byte var certPriv []byte - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { if vars.APIConfig.Server.EPConfig { certPub, _ = os.ReadFile(vars.AndroidPath + "/static/epod/ep.crt") certPriv, _ = os.ReadFile(vars.AndroidPath + "/static/epod/ep.key") @@ -184,7 +178,7 @@ func StartChipper() { os.Exit(1) } if runtime.GOOS == "android" && vars.APIConfig.Server.Port == "443" { - logger.Println("not starting chipper at port 443 because android...") + logger.Println("not starting chipper at port 443 because android") } else { logger.Println("Starting chipper server at port " + vars.APIConfig.Server.Port) listenerOne, err = tls.Listen("tcp", ":"+vars.APIConfig.Server.Port, &tls.Config{ diff --git a/chipper/pkg/logger/logger.go b/chipper/pkg/logger/logger.go index 149bffc9..2a589258 100644 --- a/chipper/pkg/logger/logger.go +++ b/chipper/pkg/logger/logger.go @@ -36,7 +36,7 @@ func Println(a ...any) { func LogUI(a ...any) { LogArray = append(LogArray, time.Now().Format("2006.01.02 15:04:05")+": "+fmt.Sprint(a...)+"\n") - if len(LogArray) >= 30 { + if len(LogArray) >= 50 { LogArray = LogArray[1:] } LogList = "" @@ -47,7 +47,7 @@ func LogUI(a ...any) { func LogTray(a ...any) { LogTrayArray = append(LogTrayArray, time.Now().Format("2006.01.02 15:04:05")+": "+fmt.Sprint(a...)+"\n") - if len(LogTrayArray) >= 30 { + if len(LogTrayArray) >= 200 { LogTrayArray = LogTrayArray[1:] } LogTrayList = "" diff --git a/chipper/pkg/mdnshandler/mdns.go b/chipper/pkg/mdnshandler/mdns.go new file mode 100644 index 00000000..6f703e0e --- /dev/null +++ b/chipper/pkg/mdnshandler/mdns.go @@ -0,0 +1,90 @@ +package mdnshandler + +import ( + "context" + "fmt" + "os" + "strings" + "time" + + "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/vars" + "github.com/kercre123/zeroconf" +) + +// legacy ZeroConf code + +var PostingmDNS bool +var MDNSNow chan bool +var MDNSTimeBeforeNextRegister float32 + +func PostmDNSWhenNewVector() { + time.Sleep(time.Second * 5) + for { + resolver, _ := zeroconf.NewResolver(nil) + entries := make(chan *zeroconf.ServiceEntry) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*80) + err := resolver.Browse(ctx, "_ankivector._tcp", "local.", entries) + if err != nil { + fmt.Println(err) + cancel() + return + } + for entry := range entries { + if strings.Contains(entry.Service, "ankivector") { + logger.Println("Vector discovered on network, broadcasting mDNS") + cancel() + time.Sleep(time.Second) + PostmDNSNow() + return + } + } + cancel() + } + +} + +func PostmDNSNow() { + logger.Println("Broadcasting mDNS now (outside of timer loop)") + select { + case MDNSNow <- true: + default: + } +} + +func PostmDNS() { + if os.Getenv("DISABLE_MDNS") == "true" { + fmt.Println("mDNS is disabled") + return + } + if PostingmDNS { + return + } + go PostmDNSWhenNewVector() + MDNSNow = make(chan bool) + go func() { + for range MDNSNow { + MDNSTimeBeforeNextRegister = 30 + } + }() + PostingmDNS = true + logger.Println("Registering escapepod.local on network (loop)") + for { + ipAddr := vars.GetOutboundIP().String() + server, _ := zeroconf.RegisterProxy("escapepod", "_app-proto._tcp", "local.", 8084, "escapepod", []string{ipAddr}, []string{"txtv=0", "lo=1", "la=2"}, nil) + if os.Getenv("PRINT_MDNS") == "true" { + logger.Println("mDNS broadcasted") + } + for { + if MDNSTimeBeforeNextRegister >= 30 { + MDNSTimeBeforeNextRegister = 0 + break + } + MDNSTimeBeforeNextRegister = MDNSTimeBeforeNextRegister + (float32(1) / float32(4)) + time.Sleep(time.Second / 4) + } + server.Shutdown() + server = nil + time.Sleep(time.Second / 3) + } +} diff --git a/chipper/pkg/scripting/bcontrol.go b/chipper/pkg/scripting/bcontrol.go new file mode 100644 index 00000000..56d05e4c --- /dev/null +++ b/chipper/pkg/scripting/bcontrol.go @@ -0,0 +1,92 @@ +package scripting + +import ( + "context" + + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" + lua "github.com/yuin/gopher-lua" +) + +func SetBControlFunctions(L *lua.LState) { + start := make(chan bool) + stop := make(chan bool) + currentlyAssumed := false + L.SetGlobal("assumeBehaviorControl", L.NewFunction(func(*lua.LState) int { + robot := gRfLS(L) + priority := vectorpb.ControlRequest_OVERRIDE_BEHAVIORS + if priority != 0 && priority != 10 && priority != 20 && priority != 30 { + logger.Println("LUA: Behavior control priority was not valid. Valid choices are 10, 20, and 30. Assuming 10.") + } else { + priority = vectorpb.ControlRequest_Priority(L.ToInt(1)) + } + controlRequest := &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRequest{ + ControlRequest: &vectorpb.ControlRequest{ + Priority: priority, + }, + }, + } + go func() { + // * begin - modified from official vector-go-sdk + r, err := robot.Conn.BehaviorControl( + context.Background(), + ) + if err != nil { + logger.Println(err) + return + } + + if err := r.Send(controlRequest); err != nil { + logger.Println(err) + return + } + + for { + ctrlresp, err := r.Recv() + if err != nil { + logger.Println(err) + return + } + if ctrlresp.GetControlGrantedResponse() != nil { + start <- true + break + } + } + + for { + select { + case <-stop: + if err := r.Send( + &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRelease{ + ControlRelease: &vectorpb.ControlRelease{}, + }, + }, + ); err != nil { + logger.Println(err) + return + } + return + default: + continue + } + } + // * end - modified from official vector-go-sdk + }() + for range start { + break + } + currentlyAssumed = true + return 0 + })) + L.SetGlobal("releaseBehaviorControl", L.NewFunction(func(*lua.LState) int { + if currentlyAssumed { + stop <- true + currentlyAssumed = false + return 0 + } + return 1 + })) + +} diff --git a/chipper/pkg/scripting/scripting.go b/chipper/pkg/scripting/scripting.go new file mode 100644 index 00000000..5f934fa9 --- /dev/null +++ b/chipper/pkg/scripting/scripting.go @@ -0,0 +1,158 @@ +package scripting + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/fforchino/vector-go-sdk/pkg/vector" + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/vars" + lualibs "github.com/vadv/gopher-lua-libs" + lua "github.com/yuin/gopher-lua" +) + +/* + +assumeBehaviorControl(priority int) + - 10,20,30 (10 highest priority, overriding behaviors. 30 lowest) +releaseBehaviorControl() + + + +sayText(text string, goroutine bool) +playAnimation(animation string, goroutine bool) + +*/ + +type ExternalLuaRequest struct { + ESN string `json:"esn"` + Script string `json:"script"` +} + +type Bot struct { + ESN string + Robot *vector.Vector +} + +func sayText(L *lua.LState) int { + textToSay := L.ToString(1) + executeWithGoroutine(L, func(L *lua.LState) error { + _, err := gRfLS(L).Conn.SayText(L.Context(), &vectorpb.SayTextRequest{Text: textToSay, UseVectorVoice: true, DurationScalar: 1.0}) + return err + }) + return 0 +} + +func playAnimation(L *lua.LState) int { + animToPlay := L.ToString(1) + executeWithGoroutine(L, func(L *lua.LState) error { + _, err := gRfLS(L).Conn.PlayAnimation(L.Context(), &vectorpb.PlayAnimationRequest{Animation: &vectorpb.Animation{Name: animToPlay}, Loops: 1}) + return err + }) + return 0 +} + +// get robot from LState +func gRfLS(L *lua.LState) *vector.Vector { + ud := L.GetGlobal("bot").(*lua.LUserData) + bot := ud.Value.(*Bot) + return bot.Robot +} + +func MakeLuaState(esn string, validating bool) (*lua.LState, error) { + L := lua.NewState() + lualibs.Preload(L) + L.SetContext(context.Background()) + L.SetGlobal("sayText", L.NewFunction(sayText)) + L.SetGlobal("playAnimation", L.NewFunction(playAnimation)) + SetBControlFunctions(L) + ud := L.NewUserData() + if !validating { + rob, err := vars.GetRobot(esn) + if err != nil { + return nil, err + } + ctx, can := context.WithTimeout(context.Background(), time.Second*3) + defer can() + _, err = rob.Conn.BatteryState(ctx, &vectorpb.BatteryStateRequest{}) + if err != nil { + return nil, err + } + ud.Value = &Bot{ESN: esn, Robot: rob} + L.SetGlobal("bot", ud) + } + return L, nil +} + +func executeWithGoroutine(L *lua.LState, fn func(L *lua.LState) error) { + goroutine := L.ToBool(2) + if goroutine { + go func() { + err := fn(L) + if err != nil { + logger.Println("LUA: failure: " + err.Error()) + } + }() + } else { + err := fn(L) + if err != nil { + logger.Println("LUA: failure: " + err.Error()) + } + } +} + +func RunLuaScript(esn string, luaScript string) error { + L, err := MakeLuaState(esn, false) + if err != nil { + return err + } + defer L.Close() + + if err := L.DoString(luaScript); err != nil { + return err + } + L.DoString("releaseBehaviorControl()") + return nil +} + +func ValidateLuaScript(luaScript string) error { + L, _ := MakeLuaState("", true) + defer L.Close() + + err := L.DoString(fmt.Sprintf("return function() %s end", luaScript)) + if err != nil { + return err + } + return nil +} + +func ScriptingAPI(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api-lua/run_script": + fBody, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, "request body couldn't be read: "+err.Error(), http.StatusInternalServerError) + return + } + var scriptReq ExternalLuaRequest + err = json.Unmarshal(fBody, &scriptReq) + if err != nil { + http.Error(w, "request body couldn't be unmarshalled: "+err.Error(), http.StatusInternalServerError) + return + } + err = RunLuaScript(scriptReq.ESN, scriptReq.Script) + if err != nil { + logger.Println(err) + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } +} + +func RegisterScriptingAPI() { + http.HandleFunc("/api-lua/", ScriptingAPI) +} diff --git a/chipper/pkg/servers/chipper/connectioncheck.go b/chipper/pkg/servers/chipper/connectioncheck.go index 84e6e0eb..119b5b9e 100755 --- a/chipper/pkg/servers/chipper/connectioncheck.go +++ b/chipper/pkg/servers/chipper/connectioncheck.go @@ -38,7 +38,7 @@ receiveLoop: for { select { case <-ctx.Done(): - logger.Println("Connection check expiration. Frames Recieved: " + strconv.Itoa(int(frames))) + logger.Println("Connection check expiration. Frames Received: " + strconv.Itoa(int(frames))) toSend.Status = "Timeout" break receiveLoop default: @@ -46,7 +46,7 @@ receiveLoop: if suberr != nil || req == nil { err = suberr - logger.Println("Connection check unexpected error. Frames Recieved: " + strconv.Itoa(int(frames))) + logger.Println("Connection check unexpected error. Frames Received: " + strconv.Itoa(int(frames))) logger.Println(err) toSend.Status = "Error" diff --git a/chipper/pkg/servers/chipper/intent_graph.go b/chipper/pkg/servers/chipper/intent_graph.go index 175ff497..29c2cb5c 100755 --- a/chipper/pkg/servers/chipper/intent_graph.go +++ b/chipper/pkg/servers/chipper/intent_graph.go @@ -14,7 +14,7 @@ func (s *Server) StreamingIntentGraph(stream pb.ChipperGrpc_StreamingIntentGraph req, err := stream.Recv() if err != nil { - logger.Println("Intent graph error") + logger.Println("Intent graph stream error") logger.Println(err) return err @@ -32,7 +32,7 @@ func (s *Server) StreamingIntentGraph(stream pb.ChipperGrpc_StreamingIntentGraph // Mode: }, ); err != nil { - logger.Println("Intent graph error") + logger.Println("Intent graph processing error") logger.Println(err) return err } diff --git a/chipper/pkg/servers/jdocs/server.go b/chipper/pkg/servers/jdocs/server.go index e5bced54..9f984ef1 100755 --- a/chipper/pkg/servers/jdocs/server.go +++ b/chipper/pkg/servers/jdocs/server.go @@ -5,6 +5,7 @@ import ( "encoding/json" "os" "strings" + "path/filepath" "github.com/digital-dream-labs/api/go/jdocspb" "github.com/kercre123/wire-pod/chipper/pkg/logger" @@ -101,7 +102,7 @@ func (s *JdocServer) ReadDocs(ctx context.Context, req *jdocspb.ReadDocsReq) (*j for num, pair := range tokenserver.SessionWriteStoreNames { if strings.EqualFold(ipAddr, strings.Split(pair[0], ":")[0]) { sessionMatched = true - fullPath := vars.SDKIniPath + pair[1] + "-" + esn + ".cert" + fullPath := filepath.Join(vars.SDKIniPath, pair[1] + "-" + esn + ".cert") if _, err := os.Stat(vars.SDKIniPath); err != nil { logger.Println("Creating " + vars.SDKIniPath + " directory") os.Mkdir(vars.SDKIniPath, 0755) @@ -112,6 +113,7 @@ func (s *JdocServer) ReadDocs(ctx context.Context, req *jdocspb.ReadDocsReq) (*j // export to ./session-certs os.WriteFile(vars.SessionCertPath+"/"+esn, tokenserver.SessionWriteStoreCerts[num], 0755) WriteToIniPrimary(pair[1], esn, botGUID, ipAddr) + vars.AddToRInfo(esn, pair[1], ipAddr) tokenserver.RemoveFromSessionStore(num) logger.Println("Session certificate successfully output") break diff --git a/chipper/pkg/servers/token/token.go b/chipper/pkg/servers/token/token.go index daa039d2..36d6d13a 100755 --- a/chipper/pkg/servers/token/token.go +++ b/chipper/pkg/servers/token/token.go @@ -192,9 +192,9 @@ func CreateJWT(ctx context.Context, skipGuid bool, isPrimary bool) *tokenpb.Toke secondaryGUID := "" secondaryHash := "" - // figure out current time and the time in one day + // figure out current time and the time in one month currentTime := time.Now().Format(TimeFormat) - expiresAt := time.Now().Add(ExpirationTime).Format(TimeFormat) + expiresAt := time.Now().AddDate(0, 1, 0).Format(TimeFormat) logger.Println("Current time: " + currentTime) logger.Println("Token expires: " + expiresAt) diff --git a/chipper/pkg/vars/config.go b/chipper/pkg/vars/config.go index ab61e003..40b60e6f 100644 --- a/chipper/pkg/vars/config.go +++ b/chipper/pkg/vars/config.go @@ -21,13 +21,21 @@ type apiConfig struct { Unit string `json:"unit"` } `json:"weather"` Knowledge struct { - Enable bool `json:"enable"` - Provider string `json:"provider"` - Key string `json:"key"` - ID string `json:"id"` - Model string `json:"model"` - IntentGraph bool `json:"intentgraph"` - RobotName string `json:"robotName"` + Enable bool `json:"enable"` + Provider string `json:"provider"` + Key string `json:"key"` + ID string `json:"id"` + Model string `json:"model"` + IntentGraph bool `json:"intentgraph"` + RobotName string `json:"robotName"` + OpenAIPrompt string `json:"openai_prompt"` + OpenAIVoice string `json:"openai_voice"` + OpenAIVoiceWithEnglish bool `json:"openai_voice_with_english"` + SaveChat bool `json:"save_chat"` + CommandsEnable bool `json:"commands_enable"` + Endpoint string `json:"endpoint"` + TopP float32 `json:"top_p"` + Temperature float32 `json:"temp"` } `json:"knowledge"` STT struct { Service string `json:"provider"` @@ -115,6 +123,12 @@ func ReadConfig() { APIConfig.PastInitialSetup = true } } + + if APIConfig.Knowledge.Model == "meta-llama/Llama-2-70b-chat-hf" { + logger.Println("Setting Together model to Llama3") + APIConfig.Knowledge.Model = "meta-llama/Llama-3-70b-chat-hf" + } + writeBytes, _ := json.Marshal(APIConfig) os.WriteFile(ApiConfigPath, writeBytes, 0644) logger.Println("API config successfully read") diff --git a/chipper/pkg/vars/vars.go b/chipper/pkg/vars/vars.go index a58631c3..73dab193 100644 --- a/chipper/pkg/vars/vars.go +++ b/chipper/pkg/vars/vars.go @@ -1,17 +1,26 @@ package vars import ( + "crypto/x509" "encoding/json" + "encoding/pem" + "errors" "fmt" + "net" "os" "path/filepath" "runtime" "strconv" "strings" + "github.com/fforchino/vector-go-sdk/pkg/vector" "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/sashabaranov/go-openai" + "github.com/wlynxg/anet" ) +var CommitSHA string + // initialize variables so they don't have to be found during runtime var VarsInited bool @@ -19,6 +28,8 @@ var VarsInited bool // if compiled into an installation package. wire-pod will use os.UserConfigDir() var Packaged bool +var IsPackagedLinux bool + var AndroidPath string var ( @@ -32,6 +43,7 @@ var ( VoskModelPath string = "../vosk/models/" WhisperModelPath string = "../whisper.cpp/models/" SessionCertPath string = "./session-certs/" + VersionFile string = "./version" ) var ( @@ -48,20 +60,32 @@ var WebPort string = "8080" var SDKIniPath string var BotJdocs []botjdoc var BotInfo RobotInfoStore -var CustomIntents IntentsStruct +var CustomIntents []CustomIntent var CustomIntentsExist bool = false var DownloadedVoskModels []string var VoskGrammerEnable bool = false // here to prevent import cycle (localization restructure) var SttInitFunc func() error -var MatchListList [][]string -var IntentsList = []string{} + +var IntentList []JsonIntent + +//var MatchListList [][]string +// var IntentsList = []string{} var ChipperCert []byte var ChipperKey []byte var ChipperKeysLoaded bool +var RecurringInfo []RecurringInfoStore + +type RememberedChat struct { + ESN string `json:"esn"` + Chats []openai.ChatCompletionMessage `json:"chats"` +} + +var RememberedChats []RememberedChat + type RobotInfoStore struct { GlobalGUID string `json:"global_guid"` Robots []struct { @@ -73,12 +97,22 @@ type RobotInfoStore struct { } `json:"robots"` } +type RecurringInfoStore struct { + // Vector-R2D2 + ID string `json:"id"` + // 00e20145 + ESN string `json:"esn"` + // 192.168.1.150 + IP string `json:"ip"` +} + type JsonIntent struct { - Name string `json:"name"` - Keyphrases []string `json:"keyphrases"` + Name string `json:"name"` + Keyphrases []string `json:"keyphrases"` + RequireExactMatch bool `json:"requiresexact"` } -type IntentsStruct []struct { +type CustomIntent struct { Name string `json:"name"` Description string `json:"description"` Utterances []string `json:"utterances"` @@ -90,6 +124,7 @@ type IntentsStruct []struct { Exec string `json:"exec"` ExecArgs []string `json:"execargs"` IsSystemIntent bool `json:"issystem"` + LuaScript string `json:"luascript"` } type AJdoc struct { @@ -113,6 +148,7 @@ func join(p1, p2 string) string { } func Init() { + logger.Println("Commit SHA: " + CommitSHA) if VarsInited { logger.Println("Not initting vars again") return @@ -122,7 +158,7 @@ func Init() { if Packaged { logger.Println("This version of wire-pod is packaged. Set vars to include UserConfigDir...") var confDir string - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { confDir = AndroidPath } else { confDir, _ = os.UserConfigDir() @@ -143,6 +179,9 @@ func Init() { ServerConfigPath = join(podDir, "./certs/server_config.json") Certs = join(podDir, "./certs") SessionCertPath = join(podDir, SessionCertPath) + if runtime.GOOS == "android" { + VersionFile = AndroidPath + "/static/version" + } os.Mkdir(JdocsDir, 0777) os.Mkdir(SessionCertPath, 0777) os.Mkdir(Certs, 0777) @@ -168,7 +207,7 @@ func Init() { if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { dir, _ := os.UserHomeDir() SDKIniPath = dir + "/.anki_vector/" - } else if runtime.GOOS == "android" { + } else if runtime.GOOS == "android" || runtime.GOOS == "ios" { SDKIniPath = filepath.Join(AndroidPath, "/wire-pod/anki_vector") } else { if podPathSplit[len(podPathSplit)-1] != "chipper" || podPathSplit[len(podPathSplit)-2] != PodName { @@ -213,6 +252,8 @@ func Init() { } logger.Println("Loaded bot info file, known bots: " + fmt.Sprint(botList)) } + + ReadSessionCerts() LoadCustomIntents() VarsInited = true } @@ -240,36 +281,35 @@ func LoadCustomIntents() { } } -func LoadIntents() ([][]string, []string, error) { +func LoadIntents() ([]JsonIntent, error) { var path string if runtime.GOOS == "darwin" && Packaged { appPath, _ := os.Executable() path = filepath.Dir(appPath) + "/../Frameworks/chipper/" - } else if runtime.GOOS == "android" { + } else if runtime.GOOS == "android" || runtime.GOOS == "ios" { path = AndroidPath + "/static/" } else { path = "./" } jsonFile, err := os.ReadFile(path + "intent-data/" + APIConfig.STT.Language + ".json") - var matches [][]string - var intents []string - + // var matches [][]string + // var intents []string + var jsonIntents []JsonIntent if err == nil { - var jsonIntents []JsonIntent err = json.Unmarshal(jsonFile, &jsonIntents) - if err != nil { - logger.Println("Failed to load intents: " + err.Error()) - } - - for _, element := range jsonIntents { - //logger.Println("Loading intent " + strconv.Itoa(index) + " --> " + element.Name + "( " + strconv.Itoa(len(element.Keyphrases)) + " keyphrases )") - intents = append(intents, element.Name) - matches = append(matches, element.Keyphrases) - } - logger.Println("Loaded " + strconv.Itoa(len(jsonIntents)) + " intents and " + strconv.Itoa(len(matches)) + " matches (language: " + APIConfig.STT.Language + ")") + // if err != nil { + // logger.Println("Failed to load intents: " + err.Error()) + // } + + // for _, element := range jsonIntents { + // //logger.Println("Loading intent " + strconv.Itoa(index) + " --> " + element.Name + "( " + strconv.Itoa(len(element.Keyphrases)) + " keyphrases )") + // intents = append(intents, element.Name) + // matches = append(matches, element.Keyphrases) + // } + // logger.Println("Loaded " + strconv.Itoa(len(jsonIntents)) + " intents and " + strconv.Itoa(len(matches)) + " matches (language: " + APIConfig.STT.Language + ")") } - return matches, intents, err + return jsonIntents, err } func WriteJdocs() { @@ -324,3 +364,102 @@ func AddJdoc(thing string, name string, jdoc AJdoc) uint64 { WriteJdocs() return latestVersion } + +func ReadSessionCerts() { + logger.Println("Reading session certs for robot IDs") + var rinfo RecurringInfoStore + certDir, err := os.ReadDir(SessionCertPath) + if err != nil { + logger.Println(err) + return + } + for _, entry := range certDir { + if entry.Name() == "placeholder" { + continue + } + esn := entry.Name() + var ip string + certBytes, err := os.ReadFile(filepath.Join(SessionCertPath, entry.Name())) + if err != nil { + logger.Println(err) + return + } + pemBytes, _ := pem.Decode(certBytes) + cert, _ := x509.ParseCertificate(pemBytes.Bytes) + for _, robot := range BotInfo.Robots { + if esn == robot.Esn { + ip = robot.IPAddress + break + } + } + rinfo.ESN = esn + rinfo.ID = cert.Issuer.CommonName + rinfo.IP = ip + RecurringInfo = append(RecurringInfo, rinfo) + } +} + +func AddToRInfo(esn string, id string, ip string) { + // the only bot constant is ESN + for i := range RecurringInfo { + if RecurringInfo[i].ESN == esn { + RecurringInfo[i].ID = id + RecurringInfo[i].IP = ip + return + } + } + var rinfo RecurringInfoStore + rinfo.ESN = esn + rinfo.ID = id + rinfo.IP = ip + RecurringInfo = append(RecurringInfo, rinfo) +} + +func GetRobot(esn string) (*vector.Vector, error) { + var guid string + var target string + matched := false + for _, bot := range BotInfo.Robots { + if esn == bot.Esn { + guid = bot.GUID + target = bot.IPAddress + ":443" + matched = true + break + } + } + if !matched { + return nil, errors.New("robot not in botsdkinfo") + } + robot, err := vector.New(vector.WithSerialNo(esn), vector.WithToken(guid), vector.WithTarget(target)) + if err != nil { + return nil, err + } + return robot, nil +} + +func GetOutboundIP() net.IP { + if runtime.GOOS == "android" { + ifaces, _ := anet.Interfaces() + for _, iface := range ifaces { + if iface.Name == "wlan0" { + adrs, err := anet.InterfaceAddrsByInterface(&iface) + if err != nil { + logger.Println(err) + break + } + if len(adrs) > 0 { + localAddr := adrs[0].(*net.IPNet) + return localAddr.IP + } + } + } + } + conn, err := net.Dial("udp", OutboundIPTester) + if err != nil { + logger.Println("not connected to a network: ", err) + return net.IPv4(0, 0, 0, 0) + } + defer conn.Close() + localAddr := conn.LocalAddr().(*net.UDPAddr) + return localAddr.IP +} diff --git a/chipper/pkg/wirepod/config-ws/webserver.go b/chipper/pkg/wirepod/config-ws/webserver.go index 16352688..72a42a3d 100755 --- a/chipper/pkg/wirepod/config-ws/webserver.go +++ b/chipper/pkg/wirepod/config-ws/webserver.go @@ -3,14 +3,17 @@ package webserver import ( "encoding/json" "fmt" + "io" "net/http" + "net/url" "os" + "path" "path/filepath" "runtime" - "strconv" "strings" "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/scripting" "github.com/kercre123/wire-pod/chipper/pkg/vars" "github.com/kercre123/wire-pod/chipper/pkg/wirepod/localization" processreqs "github.com/kercre123/wire-pod/chipper/pkg/wirepod/preqs" @@ -22,344 +25,390 @@ var SttInitFunc func() error func apiHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Headers", "*") - switch { + + switch strings.TrimPrefix(r.URL.Path, "/api/") { + case "add_custom_intent": + handleAddCustomIntent(w, r) + case "edit_custom_intent": + handleEditCustomIntent(w, r) + case "get_custom_intents_json": + handleGetCustomIntentsJSON(w) + case "remove_custom_intent": + handleRemoveCustomIntent(w, r) + case "set_weather_api": + handleSetWeatherAPI(w, r) + case "get_weather_api": + handleGetWeatherAPI(w) + case "set_kg_api": + handleSetKGAPI(w, r) + case "get_kg_api": + handleGetKGAPI(w) + case "set_stt_info": + handleSetSTTInfo(w, r) + case "get_download_status": + handleGetDownloadStatus(w) + case "get_stt_info": + handleGetSTTInfo(w) + case "get_config": + handleGetConfig(w) + case "get_logs": + handleGetLogs(w) + case "get_debug_logs": + handleGetDebugLogs(w) + case "is_running": + handleIsRunning(w) + case "delete_chats": + handleDeleteChats(w) + case "get_ota": + handleGetOTA(w, r) + case "get_version_info": + handleGetVersionInfo(w) + case "generate_certs": + handleGenerateCerts(w) + case "is_api_v3": + fmt.Fprintf(w, "it is!") default: http.Error(w, "not found", http.StatusNotFound) + } +} + +func handleAddCustomIntent(w http.ResponseWriter, r *http.Request) { + var intent vars.CustomIntent + if err := json.NewDecoder(r.Body).Decode(&intent); err != nil { + http.Error(w, "invalid request body", http.StatusBadRequest) return - case r.URL.Path == "/api/add_custom_intent": - name := r.FormValue("name") - description := r.FormValue("description") - utterances := r.FormValue("utterances") - intent := r.FormValue("intent") - paramName := r.FormValue("paramname") - paramValue := r.FormValue("paramvalue") - exec := r.FormValue("exec") - execArgs := r.FormValue("execargs") - if name == "" || description == "" || utterances == "" || intent == "" { - fmt.Fprintf(w, "missing required field (name, description, utterances, and intent are required)") - return - } - vars.CustomIntentsExist = true - vars.CustomIntents = append(vars.CustomIntents, struct { - Name string `json:"name"` - Description string `json:"description"` - Utterances []string `json:"utterances"` - Intent string `json:"intent"` - Params struct { - ParamName string `json:"paramname"` - ParamValue string `json:"paramvalue"` - } `json:"params"` - Exec string `json:"exec"` - ExecArgs []string `json:"execargs"` - IsSystemIntent bool `json:"issystem"` - }{Name: name, Description: description, Utterances: strings.Split(utterances, ","), Intent: intent, Params: struct { - ParamName string `json:"paramname"` - ParamValue string `json:"paramvalue"` - }{ParamName: paramName, ParamValue: paramValue}, Exec: exec, ExecArgs: strings.Split(execArgs, ","), IsSystemIntent: false}) - customIntentJSONFile, _ := json.Marshal(vars.CustomIntents) - os.WriteFile(vars.CustomIntentsPath, customIntentJSONFile, 0644) - fmt.Fprintf(w, "intent added successfully") + } + if anyEmpty(intent.Name, intent.Description, intent.Intent) || len(intent.Utterances) == 0 { + http.Error(w, "missing required field (name, description, utterances, and intent are required)", http.StatusBadRequest) return - case r.URL.Path == "/api/edit_custom_intent": - number := r.FormValue("number") - name := r.FormValue("name") - description := r.FormValue("description") - utterances := r.FormValue("utterances") - intent := r.FormValue("intent") - paramName := r.FormValue("paramname") - paramValue := r.FormValue("paramvalue") - exec := r.FormValue("exec") - execArgs := r.FormValue("execargs") - if number == "" { - fmt.Fprintf(w, "err: a number is required") - return - } - if name == "" && description == "" && utterances == "" && intent == "" && paramName == "" && paramValue == "" && exec == "" { - fmt.Fprintf(w, "err: an entry must be edited") - return - } - if !vars.CustomIntentsExist { - fmt.Fprintf(w, "err: you must create an intent first") - return - } - newNumbera, _ := strconv.Atoi(number) - newNumber := newNumbera - 1 - if newNumber > len(vars.CustomIntents) { - fmt.Fprintf(w, "err: there are only "+strconv.Itoa(len(vars.CustomIntents))+" intents") + } + intent.LuaScript = strings.TrimSpace(intent.LuaScript) + if intent.LuaScript != "" { + if err := scripting.ValidateLuaScript(intent.LuaScript); err != nil { + http.Error(w, "lua validation error: "+err.Error(), http.StatusBadRequest) return } - if name != "" { - vars.CustomIntents[newNumber].Name = name - } - if description != "" { - vars.CustomIntents[newNumber].Description = description - } - if utterances != "" { - vars.CustomIntents[newNumber].Utterances = strings.Split(utterances, ",") - } - if intent != "" { - vars.CustomIntents[newNumber].Intent = intent - } - if paramName != "" { - vars.CustomIntents[newNumber].Params.ParamName = paramName - } - if paramValue != "" { - vars.CustomIntents[newNumber].Params.ParamValue = paramValue - } - if exec != "" { - vars.CustomIntents[newNumber].Exec = exec - } - if execArgs != "" { - vars.CustomIntents[newNumber].ExecArgs = strings.Split(execArgs, ",") - } - vars.CustomIntents[newNumber].IsSystemIntent = false - newCustomIntentJSONFile, _ := json.Marshal(vars.CustomIntents) - os.WriteFile(vars.CustomIntentsPath, newCustomIntentJSONFile, 0644) - fmt.Fprintf(w, "intent edited successfully") + } + vars.CustomIntentsExist = true + vars.CustomIntents = append(vars.CustomIntents, intent) + saveCustomIntents() + fmt.Fprint(w, "Intent added successfully.") +} + +func handleEditCustomIntent(w http.ResponseWriter, r *http.Request) { + var request struct { + Number int `json:"number"` + vars.CustomIntent + } + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + http.Error(w, "invalid request body", http.StatusBadRequest) + return + } + if request.Number < 1 || request.Number > len(vars.CustomIntents) { + http.Error(w, "invalid intent number", http.StatusBadRequest) return - case r.URL.Path == "/api/get_custom_intents_json": - if !vars.CustomIntentsExist { - fmt.Fprintf(w, "error: you must create an intent first") + } + intent := &vars.CustomIntents[request.Number-1] + if request.Name != "" { + intent.Name = request.Name + } + if request.Description != "" { + intent.Description = request.Description + } + if len(request.Utterances) != 0 { + intent.Utterances = request.Utterances + } + if request.Intent != "" { + intent.Intent = request.Intent + } + if request.Params.ParamName != "" { + intent.Params.ParamName = request.Params.ParamName + } + if request.Params.ParamValue != "" { + intent.Params.ParamValue = request.Params.ParamValue + } + if request.Exec != "" { + intent.Exec = request.Exec + } + if request.LuaScript != "" { + intent.LuaScript = request.LuaScript + if err := scripting.ValidateLuaScript(intent.LuaScript); err != nil { + http.Error(w, "lua validation error: "+err.Error(), http.StatusBadRequest) return } - customIntentJSONFile, err := os.ReadFile(vars.CustomIntentsPath) - if err != nil { - logger.Println(err) - } - fmt.Fprint(w, string(customIntentJSONFile)) + } + if len(request.ExecArgs) != 0 { + intent.ExecArgs = request.ExecArgs + } + intent.IsSystemIntent = false + saveCustomIntents() + fmt.Fprint(w, "Intent edited successfully.") +} + +func handleGetCustomIntentsJSON(w http.ResponseWriter) { + if !vars.CustomIntentsExist { + http.Error(w, "you must create an intent first", http.StatusBadRequest) return - case r.URL.Path == "/api/remove_custom_intent": - number := r.FormValue("number") - if number == "" { - fmt.Fprintf(w, "error: a number is required") + } + customIntentJSONFile, err := os.ReadFile(vars.CustomIntentsPath) + if err != nil { + http.Error(w, "could not read custom intents file", http.StatusInternalServerError) + logger.Println(err) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(customIntentJSONFile) +} + +func handleRemoveCustomIntent(w http.ResponseWriter, r *http.Request) { + var request struct { + Number int `json:"number"` + } + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + http.Error(w, "invalid request body", http.StatusBadRequest) + return + } + if request.Number < 1 || request.Number > len(vars.CustomIntents) { + http.Error(w, "invalid intent number", http.StatusBadRequest) + return + } + vars.CustomIntents = append(vars.CustomIntents[:request.Number-1], vars.CustomIntents[request.Number:]...) + saveCustomIntents() + fmt.Fprint(w, "Intent removed successfully.") +} + +func handleSetWeatherAPI(w http.ResponseWriter, r *http.Request) { + var config struct { + Provider string `json:"provider"` + Key string `json:"key"` + } + if err := json.NewDecoder(r.Body).Decode(&config); err != nil { + http.Error(w, "invalid request body", http.StatusBadRequest) + return + } + if config.Provider == "" { + vars.APIConfig.Weather.Enable = false + } else { + vars.APIConfig.Weather.Enable = true + vars.APIConfig.Weather.Key = strings.TrimSpace(config.Key) + vars.APIConfig.Weather.Provider = config.Provider + } + vars.WriteConfigToDisk() + fmt.Fprint(w, "Changes successfully applied.") +} + +func handleGetWeatherAPI(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(vars.APIConfig.Weather) +} + +func handleSetKGAPI(w http.ResponseWriter, r *http.Request) { + if err := json.NewDecoder(r.Body).Decode(&vars.APIConfig.Knowledge); err != nil { + fmt.Println(err) + http.Error(w, "invalid request body", http.StatusBadRequest) + return + } + vars.WriteConfigToDisk() + fmt.Fprint(w, "Changes successfully applied.") +} + +func handleGetKGAPI(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(vars.APIConfig.Knowledge) +} + +func handleSetSTTInfo(w http.ResponseWriter, r *http.Request) { + var request struct { + Language string `json:"language"` + } + if err := json.NewDecoder(r.Body).Decode(&request); err != nil { + http.Error(w, "invalid request body", http.StatusBadRequest) + return + } + if vars.APIConfig.STT.Service == "vosk" { + if !isValidLanguage(request.Language, localization.ValidVoskModels) { + http.Error(w, "language not valid", http.StatusBadRequest) return } - if _, err := os.Stat(vars.CustomIntentsPath); err != nil { - fmt.Fprintf(w, "error: you must create an intent first") + if !isDownloadedLanguage(request.Language, vars.DownloadedVoskModels) { + go localization.DownloadVoskModel(request.Language) + fmt.Fprint(w, "downloading language model...") return } - newNumbera, _ := strconv.Atoi(number) - newNumber := newNumbera - 1 - if newNumber > len(vars.CustomIntents) { - fmt.Fprintf(w, "err: there are only "+strconv.Itoa(len(vars.CustomIntents))+" intents") + } else if vars.APIConfig.STT.Service == "whisper.cpp" { + if !isValidLanguage(request.Language, localization.ValidVoskModels) { + http.Error(w, "language not valid", http.StatusBadRequest) return } - vars.CustomIntents = append(vars.CustomIntents[:newNumber], vars.CustomIntents[newNumber+1:]...) - newCustomIntentJSONFile, _ := json.Marshal(vars.CustomIntents) - os.WriteFile("./customIntents.json", newCustomIntentJSONFile, 0644) - fmt.Fprintf(w, "intent removed successfully") - return - case r.URL.Path == "/api/set_weather_api": - weatherProvider := r.FormValue("provider") - weatherAPIKey := r.FormValue("api_key") - if weatherProvider == "" { - vars.APIConfig.Weather.Enable = false - } else { - vars.APIConfig.Weather.Enable = true - vars.APIConfig.Weather.Key = weatherAPIKey - vars.APIConfig.Weather.Provider = weatherProvider - } - vars.WriteConfigToDisk() - fmt.Fprintf(w, "Changes successfully applied.") - return - case r.URL.Path == "/api/get_weather_api": - weatherEnabled := false - weatherProvider := "" - weatherAPIKey := "" - if vars.APIConfig.Weather.Enable { - weatherEnabled = true - weatherProvider = vars.APIConfig.Weather.Provider - weatherAPIKey = vars.APIConfig.Weather.Key - } - fmt.Fprintf(w, "{ ") - fmt.Fprintf(w, " \"weatherEnabled\": %t,", weatherEnabled) - fmt.Fprintf(w, " \"weatherProvider\": \"%s\",", weatherProvider) - fmt.Fprintf(w, " \"weatherApiKey\": \"%s\"", weatherAPIKey) - fmt.Fprintf(w, "}") + } else { + http.Error(w, "service must be vosk or whisper", http.StatusBadRequest) return - case r.URL.Path == "/api/set_kg_api": - kgProvider := r.FormValue("provider") - kgAPIKey := r.FormValue("api_key") - // for houndify - kgAPIID := r.FormValue("api_id") - kgIntent := r.FormValue("intent_graph") - // for Together AI Service - kgModel := r.FormValue("model") - - if kgProvider == "" { - vars.APIConfig.Knowledge.Enable = false - } else { - vars.APIConfig.Knowledge.Enable = true - vars.APIConfig.Knowledge.Provider = kgProvider - vars.APIConfig.Knowledge.Key = kgAPIKey - vars.APIConfig.Knowledge.Model = kgModel - vars.APIConfig.Knowledge.ID = kgAPIID - } - if kgProvider == "openai" && kgIntent == "true" { - vars.APIConfig.Knowledge.IntentGraph = true - if r.FormValue("robot_name") == "" { - vars.APIConfig.Knowledge.RobotName = "Vector" - } else { - vars.APIConfig.Knowledge.RobotName = r.FormValue("robot_name") - } - } else if kgProvider == "openai" && kgIntent == "false" { - vars.APIConfig.Knowledge.IntentGraph = false - vars.APIConfig.Knowledge.RobotName = "" - } - vars.WriteConfigToDisk() - fmt.Fprintf(w, "Changes successfully applied.") + } + vars.APIConfig.STT.Language = request.Language + vars.APIConfig.PastInitialSetup = true + vars.WriteConfigToDisk() + processreqs.ReloadVosk() + logger.Println("Reloaded voice processor successfully") + fmt.Fprint(w, "Language switched successfully.") +} + +func handleGetDownloadStatus(w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(localization.DownloadStatus)) + if localization.DownloadStatus == "success" || strings.Contains(localization.DownloadStatus, "error") { + localization.DownloadStatus = "not downloading" + } +} + +func handleGetSTTInfo(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(vars.APIConfig.STT) +} + +func handleGetConfig(w http.ResponseWriter) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(vars.APIConfig) +} + +func handleGetLogs(w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(logger.LogList)) +} + +func handleGetDebugLogs(w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(logger.LogTrayList)) +} + +func handleIsRunning(w http.ResponseWriter) { + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte("true")) +} + +func handleDeleteChats(w http.ResponseWriter) { + vars.RememberedChats = []vars.RememberedChat{} + fmt.Fprint(w, "done") +} + +func handleGetOTA(w http.ResponseWriter, r *http.Request) { + otaName := strings.Split(r.URL.Path, "/")[3] + targetURL, err := url.Parse("https://archive.org/download/vector-pod-firmware/" + strings.TrimSpace(otaName)) + if err != nil { + http.Error(w, "failed to parse URL", http.StatusInternalServerError) return - case r.URL.Path == "/api/get_kg_api": - kgEnabled := false - kgProvider := "" - kgAPIKey := "" - kgAPIID := "" - kgModel := "" - kgIntent := false - kgRobotName := "" - if vars.APIConfig.Knowledge.Enable { - kgEnabled = true - kgProvider = vars.APIConfig.Knowledge.Provider - kgAPIKey = vars.APIConfig.Knowledge.Key - kgModel = vars.APIConfig.Knowledge.Model - kgAPIID = vars.APIConfig.Knowledge.ID - kgIntent = vars.APIConfig.Knowledge.IntentGraph - kgRobotName = vars.APIConfig.Knowledge.RobotName - } - fmt.Fprintf(w, "{ ") - fmt.Fprintf(w, " \"kgEnabled\": %t,", kgEnabled) - fmt.Fprintf(w, " \"kgProvider\": \"%s\",", kgProvider) - fmt.Fprintf(w, " \"kgApiKey\": \"%s\",", kgAPIKey) - fmt.Fprintf(w, " \"kgModel\": \"%s\",", kgModel) - fmt.Fprintf(w, " \"kgApiID\": \"%s\",", kgAPIID) - fmt.Fprintf(w, " \"kgIntentGraph\": \"%t\",", kgIntent) - fmt.Fprintf(w, " \"kgRobotName\": \"%s\"", kgRobotName) - fmt.Fprintf(w, "}") + } + req, err := http.NewRequest(r.Method, targetURL.String(), nil) + if err != nil { + http.Error(w, "failed to create request", http.StatusInternalServerError) return - case r.URL.Path == "/api/set_stt_info": - language := r.FormValue("language") - if vars.APIConfig.STT.Service == "vosk" { - // check if language is valid - matched := false - for _, lang := range localization.ValidVoskModels { - if lang == language { - matched = true - break - } - } - if !matched { - fmt.Fprint(w, "error: language not valid") - return - } - // check if language is downloaded already - matched = false - for _, lang := range vars.DownloadedVoskModels { - if lang == language { - matched = true - break - } - } - if !matched { - go localization.DownloadVoskModel(language) - fmt.Fprint(w, "downloading language model") - } else { - vars.APIConfig.STT.Language = language - vars.APIConfig.PastInitialSetup = true - vars.WriteConfigToDisk() - processreqs.ReloadVosk() - logger.Println("Reloaded voice processor successfully") - fmt.Fprint(w, "language switched successfully") - } - } else if vars.APIConfig.STT.Service == "whisper.cpp" { - matched := false - for _, lang := range localization.ValidVoskModels { - if lang == language { - matched = true - break - } - } - if !matched { - fmt.Fprint(w, "error: language not valid") - return - } - - vars.APIConfig.STT.Language = language - vars.APIConfig.PastInitialSetup = true - vars.WriteConfigToDisk() - processreqs.ReloadVosk() - logger.Println("Reloaded voice processor successfully") - fmt.Fprint(w, "language switched successfully") - } else { - fmt.Fprint(w, "error: service must be vosk or whisper") + } + for key, values := range r.Header { + for _, value := range values { + req.Header.Add(key, value) } + } + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + http.Error(w, "failed to perform request", http.StatusInternalServerError) return - case r.URL.Path == "/api/get_download_status": - fmt.Fprint(w, localization.DownloadStatus) - if localization.DownloadStatus == "success" || strings.Contains(localization.DownloadStatus, "error") { - localization.DownloadStatus = "not downloading" + } + defer resp.Body.Close() + for key, values := range resp.Header { + for _, value := range values { + w.Header().Add(key, value) } + } + _, err = io.Copy(w, resp.Body) + if err != nil { + http.Error(w, "failed to copy response body", http.StatusInternalServerError) + } +} + +func handleGetVersionInfo(w http.ResponseWriter) { + var installedVer string + ver, err := os.ReadFile(vars.VersionFile) + if err == nil { + installedVer = strings.TrimSpace(string(ver)) + } + currentVer, err := GetLatestReleaseTag("kercre123", "WirePod") + if err != nil { + http.Error(w, "error communicating with github (ver): "+err.Error(), http.StatusInternalServerError) return - case r.URL.Path == "/api/get_stt_info": - sttLanguage := vars.APIConfig.STT.Language - sttProvider := vars.APIConfig.STT.Service - fmt.Fprintf(w, "{ ") - fmt.Fprintf(w, " \"sttProvider\": \"%s\",", sttProvider) - fmt.Fprintf(w, " \"sttLanguage\": \"%s\"", sttLanguage) - fmt.Fprintf(w, "}") - return - case r.URL.Path == "/api/get_config": - writeBytes, _ := json.Marshal(vars.APIConfig) - w.Write(writeBytes) - return - case r.URL.Path == "/api/get_logs": - fmt.Fprintf(w, logger.LogList) - return - case r.URL.Path == "/api/is_running": - fmt.Fprintf(w, "true") - return - case r.URL.Path == "/api/generate_certs": - err := botsetup.CreateCertCombo() - if err != nil { - fmt.Fprint(w, "error: "+err.Error()) - } - fmt.Fprint(w, "done") + } + currentCommit, err := GetLatestCommitSha() + if err != nil { + http.Error(w, "error communicating with github (commit): "+err.Error(), http.StatusInternalServerError) return } + type VersionInfo struct { + FromSource bool `json:"fromsource"` + InstalledVer string `json:"installedversion"` + InstalledCommit string `json:"installedcommit"` + CurrentVer string `json:"currentversion"` + CurrentCommit string `json:"currentcommit"` + UpdateAvailable bool `json:"avail"` + } + var fromSource bool + if installedVer == "" { + fromSource = true + } + var uAvail bool + if fromSource { + uAvail = vars.CommitSHA != strings.TrimSpace(currentCommit) + } else { + uAvail = installedVer != strings.TrimSpace(currentVer) + } + verInfo := VersionInfo{ + FromSource: fromSource, + InstalledVer: installedVer, + InstalledCommit: vars.CommitSHA, + CurrentVer: strings.TrimSpace(currentVer), + CurrentCommit: strings.TrimSpace(currentCommit), + UpdateAvailable: uAvail, + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(verInfo) } -func certHandler(w http.ResponseWriter, r *http.Request) { - switch { - case strings.Contains(r.URL.Path, "/session-certs/"): - split := strings.Split(r.URL.Path, "/") - if len(split) < 3 { - fmt.Fprint(w, "error: must request a cert by esn (ex. /session-certs/00e20145)") - return - } - esn := split[2] - fileBytes, err := os.ReadFile(vars.SessionCertPath + esn) - if err != nil { - fmt.Fprint(w, "error: cert does not exist") - return - } - w.Write(fileBytes) +func handleGenerateCerts(w http.ResponseWriter) { + if err := botsetup.CreateCertCombo(); err != nil { + http.Error(w, "error: "+err.Error(), http.StatusInternalServerError) return } + fmt.Fprint(w, "done") +} + +func saveCustomIntents() { + customIntentJSONFile, _ := json.Marshal(vars.CustomIntents) + os.WriteFile(vars.CustomIntentsPath, customIntentJSONFile, 0644) +} + +func DisableCachingAndSniffing(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0") + w.Header().Set("pragma", "no-cache") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Expires", "0") + next.ServeHTTP(w, r) + }) } func StartWebServer() { botsetup.RegisterSSHAPI() + botsetup.RegisterBLEAPI() http.HandleFunc("/api/", apiHandler) http.HandleFunc("/session-certs/", certHandler) var webRoot http.Handler if runtime.GOOS == "darwin" && vars.Packaged { appPath, _ := os.Executable() webRoot = http.FileServer(http.Dir(filepath.Dir(appPath) + "/../Frameworks/chipper/webroot")) - } else if runtime.GOOS == "android" { + } else if runtime.GOOS == "android" || runtime.GOOS == "ios" { webRoot = http.FileServer(http.Dir(vars.AndroidPath + "/static/webroot")) } else { webRoot = http.FileServer(http.Dir("./webroot")) } - http.Handle("/", webRoot) + http.Handle("/", DisableCachingAndSniffing(webRoot)) fmt.Printf("Starting webserver at port " + vars.WebPort + " (http://localhost:" + vars.WebPort + ")\n") if err := http.ListenAndServe(":"+vars.WebPort, nil); err != nil { logger.Println("Error binding to " + vars.WebPort + ": " + err.Error()) @@ -369,3 +418,100 @@ func StartWebServer() { os.Exit(1) } } + +func GetLatestCommitSha() (string, error) { + client := &http.Client{} + req, err := http.NewRequest("GET", "https://api.github.com/repos/kercre123/wire-pod/commits", nil) + if err != nil { + return "", err + } + resp, err := client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("failed to get commits: %s", resp.Status) + } + type Commit struct { + Sha string `json:"sha"` + } + var commits []Commit + if err := json.NewDecoder(resp.Body).Decode(&commits); err != nil { + return "", err + } + if len(commits) == 0 { + return "", fmt.Errorf("no commits found") + } + return commits[0].Sha[:7], nil +} + +func GetLatestReleaseTag(owner, repo string) (string, error) { + url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo) + + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + type Release struct { + TagName string `json:"tag_name"` + } + var release Release + if err := json.Unmarshal(body, &release); err != nil { + return "", err + } + + return release.TagName, nil +} + +func certHandler(w http.ResponseWriter, r *http.Request) { + switch { + case strings.Contains(r.URL.Path, "/session-certs/"): + split := strings.Split(r.URL.Path, "/") + if len(split) < 3 { + http.Error(w, "must request a cert by esn (ex. /session-certs/00e20145)", http.StatusBadRequest) + return + } + esn := split[2] + fileBytes, err := os.ReadFile(path.Join(vars.SessionCertPath, esn)) + if err != nil { + http.Error(w, "cert does not exist", http.StatusNotFound) + return + } + w.Write(fileBytes) + } +} + +func anyEmpty(values ...string) bool { + for _, v := range values { + if v == "" { + return true + } + } + return false +} + +func isValidLanguage(language string, validLanguages []string) bool { + for _, lang := range validLanguages { + if lang == language { + return true + } + } + return false +} + +func isDownloadedLanguage(language string, downloadedLanguages []string) bool { + for _, lang := range downloadedLanguages { + if lang == language { + return true + } + } + return false +} diff --git a/chipper/pkg/wirepod/localization/download.go b/chipper/pkg/wirepod/localization/download.go index 18feec00..afcc3ba9 100644 --- a/chipper/pkg/wirepod/localization/download.go +++ b/chipper/pkg/wirepod/localization/download.go @@ -8,6 +8,7 @@ package localization import ( "archive/zip" + "crypto/tls" "fmt" "io" "log" @@ -48,6 +49,16 @@ func DownloadVoskModel(language string) { filename = filename + "pl-0.22.zip" } else if language == "zh-CN" { filename = filename + "cn-0.22.zip" + } else if language == "tr-TR" { + filename = filename + "tr-0.3.zip" + } else if language == "ru-RU" { + filename = filename + "ru-0.22.zip" + } else if language == "nt-NL" { + filename = filename + "nl-0.22.zip" + } else if language == "uk-UA" { + filename = filename + "uk-v3-small.zip" + } else if language == "vi-VN" { + filename = filename + "vn-0.4.zip" } else { logger.Println("Language not valid? " + language) return @@ -55,7 +66,7 @@ func DownloadVoskModel(language string) { os.MkdirAll(vars.VoskModelPath, 0755) url := URLPrefix + filename var filep string - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { filep = filepath.Join(vars.AndroidPath, "/"+filename) } else { filep = os.TempDir() + "/" + filename @@ -108,6 +119,7 @@ func PrintDownloadPercent(done chan int64, path string, total int64) { func DownloadFile(url string, dest string) { if strings.Contains(DownloadStatus, "success") || strings.Contains(DownloadStatus, "error") || strings.Contains(DownloadStatus, "not downloading") { + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} logger.Println("Downloading " + url + " to " + dest) out, _ := os.Create(dest) defer out.Close() diff --git a/chipper/pkg/wirepod/localization/localization.go b/chipper/pkg/wirepod/localization/localization.go index e5f130ed..69f7f610 100755 --- a/chipper/pkg/wirepod/localization/localization.go +++ b/chipper/pkg/wirepod/localization/localization.go @@ -2,7 +2,7 @@ package localization import "github.com/kercre123/wire-pod/chipper/pkg/vars" -var ValidVoskModels []string = []string{"en-US", "it-IT", "es-ES", "fr-FR", "de-DE", "pt-BR", "pl-PL", "zh-CN"} +var ValidVoskModels []string = []string{"en-US", "it-IT", "es-ES", "fr-FR", "de-DE", "pt-BR", "pl-PL", "zh-CN", "tr-TR", "ru-RU", "nt-NL", "uk-UA", "vi-VN"} const STR_WEATHER_IN = "str_weather_in" const STR_WEATHER_FORECAST = "str_weather_forecast" @@ -38,6 +38,40 @@ const STR_NAME_IS = "str_name_is" const STR_NAME_IS2 = "str_name_is1" const STR_NAME_IS3 = "str_name_is2" const STR_FOR = "str_for" +const STR_ZERO = "str_zero" +const STR_ONE = "str_one" +const STR_TWO = "str_two" +const STR_THREE = "str_three" +const STR_FOUR = "str_four" +const STR_FIVE = "str_five" +const STR_SIX = "str_six" +const STR_SEVEN = "str_seven" +const STR_EIGHT = "str_eight" +const STR_NINE = "str_nine" +const STR_TEN = "str_ten" +const STR_ELEVEN = "str_eleven" +const STR_TWELVE = "str_twelve" +const STR_THIRTEEN = "str_thirteen" +const STR_FOURTEEN = "str_fourteen" +const STR_FIFTEEN = "str_fifteen" +const STR_SIXTEEN = "str_sixteen" +const STR_SEVENTEEN = "str_seventeen" +const STR_EIGHTEEN = "str_eighteen" +const STR_NINETEEN = "str_nineteen" +const STR_TWENTY = "str_twenty" +const STR_THIRTY = "str_thirty" +const STR_FOURTY = "str_fourty" +const STR_FIFTY = "str_fifty" +const STR_SIXTY = "str_sixty" +const STR_SEVENTY = "str_seventy" +const STR_EIGHTY = "str_eighty" +const STR_NINETY = "str_ninety" +const STR_ONE_HUNDRED = "str_one_hundred" +const STR_ONE_HOUR = "str_one_hour" +const STR_ONE_HOUR_ALT = "str_one_hour_alt" +const STR_HOUR = "str_hour" +const STR_MINUTE = "str_minute" +const STR_SECOND = "str_second" // for grammer var ALL_STR []string = []string{ @@ -75,46 +109,114 @@ var ALL_STR []string = []string{ "str_name_is1", "str_name_is2", "str_for", + "str_zero", + "str_one", + "str_two", + "str_three", + "str_four", + "str_five", + "str_six", + "str_seven", + "str_eight", + "str_nine", + "str_ten", + "str_eleven", + "str_twelve", + "str_thirteen", + "str_fourteen", + "str_fifteen", + "str_sixteen", + "str_seventeen", + "str_eighteen", + "str_nineteen", + "str_twenty", + "str_thirty", + "str_fourty", + "str_fifty", + "str_sixty", + "str_seventy", + "str_eighty", + "str_ninety", + "str_one_hundred", + "str_one_hour", + "str_one_hour_alt", + "str_hour", + "str_minute", + "str_second", } // All text must be lowercase! var texts = map[string][]string{ - // key en-US it-IT es-ES fr-FR de-DE pl-PL - STR_WEATHER_IN: {" in ", " a ", " en ", " en ", " in ", " w ", " 的 "}, - STR_WEATHER_FORECAST: {"forecast", "previsioni", "pronóstico", "prévisions", "wettervorhersage", "prognoza", "预报"}, - STR_WEATHER_TOMORROW: {"tomorrow", "domani", "mañana", "demain", "morgen", "jutro", "明天"}, - STR_WEATHER_THE_DAY_AFTER_TOMORROW: {"day after tomorrow", "dopodomani", "el día después de mañana", "lendemain de demain", "am tag nach morgen", "pojutrze", "后天"}, - STR_WEATHER_TONIGHT: {"tonight", "stasera", "esta noche", "ce soir", "heute abend", "dziś wieczorem", "今晚"}, - STR_WEATHER_THIS_AFTERNOON: {"afternoon", "pomeriggio", "esta tarde", "après-midi", "heute nachmittag", "popołudniu", "下午"}, - STR_EYE_COLOR_PURPLE: {"purple", "lilla", "violeta", "violet", "violett", "fioletowy", "紫色"}, - STR_EYE_COLOR_BLUE: {"blue", "blu", "azul", "bleu", "blau", "niebieski", "蓝色"}, - STR_EYE_COLOR_SAPPHIRE: {"sapphire", "zaffiro", "zafiro", "saphir", "saphir", "szafir", "天蓝"}, - STR_EYE_COLOR_YELLOW: {"yellow", "giallo", "amarillo", "jaune", "gelb", "żółty", "黄色"}, - STR_EYE_COLOR_TEAL: {"teal", "verde acqua", "verde azulado", "sarcelle", "blaugrün", "morski", "浅绿"}, - STR_EYE_COLOR_TEAL2: {"tell", "acquamarina", "aguamarina", "acquamarina", "acquamarina", "akwamaryn", "蓝绿"}, - STR_EYE_COLOR_GREEN: {"green", "verde", "verde", "vert", "grün", "zielony", "绿色"}, - STR_EYE_COLOR_ORANGE: {"orange", "arancio", "naranja", "orange", "orange", "pomarańczowy", "橙色"}, - STR_ME: {"me", "me", "me", "moi", "mir", "mnie", "我"}, - STR_SELF: {"self", "mi", "mía", "moi", "mein", "ja", "自己"}, - STR_VOLUME_LOW: {"low", "basso", "bajo", "bas", "niedrig", "niski", "低"}, - STR_VOLUME_QUIET: {"quiet", "poco rumoroso", "tranquilo", "silencieux", "ruhig", "cichy", "安静"}, - STR_VOLUME_MEDIUM_LOW: {"medium low", "medio basso", "medio-bajo", "moyen-doux", "mittelschwer", "średnio niski", "中低"}, - STR_VOLUME_MEDIUM: {"medium", "medio", "medio", "moyen", "mittel", "średni", "中档"}, - STR_VOLUME_NORMAL: {"normal", "normale", "normal", "normal", "normal", "normalny", "正常"}, - STR_VOLUME_REGULAR: {"regular", "regolare", "regular", "régulier", "regulär", "zwyczajny", "标准"}, - STR_VOLUME_MEDIUM_HIGH: {"medium high", "medio alto", "medio-alto", "moyen-élevé", "mittelhoch", "średno wysoki", "中高"}, - STR_VOLUME_HIGH: {"high", "alto", "alto", "élevé", "hoch", "wysoki", "高档"}, - STR_VOLUME_LOUD: {"loud", "rumoroso", "fuerte", "fort", "laut", "głośny", "高"}, - STR_VOLUME_MUTE: {"mute", "muto", "mudo", "", "stumm", "wyciszony", "静音"}, - STR_VOLUME_NOTHING: {"nothing", "nessuno", "nada", "rien", "nichts", "nic", "无声"}, - STR_VOLUME_SILENT: {"silent", "silenzioso", "silencio", "silencieux", "still", "cichy", "悄声"}, - STR_VOLUME_OFF: {"off", "spento", "apagado", "éteindre", "aus", "wyłączony", "关闭"}, - STR_VOLUME_ZERO: {"zero", "zero", "cero", "zéro", "null", "zero", "零"}, - STR_NAME_IS: {" is ", " è ", " es ", " est ", " ist ", " to ", "到"}, - STR_NAME_IS2: {"'s", "sono ", "soy ", "suis ", "bin ", " się ", "的"}, - STR_NAME_IS3: {"names", " chiamo ", " llamo ", "appelle ", "werde", "imię", "名字"}, - STR_FOR: {" for ", " per ", " para ", " pour ", " für ", " dla ", "给"}, + // key en-US it-IT es-ES fr-FR de-DE pl-PL tr-TR ru-RU nt-NL uk-UA vi-VN + STR_WEATHER_IN: {" in ", " a ", " en ", " en ", " in ", " w ", " 的 ", " içinde ", " в ", " in ", " в ", " ở "}, + STR_WEATHER_FORECAST: {"forecast", "previsioni", "pronóstico", "prévisions", "wettervorhersage", "prognoza", "预报", "tahmin", "прогноз", "voorspelling", "прогноз", "dự báo"}, + STR_WEATHER_TOMORROW: {"tomorrow", "domani", "mañana", "demain", "morgen", "jutro", "明天", "yarın", "завтра", "morgen", "завтра", "ngày mai"}, + STR_WEATHER_THE_DAY_AFTER_TOMORROW: {"day after tomorrow", "dopodomani", "el día después de mañana", "lendemain de demain", "am tag nach morgen", "pojutrze", "后天", "yarından sonra", "послезавтра", "overmorgen", "післязавтра", "ngày mốt"}, + STR_WEATHER_TONIGHT: {"tonight", "stasera", "esta noche", "ce soir", "heute abend", "dziś wieczorem", "今晚", "bu gece", "сегодня вечером", "vanavond", "сьогодні ввечері", "tối nay"}, + STR_WEATHER_THIS_AFTERNOON: {"afternoon", "pomeriggio", "esta tarde", "après-midi", "heute nachmittag", "popołudniu", "下午", "bu öğleden sonra", "после полудня", "middag", "після полудня", "chiều nay"}, + STR_EYE_COLOR_PURPLE: {"purple", "lilla", "violeta", "violet", "violett", "fioletowy", "紫色", "mor", "фиолетовый", "paars", "фіолетовий", "màu tím"}, + STR_EYE_COLOR_BLUE: {"blue", "blu", "azul", "bleu", "blau", "niebieski", "蓝色", "mavi", "голубой", "blauw", "голубий", "màu xanh"}, + STR_EYE_COLOR_SAPPHIRE: {"sapphire", "zaffiro", "zafiro", "saphir", "saphir", "szafir", "天蓝", "safir", "синий", "saffier", "синій", "màu ngọc bích"}, + STR_EYE_COLOR_YELLOW: {"yellow", "giallo", "amarillo", "jaune", "gelb", "żółty", "黄色", "sarı", "жёлтый", "geel", "жовтий", "màu vàng"}, + STR_EYE_COLOR_TEAL: {"teal", "verde acqua", "verde azulado", "sarcelle", "blaugrün", "morski", "浅绿", "teal", "бирюзовый", "wintertaling", "бірюзовий", "xanh lá cây"}, + STR_EYE_COLOR_TEAL2: {"tell", "acquamarina", "aguamarina", "acquamarine", "acquamarina", "akwamaryn", "蓝绿", "turkuaz", "аквамарин", "vertellen", "аквамариновий", "màu xanh ngọc"}, + STR_EYE_COLOR_GREEN: {"green", "verde", "verde", "vert", "grün", "zielony", "绿色", "yeşil", "зелёный", "groente", "зелений", "màu xanh lá"}, + STR_EYE_COLOR_ORANGE: {"orange", "arancio", "naranja", "orange", "orange", "pomarańczowy", "橙色", "turuncu", "оранжевый", "oranje", "оранжевий", "màu cam"}, + STR_ME: {"me", "me", "me", "moi", "mir", "mnie", "我", "ben", "меня", "mij", "мене", "tôi"}, + STR_SELF: {"self", "mi", "mía", "moi", "mein", "ja", "自己", "kendim", "себя", "zelf", "себе", "bản thân"}, + STR_VOLUME_LOW: {"low", "basso", "bajo", "bas", "niedrig", "niski", "低", "düşük", "низкий", "laag", "на мінімум", "thấp"}, + STR_VOLUME_QUIET: {"quiet", "poco rumoroso", "tranquilo", "silencieux", "ruhig", "cichy", "安静", "sessiz", "тихо", "rustig", "тихо", "yên tĩnh"}, + STR_VOLUME_MEDIUM_LOW: {"medium low", "medio basso", "medio-bajo", "moyen bas", "mittelschwer", "średnio niski", "中低", "orta düşük", "ниже среднего", "middel laag", "нижче середнього", "vừa thấp"}, + STR_VOLUME_MEDIUM: {"medium", "medio", "medio", "moyen", "mittel", "średni", "中档", "orta", "средний", "medium", "середню", "vừa"}, + STR_VOLUME_NORMAL: {"normal", "normale", "normal", "normal", "normal", "normalny", "正常", "normal", "нормальный", "normaal", "нормальна", "bình thường"}, + STR_VOLUME_REGULAR: {"regular", "regolare", "regular", "standard", "regulär", "zwyczajny", "标准", "düzenli", "обычный", "normaal", "звичайна", "thông thường"}, + STR_VOLUME_MEDIUM_HIGH: {"medium high", "medio alto", "medio-alto", "moyen-élevé", "mittelhoch", "średno wysoki", "中高", "orta yüksek", "выше среднего", "gemiddeld hoog", "вище середнього", "vừa cao"}, + STR_VOLUME_HIGH: {"high", "alto", "alto", "élevé", "hoch", "wysoki", "高档", "yüksek", "высокий", "hoog", "висока", "cao"}, + STR_VOLUME_LOUD: {"loud", "rumoroso", "fuerte", "fort", "laut", "głośny", "高", "gürültülü", "громкий", "luidruchtig", "гучний", "to"}, + STR_VOLUME_MUTE: {"mute", "muto", "mudo", "muet", "stumm", "wyciszony", "静音", "sessiz", "немой", "stom", "німий", "im lặng"}, + STR_VOLUME_NOTHING: {"nothing", "nessuno", "nada", "rien", "nichts", "nic", "无声", "hiçbir şey", "", "Niets", "нічого", "không có gì"}, + STR_VOLUME_SILENT: {"silent", "silenzioso", "silencio", "silencieux", "still", "cichy", "悄声", "sessiz", "тихий", "stil", "тихий", "yên lặng"}, + STR_VOLUME_OFF: {"off", "spento", "apagado", "éteindre", "aus", "wyłączony", "关闭", "kapalı", "выключить", "uit", "вимкнути", "tắt"}, + STR_VOLUME_ZERO: {"zero", "zero", "cero", "zéro", "null", "zero", "零", "sıfır", "ноль", "nul", "нуль", "không"}, + STR_NAME_IS: {" is ", " è ", " es ", " est ", " ist ", " to ", "到", " olan ", "", " is ", "", " là "}, + STR_NAME_IS2: {"'s", "sono ", "soy ", "suis ", "bin ", " się ", "的", "'nin", "", "", "", "của"}, + STR_NAME_IS3: {"names", " chiamo ", " llamo ", "appelle ", "werde", "imię", "名字", "adlar", "имена", "namen", "імена", "tên"}, + STR_FOR: {" for ", " per ", " para ", " pour ", " für ", " dla ", "给", " için ", "для", " voor ", " для ", " cho "}, + STR_ZERO: {"zero","zero","zero","zéro","zero","zero","zero","zero","zero","zero","zero","zero",}, + STR_ONE: {"one","one","one","un","one","one","one","one","one","one","one","one"}, + STR_TWO: {"two","two","two","deux","two","two","two","two","two","two","two","two"}, + STR_THREE: {"three","three","three","trois","three","three","three","three","three","three","three","three"}, + STR_FOUR: {"four","four","four","quatre","four","four","four","four","four","four","four","four"}, + STR_FIVE: {"five","five","five","cinq","five","five","five","five","five","five","five","five"}, + STR_SIX: {"six","six","six","six","six","six","six","six","six","six","six","six"}, + STR_SEVEN: {"seven","seven","seven","sept","seven","seven","seven","seven","seven","seven","seven","seven"}, + STR_EIGHT: {"eight","eight","eight","huit","eight","eight","eight","eight","eight","eight","eight","eight"}, + STR_NINE: {"nine","nine","nine","neuf","nine","nine","nine","nine","nine","nine","nine","nine"}, + STR_TEN: {"ten","ten","ten","dix","ten","ten","ten","ten","ten","ten","ten","ten"}, + STR_ELEVEN: {"eleven","eleven","eleven","onze","eleven","eleven","eleven","eleven","eleven","eleven","eleven","eleven"}, + STR_TWELVE: {"twelve","twelve","twelve","douze","twelve","twelve","twelve","twelve","twelve","twelve","twelve","twelve"}, + STR_THIRTEEN: {"thirteen","thirteen","thirteen","treize","thirteen","thirteen","thirteen","thirteen","thirteen","thirteen","thirteen","thirteen"}, + STR_FOURTEEN: {"fourteen","fourteen","fourteen","quatorze","fourteen","fourteen","fourteen","fourteen","fourteen","fourteen","fourteen","fourteen"}, + STR_FIFTEEN: {"fifteen","fifteen","fifteen","quinze","fifteen","fifteen","fifteen","fifteen","fifteen","fifteen","fifteen","fifteen"}, + STR_SIXTEEN: {"sixteen","sixteen","sixteen","seize","sixteen","sixteen","sixteen","sixteen","sixteen","sixteen","sixteen","sixteen"}, + STR_SEVENTEEN: {"seventeen","seventeen","seventeen","dix-sept","seventeen","seventeen","seventeen","seventeen","seventeen","seventeen","seventeen","seventeen"}, + STR_EIGHTEEN: {"eighteen","eighteen","eighteen","dix-huit","eighteen","eighteen","eighteen","eighteen","eighteen","eighteen","eighteen","eighteen"}, + STR_NINETEEN: {"nineteen","nineteen","nineteen","dix-neuf","nineteen","nineteen","nineteen","nineteen","nineteen","nineteen","nineteen","nineteen"}, + STR_TWENTY: {"twenty","twenty","twenty","vingt","twenty","twenty","twenty","twenty","twenty","twenty","twenty","twenty"}, + STR_THIRTY: {"thirty","thirty","thirty","trente","thirty","thirty","thirty","thirty","thirty","thirty","thirty","thirty"}, + STR_FOURTY: {"fourty","fourty","fourty","quarante","fourty","fourty","fourty","fourty","fourty","fourty","fourty","fourty"}, + STR_FIFTY: {"fifty","fifty","fifty","cinquante","fifty","fifty","fifty","fifty","fifty","fifty","fifty","fifty"}, + STR_SIXTY: {"sixty","sixty","sixty","soixante","sixty","sixty","sixty","sixty","sixty","sixty","sixty","sixty"}, + STR_SEVENTY: {"seventy","seventy","seventy","soixante-dix","seventy","seventy","seventy","seventy","seventy","seventy","seventy","seventy"}, + STR_EIGHTY: {"eighty","eighty","eighty","quatre-vingt","eighty","eighty","eighty","eighty","eighty","eighty","eighty","eighty"}, + STR_NINETY: {"ninety","ninety","ninety","quatre vingt dix","ninety","ninety","ninety","ninety","ninety","ninety","ninety","ninety"}, + STR_ONE_HUNDRED: {"one hundred","one hundred","one hundred","cent","one hundred","one hundred","one hundred","one hundred","one hundred","one hundred","one hundred","one hundred"}, + STR_ONE_HOUR: {"one hour","one hour","one hour","une heure","one hour","one hour","one hour","one hour","one hour","one hour","one hour","one hour"}, + STR_ONE_HOUR_ALT: {"an hour","an hour","an hour","une heure","an hour","an hour","an hour","an hour","an hour","an hour","an hour","an hour"}, + STR_HOUR: {"hour","hour","hour","heure","hour","hour","hour","hour","hour","hour","hour","hour"}, + STR_MINUTE: {"minute","minute","minute","minute","minute","minute","minute","minute","minute","minute","minute","minute"}, + STR_SECOND: {"second","second","second","seconde","second","second","second","second","second","second","second","second"}, } func GetText(key string) string { @@ -132,6 +234,16 @@ func GetText(key string) string { return data[5] } else if vars.APIConfig.STT.Language == "zh-CN" { return data[6] + } else if vars.APIConfig.STT.Language == "tr-TR" { + return data[7] + } else if vars.APIConfig.STT.Language == "ru-RU" { + return data[8] + } else if vars.APIConfig.STT.Language == "nt-NL" { + return data[9] + } else if vars.APIConfig.STT.Language == "uk-UA" { + return data[10] + } else if vars.APIConfig.STT.Language == "vi-VN" { + return data[11] } } return data[0] @@ -139,7 +251,7 @@ func GetText(key string) string { func ReloadVosk() { if vars.APIConfig.STT.Service == "vosk" || vars.APIConfig.STT.Service == "whisper.cpp" { - vars.MatchListList, vars.IntentsList, _ = vars.LoadIntents() + vars.IntentList, _ = vars.LoadIntents() vars.SttInitFunc() } } diff --git a/chipper/pkg/wirepod/preqs/intent.go b/chipper/pkg/wirepod/preqs/intent.go index 88f424f5..6513dbc3 100755 --- a/chipper/pkg/wirepod/preqs/intent.go +++ b/chipper/pkg/wirepod/preqs/intent.go @@ -1,6 +1,8 @@ package processreqs import ( + "strings" + "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" "github.com/kercre123/wire-pod/chipper/pkg/vtt" @@ -10,7 +12,6 @@ import ( // This is here for compatibility with 1.6 and older software func (s *Server) ProcessIntent(req *vtt.IntentRequest) (*vtt.IntentResponse, error) { - Interrupt(req.Device) var successMatched bool speechReq := sr.ReqToSpeechRequest(req) var transcribedText string @@ -18,16 +19,20 @@ func (s *Server) ProcessIntent(req *vtt.IntentRequest) (*vtt.IntentResponse, err var err error transcribedText, err = sttHandler(speechReq) if err != nil { - ttr.IntentPass(req, "intent_system_noaudio", "voice processing error", map[string]string{"error": err.Error()}, true) + ttr.IntentPass(req, "intent_system_noaudio", "voice processing error: "+err.Error(), map[string]string{"error": err.Error()}, true) + return nil, nil + } + if strings.TrimSpace(transcribedText) == "" { + ttr.IntentPass(req, "intent_system_noaudio", "", map[string]string{}, false) return nil, nil } - successMatched = ttr.ProcessTextAll(req, transcribedText, vars.MatchListList, vars.IntentsList, speechReq.IsOpus) + successMatched = ttr.ProcessTextAll(req, transcribedText, vars.IntentList, speechReq.IsOpus) } else { intent, slots, err := stiHandler(speechReq) if err != nil { if err.Error() == "inference not understood" { logger.Println("No intent was matched") - ttr.IntentPass(req, "intent_system_noaudio", "voice processing error", map[string]string{"error": err.Error()}, true) + ttr.IntentPass(req, "intent_system_unmatched", "voice processing error", map[string]string{"error": err.Error()}, true) return nil, nil } logger.Println(err) @@ -38,14 +43,20 @@ func (s *Server) ProcessIntent(req *vtt.IntentRequest) (*vtt.IntentResponse, err return nil, nil } if !successMatched { - if vars.APIConfig.Knowledge.IntentGraph { - RemoveFromInterrupt(req.Device) - resp := openaiRequest(transcribedText) - logger.LogUI("OpenAI response for device " + req.Device + ": " + resp) - KGSim(req.Device, resp) + if vars.APIConfig.Knowledge.IntentGraph && vars.APIConfig.Knowledge.Enable { + logger.Println("Making LLM request for device " + req.Device + "...") + _, err := ttr.StreamingKGSim(req, req.Device, transcribedText, false) + if err != nil { + logger.Println("LLM error: " + err.Error()) + logger.LogUI("LLM error: " + err.Error()) + ttr.IntentPass(req, "intent_system_unmatched", transcribedText, map[string]string{"": ""}, false) + ttr.KGSim(req.Device, "There was an error getting a response from the L L M. Check the logs in the web interface.") + } + logger.Println("Bot " + speechReq.Device + " request served.") + return nil, nil } logger.Println("No intent was matched.") - ttr.IntentPass(req, "intent_system_noaudio", transcribedText, map[string]string{"": ""}, false) + ttr.IntentPass(req, "intent_system_unmatched", transcribedText, map[string]string{"": ""}, false) return nil, nil } logger.Println("Bot " + speechReq.Device + " request served.") diff --git a/chipper/pkg/wirepod/preqs/intent_graph.go b/chipper/pkg/wirepod/preqs/intent_graph.go index 7152172d..83911e86 100755 --- a/chipper/pkg/wirepod/preqs/intent_graph.go +++ b/chipper/pkg/wirepod/preqs/intent_graph.go @@ -1,7 +1,8 @@ package processreqs import ( - pb "github.com/digital-dream-labs/api/go/chipperpb" + "strings" + "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" "github.com/kercre123/wire-pod/chipper/pkg/vtt" @@ -17,16 +18,20 @@ func (s *Server) ProcessIntentGraph(req *vtt.IntentGraphRequest) (*vtt.IntentGra var err error transcribedText, err = sttHandler(speechReq) if err != nil { - ttr.IntentPass(req, "intent_system_noaudio", "voice processing error", map[string]string{"error": err.Error()}, true) + ttr.IntentPass(req, "intent_system_noaudio", "voice processing error: "+err.Error(), map[string]string{"error": err.Error()}, true) + return nil, nil + } + if strings.TrimSpace(transcribedText) == "" { + ttr.IntentPass(req, "intent_system_noaudio", "", map[string]string{}, false) return nil, nil } - successMatched = ttr.ProcessTextAll(req, transcribedText, vars.MatchListList, vars.IntentsList, speechReq.IsOpus) + successMatched = ttr.ProcessTextAll(req, transcribedText, vars.IntentList, speechReq.IsOpus) } else { intent, slots, err := stiHandler(speechReq) if err != nil { if err.Error() == "inference not understood" { logger.Println("Bot " + speechReq.Device + " No intent was matched") - ttr.IntentPass(req, "intent_system_noaudio", "voice processing error", map[string]string{"error": err.Error()}, true) + ttr.IntentPass(req, "intent_system_unmatched", "voice processing error", map[string]string{"error": err.Error()}, true) return nil, nil } logger.Println(err) @@ -36,22 +41,39 @@ func (s *Server) ProcessIntentGraph(req *vtt.IntentGraphRequest) (*vtt.IntentGra ttr.ParamCheckerSlotsEnUS(req, intent, slots, speechReq.IsOpus, speechReq.Device) return nil, nil } + // if !successMatched { + // logger.Println("No intent was matched.") + // if vars.APIConfig.Knowledge.Enable && vars.APIConfig.Knowledge.Provider == "openai" && len([]rune(transcribedText)) >= 8 { + // apiResponse := openaiRequest(transcribedText) + // response := &pb.IntentGraphResponse{ + // Session: req.Session, + // DeviceId: req.Device, + // ResponseType: pb.IntentGraphMode_KNOWLEDGE_GRAPH, + // SpokenText: apiResponse, + // QueryText: transcribedText, + // IsFinal: true, + // } + // req.Stream.Send(response) + // return nil, nil + // } + // ttr.IntentPass(req, "intent_system_unmatched", transcribedText, map[string]string{"": ""}, false) + // return nil, nil + // } if !successMatched { - logger.Println("No intent was matched.") - if vars.APIConfig.Knowledge.Enable && vars.APIConfig.Knowledge.Provider == "openai" && len([]rune(transcribedText)) >= 8 { - apiResponse := openaiRequest(transcribedText) - response := &pb.IntentGraphResponse{ - Session: req.Session, - DeviceId: req.Device, - ResponseType: pb.IntentGraphMode_KNOWLEDGE_GRAPH, - SpokenText: apiResponse, - QueryText: transcribedText, - IsFinal: true, + if vars.APIConfig.Knowledge.IntentGraph && vars.APIConfig.Knowledge.Enable { + logger.Println("Making LLM request for device " + req.Device + "...") + _, err := ttr.StreamingKGSim(req, req.Device, transcribedText, false) + if err != nil { + logger.Println("LLM error: " + err.Error()) + logger.LogUI("LLM error: " + err.Error()) + ttr.IntentPass(req, "intent_system_unmatched", transcribedText, map[string]string{"": ""}, false) + ttr.KGSim(req.Device, "There was an error getting a response from the L L M. Check the logs in the web interface.") } - req.Stream.Send(response) + logger.Println("Bot " + speechReq.Device + " request served.") return nil, nil } - ttr.IntentPass(req, "intent_system_noaudio", transcribedText, map[string]string{"": ""}, false) + logger.Println("No intent was matched.") + ttr.IntentPass(req, "intent_system_unmatched", transcribedText, map[string]string{"": ""}, false) return nil, nil } logger.Println("Bot " + speechReq.Device + " request served.") diff --git a/chipper/pkg/wirepod/preqs/kgsim.go b/chipper/pkg/wirepod/preqs/kgsim.go deleted file mode 100644 index 516fbc1f..00000000 --- a/chipper/pkg/wirepod/preqs/kgsim.go +++ /dev/null @@ -1,217 +0,0 @@ -package processreqs - -import ( - "context" - "log" - "strings" - "time" - - "github.com/fforchino/vector-go-sdk/pkg/vector" - "github.com/fforchino/vector-go-sdk/pkg/vectorpb" - "github.com/kercre123/wire-pod/chipper/pkg/logger" - "github.com/kercre123/wire-pod/chipper/pkg/vars" -) - -var BotsToInterrupt struct { - ESNs []string -} - -func ShouldBeInterrupted(esn string) bool { - for _, sn := range BotsToInterrupt.ESNs { - if esn == sn { - RemoveFromInterrupt(esn) - return true - } - } - return false -} - -func Interrupt(esn string) { - BotsToInterrupt.ESNs = append(BotsToInterrupt.ESNs, esn) -} - -func RemoveFromInterrupt(esn string) { - var newList []string - for _, bot := range BotsToInterrupt.ESNs { - if bot != esn { - newList = append(newList, bot) - } - } - BotsToInterrupt.ESNs = newList -} - -func KGSim(esn string, textToSay string) error { - ctx := context.Background() - matched := false - var robot *vector.Vector - var guid string - var target string - for _, bot := range vars.BotInfo.Robots { - if esn == bot.Esn { - guid = bot.GUID - target = bot.IPAddress + ":443" - matched = true - break - } - } - if matched { - var err error - robot, err = vector.New(vector.WithSerialNo(esn), vector.WithToken(guid), vector.WithTarget(target)) - if err != nil { - return err - } - } - controlRequest := &vectorpb.BehaviorControlRequest{ - RequestType: &vectorpb.BehaviorControlRequest_ControlRequest{ - ControlRequest: &vectorpb.ControlRequest{ - Priority: vectorpb.ControlRequest_OVERRIDE_BEHAVIORS, - }, - }, - } - go func() { - start := make(chan bool) - stop := make(chan bool) - - go func() { - // * begin - modified from official vector-go-sdk - r, err := robot.Conn.BehaviorControl( - ctx, - ) - if err != nil { - log.Println(err) - return - } - - if err := r.Send(controlRequest); err != nil { - log.Println(err) - return - } - - for { - ctrlresp, err := r.Recv() - if err != nil { - log.Println(err) - return - } - if ctrlresp.GetControlGrantedResponse() != nil { - start <- true - break - } - } - - for { - select { - case <-stop: - logger.Println("KGSim: releasing behavior control (interrupt)") - if err := r.Send( - &vectorpb.BehaviorControlRequest{ - RequestType: &vectorpb.BehaviorControlRequest_ControlRelease{ - ControlRelease: &vectorpb.ControlRelease{}, - }, - }, - ); err != nil { - log.Println(err) - return - } - return - default: - continue - } - } - // * end - modified from official vector-go-sdk - }() - - var stopTTSLoop bool - var TTSLoopStopped bool - for range start { - time.Sleep(time.Millisecond * 300) - robot.Conn.PlayAnimation( - ctx, - &vectorpb.PlayAnimationRequest{ - Animation: &vectorpb.Animation{ - Name: "anim_getin_tts_01", - }, - Loops: 1, - }, - ) - go func() { - for { - if stopTTSLoop { - TTSLoopStopped = true - break - } - robot.Conn.PlayAnimation( - ctx, - &vectorpb.PlayAnimationRequest{ - Animation: &vectorpb.Animation{ - Name: "anim_tts_loop_02", - }, - Loops: 1, - }, - ) - } - }() - var stopTTS bool - go func() { - for { - time.Sleep(time.Millisecond * 50) - if ShouldBeInterrupted(esn) { - RemoveFromInterrupt(esn) - robot.Conn.SayText( - ctx, - &vectorpb.SayTextRequest{ - Text: "", - UseVectorVoice: true, - DurationScalar: 1.0, - }, - ) - stop <- true - stopTTSLoop = true - stopTTS = true - break - } - } - }() - textToSaySplit := strings.Split(textToSay, ". ") - for _, str := range textToSaySplit { - _, err := robot.Conn.SayText( - ctx, - &vectorpb.SayTextRequest{ - Text: str + ".", - UseVectorVoice: true, - DurationScalar: 1.0, - }, - ) - if err != nil { - logger.Println("KG SayText error: " + err.Error()) - stop <- true - break - } - if stopTTS { - return - } - } - stopTTSLoop = true - for { - if TTSLoopStopped { - break - } else { - time.Sleep(time.Millisecond * 10) - } - } - time.Sleep(time.Millisecond * 100) - robot.Conn.PlayAnimation( - ctx, - &vectorpb.PlayAnimationRequest{ - Animation: &vectorpb.Animation{ - Name: "anim_knowledgegraph_success_01", - }, - Loops: 1, - }, - ) - //time.Sleep(time.Millisecond * 3300) - stop <- true - } - }() - return nil -} diff --git a/chipper/pkg/wirepod/preqs/knowledgegraph.go b/chipper/pkg/wirepod/preqs/knowledgegraph.go index aebc68d8..0c594457 100755 --- a/chipper/pkg/wirepod/preqs/knowledgegraph.go +++ b/chipper/pkg/wirepod/preqs/knowledgegraph.go @@ -1,10 +1,7 @@ package processreqs import ( - "bytes" "encoding/json" - "io" - "net/http" "strings" pb "github.com/digital-dream-labs/api/go/chipperpb" @@ -12,6 +9,7 @@ import ( "github.com/kercre123/wire-pod/chipper/pkg/vars" "github.com/kercre123/wire-pod/chipper/pkg/vtt" sr "github.com/kercre123/wire-pod/chipper/pkg/wirepod/speechrequest" + ttr "github.com/kercre123/wire-pod/chipper/pkg/wirepod/ttr" "github.com/pkg/errors" "github.com/soundhound/houndify-sdk-go" ) @@ -68,124 +66,32 @@ func houndifyKG(req sr.SpeechRequest) string { return apiResponse } -func togetherRequest(transcribedText string) string { - sendString := "You are a helpful robot called Vector . You will be given a question asked by a user and you must provide the best answer you can. It may not be punctuated or spelled correctly. Keep the answer concise yet informative. Here is the question: " + "\\" + "\"" + transcribedText + "\\" + "\"" + " , Answer: " - url := "https://api.together.xyz/inference" - model := vars.APIConfig.Knowledge.Model - formData := `{ -"model": "` + model + `", -"prompt": "` + sendString + `", -"temperature": 0.7, -"max_tokens": 256, -"top_p": 1 -}` - logger.Println("Making request to Together API...") - logger.Println("Model is " + model) - req, _ := http.NewRequest("POST", url, bytes.NewBuffer([]byte(formData))) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+vars.APIConfig.Knowledge.Key) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "There was an error making the request to Together API" - } - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - var togetherResponse map[string]any - err = json.Unmarshal(body, &togetherResponse) - if err != nil { - return "Together API returned no response." - } - output := togetherResponse["output"].(map[string]any) - choice := output["choices"].([]any) - for _, val := range choice { - x := val.(map[string]any) - textResponse := x["text"].(string) - apiResponse := strings.TrimSuffix(textResponse, "") - logger.Println("Together response: " + apiResponse) - return apiResponse - } - // In case text is not present in result from API, return a string saying answer was not found - return "Answer was not found" -} - -func openaiRequest(transcribedText string) string { - sendString := "You are a helpful robot called " + vars.APIConfig.Knowledge.RobotName + ". You will be given a question asked by a user and you must provide the best answer you can. It may not be punctuated or spelled correctly as the STT model is small. The answer will be put through TTS, so it should be a speakable string. Keep the answer concise yet informative. Here is the question: " + "\\" + "\"" + transcribedText + "\\" + "\"" + " , Answer: " - logger.Println("Making request to OpenAI...") - url := "https://api.openai.com/v1/completions" - formData := `{ -"model": "gpt-3.5-turbo-instruct", -"prompt": "` + sendString + `", -"temperature": 0.7, -"max_tokens": 256, -"top_p": 1, -"frequency_penalty": 0.2, -"presence_penalty": 0 -}` - req, _ := http.NewRequest("POST", url, bytes.NewBuffer([]byte(formData))) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+vars.APIConfig.Knowledge.Key) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - logger.Println(err) - return "There was an error making the request to OpenAI." - } - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) - type openAIStruct struct { - ID string `json:"id"` - Object string `json:"object"` - Created int `json:"created"` - Model string `json:"model"` - Choices []struct { - Text string `json:"text"` - Index int `json:"index"` - Logprobs interface{} `json:"logprobs"` - FinishReason string `json:"finish_reason"` - } `json:"choices"` - Usage struct { - PromptTokens int `json:"prompt_tokens"` - CompletionTokens int `json:"completion_tokens"` - TotalTokens int `json:"total_tokens"` - } `json:"usage"` - } - var openAIResponse openAIStruct - err = json.Unmarshal(body, &openAIResponse) - if err != nil || len(openAIResponse.Choices) == 0 { - logger.Println("OpenAI returned no response.") - return "OpenAI returned no response." - } - apiResponse := strings.TrimSpace(openAIResponse.Choices[0].Text) - logger.Println("OpenAI response: " + apiResponse) - return apiResponse -} - -func openaiKG(speechReq sr.SpeechRequest) string { +func streamingKG(req *vtt.KnowledgeGraphRequest, speechReq sr.SpeechRequest) string { + // have him start "thinking" right after the text is transcribed transcribedText, err := sttHandler(speechReq) if err != nil { return "There was an error." } - return openaiRequest(transcribedText) -} - -func togetherKG(speechReq sr.SpeechRequest) string { - transcribedText, err := sttHandler(speechReq) + kg := pb.KnowledgeGraphResponse{ + Session: req.Session, + DeviceId: req.Device, + CommandType: NoResult, + SpokenText: "bla bla bla bla bla bla bla bla bla bla", + } + req.Stream.Send(&kg) + _, err = ttr.StreamingKGSim(req, req.Device, transcribedText, true) if err != nil { - return "There was an error." + logger.Println("LLM error: " + err.Error()) } - return togetherRequest(transcribedText) + logger.Println("(KG) Bot " + speechReq.Device + " request served.") + return "" } // Takes a SpeechRequest, figures out knowledgegraph provider, makes request, returns API response -func KgRequest(speechReq sr.SpeechRequest) string { +func KgRequest(req *vtt.KnowledgeGraphRequest, speechReq sr.SpeechRequest) string { if vars.APIConfig.Knowledge.Enable { if vars.APIConfig.Knowledge.Provider == "houndify" { return houndifyKG(speechReq) - } else if vars.APIConfig.Knowledge.Provider == "openai" { - return openaiKG(speechReq) - } else if vars.APIConfig.Knowledge.Provider == "together" { - return togetherKG(speechReq) } } return "Knowledge graph is not enabled. This can be enabled in the web interface." @@ -194,16 +100,20 @@ func KgRequest(speechReq sr.SpeechRequest) string { func (s *Server) ProcessKnowledgeGraph(req *vtt.KnowledgeGraphRequest) (*vtt.KnowledgeGraphResponse, error) { InitKnowledge() speechReq := sr.ReqToSpeechRequest(req) - apiResponse := KgRequest(speechReq) - kg := pb.KnowledgeGraphResponse{ - Session: req.Session, - DeviceId: req.Device, - CommandType: NoResult, - SpokenText: apiResponse, - } - logger.Println("(KG) Bot " + speechReq.Device + " request served.") - if err := req.Stream.Send(&kg); err != nil { - return nil, err + if vars.APIConfig.Knowledge.Enable && vars.APIConfig.Knowledge.Provider != "houndify" { + streamingKG(req, speechReq) + } else { + apiResponse := KgRequest(req, speechReq) + kg := pb.KnowledgeGraphResponse{ + Session: req.Session, + DeviceId: req.Device, + CommandType: NoResult, + SpokenText: apiResponse, + } + logger.Println("(KG) Bot " + speechReq.Device + " request served.") + if err := req.Stream.Send(&kg); err != nil { + return nil, err + } } return nil, nil diff --git a/chipper/pkg/wirepod/preqs/server.go b/chipper/pkg/wirepod/preqs/server.go index 4c3b9c73..cd18626b 100755 --- a/chipper/pkg/wirepod/preqs/server.go +++ b/chipper/pkg/wirepod/preqs/server.go @@ -15,8 +15,9 @@ type Server struct{} var VoiceProcessor = "" type JsonIntent struct { - Name string `json:"name"` - Keyphrases []string `json:"keyphrases"` + Name string `json:"name"` + Keyphrases []string `json:"keyphrases"` + RequireExactMatch bool `json:"requiresexact"` } var sttLanguage string = "en-US" @@ -32,7 +33,7 @@ var isSti bool = false func ReloadVosk() { if vars.APIConfig.STT.Service == "vosk" || vars.APIConfig.STT.Service == "whisper.cpp" { vars.SttInitFunc() - vars.MatchListList, vars.IntentsList, _ = vars.LoadIntents() + vars.IntentList, _ = vars.LoadIntents() } } @@ -44,7 +45,7 @@ func New(InitFunc func() error, SttHandler interface{}, voiceProcessor string) ( vars.APIConfig.STT.Language = "en-US" } sttLanguage = vars.APIConfig.STT.Language - vars.MatchListList, vars.IntentsList, _ = vars.LoadIntents() + vars.IntentList, _ = vars.LoadIntents() logger.Println("Initiating " + voiceProcessor + " voice processor with language " + sttLanguage) vars.SttInitFunc = InitFunc err := InitFunc() diff --git a/chipper/pkg/wirepod/preqs/stream_houndify.go b/chipper/pkg/wirepod/preqs/stream_houndify.go index 70de84a3..0fa49dd5 100644 --- a/chipper/pkg/wirepod/preqs/stream_houndify.go +++ b/chipper/pkg/wirepod/preqs/stream_houndify.go @@ -28,7 +28,7 @@ func StreamAudioToHoundify(sreq sr.SpeechRequest, client houndify.Client) string default: var chunk []byte chunk, err = sreq.GetNextStreamChunkOpus() - speechDone = sreq.DetectEndOfSpeech() + speechDone, _ = sreq.DetectEndOfSpeech() if err != nil { fmt.Println("End of stream") return @@ -45,7 +45,7 @@ func StreamAudioToHoundify(sreq sr.SpeechRequest, client houndify.Client) string go func() { for partial := range partialTranscripts { if *partial.SafeToStopAudio { - fmt.Println("SafeToStopAudio recieved") + fmt.Println("SafeToStopAudio received") done <- true return } diff --git a/chipper/pkg/wirepod/sdkapp/jdocspinger.go b/chipper/pkg/wirepod/sdkapp/jdocspinger.go index 3dbd8f38..43f23c62 100644 --- a/chipper/pkg/wirepod/sdkapp/jdocspinger.go +++ b/chipper/pkg/wirepod/sdkapp/jdocspinger.go @@ -13,6 +13,7 @@ import ( "github.com/fforchino/vector-go-sdk/pkg/vectorpb" "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" + "github.com/kercre123/zeroconf" ) var JdocsPingerBots struct { @@ -101,7 +102,7 @@ func InitJdocsPinger() { if !bot.Stopped { JdocsPingerBots.Robots[i].TimeSinceLastCheck = JdocsPingerBots.Robots[i].TimeSinceLastCheck + 1 if JdocsPingerBots.Robots[i].TimeSinceLastCheck > 15 { - logger.Println("Haven't recieved a conn check from " + bot.ESN + " in 15 seconds, will ping jdocs on next check") + logger.Println("Haven't received a conn check from " + bot.ESN + " in 15 seconds, will ping jdocs on next check") JdocsPingerBots.Robots[i].Stopped = true } } @@ -159,8 +160,13 @@ func connCheck(w http.ResponseWriter, r *http.Request) { http.Error(w, "not found", http.StatusNotFound) return case strings.Contains(r.URL.Path, "/ok"): + if r.FormValue("runMDNS") == "true" { + RunMDNS("t") + fmt.Fprintf(w, "ran") + return + } if PingerEnabled { - // logger.Println("connCheck request from " + r.RemoteAddr) + //logger.Println("connCheck request from " + r.RemoteAddr) robotTarget := strings.Split(r.RemoteAddr, ":")[0] jsonB, _ := json.Marshal(vars.BotInfo) json := string(jsonB) @@ -169,9 +175,59 @@ func connCheck(w http.ResponseWriter, r *http.Request) { if ping { pingJdocs(robotTarget) } + } else { + go RunMDNS(strings.Split(r.RemoteAddr, ":")[0]) } } fmt.Fprintf(w, "ok") return } } + +var MDNSAlreadyRun []string + +var RunningMDNS bool + +func RunMDNS(botIP string) { + for _, ip := range MDNSAlreadyRun { + if ip == botIP { + return + } + } + fmt.Println("Running mDNS...") + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + entries := make(chan *zeroconf.ServiceEntry) + resolver, _ := zeroconf.NewResolver() + resolver.Browse(ctx, "_ankivector._tcp", "local", entries) + for entry := range entries { + robotID := strings.Split(entry.HostName, ".")[0] + matched := false + for _, rinf := range vars.RecurringInfo { + if rinf.ID == robotID { + vars.AddToRInfo(rinf.ESN, robotID, fmt.Sprint(entry.AddrIPv4[0])) + for i, rob := range vars.BotInfo.Robots { + if rob.Esn == rinf.ESN { + vars.BotInfo.Robots[i].IPAddress = fmt.Sprint(entry.AddrIPv4[0]) + jsonBytes, _ := json.Marshal(vars.BotInfo) + fmt.Println("Updating robot " + robotID) + go os.WriteFile(vars.BotInfoPath, jsonBytes, 0777) + } + } + go func() { + // wait for escapepod.local trasmit + if vars.APIConfig.Server.EPConfig { + time.Sleep(time.Second) + } + pingJdocs(fmt.Sprint(entry.AddrIPv4[0])) + }() + matched = true + break + } + } + if !matched { + MDNSAlreadyRun = append(MDNSAlreadyRun, botIP) + } + } + fmt.Println("Done running mDNS") +} diff --git a/chipper/pkg/wirepod/sdkapp/robot.go b/chipper/pkg/wirepod/sdkapp/robot.go index 15818efa..028d5f0a 100644 --- a/chipper/pkg/wirepod/sdkapp/robot.go +++ b/chipper/pkg/wirepod/sdkapp/robot.go @@ -129,6 +129,11 @@ func getRobot(serial string) (Robot, int, error) { // if connection is inactive for more than 5 minutes, remove robot // run this as a goroutine func connTimer(ind int) { + // Check if the index is in the list + if len(robots) <= ind { + return + } + robots[ind].ConnTimer = 0 for { time.Sleep(time.Second) @@ -150,7 +155,7 @@ func connTimer(ind int) { logger.Println("Closing SDK connection for " + robots[ind].ESN + ", source: connTimer") removeRobot(robots[ind].ESN, "connTimer") return - } + } robots[ind].ConnTimer = robots[ind].ConnTimer + 1 } } diff --git a/chipper/pkg/wirepod/sdkapp/server.go b/chipper/pkg/wirepod/sdkapp/server.go index 453e84fb..9543c067 100755 --- a/chipper/pkg/wirepod/sdkapp/server.go +++ b/chipper/pkg/wirepod/sdkapp/server.go @@ -2,6 +2,7 @@ package sdkapp import ( "bytes" + "context" "encoding/json" "fmt" "image" @@ -17,8 +18,8 @@ import ( "github.com/fforchino/vector-go-sdk/pkg/vectorpb" "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/scripting" "github.com/kercre123/wire-pod/chipper/pkg/vars" - botsetup "github.com/kercre123/wire-pod/chipper/pkg/wirepod/setup" ) var serverFiles string = "./webroot/sdkapp" @@ -95,6 +96,10 @@ func SdkapiHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "done") return case r.URL.Path == "/api-sdk/get_sdk_info": + if len(vars.BotInfo.Robots) == 0 { + http.Error(w, "no bots are authenticated", http.StatusInternalServerError) + return + } jsonBytes, err := json.Marshal(vars.BotInfo) if err != nil { fmt.Fprintf(w, "error marshaling json") @@ -103,16 +108,108 @@ func SdkapiHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, string(jsonBytes)) return case r.URL.Path == "/api-sdk/get_sdk_settings": - resp, err := robot.Conn.PullJdocs(ctx, &vectorpb.PullJdocsRequest{ - JdocTypes: []vectorpb.JdocType{vectorpb.JdocType_ROBOT_SETTINGS}, + i := 0 + for { + resp, err := robot.Conn.PullJdocs(ctx, &vectorpb.PullJdocsRequest{ + JdocTypes: []vectorpb.JdocType{vectorpb.JdocType_ROBOT_SETTINGS}, + }) + if err != nil { + w.Write([]byte(err.Error())) + return + } + if strings.Contains(resp.NamedJdocs[0].Doc.JsonDoc, "BStat.ReactedToTriggerWord") { + time.Sleep(time.Second / 2) + if i > 3 { + logger.Println("Bot refuses to return RobotSettings jdoc...") + logger.Println("Returned Jdoc: ", resp.NamedJdocs[0].Doc.JsonDoc) + w.Write([]byte("error: bot refuses to return robotsettings")) + return + } + i = i + 1 + continue + } + json := resp.NamedJdocs[0].Doc.JsonDoc + var ajdoc vars.AJdoc + ajdoc.DocVersion = resp.NamedJdocs[0].Doc.DocVersion + ajdoc.FmtVersion = resp.NamedJdocs[0].Doc.FmtVersion + ajdoc.JsonDoc = resp.NamedJdocs[0].Doc.JsonDoc + vars.AddJdoc("vic:"+robotObj.ESN, "vic.RobotSettings", ajdoc) + logger.Println("Updating vic.RobotSettings (source: sdkapp)") + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/octet-stream") + w.Write([]byte(json)) + return + } + + case r.URL.Path == "/api-sdk/play_sound": + file, _, err := r.FormFile("sound") + if err != nil { + println("Error retrieving the file:", err) + return + } + defer file.Close() + + // Lê o conteúdo do arquivo em um slice de bytes + pcmFile, err := io.ReadAll(file) + if err != nil { + println("Error reading the file:", err) + return + } + + var audioChunks [][]byte + for len(pcmFile) >= 1024 { + audioChunks = append(audioChunks, pcmFile[:1024]) + pcmFile = pcmFile[1024:] + } + + var audioClient vectorpb.ExternalInterface_ExternalAudioStreamPlaybackClient + audioClient, _ = robot.Conn.ExternalAudioStreamPlayback(ctx) + audioClient.SendMsg(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamPrepare{ + AudioStreamPrepare: &vectorpb.ExternalAudioStreamPrepare{ + AudioFrameRate: 8000, + AudioVolume: uint32(100), + }, + }, + }) + + for _, chunk := range audioChunks { + audioClient.SendMsg(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamChunk{ + AudioStreamChunk: &vectorpb.ExternalAudioStreamChunk{ + AudioChunkSizeBytes: uint32(len(chunk)), + AudioChunkSamples: chunk, + }, + }, + }) + time.Sleep(time.Millisecond * 60) + } + + audioClient.SendMsg(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamComplete{ + AudioStreamComplete: &vectorpb.ExternalAudioStreamComplete{}, + }, }) + + return + + case r.URL.Path == "/api-sdk/get_battery": + // Ensure the endpoint times out after 15 seconds + ctx := r.Context() // Get the request context + ctx, cancel := context.WithTimeout(ctx, 15*time.Second) + defer cancel() + + resp, err := robot.Conn.BatteryState(ctx, &vectorpb.BatteryStateRequest{}) if err != nil { - w.Write([]byte(err.Error())) + fmt.Fprint(w, "error: "+err.Error()) + return } - json := resp.NamedJdocs[0].Doc.JsonDoc - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/octet-stream") - w.Write([]byte(json)) + jsonBytes, err := json.Marshal(resp) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, string(jsonBytes)) return case r.URL.Path == "/api-sdk/time_format_12": setSettingSDKintbool(robotObj, "clock_24_hour", "false") @@ -405,6 +502,17 @@ func SdkapiHandler(w http.ResponseWriter, r *http.Request) { } fmt.Fprint(w, "done") return + case r.URL.Path == "/api-sdk/get_robot_stats": + resp, err := robot.Conn.PullJdocs(ctx, + &vectorpb.PullJdocsRequest{ + JdocTypes: []vectorpb.JdocType{vectorpb.JdocType_ROBOT_LIFETIME_STATS}, + }) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + w.Write([]byte(resp.GetNamedJdocs()[0].Doc.JsonDoc)) + return case r.URL.Path == "/api-sdk/print_robot_info": fmt.Fprint(w, robot) return @@ -478,17 +586,27 @@ func camStreamHandler(w http.ResponseWriter, r *http.Request) { } } +func DisableCachingAndSniffing(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate;") + w.Header().Set("pragma", "no-cache") + w.Header().Set("X-Content-Type-Options", "nosniff") + next.ServeHTTP(w, r) + }) +} + func BeginServer() { + scripting.RegisterScriptingAPI() if os.Getenv("JDOCS_PINGER_ENABLED") == "false" { PingerEnabled = false logger.Println("Jdocs pinger has been disabled") } http.HandleFunc("/api-sdk/", SdkapiHandler) - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { serverFiles = filepath.Join(vars.AndroidPath, "/static/webroot") } fileServer := http.FileServer(http.Dir(serverFiles)) - http.Handle("/sdk-app", fileServer) + http.Handle("/sdk-app", DisableCachingAndSniffing(fileServer)) // in jdocspinger.go http.HandleFunc("/ok:80", connCheck) http.HandleFunc("/ok", connCheck) @@ -497,7 +615,7 @@ func BeginServer() { http.HandleFunc("/cam-stream", camStreamHandler) logger.Println("Starting SDK app") fmt.Printf("Starting server at port 80 for connCheck\n") - ipAddr := botsetup.GetOutboundIP().String() + ipAddr := vars.GetOutboundIP().String() logger.Println("\033[1;36mConfiguration page: http://" + ipAddr + ":" + vars.WebPort + "\033[0m") if runtime.GOOS != "android" { if err := http.ListenAndServe(":80", nil); err != nil { @@ -508,3 +626,58 @@ func BeginServer() { } } } + +func rgbToBytes(rgbValues [][][3]uint8) ([]byte, error) { + var buffer bytes.Buffer + + for _, row := range rgbValues { + for _, pixel := range row { + // Directly add the R, G and B values ​​to the buffer + buffer.WriteByte(pixel[0]) // R + buffer.WriteByte(pixel[1]) // G + buffer.WriteByte(pixel[2]) // B + } + } + + return buffer.Bytes(), nil +} +func imageToBytes(img image.Image) ([]byte, error) { + bounds := img.Bounds() + var buffer bytes.Buffer + + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + for x := bounds.Min.X; x < bounds.Max.X; x++ { + // Obtém a cor do pixel + c := img.At(x, y) + r, g, b, _ := c.RGBA() // Ignorando o valor Alpha + + // Converte de uint32 para uint8 + buffer.WriteByte(uint8(r >> 8)) + buffer.WriteByte(uint8(g >> 8)) + buffer.WriteByte(uint8(b >> 8)) + } + } + + return buffer.Bytes(), nil +} + +func resizeImage(original image.Image, width, height int) image.Image { + if width <= 0 || height <= 0 { + return original + } + + newImage := image.NewRGBA(image.Rect(0, 0, width, height)) + + scaleX := float64(original.Bounds().Dx()) / float64(width) + scaleY := float64(original.Bounds().Dy()) / float64(height) + + for y := 0; y < height; y++ { + for x := 0; x < width; x++ { + srcX := int(float64(x) * scaleX) + srcY := int(float64(y) * scaleY) + newImage.Set(x, y, original.At(srcX, srcY)) + } + } + + return newImage +} diff --git a/chipper/pkg/wirepod/setup/ble.go b/chipper/pkg/wirepod/setup/ble.go new file mode 100644 index 00000000..12524d36 --- /dev/null +++ b/chipper/pkg/wirepod/setup/ble.go @@ -0,0 +1,520 @@ +//go:build inbuiltble +// +build inbuiltble + +package botsetup + +import ( + "archive/tar" + "compress/bzip2" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/digital-dream-labs/vector-bluetooth/ble" + "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/mdnshandler" + "github.com/kercre123/wire-pod/chipper/pkg/vars" +) + +// need JSONable type +type VectorsBle struct { + ID int `json:"id"` + Name string `json:"name"` + Address string `json:"address"` +} + +type WifiNetwork struct { + SSID string `json:"ssid"` + AuthType int `json:"authtype"` +} + +var BleClient *ble.VectorBLE +var BleStatusChan chan ble.StatusChannel +var BleInited bool +var OtaStatus string +var LogStatus string + +func doOTAStatus() { + for { + r := <-BleStatusChan + if r.OTAStatus.Error != "" { + OtaStatus = "Error downloading OTA: " + r.OTAStatus.Error + return + } + if r.OTAStatus.PacketNumber == 0 { + OtaStatus = "OTA download progress: 0%" + } else { + percent := (float64(r.OTAStatus.PacketNumber) / float64(r.OTAStatus.PacketTotal)) * 100 + OtaStatus = "OTA download progress: " + fmt.Sprint(roundFloat(float64(percent), 3)) + "%" + if r.OTAStatus.PacketNumber == r.OTAStatus.PacketTotal { + OtaStatus = "OTA download is complete!" + return + } + } + } +} + +func doLogStatus() { + for { + r := <-BleStatusChan + if r.LogStatus.Error != "" { + LogStatus = "Error downloading logs: " + r.LogStatus.Error + return + } + if r.LogStatus.PacketNumber == 0 { + LogStatus = "Log download progress: 0%" + } else { + percent := (float64(r.LogStatus.PacketNumber) / float64(r.LogStatus.PacketTotal)) * 100 + OtaStatus = "Log download progress: " + fmt.Sprint(roundFloat(float64(percent), 3)) + "%" + if r.LogStatus.PacketNumber == r.LogStatus.PacketTotal { + OtaStatus = "Log download is complete!" + return + } + } + } +} + +func roundFloat(val float64, precision uint) float64 { + ratio := math.Pow(10, float64(precision)) + return math.Round(val*ratio) / ratio +} + +func IsDevRobot(esn, firmware string) bool { + if strings.Contains(firmware, "ankidev") { + return true + } + if strings.HasPrefix(esn, "00e") { + return true + } + return false +} + +func InitBle() (*ble.VectorBLE, error) { + BleStatusChan = nil + BleStatusChan = make(chan ble.StatusChannel) + done := make(chan bool) + var client *ble.VectorBLE + var err error + + go func() { + client, err = ble.New( + ble.WithStatusChan(BleStatusChan), + ble.WithLogDirectory(os.TempDir()), + ) + done <- true + }() + + select { + case <-done: + if err != nil { + if strings.Contains(err.Error(), "hci0: can't down device") || strings.Contains(err.Error(), "hci0: can't up device") { + FixBLEDriver() + client, err = ble.New( + ble.WithStatusChan(BleStatusChan), + ble.WithLogDirectory(os.TempDir()), + ) + return client, err + } + } + return client, err + case <-time.After(5 * time.Second): + done2 := make(chan bool) + FixBLEDriver() + go func() { + client, err = ble.New( + ble.WithStatusChan(BleStatusChan), + ble.WithLogDirectory(os.TempDir()), + ) + done2 <- true + }() + + select { + case <-done2: + return client, err + case <-time.After(5 * time.Second): + return nil, errors.New("error: took more than 5 seconds") + } + } +} + +func FixBLEDriver() { + logger.Println("BLE driver has broken. Removing then inserting bluetooth kernel modules") + rmmodList := []string{"btusb", "btrtl", "btmtk", "btintel", "btbcm"} + modprobeList := []string{"btrtl", "btmtk", "btintel", "btbcm", "btusb"} + for _, mod := range rmmodList { + exec.Command("/bin/rmmod", mod).Run() + } + time.Sleep(time.Second / 2) + for _, mod := range modprobeList { + exec.Command("/bin/modprobe", mod).Run() + } + time.Sleep(time.Second / 2) +} + +func ScanForVectors(client *ble.VectorBLE) ([]VectorsBle, error) { + var returnDevices []VectorsBle + resp, err := client.Scan() + if err != nil { + return nil, err + } + for _, device := range resp.Devices { + var vectorble VectorsBle + vectorble.Address = device.Address + vectorble.ID = device.ID + vectorble.Name = device.Name + returnDevices = append(returnDevices, vectorble) + } + return returnDevices, nil +} + +func ConnectVector(client *ble.VectorBLE, device int) error { + done := make(chan bool) + var err error + go func() { + err = client.Connect(device) + done <- true + }() + + select { + case <-done: + return err + case <-time.After(5 * time.Second): + FixBLEDriver() + return errors.New("error: took more than 5 seconds") + } +} + +func SendPin(pin string, client *ble.VectorBLE) error { + if len([]rune(pin)) != 6 { + return fmt.Errorf("error: length of pin must be 6") + } + err := client.SendPin(pin) + return err +} + +func RobotStatus(client *ble.VectorBLE) string { + var firmware string + var esn string + //v1.8.1.6051-453e582_os1.8.1.6051ep-1536e0d-202202282217 + //v0.9.0-12efb91_os0.9.0-3e8307e-201806191226 + + status, _ := client.GetStatus() + esn = status.ESN + firmware = status.Version + + if IsDevRobot(esn, firmware) && strings.Contains(firmware, "0.9.0") { + return "in_recovery_dev" + } + if strings.Contains(firmware, "0.9.0") { + return "in_recovery_prod" + } + if IsDevRobot(esn, firmware) { + return "in_firmware_dev" + } + if strings.Contains(firmware, "ep-") { + return "in_firmware_ep" + } + return "in_firmware_nonep" +} + +func AuthRobot(client *ble.VectorBLE) (bool, error) { + resp, err := client.Auth("2vMhFgktH3Jrbemm2WHkfGN") + if err != nil { + return false, err + } + return resp.Success, nil +} + +func BluetoothSetupAPI(w http.ResponseWriter, r *http.Request) { + switch { + case r.URL.Path == "/api-ble/init": + if BleInited { + fmt.Fprint(w, "success (ble already initiated, disconnect to reinit)") + return + } + var err error + BleClient, err = InitBle() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + BleInited = true + fmt.Fprint(w, "success") + return + case r.URL.Path == "/api-ble/scan": + if !BleInited { + fmt.Fprint(w, "error: init ble first") + return + } + devices, err := ScanForVectors(BleClient) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + returnBytes, _ := json.Marshal(devices) + w.Write(returnBytes) + return + case r.URL.Path == "/api-ble/connect": + if !BleInited { + fmt.Fprint(w, "error: init ble first") + return + } + id, err := strconv.Atoi(r.FormValue("id")) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + err = ConnectVector(BleClient, id) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + if err.Error() == "error: took more than 5 seconds" { + logger.Println("It took too long to connect to Vector. Quitting and letting systemd restart") + os.Exit(1) + } + } + fmt.Fprint(w, "success") + return + case r.URL.Path == "/api-ble/send_pin": + if !BleInited { + fmt.Fprint(w, "error: init ble first") + return + } + pin := r.FormValue("pin") + err := SendPin(pin, BleClient) + if err != nil { + if strings.Contains(err.Error(), "EOF") { + logger.Println("Wrong BLE pin was entered (sendpin error = eof), reinitializing BLE client") + BleClient.Close() + time.Sleep(time.Second) + BleClient, err = InitBle() + if err != nil { + fmt.Fprint(w, "error reinitializing ble: "+err.Error()) + return + } + fmt.Fprint(w, "incorrect pin") + } else { + fmt.Fprint(w, "error: "+err.Error()) + } + return + } + fmt.Fprint(w, "success") + return + case r.URL.Path == "/api-ble/get_wifi_status": + resp, err := BleClient.GetStatus() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, resp.WifiState) + return + case r.URL.Path == "/api-ble/get_firmware_version": + //v1.8.1.6051-453e582_os1.8.1.6051ep-1536e0d-202202282217 + //v0.9.0-12efb91_os0.9.0-3e8307e-201806191226 + resp, err := BleClient.GetStatus() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + // split version to just get ankiversion + splitOsAndRev := strings.Split(resp.Version, "_os") + splitOs := strings.Split(splitOsAndRev[1], "-")[0] + fmt.Fprint(w, splitOs) + return + case r.URL.Path == "/api-ble/get_ip_address": + resp, err := BleClient.WifiIP() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, resp.IPv4) + return + case r.URL.Path == "/api-ble/start_ota": + otaUrl := r.FormValue("url") + if strings.TrimSpace(otaUrl) == "local" { + logger.Println("Starting proxy download from archive.org") + otaUrl = "http://" + vars.GetOutboundIP().String() + ":" + vars.WebPort + "/api/get_ota/vicos-2.0.1.6076ep.ota" + logger.Println("(" + otaUrl + ")") + } + if strings.Contains(otaUrl, "https://") { + fmt.Fprint(w, "error: ota URL must be http") + return + } + go doOTAStatus() + done := make(chan bool) + var resp *ble.OTAStartResponse + var err error + + go func() { + resp, err = BleClient.OTAStart(otaUrl) + done <- true + }() + + select { + case <-done: + fmt.Println("done") + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, resp.Status) + return + case <-time.After(15 * time.Second): + fmt.Fprint(w, "likely success") + return + } + case r.URL.Path == "/api-ble/get_ota_status": + fmt.Fprint(w, OtaStatus) + return + case r.URL.Path == "/api-ble/stop_ota": + resp, err := BleClient.OTACancel() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, "success: "+string(resp)) + return + case r.URL.Path == "/api-ble/do_dev_setup": + go doLogStatus() + resp, err := BleClient.DownloadLogs() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + logger.Println(resp.Filename) + zip, err := os.Open(resp.Filename) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + tarFile := bzip2.NewReader(zip) + tarReader := tar.NewReader(tarFile) + for { + header, err := tarReader.Next() + if err != nil { + if err == io.EOF { + break + } + fmt.Fprint(w, "error: "+err.Error()) + return + } + name := header.FileInfo().Name() + logger.Println(name) + } + fmt.Fprint(w, "done") + return + case r.URL.Path == "/api-ble/scan_wifi": + var returnNetworks []WifiNetwork + resp, err := BleClient.WifiScan() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + for _, network := range resp.Networks { + var returnNetwork WifiNetwork + returnNetwork.SSID = network.WifiSSID + returnNetwork.AuthType = network.AuthType + returnNetworks = append(returnNetworks, returnNetwork) + } + returnJson, _ := json.Marshal(returnNetworks) + w.Write(returnJson) + return + case r.URL.Path == "/api-ble/connect_wifi": + if r.FormValue("ssid") == "" || r.FormValue("password") == "" { + fmt.Fprint(w, "error: ssid or password empty") + return + } + authType, err := strconv.Atoi(r.FormValue("authType")) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + } + resp, err := BleClient.WifiConnect(r.FormValue("ssid"), r.FormValue("password"), 15, authType) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + fmt.Fprint(w, resp.Result) + case r.URL.Path == "/api-ble/get_robot_status": + if !BleInited { + fmt.Fprint(w, "error: init ble first") + return + } + fmt.Fprint(w, RobotStatus(BleClient)) + case r.URL.Path == "/api-ble/do_auth": + if !BleInited { + fmt.Fprint(w, "error: init ble first") + return + } + for i := 0; i <= 3; i++ { + resp, err := AuthRobot(BleClient) + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + if resp { + time.Sleep(time.Second) + fmt.Fprint(w, "success") + return + } else { + if vars.APIConfig.Server.EPConfig { + logger.Println("BLE authentication was not successful. Posting mDNS and trying again (" + fmt.Sprint(i) + "/3)...") + mdnshandler.PostmDNSNow() + time.Sleep(time.Second * 2) + continue + } else { + logger.Println("BLE authentication was not successful") + fmt.Fprint(w, "error authenticating") + return + } + } + } + logger.Println("BLE authentication attempts exhausted") + fmt.Fprint(w, "error authenticating") + return + case r.URL.Path == "/api-ble/reset_onboarding": + BleClient.SDKProxy( + &ble.SDKProxyRequest{ + URLPath: "/v1/send_onboarding_input", + Body: `{"onboarding_set_phase_request": {"phase": 2}}`, + }, + ) + case r.URL.Path == "/api-ble/onboard": + wAnim := r.FormValue("with_anim") + if wAnim == "true" { + BleClient.SDKProxy( + &ble.SDKProxyRequest{ + URLPath: "/v1/send_onboarding_input", + Body: `{"onboarding_wake_up_request": {}}`, + }, + ) + time.Sleep(time.Second * 21) + } + BleClient.SDKProxy( + &ble.SDKProxyRequest{ + URLPath: "/v1/send_onboarding_input", + Body: `{"onboarding_mark_complete_and_exit": {}}`, + }, + ) + fmt.Fprint(w, "done") + case r.URL.Path == "/api-ble/disconnect": + err := BleClient.Close() + if err != nil { + fmt.Fprint(w, "error: "+err.Error()) + return + } + BleInited = false + fmt.Fprint(w, "success") + return + } +} + +func RegisterBLEAPI() { + http.HandleFunc("/api-ble/", BluetoothSetupAPI) +} diff --git a/chipper/pkg/wirepod/setup/ble_other.go b/chipper/pkg/wirepod/setup/ble_other.go new file mode 100644 index 00000000..3610146b --- /dev/null +++ b/chipper/pkg/wirepod/setup/ble_other.go @@ -0,0 +1,10 @@ +//go:build !inbuiltble +// +build !inbuiltble + +package botsetup + +import "github.com/kercre123/wire-pod/chipper/pkg/logger" + +func RegisterBLEAPI() { + logger.Println("BLE API is unregistered") +} diff --git a/chipper/pkg/wirepod/setup/certs.go b/chipper/pkg/wirepod/setup/certs.go index abbe28a5..651381c7 100644 --- a/chipper/pkg/wirepod/setup/certs.go +++ b/chipper/pkg/wirepod/setup/certs.go @@ -8,7 +8,6 @@ import ( "crypto/x509/pkix" "encoding/json" "encoding/pem" - "log" "math/big" "net" "os" @@ -27,20 +26,10 @@ type ClientServerConfig struct { Appkey string `json:"appkey"` } -func GetOutboundIP() net.IP { - conn, err := net.Dial("udp", vars.OutboundIPTester) - if err != nil { - log.Fatal(err) - } - defer conn.Close() - localAddr := conn.LocalAddr().(*net.UDPAddr) - return localAddr.IP -} - // creates and exports a priv/pub key combo generated with IP address func CreateCertCombo() error { // get preferred IP address of machine - ipAddr := GetOutboundIP() + ipAddr := vars.GetOutboundIP() // ca certificate ca := &x509.Certificate{ @@ -120,7 +109,7 @@ func CreateServerConfig() { config.Logfiles = "s3://anki-device-logs-prod/victor" config.Appkey = "oDoa0quieSeir6goowai7f" } else { - ip := GetOutboundIP() + ip := vars.GetOutboundIP() ipString := ip.String() url := ipString + ":" + vars.APIConfig.Server.Port config.Jdocs = url diff --git a/chipper/pkg/wirepod/setup/ssh.go b/chipper/pkg/wirepod/setup/ssh.go index fa3bf800..a274fc8f 100644 --- a/chipper/pkg/wirepod/setup/ssh.go +++ b/chipper/pkg/wirepod/setup/ssh.go @@ -43,10 +43,17 @@ func runCmd(client *ssh.Client, cmd string) (string, error) { return string(output), nil } +func setCPURAMfreq(client *ssh.Client, cpufreq string, ramfreq string, gov string) { + runCmd(client, "echo "+cpufreq+" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq && echo disabled > /sys/kernel/debug/msm_otg/bus_voting && echo 0 > /sys/kernel/debug/msm-bus-dbg/shell-client/update_request && echo 1 > /sys/kernel/debug/msm-bus-dbg/shell-client/mas && echo 512 > /sys/kernel/debug/msm-bus-dbg/shell-client/slv && echo 0 > /sys/kernel/debug/msm-bus-dbg/shell-client/ab && echo active clk2 0 1 max "+ramfreq+" > /sys/kernel/debug/rpm_send_msg/message && echo "+gov+" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor && echo 1 > /sys/kernel/debug/msm-bus-dbg/shell-client/update_request") +} + func SetupBotViaSSH(ip string, key []byte) error { - if runtime.GOOS == "android" { + if runtime.GOOS == "android" || runtime.GOOS == "ios" { SetupScriptPath = vars.AndroidPath + "/static/pod-bot-install.sh" } + if vars.IsPackagedLinux { + SetupScriptPath = "./pod-bot-install.sh" + } if !SSHSettingUp { logger.Println("Setting up " + ip + " via SSH") SetupSSHStatus = "Setting up SSH connection..." @@ -61,7 +68,7 @@ func SetupBotViaSSH(ip string, key []byte) error { ssh.PublicKeys(signer), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), - HostKeyAlgorithms: []string{"ssh-rsa"}, + HostKeyAlgorithms: []string{"ssh-rsa", "ecdsa-sha2-nistp256"}, Timeout: time.Second * 5, } client, err := ssh.Dial("tcp", ip+":22", config) @@ -76,13 +83,30 @@ func SetupBotViaSSH(ip string, key []byte) error { if !strings.Contains(output, "Vector") { return doErr(fmt.Errorf("the remote device is not a vector"), "checking if vector") } + SetupSSHStatus = "Checking if Vector is running CFW..." + output, err = runCmd(client, "cat /build.prop") + if err != nil { + return doErr(err, "checking if cfw") + } + // outputWired, _ := runCmd(client, "cat /etc/wired/webroot/index.html") + var doCloud bool = true + var initCommand string = "mount -o rw,remount / && mount -o rw,remount,exec /data && systemctl stop anki-robot.target mm-anki-camera mm-qcamera-daemon" + if strings.Contains(output, "wire_os") { + //|| strings.Contains(outputWired, "revertDefaultWakeWord") { + initCommand = "mount -o rw,remount,exec /data && systemctl stop anki-robot.target mm-anki-camera mm-qcamera-daemon" + // my cfw already has a wire-pod compatible vic-cloud + doCloud = false + } SetupSSHStatus = "Running initial commands before transfers (screen will go blank, this is normal)..." - _, err = runCmd(client, "mount -o rw,remount / && mount -o rw,remount,exec /data && systemctl stop anki-robot.target && mv /anki/data/assets/cozmo_resources/config/server_config.json /anki/data/assets/cozmo_resources/config/server_config.json.bak") + _, err = runCmd(client, initCommand) if err != nil { if !strings.Contains(err.Error(), "Process exited with status 1") { return doErr(err, "initial commands") } } + setCPURAMfreq(client, "1267200", "800000", "performance") + SetupSSHStatus = "Waiting a few seconds for filesystem syncing" + time.Sleep(time.Second * 3) SetupSSHStatus = "Transferring bot setup script and certs..." scpClient, err := scp.NewClientBySSH(client) if err != nil { @@ -96,7 +120,7 @@ func SetupBotViaSSH(ip string, key []byte) error { if err != nil { return doErr(err, "copying pod-bot-install") } - scpClient.Session.Close() + scpClient.Close() serverConfig, err := os.Open(vars.ServerConfigPath) if err != nil { return doErr(err, "opening server config on disk") @@ -105,36 +129,66 @@ func SetupBotViaSSH(ip string, key []byte) error { if err != nil { return doErr(err, "new scp client 2") } - err = scpClient.CopyFile(context.Background(), serverConfig, "/anki/data/assets/cozmo_resources/config/server_config.json", "0755") + err = scpClient.CopyFile(context.Background(), serverConfig, "/data/data/server_config.json", "0755") if err != nil { return doErr(err, "copying server-config.json") } - scpClient.Session.Close() - cloud, err := os.Open("../vector-cloud/build/vic-cloud") - if err != nil { - return doErr(err, "transferring new vic-cloud") - } - SetupSSHStatus = "Transferring new vic-cloud..." - scpClient, err = scp.NewClientBySSH(client) - if err != nil { - return doErr(err, "new scp client 3") - } - err = scpClient.CopyFile(context.Background(), cloud, "/anki/bin/vic-cloud", "0755") - if err != nil { - time.Sleep(time.Second * 1) - scpClient, err = scp.NewClientBySSH(client) - if err != nil { - return doErr(err, "copying vic-cloud") - } - err = scpClient.CopyFile(context.Background(), cloud, "/anki/bin/vic-cloud", "0755") - if err != nil { - return doErr(err, "copying vic-cloud") + scpClient.Close() + if doCloud { + if runtime.GOOS != "android" && !vars.Packaged { + cloud, err := os.Open("../vector-cloud/build/vic-cloud") + if err != nil { + return doErr(err, "transferring new vic-cloud") + } + SetupSSHStatus = "Transferring new vic-cloud..." + scpClient, err = scp.NewClientBySSH(client) + if err != nil { + return doErr(err, "new scp client 3") + } + err = scpClient.CopyFile(context.Background(), cloud, "/anki/bin/vic-cloud", "0755") + if err != nil { + time.Sleep(time.Second * 1) + scpClient, err = scp.NewClientBySSH(client) + if err != nil { + return doErr(err, "copying vic-cloud") + } + err = scpClient.CopyFile(context.Background(), cloud, "/anki/bin/vic-cloud", "0755") + if err != nil { + return doErr(err, "copying vic-cloud") + } + } + } else { + resp, err := http.Get("https://github.com/kercre123/wire-pod/raw/main/vector-cloud/build/vic-cloud") + if err != nil { + return doErr(err, "transferring new vic-cloud (download)") + } + SetupSSHStatus = "Transferring new vic-cloud..." + scpClient, err = scp.NewClientBySSH(client) + if err != nil { + return doErr(err, "new scp client 3") + } + err = scpClient.CopyFile(context.Background(), resp.Body, "/anki/bin/vic-cloud", "0755") + if err != nil { + time.Sleep(time.Second * 1) + scpClient, err = scp.NewClientBySSH(client) + if err != nil { + return doErr(err, "copying vic-cloud") + } + err = scpClient.CopyFile(context.Background(), resp.Body, "/anki/bin/vic-cloud", "0755") + if err != nil { + return doErr(err, "copying vic-cloud") + } + } } } - scpClient.Session.Close() + scpClient.Close() certPath := vars.CertPath if vars.APIConfig.Server.EPConfig { - certPath = "./epod/ep.crt" + if runtime.GOOS == "android" || runtime.GOOS == "ios" { + certPath = vars.AndroidPath + "/static/epod/ep.crt" + } else { + certPath = "./epod/ep.crt" + } } cert, err := os.Open(certPath) if err != nil { @@ -144,20 +198,17 @@ func SetupBotViaSSH(ip string, key []byte) error { if err != nil { return doErr(err, "new scp client 4") } - err = scpClient.CopyFile(context.Background(), cert, "/anki/etc/wirepod-cert.crt", "0755") + err = scpClient.CopyFile(context.Background(), cert, "/data/data/wirepod-cert.crt", "0755") if err != nil { return doErr(err, "copying wire-pod cert") } - scpClient.Session.Close() - _, err = runCmd(client, "cp /anki/etc/wirepod-cert.crt /data/data/wirepod-cert.crt") - if err != nil { - return doErr(err, "copying wire-pod cert in robot") - } + scpClient.Close() SetupSSHStatus = "Generating new robot certificate (this may take a while)..." - _, err = runCmd(client, "chmod +rwx /anki/data/assets/cozmo_resources/config/server_config.json /anki/bin/vic-cloud /data/data/wirepod-cert.crt /anki/etc/wirepod-cert.crt /data/pod-bot-install.sh && /data/pod-bot-install.sh") + _, err = runCmd(client, "chmod +rwx /data/data/server_config.json /data/data/wirepod-cert.crt /data/pod-bot-install.sh && /data/pod-bot-install.sh") if err != nil { return doErr(err, "generating new robot cert") } + setCPURAMfreq(client, "733333", "500000", "interactive") client.Close() SetupSSHStatus = "done" } else { @@ -179,7 +230,7 @@ func SSHSetup(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "error: must provide ssh key ("+err.Error()+")") return } - keyBytes, _ := io.ReadAll(key) + keyBytes, err := io.ReadAll(key) if len(keyBytes) < 5 { fmt.Fprint(w, "error: must provide ssh key ("+err.Error()+")") return diff --git a/chipper/pkg/wirepod/speechrequest/speechrequest.go b/chipper/pkg/wirepod/speechrequest/speechrequest.go index c6f504c4..c808b400 100755 --- a/chipper/pkg/wirepod/speechrequest/speechrequest.go +++ b/chipper/pkg/wirepod/speechrequest/speechrequest.go @@ -1,9 +1,13 @@ package speechrequest import ( + "bytes" "encoding/binary" "errors" + "fmt" + "math" "os" + "time" pb "github.com/digital-dream-labs/api/go/chipperpb" "github.com/digital-dream-labs/opus-go/opus" @@ -19,22 +23,23 @@ var debugWriteFile bool = false var debugFile *os.File type SpeechRequest struct { - Device string - Session string - FirstReq []byte - Stream interface{} - IsKG bool - IsIG bool - MicData []byte - DecodedMicData []byte - PrevLen int - PrevLenRaw int - InactiveFrames int - ActiveFrames int - VADInst *webrtcvad.VAD - LastAudioChunk []byte - IsOpus bool - OpusStream *opus.OggStream + Device string + Session string + FirstReq []byte + Stream interface{} + IsKG bool + IsIG bool + MicData []byte + DecodedMicData []byte + FilteredMicData []byte + PrevLen int + PrevLenRaw int + InactiveFrames int + ActiveFrames int + VADInst *webrtcvad.VAD + LastAudioChunk []byte + IsOpus bool + OpusStream *opus.OggStream } func BytesToSamples(buf []byte) []int16 { @@ -67,7 +72,7 @@ func (req *SpeechRequest) OpusDecode(chunk []byte) []byte { } return n } else { - return req.MicData + return chunk } } @@ -101,17 +106,15 @@ func BytesToIntVAD(stream opus.OggStream, data []byte, die bool, isOpus bool) [] } // Uses VAD to detect when the user stops speaking -func (req *SpeechRequest) DetectEndOfSpeech() bool { +func (req *SpeechRequest) DetectEndOfSpeech() (bool, bool) { // changes InactiveFrames and ActiveFrames in req inactiveNumMax := 23 - vad := req.VADInst - vad.SetMode(3) for _, chunk := range SplitVAD(req.LastAudioChunk) { - active, err := vad.Process(16000, chunk) + active, err := req.VADInst.Process(16000, chunk) if err != nil { logger.Println("VAD err:") logger.Println(err) - return true + return true, false } if active { req.ActiveFrames = req.ActiveFrames + 1 @@ -121,10 +124,87 @@ func (req *SpeechRequest) DetectEndOfSpeech() bool { } if req.InactiveFrames >= inactiveNumMax && req.ActiveFrames > 18 { logger.Println("(Bot " + req.Device + ") End of speech detected.") - return true + return true, true } } - return false + if req.ActiveFrames < 5 { + return false, false + } + return false, true +} + +func bytesToInt16(data []byte) ([]int16, error) { + var samples []int16 + buf := bytes.NewReader(data) + for buf.Len() > 0 { + var sample int16 + err := binary.Read(buf, binary.LittleEndian, &sample) + if err != nil { + return nil, err + } + samples = append(samples, sample) + } + return samples, nil +} + +func int16ToBytes(samples []int16) []byte { + buf := new(bytes.Buffer) + for _, sample := range samples { + err := binary.Write(buf, binary.LittleEndian, sample) + if err != nil { + return nil + } + } + return buf.Bytes() +} + +func applyGain(samples []int16, gain float64) []int16 { + for i, sample := range samples { + amplifiedSample := float64(sample) * gain + if amplifiedSample > math.MaxInt16 { + samples[i] = math.MaxInt16 + } else if amplifiedSample < math.MinInt16 { + samples[i] = math.MinInt16 + } else { + samples[i] = int16(amplifiedSample) + } + } + return samples +} + +// remove noise +func highPassFilter(data []byte) []byte { + bTime := time.Now() + sampleRate := 16000 + cutoffFreq := 300.0 + samples, err := bytesToInt16(data) + if err != nil { + return nil + } + samples = applyGain(samples, 5) + filteredSamples := make([]float64, len(samples)) + rc := 1.0 / (2.0 * math.Pi * cutoffFreq) + dt := 1.0 / float64(sampleRate) + alpha := dt / (rc + dt) + + previous := float64(samples[0]) + for i := 1; i < len(samples); i++ { + current := float64(samples[i]) + filtered := alpha * (filteredSamples[i-1] + current - previous) + filteredSamples[i] = filtered + previous = current + } + int16FilteredSamples := make([]int16, len(filteredSamples)) + for i, sample := range filteredSamples { + int16FilteredSamples[i] = int16(sample) + } + + gained := applyGain(int16FilteredSamples, 1.5) + if os.Getenv("DEBUG_PRINT_HIGHPASS") == "true" { + logger.Println("highpass filter took: " + fmt.Sprint(time.Since(bTime))) + } + + return int16ToBytes(gained) } // Converts a vtt.*Request to a SpeechRequest, which allows functions like DetectEndOfSpeech to work @@ -136,6 +216,7 @@ func ReqToSpeechRequest(req interface{}) SpeechRequest { request.PrevLen = 0 var err error request.VADInst, err = webrtcvad.New() + request.VADInst.SetMode(2) if err != nil { logger.Println(err) } @@ -172,9 +253,10 @@ func ReqToSpeechRequest(req interface{}) SpeechRequest { if isOpus { request.OpusStream = &opus.OggStream{} decodedFirstReq, _ := request.OpusStream.Decode(request.FirstReq) - request.FirstReq = decodedFirstReq + request.FirstReq = highPassFilter(decodedFirstReq) + request.FilteredMicData = append(request.FilteredMicData, request.FirstReq...) request.DecodedMicData = append(request.DecodedMicData, decodedFirstReq...) - request.LastAudioChunk = request.DecodedMicData[request.PrevLen:] + request.LastAudioChunk = request.FilteredMicData[request.PrevLen:] request.PrevLen = len(request.DecodedMicData) request.IsOpus = true } @@ -193,8 +275,9 @@ func (req *SpeechRequest) GetNextStreamChunk() ([]byte, error) { } req.MicData = append(req.MicData, chunk.InputAudio...) req.DecodedMicData = append(req.DecodedMicData, req.OpusDecode(chunk.InputAudio)...) + req.FilteredMicData = append(req.FilteredMicData, highPassFilter(req.OpusDecode(chunk.InputAudio))...) dataReturn := req.DecodedMicData[req.PrevLen:] - req.LastAudioChunk = req.DecodedMicData[req.PrevLen:] + req.LastAudioChunk = req.FilteredMicData[req.PrevLen:] req.PrevLen = len(req.DecodedMicData) return dataReturn, nil } else if str, ok := req.Stream.(pb.ChipperGrpc_StreamingIntentGraphServer); ok { @@ -206,8 +289,9 @@ func (req *SpeechRequest) GetNextStreamChunk() ([]byte, error) { } req.MicData = append(req.MicData, chunk.InputAudio...) req.DecodedMicData = append(req.DecodedMicData, req.OpusDecode(chunk.InputAudio)...) + req.FilteredMicData = append(req.FilteredMicData, highPassFilter(req.OpusDecode(chunk.InputAudio))...) dataReturn := req.DecodedMicData[req.PrevLen:] - req.LastAudioChunk = req.DecodedMicData[req.PrevLen:] + req.LastAudioChunk = req.FilteredMicData[req.PrevLen:] req.PrevLen = len(req.DecodedMicData) if debugWriteFile { debugFile.Write(chunk.InputAudio) @@ -222,8 +306,9 @@ func (req *SpeechRequest) GetNextStreamChunk() ([]byte, error) { } req.MicData = append(req.MicData, chunk.InputAudio...) req.DecodedMicData = append(req.DecodedMicData, req.OpusDecode(chunk.InputAudio)...) + req.FilteredMicData = append(req.FilteredMicData, highPassFilter(req.OpusDecode(chunk.InputAudio))...) dataReturn := req.DecodedMicData[req.PrevLen:] - req.LastAudioChunk = req.DecodedMicData[req.PrevLen:] + req.LastAudioChunk = req.FilteredMicData[req.PrevLen:] req.PrevLen = len(req.DecodedMicData) return dataReturn, nil } diff --git a/chipper/pkg/wirepod/stt/coqui/Coqui.go b/chipper/pkg/wirepod/stt/coqui/Coqui.go index 757f788f..47dfb995 100755 --- a/chipper/pkg/wirepod/stt/coqui/Coqui.go +++ b/chipper/pkg/wirepod/stt/coqui/Coqui.go @@ -70,7 +70,7 @@ func STT(req sr.SpeechRequest) (string, error) { return "", err } coquiStream.FeedAudioContent(sr.BytesToSamples(chunk)) - speechIsDone = req.DetectEndOfSpeech() + speechIsDone, _ = req.DetectEndOfSpeech() if speechIsDone { break } diff --git a/chipper/pkg/wirepod/stt/houndify/Houndify.go b/chipper/pkg/wirepod/stt/houndify/Houndify.go index 59a94049..26eefb3e 100755 --- a/chipper/pkg/wirepod/stt/houndify/Houndify.go +++ b/chipper/pkg/wirepod/stt/houndify/Houndify.go @@ -58,7 +58,7 @@ func STT(sreq sr.SpeechRequest) (string, error) { default: var chunk []byte chunk, err = sreq.GetNextStreamChunkOpus() - speechDone = sreq.DetectEndOfSpeech() + speechDone, _ = sreq.DetectEndOfSpeech() if err != nil { fmt.Println("End of stream") return @@ -75,7 +75,7 @@ func STT(sreq sr.SpeechRequest) (string, error) { go func() { for partial := range partialTranscripts { if *partial.SafeToStopAudio { - fmt.Println("SafeToStopAudio recieved") + fmt.Println("SafeToStopAudio received") done <- true return } diff --git a/chipper/pkg/wirepod/stt/leopard/Leopard.go b/chipper/pkg/wirepod/stt/leopard/Leopard.go index 6941651e..d946e85c 100755 --- a/chipper/pkg/wirepod/stt/leopard/Leopard.go +++ b/chipper/pkg/wirepod/stt/leopard/Leopard.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - leopard "github.com/Picovoice/leopard/binding/go" + leopard "github.com/Picovoice/leopard/binding/go/v2" "github.com/kercre123/wire-pod/chipper/pkg/logger" sr "github.com/kercre123/wire-pod/chipper/pkg/wirepod/speechrequest" ) @@ -77,7 +77,7 @@ func STT(req sr.SpeechRequest) (transcribedText string, err error) { BotNumMu.Unlock() return "", err } - speechIsDone = req.DetectEndOfSpeech() + speechIsDone, _ = req.DetectEndOfSpeech() if speechIsDone { break } diff --git a/chipper/pkg/wirepod/stt/vosk/Vosk.go b/chipper/pkg/wirepod/stt/vosk/Vosk.go index 7f2b908e..286c3203 100755 --- a/chipper/pkg/wirepod/stt/vosk/Vosk.go +++ b/chipper/pkg/wirepod/stt/vosk/Vosk.go @@ -6,9 +6,11 @@ import ( "log" "os" "path/filepath" + "runtime" "sync" + "time" - vosk "github.com/alphacep/vosk-api/go" + vosk "github.com/kercre123/vosk-api/go" "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" sr "github.com/kercre123/wire-pod/chipper/pkg/wirepod/speechrequest" @@ -94,10 +96,51 @@ func Init() error { } modelLoaded = true logger.Println("VOSK initiated successfully") + runTest() } return nil } +func runTest() { + // make sure recognizer is all loaded into RAM + logger.Println("Running recognizer test") + var withGrm bool + if GrammerEnable { + logger.Println("Using grammer-optimized recognizer") + withGrm = true + } else { + logger.Println("Using general recognizer") + withGrm = false + } + rec, recind := getRec(withGrm) + sttTestPath := "./stttest.pcm" + if runtime.GOOS == "android" { + sttTestPath = vars.AndroidPath + "/static/stttest.pcm" + } + pcmBytes, _ := os.ReadFile(sttTestPath) + var micData [][]byte + cTime := time.Now() + micData = sr.SplitVAD(pcmBytes) + for _, sample := range micData { + rec.AcceptWaveform(sample) + } + var jres map[string]interface{} + json.Unmarshal([]byte(rec.FinalResult()), &jres) + if withGrm { + grmRecs[recind].InUse = false + } else { + gpRecs[recind].InUse = false + } + transcribedText := jres["text"].(string) + tTime := time.Now().Sub(cTime) + logger.Println("Text (from test):", transcribedText) + if tTime.Seconds() > 3 { + logger.Println("Vosk test took a while, performance may be degraded. (" + fmt.Sprint(tTime) + ")") + } + logger.Println("Vosk test successful! (Took " + fmt.Sprint(tTime) + ")") + +} + func getRec(withGrm bool) (*vosk.VoskRecognizer, int) { recsmu.Lock() defer recsmu.Unlock() @@ -142,7 +185,6 @@ func getRec(withGrm bool) (*vosk.VoskRecognizer, int) { func STT(req sr.SpeechRequest) (string, error) { logger.Println("(Bot " + req.Device + ", Vosk) Processing...") - speechIsDone := false var withGrm bool if (vars.APIConfig.Knowledge.IntentGraph || req.IsKG) || !GrammerEnable { logger.Println("Using general recognizer") @@ -160,9 +202,10 @@ func STT(req sr.SpeechRequest) (string, error) { if err != nil { return "", err } - rec.AcceptWaveform(chunk) - // has to be split into 320 []byte chunks for VAD - speechIsDone = req.DetectEndOfSpeech() + speechIsDone, doProcess := req.DetectEndOfSpeech() + if doProcess { + rec.AcceptWaveform(chunk) + } if speechIsDone { break } diff --git a/chipper/pkg/wirepod/stt/vosk/context.go b/chipper/pkg/wirepod/stt/vosk/context.go index 40f25ac6..72629410 100644 --- a/chipper/pkg/wirepod/stt/vosk/context.go +++ b/chipper/pkg/wirepod/stt/vosk/context.go @@ -24,8 +24,8 @@ func GetGrammerList(lang string) string { var wordsList []string var grammer string // add words in intent json - for _, words := range vars.MatchListList { - for _, word := range words { + for _, words := range vars.IntentList { + for _, word := range words.Keyphrases { wors := strings.Split(word, " ") for _, wor := range wors { found := model.FindWord(wor) diff --git a/chipper/pkg/wirepod/stt/whisper.cpp/WhisperCpp.go b/chipper/pkg/wirepod/stt/whisper.cpp/WhisperCpp.go index 304bbde2..af767c11 100644 --- a/chipper/pkg/wirepod/stt/whisper.cpp/WhisperCpp.go +++ b/chipper/pkg/wirepod/stt/whisper.cpp/WhisperCpp.go @@ -1,41 +1,66 @@ package wirepod_whispercpp import ( - "fmt" + "encoding/binary" + "math" "os" "path/filepath" - "strings" - "math" - "encoding/binary" "runtime" - - "github.com/ggerganov/whisper.cpp/bindings/go" + "strings" + + whisper "github.com/kercre123/whisper.cpp/bindings/go" "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" sr "github.com/kercre123/wire-pod/chipper/pkg/wirepod/speechrequest" ) - var Name string = "whisper.cpp" var context *whisper.Context var params whisper.Params +func padPCM(data []byte) []byte { + const sampleRate = 16000 + const minDurationMs = 1020 + const minDurationSamples = sampleRate * minDurationMs / 1000 + const bytesPerSample = 2 + + currentSamples := len(data) / bytesPerSample + + if currentSamples >= minDurationSamples { + return data + } + + logger.Println("Padding audio data to be 1000ms") + + paddingSamples := minDurationSamples - currentSamples + paddingBytes := make([]byte, paddingSamples*bytesPerSample) + + return append(data, paddingBytes...) +} + func Init() error { + whispModel := os.Getenv("WHISPER_MODEL") + if whispModel == "" { + logger.Println("WHISPER_MODEL not defined, assuming tiny") + whispModel = "tiny" + } else { + whispModel = strings.TrimSpace(whispModel) + } var sttLanguage string if len(vars.APIConfig.STT.Language) == 0 { sttLanguage = "en" - } else { + } else { sttLanguage = strings.Split(vars.APIConfig.STT.Language, "-")[0] } - modelPath := filepath.Join(vars.WhisperModelPath, "ggml-tiny.bin") + modelPath := filepath.Join(vars.WhisperModelPath, "ggml-"+whispModel+".bin") if _, err := os.Stat(modelPath); err != nil { - fmt.Println("Path does not exist: " + modelPath) + logger.Println("Model does not exist: " + modelPath) return err } logger.Println("Opening Whisper model (" + modelPath + ")") - logger.Println(whisper.Whisper_print_system_info()) + //logger.Println(whisper.Whisper_print_system_info()) context = whisper.Whisper_init(modelPath) params = context.Whisper_full_default_params(whisper.SAMPLING_GREEDY) params.SetTranslate(false) @@ -60,13 +85,12 @@ func STT(req sr.SpeechRequest) (string, error) { return "", err } // has to be split into 320 []byte chunks for VAD - speechIsDone = req.DetectEndOfSpeech() + speechIsDone, _ = req.DetectEndOfSpeech() if speechIsDone { break } } - - transcribedText, err := process(BytesToFloat32Buffer(req.DecodedMicData)) + transcribedText, err := process(BytesToFloat32Buffer(padPCM(req.DecodedMicData))) if err != nil { return "", err } @@ -90,4 +114,4 @@ func BytesToFloat32Buffer(buf []byte) []float32 { newB[i] = float32(float64(int16(binary.LittleEndian.Uint16(buf[i*2:]))) / factor) } return newB -} \ No newline at end of file +} diff --git a/chipper/pkg/wirepod/stt/whisper/Whisper.go b/chipper/pkg/wirepod/stt/whisper/Whisper.go index e5dd67a6..96e985b1 100755 --- a/chipper/pkg/wirepod/stt/whisper/Whisper.go +++ b/chipper/pkg/wirepod/stt/whisper/Whisper.go @@ -120,7 +120,7 @@ func STT(req sr.SpeechRequest) (string, error) { return "", err } // has to be split into 320 []byte chunks for VAD - speechIsDone = req.DetectEndOfSpeech() + speechIsDone, _ = req.DetectEndOfSpeech() if speechIsDone { break } diff --git a/chipper/pkg/wirepod/ttr/bcontrol.go b/chipper/pkg/wirepod/ttr/bcontrol.go index fb347e9c..a5ef94a0 100644 --- a/chipper/pkg/wirepod/ttr/bcontrol.go +++ b/chipper/pkg/wirepod/ttr/bcontrol.go @@ -6,6 +6,7 @@ import ( "github.com/fforchino/vector-go-sdk/pkg/vector" "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" ) func sayText(robot *vector.Vector, text string) { @@ -79,3 +80,62 @@ func sayText(robot *vector.Vector, text string) { } }() } + +func BControl(robot *vector.Vector, ctx context.Context, start, stop chan bool) { + controlRequest := &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRequest{ + ControlRequest: &vectorpb.ControlRequest{ + Priority: vectorpb.ControlRequest_OVERRIDE_BEHAVIORS, + }, + }, + } + + go func() { + // * begin - modified from official vector-go-sdk + r, err := robot.Conn.BehaviorControl( + ctx, + ) + if err != nil { + logger.Println(err) + return + } + + if err := r.Send(controlRequest); err != nil { + logger.Println(err) + return + } + + for { + ctrlresp, err := r.Recv() + if err != nil { + logger.Println(err) + return + } + if ctrlresp.GetControlGrantedResponse() != nil { + start <- true + break + } + } + + for { + select { + case <-stop: + logger.Println("KGSim: releasing behavior control (interrupt)") + if err := r.Send( + &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRelease{ + ControlRelease: &vectorpb.ControlRelease{}, + }, + }, + ); err != nil { + logger.Println(err) + return + } + return + default: + continue + } + } + // * end - modified from official vector-go-sdk + }() +} diff --git a/chipper/pkg/wirepod/ttr/convert.go b/chipper/pkg/wirepod/ttr/convert.go new file mode 100644 index 00000000..42a8d81b --- /dev/null +++ b/chipper/pkg/wirepod/ttr/convert.go @@ -0,0 +1,92 @@ +package wirepod_ttr + +import ( + "encoding/binary" + "math" +) + +func bytesToInt16s(data []byte) []int16 { + int16s := make([]int16, len(data)/2) + for i := range int16s { + int16s[i] = int16(binary.LittleEndian.Uint16(data[i*2 : i*2+2])) + } + return int16s +} + +func int16sToBytes(data []int16) []byte { + bytes := make([]byte, len(data)*2) + for i, val := range data { + binary.LittleEndian.PutUint16(bytes[i*2:], uint16(val)) + } + return bytes +} + +func downsample24kTo16k(input []byte) [][]byte { + outBytes := downsample24kTo16kLinear(input) + var audioChunks [][]byte + filteredBytes := lowPassFilter(outBytes, 4000, 16000) + iVolBytes := increaseVolume(filteredBytes, 5) + for len(iVolBytes) > 0 { + if len(iVolBytes) < 1024 { + chunk := make([]byte, 1024) + copy(chunk, iVolBytes) + audioChunks = append(audioChunks, chunk) + break + } + audioChunks = append(audioChunks, iVolBytes[:1024]) + iVolBytes = iVolBytes[1024:] + } + + return audioChunks +} + +func increaseVolume(data []byte, factor float64) []byte { + int16s := bytesToInt16s(data) + + for i := range int16s { + scaled := float64(int16s[i]) * factor + if scaled > math.MaxInt16 { + int16s[i] = math.MaxInt16 + } else if scaled < math.MinInt16 { + int16s[i] = math.MinInt16 + } else { + int16s[i] = int16(scaled) + } + } + + return int16sToBytes(int16s) +} + +// this is copied +func lowPassFilter(data []byte, cutoffFreq float64, sampleRate int) []byte { + int16s := bytesToInt16s(data) + filtered := make([]int16, len(int16s)) + rc := 1.0 / (2 * 3.1416 * cutoffFreq) + dt := 1.0 / float64(sampleRate) + alpha := dt / (rc + dt) + filtered[0] = int16s[0] + for i := 1; i < len(int16s); i++ { + current := alpha*float64(int16s[i]) + (1-alpha)*float64(filtered[i-1]) + filtered[i] = int16(current) + } + + return int16sToBytes(filtered) +} + +// copied too +func downsample24kTo16kLinear(input []byte) []byte { + int16s := bytesToInt16s(input) + outputLength := (len(int16s) * 2) / 3 + output := make([]int16, outputLength) + + j := 0 + for i := 0; i < len(int16s)-2; i += 3 { + first := (2*int32(int16s[i]) + int32(int16s[i+1])) / 3 + second := (int32(int16s[i+1]) + 2*int32(int16s[i+2])) / 3 + output[j] = int16(first) + output[j+1] = int16(second) + j += 2 + } + + return int16sToBytes(output) +} diff --git a/chipper/pkg/wirepod/ttr/intentparam.go b/chipper/pkg/wirepod/ttr/intentparam.go index e8dc82b9..ba130f90 100755 --- a/chipper/pkg/wirepod/ttr/intentparam.go +++ b/chipper/pkg/wirepod/ttr/intentparam.go @@ -22,6 +22,7 @@ func ParamChecker(req interface{}, intent string, speechText string, botSerial s var botUnits string = "F" var botPlaySpecific bool = false var botIsEarlyOpus bool = false + var DoWeatherError bool = false // see if jdoc exists botJdoc, jdocExists := vars.GetJdoc("vic:"+botSerial, "vic.RobotSettings") @@ -139,7 +140,13 @@ func ParamChecker(req interface{}, intent string, speechText string, botSerial s isParam = true newIntent = intent condition, is_forecast, local_datetime, speakable_location_string, temperature, temperature_unit := weatherParser(speechText, botLocation, botUnits) - intentParams = map[string]string{"condition": condition, "is_forecast": is_forecast, "local_datetime": local_datetime, "speakable_location_string": speakable_location_string, "temperature": temperature, "temperature_unit": temperature_unit} + if local_datetime == "test" { + newIntent = "intent_system_unmatched" + isParam = false + DoWeatherError = true + } else { + intentParams = map[string]string{"condition": condition, "is_forecast": is_forecast, "local_datetime": local_datetime, "speakable_location_string": speakable_location_string, "temperature": temperature, "temperature_unit": temperature_unit} + } } else if strings.Contains(intent, "intent_imperative_volumelevel_extend") { isParam = true newIntent = intent @@ -307,6 +314,15 @@ func ParamChecker(req interface{}, intent string, speechText string, botSerial s } } IntentPass(req, newIntent, speechText, intentParams, isParam) + if DoWeatherError { + if vars.APIConfig.Weather.Enable { + logger.Println("The weather API is not configured properly.") + KGSim(botSerial, "The weather API is not configured properly. Please check the wire pod logs for more details.") + } else { + logger.Println("The weather API is not configured.") + KGSim(botSerial, "The weather API is not configured.") + } + } } // stintent @@ -499,7 +515,7 @@ func ParamCheckerSlotsEnUS(req interface{}, intent string, slots map[string]stri IntentPass(req, newIntent, intent, intentParams, isParam) } -func prehistoricParamChecker(req interface{}, intent string, speechText string, botSerial string) { +func prehistoricParamChecker(req interface{}, intent string, speechText string) { // intent.go detects if the stream uses opus or PCM. // If the stream is PCM, it is likely a bot with 0.10. // This accounts for the newer 0.10.1### builds. diff --git a/chipper/pkg/wirepod/ttr/kgsim.go b/chipper/pkg/wirepod/ttr/kgsim.go new file mode 100644 index 00000000..8d306768 --- /dev/null +++ b/chipper/pkg/wirepod/ttr/kgsim.go @@ -0,0 +1,646 @@ +package wirepod_ttr + +import ( + "context" + "errors" + "fmt" + "io" + "log" + "regexp" + "strings" + "time" + "unicode" + + "golang.org/x/text/transform" + "golang.org/x/text/unicode/norm" + + "github.com/fforchino/vector-go-sdk/pkg/vector" + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/vars" + "github.com/sashabaranov/go-openai" +) + +func GetChat(esn string) vars.RememberedChat { + for _, chat := range vars.RememberedChats { + if chat.ESN == esn { + return chat + } + } + return vars.RememberedChat{ + ESN: esn, + } +} + +func PlaceChat(chat vars.RememberedChat) { + for i, achat := range vars.RememberedChats { + if achat.ESN == chat.ESN { + vars.RememberedChats[i] = chat + return + } + } + vars.RememberedChats = append(vars.RememberedChats, chat) +} + +// remember last 16 lines of chat +func Remember(user, ai openai.ChatCompletionMessage, esn string) { + chatAppend := []openai.ChatCompletionMessage{ + user, + ai, + } + currentChat := GetChat(esn) + if len(currentChat.Chats) == 16 { + var newChat vars.RememberedChat + newChat.ESN = currentChat.ESN + for i, chat := range currentChat.Chats { + if i < 2 { + continue + } + newChat.Chats = append(newChat.Chats, chat) + } + currentChat = newChat + } + currentChat.ESN = esn + currentChat.Chats = append(currentChat.Chats, chatAppend...) + PlaceChat(currentChat) +} + +func isMn(r rune) bool { + // Remove the characters that are not related to Vietnamese. + // Retain the tonal marks and diacritics such as the circumflex, ơ, and ư in Vietnamese. + keepMarks := []rune{ + '\u0300', // Dấu huyền + '\u0301', // Dấu sắc + '\u0303', // Dấu ngã + '\u0309', // Dấu hỏi + '\u0323', // Dấu nặng + '\u0302', // Dấu mũ (â, ê, ô) + '\u031B', // Dấu ơ và ư + '\u0306', // Dấu trầm + } + if unicode.Is(unicode.Mn, r) { + for _, mark := range keepMarks { + if r == mark { + return false + } + } + return true + } + return false +} + +func removeSpecialCharacters(str string) string { + + // these two lines create a transformation that decomposes characters, removes non-spacing marks (like diacritics), and then recomposes the characters, effectively removing special characters + t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC) + result, _, _ := transform.String(t, str) + + // Define the regular expression to match special characters + re := regexp.MustCompile(`[&^*#@]`) + + // Replace special characters with an empty string + result = removeEmojis(re.ReplaceAllString(result, "")) + + // Replace special characters with ASCII + // * COPY/PASTE TO ADD MORE CHARACTERS: + // result = strings.ReplaceAll(result, "", "") + result = strings.ReplaceAll(result, "‘", "'") + result = strings.ReplaceAll(result, "’", "'") + result = strings.ReplaceAll(result, "“", "\"") + result = strings.ReplaceAll(result, "”", "\"") + result = strings.ReplaceAll(result, "—", "-") + result = strings.ReplaceAll(result, "–", "-") + result = strings.ReplaceAll(result, "…", "...") + result = strings.ReplaceAll(result, "\u00A0", " ") + result = strings.ReplaceAll(result, "•", "*") + result = strings.ReplaceAll(result, "¼", "1/4") + result = strings.ReplaceAll(result, "½", "1/2") + result = strings.ReplaceAll(result, "¾", "3/4") + result = strings.ReplaceAll(result, "×", "x") + result = strings.ReplaceAll(result, "÷", "/") + result = strings.ReplaceAll(result, "ç", "c") + result = strings.ReplaceAll(result, "©", "(c)") + result = strings.ReplaceAll(result, "®", "(r)") + result = strings.ReplaceAll(result, "™", "(tm)") + result = strings.ReplaceAll(result, "@", "(a)") + result = strings.ReplaceAll(result, " AI ", " A. I. ") + return result +} + +func removeEmojis(input string) string { + // a mess, but it works! + re := regexp.MustCompile(`[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{1F1E0}-\x{1F1FF}]|[\x{2600}-\x{26FF}]|[\x{2700}-\x{27BF}]|[\x{1F900}-\x{1F9FF}]|[\x{1F004}]|[\x{1F0CF}]|[\x{1F18E}]|[\x{1F191}-\x{1F251}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]|[\x{1F004}-\x{1F0CF}]|[\x{1F191}-\x{1F251}]|[\x{2B50}]`) + result := re.ReplaceAllString(input, "") + return result +} + +func CreateAIReq(transcribedText, esn string, gpt3tryagain, isKG bool) openai.ChatCompletionRequest { + defaultPrompt := "You are a helpful, animated robot called Vector. Keep the response concise yet informative." + + var nChat []openai.ChatCompletionMessage + + smsg := openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleSystem, + } + if strings.TrimSpace(vars.APIConfig.Knowledge.OpenAIPrompt) != "" { + smsg.Content = strings.TrimSpace(vars.APIConfig.Knowledge.OpenAIPrompt) + } else { + smsg.Content = defaultPrompt + } + + var model string + + if gpt3tryagain { + model = openai.GPT3Dot5Turbo + } else if vars.APIConfig.Knowledge.Provider == "openai" { + model = openai.GPT4oMini + logger.Println("Using " + model) + } else { + logger.Println("Using " + vars.APIConfig.Knowledge.Model) + model = vars.APIConfig.Knowledge.Model + } + + smsg.Content = CreatePrompt(smsg.Content, model, isKG) + + nChat = append(nChat, smsg) + if vars.APIConfig.Knowledge.SaveChat { + rchat := GetChat(esn) + logger.Println("Using remembered chats, length of " + fmt.Sprint(len(rchat.Chats)) + " messages") + nChat = append(nChat, rchat.Chats...) + } + nChat = append(nChat, openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleUser, + Content: transcribedText, + }) + + aireq := openai.ChatCompletionRequest{ + Model: model, + MaxTokens: 2048, + Temperature: 1, + TopP: 1, + FrequencyPenalty: 0, + PresencePenalty: 0, + Messages: nChat, + Stream: true, + } + return aireq +} + +func StreamingKGSim(req interface{}, esn string, transcribedText string, isKG bool) (string, error) { + start := make(chan bool) + stop := make(chan bool) + stopStop := make(chan bool) + kgReadyToAnswer := make(chan bool) + kgStopLooping := false + ctx := context.Background() + matched := false + var robot *vector.Vector + var guid string + var target string + for _, bot := range vars.BotInfo.Robots { + if esn == bot.Esn { + guid = bot.GUID + target = bot.IPAddress + ":443" + matched = true + break + } + } + if matched { + var err error + robot, err = vector.New(vector.WithSerialNo(esn), vector.WithToken(guid), vector.WithTarget(target)) + if err != nil { + return err.Error(), err + } + } + _, err := robot.Conn.BatteryState(context.Background(), &vectorpb.BatteryStateRequest{}) + if err != nil { + return "", err + } + if isKG { + BControl(robot, ctx, start, stop) + go func() { + for { + if kgStopLooping { + kgReadyToAnswer <- true + break + } + robot.Conn.PlayAnimation(ctx, &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: "anim_knowledgegraph_searching_01", + }, + Loops: 1, + }) + time.Sleep(time.Second / 3) + } + }() + } + var fullRespText string + var fullfullRespText string + var fullRespSlice []string + var isDone bool + var c *openai.Client + if vars.APIConfig.Knowledge.Provider == "together" { + if vars.APIConfig.Knowledge.Model == "" { + vars.APIConfig.Knowledge.Model = "meta-llama/Llama-3-70b-chat-hf" + vars.WriteConfigToDisk() + } + conf := openai.DefaultConfig(vars.APIConfig.Knowledge.Key) + conf.BaseURL = "https://api.together.xyz/v1" + c = openai.NewClientWithConfig(conf) + } else if vars.APIConfig.Knowledge.Provider == "custom" { + conf := openai.DefaultConfig(vars.APIConfig.Knowledge.Key) + conf.BaseURL = vars.APIConfig.Knowledge.Endpoint + c = openai.NewClientWithConfig(conf) + } else if vars.APIConfig.Knowledge.Provider == "openai" { + c = openai.NewClient(vars.APIConfig.Knowledge.Key) + } + speakReady := make(chan string) + successIntent := make(chan bool) + + aireq := CreateAIReq(transcribedText, esn, false, isKG) + + stream, err := c.CreateChatCompletionStream(ctx, aireq) + if err != nil { + log.Printf("Error creating chat completion stream: %v", err) + if strings.Contains(err.Error(), "does not exist") && vars.APIConfig.Knowledge.Provider == "openai" { + logger.Println("GPT-4 model cannot be accessed with this API key. You likely need to add more than $5 dollars of funds to your OpenAI account.") + logger.LogUI("GPT-4 model cannot be accessed with this API key. You likely need to add more than $5 dollars of funds to your OpenAI account.") + aireq := CreateAIReq(transcribedText, esn, true, isKG) + logger.Println("Falling back to " + aireq.Model) + logger.LogUI("Falling back to " + aireq.Model) + stream, err = c.CreateChatCompletionStream(ctx, aireq) + if err != nil { + logger.Println("OpenAI still not returning a response even after falling back. Erroring.") + return "", err + } + } else { + if isKG { + kgStopLooping = true + for range kgReadyToAnswer { + break + } + stop <- true + time.Sleep(time.Second / 3) + KGSim(esn, "There was an error getting data from the L. L. M.") + } + return "", err + } + } + nChat := aireq.Messages + nChat = append(nChat, openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleAssistant, + }) + fmt.Println("LLM stream response: ") + go func() { + for { + response, err := stream.Recv() + if errors.Is(err, io.EOF) { + // prevents a crash + if len(fullRespSlice) == 0 { + logger.Println("LLM returned no response") + successIntent <- false + if isKG { + kgStopLooping = true + for range kgReadyToAnswer { + break + } + stop <- true + time.Sleep(time.Second / 3) + KGSim(esn, "There was an error getting data from the L. L. M.") + } + break + } + isDone = true + // if fullRespSlice != fullRespText, add that missing bit to fullRespSlice + newStr := fullRespSlice[0] + for i, str := range fullRespSlice { + if i == 0 { + continue + } + newStr = newStr + " " + str + } + if strings.TrimSpace(newStr) != strings.TrimSpace(fullfullRespText) { + logger.Println("LLM debug: there is content after the last punctuation mark") + extraBit := strings.TrimPrefix(fullRespText, newStr) + fullRespSlice = append(fullRespSlice, extraBit) + } + if vars.APIConfig.Knowledge.SaveChat { + Remember(openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleUser, + Content: transcribedText, + }, + openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleAssistant, + Content: newStr, + }, + esn) + } + logger.LogUI("LLM response for " + esn + ": " + newStr) + logger.Println("LLM stream finished") + return + } + + if err != nil { + logger.Println("Stream error: " + err.Error()) + return + } + + if (len(response.Choices) == 0) { + logger.Println("Empty response") + return + } + + fullfullRespText = fullfullRespText + removeSpecialCharacters(response.Choices[0].Delta.Content) + fullRespText = fullRespText + removeSpecialCharacters(response.Choices[0].Delta.Content) + if strings.Contains(fullRespText, "...") || strings.Contains(fullRespText, ".'") || strings.Contains(fullRespText, ".\"") || strings.Contains(fullRespText, ".") || strings.Contains(fullRespText, "?") || strings.Contains(fullRespText, "!") { + var sepStr string + if strings.Contains(fullRespText, "...") { + sepStr = "..." + } else if strings.Contains(fullRespText, ".'") { + sepStr = ".'" + } else if strings.Contains(fullRespText, ".\"") { + sepStr = ".\"" + } else if strings.Contains(fullRespText, ".") { + sepStr = "." + } else if strings.Contains(fullRespText, "?") { + sepStr = "?" + } else if strings.Contains(fullRespText, "!") { + sepStr = "!" + } + splitResp := strings.Split(strings.TrimSpace(fullRespText), sepStr) + fullRespSlice = append(fullRespSlice, strings.TrimSpace(splitResp[0])+sepStr) + fullRespText = splitResp[1] + select { + case successIntent <- true: + default: + } + select { + case speakReady <- strings.TrimSpace(splitResp[0]) + sepStr: + default: + } + } + } + }() + for is := range successIntent { + if is { + if !isKG { + IntentPass(req, "intent_greeting_hello", transcribedText, map[string]string{}, false) + } + break + } else { + return "", errors.New("llm returned no response") + } + } + time.Sleep(time.Millisecond * 200) + if !isKG { + BControl(robot, ctx, start, stop) + } + interrupted := false + go func() { + interrupted = InterruptKGSimWhenTouchedOrWaked(robot, stop, stopStop) + }() + var TTSLoopAnimation string + var TTSGetinAnimation string + if isKG { + TTSLoopAnimation = "anim_knowledgegraph_answer_01" + TTSGetinAnimation = "anim_knowledgegraph_searching_getout_01" + } else { + TTSLoopAnimation = "anim_tts_loop_02" + TTSGetinAnimation = "anim_getin_tts_01" + } + + var stopTTSLoop bool + TTSLoopStopped := make(chan bool) + for range start { + if isKG { + kgStopLooping = true + for range kgReadyToAnswer { + break + } + } else { + time.Sleep(time.Millisecond * 300) + } + robot.Conn.PlayAnimation( + ctx, + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: TTSGetinAnimation, + }, + Loops: 1, + }, + ) + if !vars.APIConfig.Knowledge.CommandsEnable { + go func() { + for { + if stopTTSLoop { + TTSLoopStopped <- true + break + } + robot.Conn.PlayAnimation( + ctx, + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: TTSLoopAnimation, + }, + Loops: 1, + }, + ) + } + }() + } + var disconnect bool + numInResp := 0 + for { + respSlice := fullRespSlice + if len(respSlice)-1 < numInResp { + if !isDone { + logger.Println("Waiting for more content from LLM...") + for range speakReady { + respSlice = fullRespSlice + break + } + } else { + break + } + } + if interrupted { + break + } + logger.Println(respSlice[numInResp]) + acts := GetActionsFromString(respSlice[numInResp]) + nChat[len(nChat)-1].Content = fullRespText + disconnect = PerformActions(nChat, acts, robot, stopStop) + if disconnect { + break + } + numInResp = numInResp + 1 + } + if !vars.APIConfig.Knowledge.CommandsEnable { + stopTTSLoop = true + for range TTSLoopStopped { + break + } + } + time.Sleep(time.Millisecond * 100) + // if isKG { + // robot.Conn.PlayAnimation( + // ctx, + // &vectorpb.PlayAnimationRequest{ + // Animation: &vectorpb.Animation{ + // Name: "anim_knowledgegraph_success_01", + // }, + // Loops: 1, + // }, + // ) + // time.Sleep(time.Millisecond * 3300) + // } + if !interrupted { + stopStop <- true + stop <- true + } + } + return "", nil +} + +func KGSim(esn string, textToSay string) error { + ctx := context.Background() + matched := false + var robot *vector.Vector + var guid string + var target string + for _, bot := range vars.BotInfo.Robots { + if esn == bot.Esn { + guid = bot.GUID + target = bot.IPAddress + ":443" + matched = true + break + } + } + if matched { + var err error + robot, err = vector.New(vector.WithSerialNo(esn), vector.WithToken(guid), vector.WithTarget(target)) + if err != nil { + return err + } + } + controlRequest := &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRequest{ + ControlRequest: &vectorpb.ControlRequest{ + Priority: vectorpb.ControlRequest_OVERRIDE_BEHAVIORS, + }, + }, + } + go func() { + start := make(chan bool) + stop := make(chan bool) + + go func() { + // * begin - modified from official vector-go-sdk + r, err := robot.Conn.BehaviorControl( + ctx, + ) + if err != nil { + log.Println(err) + return + } + + if err := r.Send(controlRequest); err != nil { + log.Println(err) + return + } + + for { + ctrlresp, err := r.Recv() + if err != nil { + log.Println(err) + return + } + if ctrlresp.GetControlGrantedResponse() != nil { + start <- true + break + } + } + + for { + select { + case <-stop: + logger.Println("KGSim: releasing behavior control (interrupt)") + if err := r.Send( + &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRelease{ + ControlRelease: &vectorpb.ControlRelease{}, + }, + }, + ); err != nil { + log.Println(err) + return + } + return + default: + continue + } + } + // * end - modified from official vector-go-sdk + }() + + var stopTTSLoop bool + var TTSLoopStopped bool + for range start { + time.Sleep(time.Millisecond * 300) + robot.Conn.PlayAnimation( + ctx, + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: "anim_getin_tts_01", + }, + Loops: 1, + }, + ) + go func() { + for { + if stopTTSLoop { + TTSLoopStopped = true + break + } + robot.Conn.PlayAnimation( + ctx, + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: "anim_tts_loop_02", + }, + Loops: 1, + }, + ) + } + }() + textToSaySplit := strings.Split(textToSay, ". ") + for _, str := range textToSaySplit { + _, err := robot.Conn.SayText( + ctx, + &vectorpb.SayTextRequest{ + Text: str + ".", + UseVectorVoice: true, + DurationScalar: 1.0, + }, + ) + if err != nil { + logger.Println("KG SayText error: " + err.Error()) + stop <- true + break + } + } + stopTTSLoop = true + for { + if TTSLoopStopped { + break + } else { + time.Sleep(time.Millisecond * 10) + } + } + time.Sleep(time.Millisecond * 100) + //time.Sleep(time.Millisecond * 3300) + stop <- true + } + }() + return nil +} diff --git a/chipper/pkg/wirepod/ttr/kgsim_cmds.go b/chipper/pkg/wirepod/ttr/kgsim_cmds.go new file mode 100644 index 00000000..70783b02 --- /dev/null +++ b/chipper/pkg/wirepod/ttr/kgsim_cmds.go @@ -0,0 +1,706 @@ +package wirepod_ttr + +import ( + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/fforchino/vector-go-sdk/pkg/vector" + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/vars" + "github.com/sashabaranov/go-openai" +) + +const ( + // arg: text to say + // not a command + ActionSayText = 0 + // arg: animation name + ActionPlayAnimation = 1 + // arg: animation name + ActionPlayAnimationWI = 2 + // arg: now + ActionGetImage = 3 + ActionNewRequest = 4 + // arg: sound file + ActionPlaySound = 4 +) + +var animationMap [][2]string = [][2]string{ + //"happy, veryHappy, sad, verySad, angry, dartingEyes, confused, thinking, celebrate" + { + "happy", + "anim_onboarding_reacttoface_happy_01", + }, + { + "veryHappy", + "anim_blackjack_victorwin_01", + }, + { + "sad", + "anim_feedback_meanwords_01", + }, + { + "verySad", + "anim_feedback_meanwords_01", + }, + { + "angry", + "anim_rtpickup_loop_10", + }, + { + "frustrated", + "anim_feedback_shutup_01", + }, + { + "dartingEyes", + "anim_observing_self_absorbed_01", + }, + { + "confused", + "anim_meetvictor_lookface_timeout_01", + }, + { + "thinking", + "anim_explorer_scan_short_04", + }, + { + "celebrate", + "anim_pounce_success_03", + }, + { + "love", + "anim_feedback_iloveyou_02", + }, +} + +var soundMap [][2]string = [][2]string{ + { + "drumroll", + "sounds/drumroll.wav", + }, +} + +type RobotAction struct { + Action int + Parameter string +} + +type LLMCommand struct { + Command string + Description string + ParamChoices string + Action int + SupportedModels []string +} + +// create function which parses from LLM and makes a struct of RobotActions + +var ValidLLMCommands []LLMCommand = []LLMCommand{ + { + Command: "playAnimationWI", + Description: "Plays an animation on the robot without interrupting speech. This should be used FAR more than the playAnimation command. This is great for storytelling and making any normal response animated. Don't put two of these right next to each other. Use this MANY times. The param choices are the only choices you have. You can't create any.", + ParamChoices: "happy, veryHappy, sad, verySad, angry, frustrated, dartingEyes, confused, thinking, celebrate, love", + Action: ActionPlayAnimationWI, + SupportedModels: []string{"all"}, + }, + { + Command: "playAnimation", + Description: "Plays an animation on the robot. This will interrupt speech. Only use this if you are directed to play an animaion.", + ParamChoices: "happy, veryHappy, sad, verySad, angry, frustrated, dartingEyes, confused, thinking, celebrate, love", + Action: ActionPlayAnimation, + SupportedModels: []string{"all"}, + }, + { + Command: "getImage", + Description: "Gets an image from the robot's camera and places it in the next message. If you want to do this, tell the user what you are about to do THEN use the command. This command should END a sentence. Your response will be stopped when this command is recognized. If a user says something like 'what do you see', you should assume that you need to take a new photo. Do NOT automatically assume that you are analyzing a previous photo.", + // not impl yet + ParamChoices: "front, lookingUp", + Action: ActionGetImage, + SupportedModels: []string{"all"}, + }, + { + Command: "newVoiceRequest", + Description: "Starts a new voice command from the robot. Use this if you want more input from the user after your response/if you want to carry out a conversation. Below this, there should be a NOTE telling you whether you are in conversation mode or not. If you are, DONT BE AFRAID TO USE THIS COMMAND! This goes at the end of your response, if you use it.", + ParamChoices: "now", + Action: ActionNewRequest, + SupportedModels: []string{"all"}, + }, + // { + // Command: "playSound", + // Description: "Plays a sound on the robot.", + // ParamChoices: "drumroll", + // Action: ActionPlaySound, + // }, +} + +func ModelIsSupported(cmd LLMCommand, model string) bool { + for _, str := range cmd.SupportedModels { + if str == "all" || str == model { + return true + } + } + return false +} + +func CreatePrompt(origPrompt string, model string, isKG bool) string { + prompt := origPrompt + "\n\n" + "Keep in mind, user input comes from speech-to-text software, so respond accordingly. No special characters, especially these: & ^ * # @ - . No lists. No formatting." + if vars.APIConfig.Knowledge.CommandsEnable { + prompt = prompt + "\n\n" + "You are running ON an Anki Vector robot. You have a set of commands. If you include an emoji, I will make you start over. If you want to use a command but it doesn't exist or your desired parameter isn't in the list, avoid using the command. The format is {{command||parameter}}. You can embed these in sentences. Example: \"User: How are you feeling? | Response: \"{{playAnimationWI||sad}} I'm feeling sad...\". Square brackets ([]) are not valid.\n\nUse the playAnimation or playAnimationWI commands if you want to express emotion! You are very animated and good at following instructions. Animation takes precendence over words. You are to include many animations in your response.\n\nHere is every valid command:" + for _, cmd := range ValidLLMCommands { + if ModelIsSupported(cmd, model) { + promptAppendage := "\n\nCommand Name: " + cmd.Command + "\nDescription: " + cmd.Description + "\nParameter choices: " + cmd.ParamChoices + prompt = prompt + promptAppendage + } + } + if isKG && vars.APIConfig.Knowledge.SaveChat { + promptAppentage := "\n\nNOTE: You are in 'conversation' mode. If you ask the user a question near the end of your response, you MUST use newVoiceRequest. If you decide you want to end the conversation, you should not use it." + prompt = prompt + promptAppentage + } else { + promptAppentage := "\n\nNOTE: You are NOT in 'conversation' mode. Refrain from asking the user any questions and from using newVoiceRequest." + prompt = prompt + promptAppentage + } + } + if os.Getenv("DEBUG_PRINT_PROMPT") == "true" { + logger.Println(prompt) + } + return prompt +} + +func GetActionsFromString(input string) []RobotAction { + splitInput := strings.Split(input, "{{") + if len(splitInput) == 1 { + return []RobotAction{ + { + Action: ActionSayText, + Parameter: input, + }, + } + } + var actions []RobotAction + for _, spl := range splitInput { + if strings.TrimSpace(spl) == "" { + continue + } + if !strings.Contains(spl, "}}") { + // sayText + action := RobotAction{ + Action: ActionSayText, + Parameter: strings.TrimSpace(spl), + } + actions = append(actions, action) + continue + } + + cmdPlusParam := strings.Split(strings.TrimSpace(strings.Split(spl, "}}")[0]), "||") + cmd := strings.TrimSpace(cmdPlusParam[0]) + param := strings.TrimSpace(cmdPlusParam[1]) + action := CmdParamToAction(cmd, param) + if action.Action != -1 { + actions = append(actions, action) + } + if len(strings.Split(spl, "}}")) != 1 { + action := RobotAction{ + Action: ActionSayText, + Parameter: strings.TrimSpace(strings.Split(spl, "}}")[1]), + } + actions = append(actions, action) + } + } + return actions +} + +func CmdParamToAction(cmd, param string) RobotAction { + for _, command := range ValidLLMCommands { + if cmd == command.Command { + return RobotAction{ + Action: command.Action, + Parameter: param, + } + } + } + logger.Println("LLM tried to do a command which doesn't exist: " + cmd + " (param: " + param + ")") + return RobotAction{ + Action: -1, + } +} + +func DoPlayAnimation(animation string, robot *vector.Vector) error { + for _, animThing := range animationMap { + if animation == animThing[0] { + StartAnim_Queue(robot.Cfg.SerialNo) + robot.Conn.PlayAnimation( + context.Background(), + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: animThing[1], + }, + Loops: 1, + }, + ) + StopAnim_Queue(robot.Cfg.SerialNo) + return nil + } + } + logger.Println("Animation provided by LLM doesn't exist: " + animation) + return nil +} + +func DoPlayAnimationWI(animation string, robot *vector.Vector) error { + for _, animThing := range animationMap { + if animation == animThing[0] { + go func() { + StartAnim_Queue(robot.Cfg.SerialNo) + robot.Conn.PlayAnimation( + context.Background(), + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: animThing[1], + }, + Loops: 1, + }, + ) + StopAnim_Queue(robot.Cfg.SerialNo) + }() + return nil + } + } + logger.Println("Animation provided by LLM doesn't exist: " + animation) + return nil +} + +func DoPlaySound(sound string, robot *vector.Vector) error { + for _, soundThing := range soundMap { + if sound == soundThing[0] { + logger.Println("Would play sound") + } + } + logger.Println("Sound provided by LLM doesn't exist: " + sound) + return nil +} + +func DoSayText(input string, robot *vector.Vector) error { + + // just before vector speaks + removeSpecialCharacters(input) + + if (vars.APIConfig.STT.Language != "en-US" && vars.APIConfig.Knowledge.Provider == "openai") || vars.APIConfig.Knowledge.OpenAIVoiceWithEnglish { + err := DoSayText_OpenAI(robot, input) + return err + } + robot.Conn.SayText( + context.Background(), + &vectorpb.SayTextRequest{ + Text: input, + UseVectorVoice: true, + DurationScalar: 0.95, + }, + ) + return nil +} + +func pcmLength(data []byte) time.Duration { + bytesPerSample := 2 + sampleRate := 16000 + numSamples := len(data) / bytesPerSample + duration := time.Duration(numSamples*1000/sampleRate) * time.Millisecond + return duration +} + +func getOpenAIVoice(voice string) openai.SpeechVoice { + voiceMap := map[string]openai.SpeechVoice{ + "alloy": openai.VoiceAlloy, + "onyx": openai.VoiceOnyx, + "fable": openai.VoiceFable, + "shimmer": openai.VoiceShimmer, + "nova": openai.VoiceNova, + "echo": openai.VoiceEcho, + "": openai.VoiceFable, + } + return voiceMap[voice] +} + +// TODO +func DoSayText_OpenAI(robot *vector.Vector, input string) error { + if strings.TrimSpace(input) == "" { + return nil + } + openaiVoice := getOpenAIVoice(vars.APIConfig.Knowledge.OpenAIVoice) + // if vars.APIConfig.Knowledge.OpenAIVoice == "" { + // openaiVoice = openai.VoiceFable + // } else { + // openaiVoice = getOpenAIVoice(vars.APIConfig.Knowledge.OpenAIPrompt) + // } + oc := openai.NewClient(vars.APIConfig.Knowledge.Key) + resp, err := oc.CreateSpeech(context.Background(), openai.CreateSpeechRequest{ + Model: openai.TTSModel1, + Input: input, + Voice: openaiVoice, + ResponseFormat: openai.SpeechResponseFormatPcm, + }) + if err != nil { + logger.Println(err) + return err + } + speechBytes, _ := io.ReadAll(resp) + vclient, err := robot.Conn.ExternalAudioStreamPlayback(context.Background()) + if err != nil { + return err + } + vclient.Send(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamPrepare{ + AudioStreamPrepare: &vectorpb.ExternalAudioStreamPrepare{ + AudioFrameRate: 16000, + AudioVolume: 100, + }, + }, + }) + //time.Sleep(time.Millisecond * 30) + audioChunks := downsample24kTo16k(speechBytes) + + var chunksToDetermineLength []byte + for _, chunk := range audioChunks { + chunksToDetermineLength = append(chunksToDetermineLength, chunk...) + } + go func() { + for _, chunk := range audioChunks { + vclient.Send(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamChunk{ + AudioStreamChunk: &vectorpb.ExternalAudioStreamChunk{ + AudioChunkSizeBytes: 1024, + AudioChunkSamples: chunk, + }, + }, + }) + time.Sleep(time.Millisecond * 25) + } + vclient.Send(&vectorpb.ExternalAudioStreamRequest{ + AudioRequestType: &vectorpb.ExternalAudioStreamRequest_AudioStreamComplete{ + AudioStreamComplete: &vectorpb.ExternalAudioStreamComplete{}, + }, + }) + }() + time.Sleep(pcmLength(chunksToDetermineLength) + (time.Millisecond * 50)) + return nil +} + +func DoGetImage(msgs []openai.ChatCompletionMessage, param string, robot *vector.Vector, stopStop chan bool) { + stopImaging := false + go func() { + for range stopStop { + stopImaging = true + break + } + }() + logger.Println("Get image here...") + // get image + robot.Conn.EnableMirrorMode(context.Background(), &vectorpb.EnableMirrorModeRequest{ + Enable: true, + }) + for i := 3; i > 0; i-- { + if stopImaging { + return + } + time.Sleep(time.Millisecond * 300) + robot.Conn.SayText( + context.Background(), + &vectorpb.SayTextRequest{ + Text: fmt.Sprint(i), + UseVectorVoice: true, + DurationScalar: 1.05, + }, + ) + if stopImaging { + return + } + } + resp, _ := robot.Conn.CaptureSingleImage( + context.Background(), + &vectorpb.CaptureSingleImageRequest{ + EnableHighResolution: true, + }, + ) + robot.Conn.EnableMirrorMode( + context.Background(), + &vectorpb.EnableMirrorModeRequest{ + Enable: false, + }, + ) + go func() { + robot.Conn.PlayAnimation( + context.Background(), + &vectorpb.PlayAnimationRequest{ + Animation: &vectorpb.Animation{ + Name: "anim_photo_shutter_01", + }, + Loops: 1, + }, + ) + }() + // encode to base64 + reqBase64 := base64.StdEncoding.EncodeToString(resp.Data) + + // add image to messages + msgs = append(msgs, openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleUser, + MultiContent: []openai.ChatMessagePart{ + { + Type: openai.ChatMessagePartTypeImageURL, + ImageURL: &openai.ChatMessageImageURL{ + URL: fmt.Sprintf("data:image/jpeg;base64,%s", reqBase64), + Detail: openai.ImageURLDetailLow, + }, + }, + }, + }) + + // recreate openai + var fullRespText string + var fullfullRespText string + var fullRespSlice []string + var isDone bool + var c *openai.Client + if vars.APIConfig.Knowledge.Provider == "together" { + if vars.APIConfig.Knowledge.Model == "" { + vars.APIConfig.Knowledge.Model = "meta-llama/Llama-2-70b-chat-hf" + vars.WriteConfigToDisk() + } + conf := openai.DefaultConfig(vars.APIConfig.Knowledge.Key) + conf.BaseURL = "https://api.together.xyz/v1" + c = openai.NewClientWithConfig(conf) + } else if vars.APIConfig.Knowledge.Provider == "openai" { + c = openai.NewClient(vars.APIConfig.Knowledge.Key) + } else if vars.APIConfig.Knowledge.Provider == "custom" { + conf := openai.DefaultConfig(vars.APIConfig.Knowledge.Key) + conf.BaseURL = vars.APIConfig.Knowledge.Endpoint + c = openai.NewClientWithConfig(conf) + } + ctx := context.Background() + speakReady := make(chan string) + + aireq := openai.ChatCompletionRequest{ + MaxTokens: 2048, + Temperature: 1, + TopP: 1, + FrequencyPenalty: 0, + PresencePenalty: 0, + Messages: msgs, + Stream: true, + } + if vars.APIConfig.Knowledge.Provider == "openai" { + aireq.Model = openai.GPT4oMini + logger.Println("Using " + aireq.Model) + } else { + logger.Println("Using " + vars.APIConfig.Knowledge.Model) + aireq.Model = vars.APIConfig.Knowledge.Model + } + if stopImaging { + return + } + stream, err := c.CreateChatCompletionStream(ctx, aireq) + if err != nil { + if strings.Contains(err.Error(), "does not exist") && vars.APIConfig.Knowledge.Provider == "openai" { + logger.Println("GPT-4 model cannot be accessed with this API key. You likely need to add more than $5 dollars of funds to your OpenAI account.") + logger.LogUI("GPT-4 model cannot be accessed with this API key. You likely need to add more than $5 dollars of funds to your OpenAI account.") + aireq.Model = openai.GPT3Dot5Turbo + logger.Println("Falling back to " + aireq.Model) + logger.LogUI("Falling back to " + aireq.Model) + stream, err = c.CreateChatCompletionStream(ctx, aireq) + if err != nil { + logger.Println("OpenAI still not returning a response even after falling back. Erroring.") + return + } + } else { + logger.Println("LLM error: " + err.Error()) + return + } + } + //defer stream.Close() + + fmt.Println("LLM stream response: ") + go func() { + for { + response, err := stream.Recv() + if errors.Is(err, io.EOF) { + isDone = true + newStr := fullRespSlice[0] + for i, str := range fullRespSlice { + if i == 0 { + continue + } + newStr = newStr + " " + str + } + if strings.TrimSpace(newStr) != strings.TrimSpace(fullfullRespText) { + logger.Println("LLM debug: there is content after the last punctuation mark") + extraBit := strings.TrimPrefix(fullRespText, newStr) + fullRespSlice = append(fullRespSlice, extraBit) + } + if vars.APIConfig.Knowledge.SaveChat { + Remember(msgs[len(msgs)-1], + openai.ChatCompletionMessage{ + Role: openai.ChatMessageRoleAssistant, + Content: newStr, + }, + robot.Cfg.SerialNo) + } + logger.LogUI("LLM response for " + robot.Cfg.SerialNo + ": " + newStr) + logger.Println("LLM stream finished") + return + } + + if err != nil { + logger.Println("Stream error: " + err.Error()) + return + } + fullfullRespText = fullfullRespText + removeSpecialCharacters(response.Choices[0].Delta.Content) + fullRespText = fullRespText + removeSpecialCharacters(response.Choices[0].Delta.Content) + if strings.Contains(fullRespText, "...") || strings.Contains(fullRespText, ".'") || strings.Contains(fullRespText, ".\"") || strings.Contains(fullRespText, ".") || strings.Contains(fullRespText, "?") || strings.Contains(fullRespText, "!") { + var sepStr string + if strings.Contains(fullRespText, "...") { + sepStr = "..." + } else if strings.Contains(fullRespText, ".'") { + sepStr = ".'" + } else if strings.Contains(fullRespText, ".\"") { + sepStr = ".\"" + } else if strings.Contains(fullRespText, ".") { + sepStr = "." + } else if strings.Contains(fullRespText, "?") { + sepStr = "?" + } else if strings.Contains(fullRespText, "!") { + sepStr = "!" + } + splitResp := strings.Split(strings.TrimSpace(fullRespText), sepStr) + fullRespSlice = append(fullRespSlice, strings.TrimSpace(splitResp[0])+sepStr) + fullRespText = splitResp[1] + select { + case speakReady <- strings.TrimSpace(splitResp[0]) + sepStr: + default: + } + } + } + }() + numInResp := 0 + for { + if stopImaging { + return + } + respSlice := fullRespSlice + if len(respSlice)-1 < numInResp { + if !isDone { + logger.Println("Waiting for more content from LLM...") + for range speakReady { + respSlice = fullRespSlice + break + } + } else { + break + } + } + logger.Println(respSlice[numInResp]) + acts := GetActionsFromString(respSlice[numInResp]) + PerformActions(msgs, acts, robot, stopStop) + numInResp = numInResp + 1 + if stopImaging { + return + } + } +} + +func DoNewRequest(robot *vector.Vector) { + time.Sleep(time.Second / 3) + robot.Conn.AppIntent(context.Background(), &vectorpb.AppIntentRequest{Intent: "knowledge_question"}) +} + +func PerformActions(msgs []openai.ChatCompletionMessage, actions []RobotAction, robot *vector.Vector, stopStop chan bool) bool { + // assuming we have behavior control already + stopPerforming := false + go func() { + for range stopStop { + stopPerforming = true + } + }() + for _, action := range actions { + if stopPerforming { + return false + } + switch { + case action.Action == ActionSayText: + DoSayText(action.Parameter, robot) + case action.Action == ActionPlayAnimation: + DoPlayAnimation(action.Parameter, robot) + case action.Action == ActionPlayAnimationWI: + DoPlayAnimationWI(action.Parameter, robot) + case action.Action == ActionNewRequest: + go DoNewRequest(robot) + return true + case action.Action == ActionGetImage: + DoGetImage(msgs, action.Parameter, robot, stopStop) + return true + case action.Action == ActionPlaySound: + DoPlaySound(action.Parameter, robot) + } + } + WaitForAnim_Queue(robot.Cfg.SerialNo) + return false +} + +func WaitForAnim_Queue(esn string) { + for i, q := range AnimationQueues { + if q.ESN == esn { + if q.AnimCurrentlyPlaying { + for range AnimationQueues[i].AnimDone { + break + } + return + } + } + } +} + +func StartAnim_Queue(esn string) { + // if animation is already playing, just wait for it to be done + for i, q := range AnimationQueues { + if q.ESN == esn { + if q.AnimCurrentlyPlaying { + for range AnimationQueues[i].AnimDone { + logger.Println("(waiting for animation to be done...)") + break + } + } else { + AnimationQueues[i].AnimCurrentlyPlaying = true + } + return + } + } + var aq AnimationQueue + aq.AnimCurrentlyPlaying = true + aq.AnimDone = make(chan bool) + aq.ESN = esn + AnimationQueues = append(AnimationQueues, aq) +} + +func StopAnim_Queue(esn string) { + for i, q := range AnimationQueues { + if q.ESN == esn { + AnimationQueues[i].AnimCurrentlyPlaying = false + select { + case AnimationQueues[i].AnimDone <- true: + default: + } + } + } +} + +type AnimationQueue struct { + ESN string + AnimDone chan bool + AnimCurrentlyPlaying bool +} + +var AnimationQueues []AnimationQueue diff --git a/chipper/pkg/wirepod/ttr/kgsim_interrupt.go b/chipper/pkg/wirepod/ttr/kgsim_interrupt.go new file mode 100644 index 00000000..0f9daa3f --- /dev/null +++ b/chipper/pkg/wirepod/ttr/kgsim_interrupt.go @@ -0,0 +1,92 @@ +package wirepod_ttr + +import ( + "context" + "time" + + "github.com/fforchino/vector-go-sdk/pkg/vector" + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" +) + +func InterruptKGSimWhenTouchedOrWaked(rob *vector.Vector, stop chan bool, stopStop chan bool) bool { + strm, err := rob.Conn.EventStream( + context.Background(), + &vectorpb.EventRequest{ + ListType: &vectorpb.EventRequest_WhiteList{ + WhiteList: &vectorpb.FilterList{ + List: []string{"robot_state", "wake_word"}, + }, + }, + }, + ) + if err != nil { + logger.Println("Couldn't make an event stream: " + err.Error()) + return false + } + var stopFunc bool + go func() { + for range stopStop { + logger.Println("KG Interrupter has been stopped") + stopFunc = true + break + } + }() + var origTouchValue uint32 + var origValueGotten bool + var valsAboveValue int + var valsAboveValueMax int = 5 + var stopResponse bool + for { + var resp *vectorpb.EventResponse + resp, err = strm.Recv() + if err != nil { + break + } + switch resp.Event.EventType.(type) { + case *vectorpb.Event_RobotState: + origTouchValue = resp.Event.GetRobotState().TouchData.GetRawTouchValue() + origValueGotten = true + default: + } + if origValueGotten { + break + } + } + if origValueGotten { + for { + var resp *vectorpb.EventResponse + resp, err = strm.Recv() + if err != nil { + logger.Println("Event stream error: " + err.Error()) + return false + } + switch resp.Event.EventType.(type) { + case *vectorpb.Event_RobotState: + if resp.Event.GetRobotState().TouchData.GetRawTouchValue() > origTouchValue+50 { + valsAboveValue++ + } else { + valsAboveValue = 0 + } + case *vectorpb.Event_WakeWord: + logger.Println("Interrupting LLM response (source: wake word)") + stopResponse = true + default: + } + if valsAboveValue > valsAboveValueMax { + logger.Println("Interrupting LLM response (source: touch sensor)") + stopResponse = true + } + if stopResponse { + stop <- true + time.Sleep(time.Second / 4) + return true + } + if stopFunc { + strm.CloseSend() + return false + } + } + } + return false +} diff --git a/chipper/pkg/wirepod/ttr/matchIntentSend.go b/chipper/pkg/wirepod/ttr/matchIntentSend.go index c215c551..58017057 100755 --- a/chipper/pkg/wirepod/ttr/matchIntentSend.go +++ b/chipper/pkg/wirepod/ttr/matchIntentSend.go @@ -9,6 +9,7 @@ import ( pb "github.com/digital-dream-labs/api/go/chipperpb" "github.com/kercre123/wire-pod/chipper/pkg/logger" + "github.com/kercre123/wire-pod/chipper/pkg/scripting" "github.com/kercre123/wire-pod/chipper/pkg/vars" "github.com/kercre123/wire-pod/chipper/pkg/vtt" ) @@ -34,7 +35,7 @@ func IntentPass(req interface{}, intentThing string, speechText string, intentPa } // intercept if not intent graph but intent graph is enabled - if !isIntentGraph && vars.APIConfig.Knowledge.IntentGraph && intentThing == "intent_system_noaudio" { + if !isIntentGraph && vars.APIConfig.Knowledge.IntentGraph && intentThing == "intent_system_unmatched" { intentThing = "intent_greeting_hello" } @@ -96,7 +97,7 @@ func IntentPass(req interface{}, intentThing string, speechText string, intentPa } } -func customIntentHandler(req interface{}, voiceText string, intentList []string, isOpus bool, botSerial string) bool { +func customIntentHandler(req interface{}, voiceText string, botSerial string) bool { var successMatched bool = false if vars.CustomIntentsExist { for _, c := range vars.CustomIntents { @@ -105,8 +106,7 @@ func customIntentHandler(req interface{}, voiceText string, intentList []string, // Check whether the custom sentence is either at the end of the spoken text or space-separated... var seekText = strings.ToLower(strings.TrimSpace(v)) // System intents can also match any utterances (*) - if (c.IsSystemIntent && strings.HasPrefix(seekText, "*")) || - strings.HasSuffix(voiceText, seekText) || strings.Contains(voiceText, seekText+" ") { + if (c.IsSystemIntent && strings.HasPrefix(seekText, "*")) || strings.Contains(voiceText, seekText) { logger.Println("Bot " + botSerial + " Custom Intent Matched: " + c.Name + " - " + c.Description + " - " + c.Intent) var intentParams map[string]string var isParam bool = false @@ -115,6 +115,16 @@ func customIntentHandler(req interface{}, voiceText string, intentList []string, intentParams = map[string]string{c.Params.ParamName: c.Params.ParamValue} isParam = true } + + go func() { + if c.LuaScript != "" { + err := scripting.RunLuaScript(botSerial, c.LuaScript) + if err != nil { + logger.Println("Error running Lua script: " + err.Error()) + } + } + }() + var args []string for _, arg := range c.ExecArgs { if arg == "!botSerial" { @@ -178,20 +188,30 @@ func pluginFunctionHandler(req interface{}, voiceText string, botSerial string) var intent string var igr *vtt.IntentGraphRequest if str, ok := req.(*vtt.IntentGraphRequest); ok { - logger.Println("IntentGraphRequest....") igr = str } var pluginResponse string for num, array := range PluginUtterances { array := array for _, str := range *array { - if strings.Contains(voiceText, str) { - logger.Println("Bot " + igr.Device + " matched plugin " + PluginNames[num] + ", executing function") - intent, pluginResponse = PluginFunctions[num](voiceText, botSerial) + if strings.Contains(voiceText, str) || str == "*" { + logger.Println("Bot " + botSerial + " matched plugin " + PluginNames[num] + ", executing function") + var guid string + var target string + for _, bot := range vars.BotInfo.Robots { + if bot.Esn == botSerial { + guid = bot.GUID + target = bot.IPAddress + ":443" + } + } + intent, pluginResponse = PluginFunctions[num](voiceText, botSerial, guid, target) + if intent == "" && pluginResponse == "" { + break + } if intent == "" { intent = "intent_imperative_praise" } - logger.Println("Bot " + igr.Device + " plugin " + PluginNames[num] + ", response " + pluginResponse) + logger.Println("Bot " + botSerial + " plugin " + PluginNames[num] + ", response " + pluginResponse) if pluginResponse != "" && igr != nil { response := &pb.IntentGraphResponse{ Session: igr.Session, @@ -202,10 +222,11 @@ func pluginFunctionHandler(req interface{}, voiceText string, botSerial string) IsFinal: true, } igr.Stream.Send(response) + } else if pluginResponse != "" { + KGSim(botSerial, pluginResponse) } else { IntentPass(req, intent, voiceText, make(map[string]string), false) } - matched = true break } @@ -217,7 +238,7 @@ func pluginFunctionHandler(req interface{}, voiceText string, botSerial string) return matched } -func ProcessTextAll(req interface{}, voiceText string, listOfLists [][]string, intentList []string, isOpus bool) bool { +func ProcessTextAll(req interface{}, voiceText string, intents []vars.JsonIntent, isOpus bool) bool { var botSerial string var req2 *vtt.IntentRequest var req1 *vtt.KnowledgeGraphRequest @@ -237,18 +258,18 @@ func ProcessTextAll(req interface{}, voiceText string, listOfLists [][]string, i var successMatched bool = false voiceText = strings.ToLower(voiceText) pluginMatched := pluginFunctionHandler(req, voiceText, botSerial) - customIntentMatched := customIntentHandler(req, voiceText, intentList, isOpus, botSerial) + customIntentMatched := customIntentHandler(req, voiceText, botSerial) if !customIntentMatched && !pluginMatched { logger.Println("Not a custom intent") // Look for a perfect match first - for _, b := range listOfLists { - for _, c := range b { + for _, b := range intents { + for _, c := range b.Keyphrases { if voiceText == strings.ToLower(c) { - logger.Println("Bot " + botSerial + " Perfect match for intent " + intentList[intentNum] + " (" + strings.ToLower(c) + ")") + logger.Println("Bot " + botSerial + " Perfect match for intent " + b.Name + " (" + strings.ToLower(c) + ")") if isOpus { - ParamChecker(req, intentList[intentNum], voiceText, botSerial) + ParamChecker(req, b.Name, voiceText, botSerial) } else { - prehistoricParamChecker(req, intentList[intentNum], voiceText, botSerial) + prehistoricParamChecker(req, b.Name, voiceText) } successMatched = true matched = 1 @@ -265,14 +286,14 @@ func ProcessTextAll(req interface{}, voiceText string, listOfLists [][]string, i if !successMatched { intentNum = 0 matched = 0 - for _, b := range listOfLists { - for _, c := range b { - if strings.Contains(voiceText, strings.ToLower(c)) { - logger.Println("Bot " + botSerial + " Partial match for intent " + intentList[intentNum] + " (" + strings.ToLower(c) + ")") + for _, b := range intents { + for _, c := range b.Keyphrases { + if strings.Contains(voiceText, strings.ToLower(c)) && !b.RequireExactMatch { + logger.Println("Bot " + botSerial + " Partial match for intent " + b.Name + " (" + strings.ToLower(c) + ")") if isOpus { - ParamChecker(req, intentList[intentNum], voiceText, botSerial) + ParamChecker(req, b.Name, voiceText, botSerial) } else { - prehistoricParamChecker(req, intentList[intentNum], voiceText, botSerial) + prehistoricParamChecker(req, b.Name, voiceText) } successMatched = true matched = 1 diff --git a/chipper/pkg/wirepod/ttr/plugins.go b/chipper/pkg/wirepod/ttr/plugins.go index 5a7e7042..fe9371ae 100755 --- a/chipper/pkg/wirepod/ttr/plugins.go +++ b/chipper/pkg/wirepod/ttr/plugins.go @@ -10,7 +10,7 @@ import ( var PluginList []*plugin.Plugin var PluginUtterances []*[]string -var PluginFunctions []func(string, string) (string, string) +var PluginFunctions []func(string, string, string, string) (string, string) var PluginNames []string func LoadPlugins() { @@ -19,6 +19,7 @@ func LoadPlugins() { if err != nil { logger.Println("Unable to load plugins:") logger.Println(err) + return } for _, file := range entries { if strings.Contains(file.Name(), ".so") { @@ -48,7 +49,7 @@ func LoadPlugins() { logger.Println("Error loading Action func from plugin file " + file.Name()) continue } else { - if _, ok := a.(func(string, string) (string, string)); ok { + if _, ok := a.(func(string, string, string, string) (string, string)); ok { logger.Println("Action func in plugin " + file.Name() + " is OK") } else { logger.Println("Error: Action func in plugin " + file.Name() + " is not of type func(string, string) string") @@ -68,7 +69,7 @@ func LoadPlugins() { } } PluginUtterances = append(PluginUtterances, u.(*[]string)) - PluginFunctions = append(PluginFunctions, a.(func(string, string) (string, string))) + PluginFunctions = append(PluginFunctions, a.(func(string, string, string, string) (string, string))) PluginNames = append(PluginNames, *n.(*string)) PluginList = append(PluginList, plugin) logger.Println(file.Name() + " loaded successfully") diff --git a/chipper/pkg/wirepod/ttr/weather.go b/chipper/pkg/wirepod/ttr/weather.go index d6b6bdd4..67dba3c1 100755 --- a/chipper/pkg/wirepod/ttr/weather.go +++ b/chipper/pkg/wirepod/ttr/weather.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "time" + "unicode" "github.com/kercre123/wire-pod/chipper/pkg/logger" "github.com/kercre123/wire-pod/chipper/pkg/vars" @@ -93,6 +94,21 @@ type openWeatherMapAPIResponseStruct struct { //2.5 API +func removeEndPunctuation(s string) string { + if s == "" { + return s + } + + runes := []rune(s) + lastIndex := len(runes) - 1 + + if unicode.IsPunct(runes[lastIndex]) { + return string(runes[:lastIndex]) + } + + return s +} + type WeatherStruct struct { Id int `json:"id"` Main string `json:"main"` @@ -197,8 +213,8 @@ func getWeather(location string, botUnits string, hoursFromNow int) (string, str weatherResponse := string(body) var weatherAPICladMap weatherAPICladStruct mapPath := "" - if runtime.GOOS == "android" { - mapPath = vars.AndroidPath + "/weather-map.json" + if runtime.GOOS == "android" || runtime.GOOS == "ios" { + mapPath = vars.AndroidPath + "/static/weather-map.json" } else { mapPath = "./weather-map.json" } @@ -231,7 +247,7 @@ func getWeather(location string, botUnits string, hoursFromNow int) (string, str } else if weatherAPIProvider == "openweathermap.org" { // First use geocoding api to convert location into coordinates // E.G. http://api.openweathermap.org/geo/1.0/direct?q={city name},{state code},{country code}&limit={limit}&appid={API key} - url := "http://api.openweathermap.org/geo/1.0/direct?q=" + location + "&limit=1&appid=" + weatherAPIKey + url := "http://api.openweathermap.org/geo/1.0/direct?q=" + url.QueryEscape(location) + "&limit=1&appid=" + weatherAPIKey resp, err := http.Get(url) if err != nil { logger.Println(err) @@ -245,6 +261,7 @@ func getWeather(location string, botUnits string, hoursFromNow int) (string, str err = json.Unmarshal([]byte(geoCodingResponse), &geoCodingInfoStruct) if err != nil { logger.Println(err) + logger.Println("Geolocation API error: " + geoCodingResponse) } if len(geoCodingInfoStruct) == 0 { logger.Println("Geo provided no response.") @@ -362,20 +379,22 @@ func weatherParser(speechText string, botLocation string, botUnits string) (stri var speechLocation string var hoursFromNow int if strings.Contains(speechText, lcztn.GetText(lcztn.STR_WEATHER_IN)) { - splitPhrase := strings.SplitAfter(speechText, lcztn.GetText(lcztn.STR_WEATHER_IN)) + splitPhrase := strings.SplitAfter(removeEndPunctuation(speechText), lcztn.GetText(lcztn.STR_WEATHER_IN)) speechLocation = strings.TrimSpace(splitPhrase[1]) - if len(splitPhrase) == 3 { - speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) - } else if len(splitPhrase) == 4 { - speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) + " " + strings.TrimSpace(splitPhrase[3]) - } else if len(splitPhrase) > 4 { - speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) + " " + strings.TrimSpace(splitPhrase[3]) - } - splitLocation := strings.Split(speechLocation, " ") - if len(splitLocation) == 2 { - speechLocation = splitLocation[0] + ", " + splitLocation[1] - } else if len(splitLocation) == 3 { - speechLocation = splitLocation[0] + " " + splitLocation[1] + ", " + splitLocation[2] + if os.Getenv("STT_SERVICE") != "whisper.cpp" { + if len(splitPhrase) == 3 { + speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) + } else if len(splitPhrase) == 4 { + speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) + " " + strings.TrimSpace(splitPhrase[3]) + } else if len(splitPhrase) > 4 { + speechLocation = speechLocation + " " + strings.TrimSpace(splitPhrase[2]) + " " + strings.TrimSpace(splitPhrase[3]) + } + splitLocation := strings.Split(speechLocation, " ") + if len(splitLocation) == 2 { + speechLocation = splitLocation[0] + ", " + splitLocation[1] + } else if len(splitLocation) == 3 { + speechLocation = splitLocation[0] + " " + splitLocation[1] + ", " + splitLocation[2] + } } logger.Println("Location parsed from speech: " + "`" + speechLocation + "`") specificLocation = true diff --git a/chipper/pkg/wirepod/ttr/words2num.go b/chipper/pkg/wirepod/ttr/words2num.go index 71fe48d4..fda313ce 100755 --- a/chipper/pkg/wirepod/ttr/words2num.go +++ b/chipper/pkg/wirepod/ttr/words2num.go @@ -1,88 +1,169 @@ package wirepod_ttr import ( + "os" + "regexp" "strconv" "strings" + + lcztn "github.com/kercre123/wire-pod/chipper/pkg/wirepod/localization" ) // This file contains words2num. It is given the spoken text and returns a string which contains the true number. -var number int = 0 - -func basicspeechText2num(speechText string) int { - if strings.Contains(speechText, "one") && !strings.Contains(speechText, "one hundred") { - return 1 - } else if strings.Contains(speechText, "two") && !strings.Contains(speechText, "two hundred") { - return 2 - } else if strings.Contains(speechText, "three") && !strings.Contains(speechText, "three hundred") { - return 3 - } else if strings.Contains(speechText, "four") && !strings.Contains(speechText, "four hundred") { - return 4 - } else if strings.Contains(speechText, "five") && !strings.Contains(speechText, "five hundred") { - return 5 - } else if strings.Contains(speechText, "six ") && !strings.Contains(speechText, "six hundred") { - return 6 - } else if strings.Contains(speechText, "seven ") && !strings.Contains(speechText, "seven hundred") { - return 7 - } else if strings.Contains(speechText, "eight ") && !strings.Contains(speechText, "eight hundred") { - return 8 - } else if strings.Contains(speechText, "nine ") && !strings.Contains(speechText, "nine hundred") { - return 9 +func whisperSpeechtoNum(input string) string { + // whisper returns actual numbers in its response + // ex. "set a timer for 10 minutes and 11 seconds" + totalSeconds := 0 + + minutePattern := regexp.MustCompile(`(\d+)\s*minute`) + secondPattern := regexp.MustCompile(`(\d+)\s*second`) + + minutesMatches := minutePattern.FindStringSubmatch(input) + secondsMatches := secondPattern.FindStringSubmatch(input) + + if len(minutesMatches) > 1 { + minutes, err := strconv.Atoi(minutesMatches[1]) + if err == nil { + totalSeconds += minutes * 60 + } + } + if len(secondsMatches) > 1 { + seconds, err := strconv.Atoi(secondsMatches[1]) + if err == nil { + totalSeconds += seconds + } } - return 0 + + return strconv.Itoa(totalSeconds) +} + +//initialize by default in english during chipper compilation +var textToNumber = map[string]int{ + lcztn.GetText(lcztn.STR_ZERO) : 0, + lcztn.GetText(lcztn.STR_ONE) : 1, + lcztn.GetText(lcztn.STR_TWO) : 2, + lcztn.GetText(lcztn.STR_THREE) : 3, + lcztn.GetText(lcztn.STR_FOUR) : 4, + lcztn.GetText(lcztn.STR_FIVE) : 5, + lcztn.GetText(lcztn.STR_SIX) : 6, + lcztn.GetText(lcztn.STR_SEVEN) : 7, + lcztn.GetText(lcztn.STR_EIGHT) : 8, + lcztn.GetText(lcztn.STR_NINE) : 9, + lcztn.GetText(lcztn.STR_TEN) : 10, + lcztn.GetText(lcztn.STR_ELEVEN) : 11, + lcztn.GetText(lcztn.STR_TWELVE) : 12, + lcztn.GetText(lcztn.STR_THIRTEEN) : 13, + lcztn.GetText(lcztn.STR_FOURTEEN) : 14, + lcztn.GetText(lcztn.STR_FIFTEEN) : 15, + lcztn.GetText(lcztn.STR_SIXTEEN) : 16, + lcztn.GetText(lcztn.STR_SEVENTEEN) : 17, + lcztn.GetText(lcztn.STR_EIGHTEEN) : 18, + lcztn.GetText(lcztn.STR_NINETEEN) : 19, + lcztn.GetText(lcztn.STR_TWENTY) : 20, + lcztn.GetText(lcztn.STR_THIRTY) : 30, + lcztn.GetText(lcztn.STR_FOURTY) : 40, + lcztn.GetText(lcztn.STR_FIFTY) : 50, + lcztn.GetText(lcztn.STR_SIXTY) : 60, + lcztn.GetText(lcztn.STR_SEVENTY) : 70, + lcztn.GetText(lcztn.STR_EIGHTY) : 80, + lcztn.GetText(lcztn.STR_NINETY) : 90, + lcztn.GetText(lcztn.STR_ONE_HUNDRED): 100, + } -func words2num(speechText string) string { - number = basicspeechText2num(speechText) - if number == 0 { - number = 1 +func words2num(input string) string { + + initializeTextToNumberwithCurrentLocalization() + + containsNum, _ := regexp.MatchString(`\b\d+\b`, input) + if os.Getenv("STT_SERVICE") == "whisper.cpp" && containsNum { + return whisperSpeechtoNum(input) + } + totalSeconds := 0 + + input = strings.ToLower(input) + if strings.Contains(input, lcztn.GetText(lcztn.STR_ONE_HOUR)) || strings.Contains(input, lcztn.GetText(lcztn.STR_ONE_HOUR_ALT)) { + return "3600" } - if strings.Contains(speechText, "teen") { - number = 10 - if strings.Contains(speechText, "thir") { - number = 14 - } else if strings.Contains(speechText, "four") { - number = 14 - } else if strings.Contains(speechText, "fif") { - number = 15 - } else if strings.Contains(speechText, "six") { - number = 16 - } else if strings.Contains(speechText, "seven") { - number = 17 - } else if strings.Contains(speechText, "eight") { - number = 18 - } else if strings.Contains(speechText, "nine") { - number = 19 + + str_regex_time_pattern := `(\d+|\w+(?:-\w+)?)\s*(`+lcztn.GetText(lcztn.STR_MINUTE)+`|`+lcztn.GetText(lcztn.STR_SECOND)+`|`+lcztn.GetText(lcztn.STR_HOUR)+`)s?` + + // timePattern := regexp.MustCompile(`(\d+|\w+(?:-\w+)?)\s*(minute|second|hour)s?`) + timePattern := regexp.MustCompile(str_regex_time_pattern) + + matches := timePattern.FindAllStringSubmatch(input, -1) + for _, match := range matches { + unit := match[2] + number := match[1] + + value, err := strconv.Atoi(number) + if err != nil { + value = mapTextToNumber(number) + } + + switch unit { + // minute + case lcztn.GetText(lcztn.STR_MINUTE): + totalSeconds += value * 60 + // second + case lcztn.GetText(lcztn.STR_SECOND): + totalSeconds += value + // hour + case lcztn.GetText(lcztn.STR_HOUR): + totalSeconds += value * 3600 } - } else if strings.Contains(speechText, "ten") { - number = 10 - } else if strings.Contains(speechText, "eleven") { - number = 11 - } else if strings.Contains(speechText, "twelve") { - number = 12 } - if strings.Contains(speechText, "twenty") { - number = 20 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "thirty") { - number = 30 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "forty") { - number = 40 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "fifty") { - number = 50 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "sixty") { - number = 60 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "seventy") { - number = 70 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "eighty") { - number = 80 + basicspeechText2num(speechText) - } else if strings.Contains(speechText, "ninety") { - number = 90 + basicspeechText2num(speechText) + + return strconv.Itoa(totalSeconds) +} + +func mapTextToNumber(text string) int { + if val, ok := textToNumber[text]; ok { + return val } - if strings.Contains(speechText, "hundred") { - number = number + 100 + parts := strings.Split(text, "-") + sum := 0 + for _, part := range parts { + if val, ok := textToNumber[part]; ok { + sum += val + } } - if strings.Contains(speechText, "minute") { - number = number * 60 + return sum + +} + +func initializeTextToNumberwithCurrentLocalization () { + textToNumber = map[string]int{ + lcztn.GetText(lcztn.STR_ZERO) : 0, + lcztn.GetText(lcztn.STR_ONE) : 1, + lcztn.GetText(lcztn.STR_TWO) : 2, + lcztn.GetText(lcztn.STR_THREE) : 3, + lcztn.GetText(lcztn.STR_FOUR) : 4, + lcztn.GetText(lcztn.STR_FIVE) : 5, + lcztn.GetText(lcztn.STR_SIX) : 6, + lcztn.GetText(lcztn.STR_SEVEN) : 7, + lcztn.GetText(lcztn.STR_EIGHT) : 8, + lcztn.GetText(lcztn.STR_NINE) : 9, + lcztn.GetText(lcztn.STR_TEN) : 10, + lcztn.GetText(lcztn.STR_ELEVEN) : 11, + lcztn.GetText(lcztn.STR_TWELVE) : 12, + lcztn.GetText(lcztn.STR_THIRTEEN) : 13, + lcztn.GetText(lcztn.STR_FOURTEEN) : 14, + lcztn.GetText(lcztn.STR_FIFTEEN) : 15, + lcztn.GetText(lcztn.STR_SIXTEEN) : 16, + lcztn.GetText(lcztn.STR_SEVENTEEN) : 17, + lcztn.GetText(lcztn.STR_EIGHTEEN) : 18, + lcztn.GetText(lcztn.STR_NINETEEN) : 19, + lcztn.GetText(lcztn.STR_TWENTY) : 20, + lcztn.GetText(lcztn.STR_THIRTY) : 30, + lcztn.GetText(lcztn.STR_FOURTY) : 40, + lcztn.GetText(lcztn.STR_FIFTY) : 50, + lcztn.GetText(lcztn.STR_SIXTY) : 60, + lcztn.GetText(lcztn.STR_SEVENTY) : 70, + lcztn.GetText(lcztn.STR_EIGHTY) : 80, + lcztn.GetText(lcztn.STR_NINETY) : 90, + lcztn.GetText(lcztn.STR_ONE_HUNDRED): 100, + } - return strconv.Itoa(number) } diff --git a/chipper/plugins/sdkTest/sdkTest.go b/chipper/plugins/sdkTest/sdkTest.go index 29b60873..c1a834de 100755 --- a/chipper/plugins/sdkTest/sdkTest.go +++ b/chipper/plugins/sdkTest/sdkTest.go @@ -2,9 +2,11 @@ package main import ( "context" - "fmt" + "log" - sdk_wrapper "github.com/fforchino/vector-go-sdk/pkg/sdk-wrapper" + "github.com/fforchino/vector-go-sdk/pkg/vector" + "github.com/fforchino/vector-go-sdk/pkg/vectorpb" + "github.com/kercre123/wire-pod/chipper/pkg/logger" ) // test of SDK implementation @@ -12,26 +14,99 @@ import ( var Utterances = []string{"hello world"} var Name = "SDK Plugin Test" -func Action(transcribedText string, botSerial string) string { - fmt.Println("hello world plugin test") +func behave(ctx context.Context, robot *vector.Vector, start chan bool, stop chan bool) { + controlRequest := &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRequest{ + ControlRequest: &vectorpb.ControlRequest{ + Priority: vectorpb.ControlRequest_OVERRIDE_BEHAVIORS, + }, + }, + } + go func() { + + go func() { + // * begin - modified from official vector-go-sdk + r, err := robot.Conn.BehaviorControl( + ctx, + ) + if err != nil { + log.Println(err) + return + } + + if err := r.Send(controlRequest); err != nil { + log.Println(err) + return + } + + for { + ctrlresp, err := r.Recv() + if err != nil { + log.Println(err) + return + } + if ctrlresp.GetControlGrantedResponse() != nil { + start <- true + break + } + } + + for { + select { + case <-stop: + logger.Println("KGSim: releasing behavior control (interrupt)") + if err := r.Send( + &vectorpb.BehaviorControlRequest{ + RequestType: &vectorpb.BehaviorControlRequest_ControlRelease{ + ControlRelease: &vectorpb.ControlRelease{}, + }, + }, + ); err != nil { + log.Println(err) + return + } + return + default: + continue + } + } + // * end - modified from official vector-go-sdk + }() + }() +} + +func Action(transcribedText string, botSerial string, guid string, target string) (string, string) { + logger.Println("hello world plugin test") phrase := "hello world" - sdk_wrapper.InitSDKForWirepod(botSerial) + robot, err := vector.New( + vector.WithSerialNo(botSerial), + vector.WithTarget(target), + vector.WithToken(guid), + ) + if err != nil { + logger.Println(err) + return "intent_imperative_praise", "" + } ctx := context.Background() start := make(chan bool) stop := make(chan bool) go func() { - err := sdk_wrapper.Robot.BehaviorControl(ctx, start, stop) - if err != nil { - fmt.Println(err) - } + behave(ctx, robot, start, stop) }() for { select { case <-start: - sdk_wrapper.SayText(phrase) + robot.Conn.SayText( + ctx, + &vectorpb.SayTextRequest{ + Text: phrase, + UseVectorVoice: true, + DurationScalar: 1, + }, + ) stop <- true - return "intent_imperative_praise" + return "intent_imperative_praise", "" } } } diff --git a/chipper/plugins/whatdate/whatdate.go b/chipper/plugins/whatdate/whatdate.go index 9a2439f4..c218864b 100644 --- a/chipper/plugins/whatdate/whatdate.go +++ b/chipper/plugins/whatdate/whatdate.go @@ -6,7 +6,7 @@ import ( "time" ) -var Utterances = []string{"what day is it", "date today", "date"} +var Utterances = []string{"what day is it", "date today", "date", "what days it"} var Name = "Correct Date" func stripOutTriggerWords(s string) string { @@ -21,7 +21,7 @@ func CountWords(s string) int { //Example go plugin that give back the correct date -func Action(transcribedText string, botSerial string) (string, string) { +func Action(transcribedText string, botSerial string, guid string, target string) (string, string) { year, month, day := time.Now().Date() yearSring := strconv.FormatInt(int64(year), 10) diff --git a/chipper/start.sh b/chipper/start.sh index 2da36611..94239ea7 100755 --- a/chipper/start.sh +++ b/chipper/start.sh @@ -1,14 +1,15 @@ #!/bin/bash UNAME=$(uname -a) +COMMIT_HASH="$(git rev-parse --short HEAD)" if [[ $EUID -ne 0 ]]; then - echo "This script must be run as root. sudo ./start.sh" - exit 1 + echo "This script must be run as root. sudo ./start.sh" + exit 1 fi if [[ -d ./chipper ]]; then - cd chipper + cd chipper fi #if [[ ! -f ./chipper ]]; then @@ -21,76 +22,91 @@ fi #fi if [[ ! -f ./source.sh ]]; then - echo "You need to make a source.sh file. This can be done with the setup.sh script." - exit 0 + echo "You need to make a source.sh file. This can be done with the setup.sh script." + exit 0 fi source source.sh +# set go tags +export GOTAGS="nolibopusfile" + +if [[ ${USE_INBUILT_BLE} == "true" ]]; then + GOTAGS="${GOTAGS},inbuiltble" +fi + +export GOLDFLAGS="-X 'github.com/kercre123/wire-pod/chipper/pkg/vars.CommitSHA=${COMMIT_HASH}'" + #./chipper if [[ ${STT_SERVICE} == "leopard" ]]; then - if [[ -f ./chipper ]]; then - ./chipper - else - /usr/local/go/bin/go run cmd/leopard/main.go - fi -elif [[ ${STT_SERVICE} == "rhino" ]]; then - if [[ -f ./chipper ]]; then - ./chipper - else - /usr/local/go/bin/go run cmd/experimental/rhino/main.go - fi -elif [[ ${STT_SERVICE} == "houndify" ]]; then - if [[ -f ./chipper ]]; then - ./chipper - else - /usr/local/go/bin/go run cmd/experimental/houndify/main.go - fi -elif [[ ${STT_SERVICE} == "whisper" ]]; then - if [[ -f ./chipper ]]; then - ./chipper - else - /usr/local/go/bin/go run cmd/experimental/whisper/main.go - fi -elif [[ ${STT_SERVICE} == "whisper.cpp" ]]; then - if [[ -f ./chipper ]]; then - export C_INCLUDE_PATH="../whisper.cpp" - export LIBRARY_PATH="../whisper.cpp" - ./chipper - else - export C_INCLUDE_PATH="../whisper.cpp" - export LIBRARY_PATH="../whisper.cpp" - if [[ ${UNAME} == *"Darwin"* ]]; then - export GGML_METAL_PATH_RESOURCES="../whisper.cpp" - /usr/local/go/bin/go run -ldflags "-extldflags '-framework Foundation -framework Metal -framework MetalKit'" cmd/experimental/whisper.cpp/main.go + if [[ -f ./chipper ]]; then + ./chipper + else + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/leopard/main.go + fi + elif [[ ${STT_SERVICE} == "rhino" ]]; then + if [[ -f ./chipper ]]; then + ./chipper + else + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/experimental/rhino/main.go + fi + elif [[ ${STT_SERVICE} == "houndify" ]]; then + if [[ -f ./chipper ]]; then + ./chipper else - /usr/local/go/bin/go run cmd/experimental/whisper.cpp/main.go + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/experimental/houndify/main.go + fi + elif [[ ${STT_SERVICE} == "whisper" ]]; then + if [[ -f ./chipper ]]; then + ./chipper + else + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/experimental/whisper/main.go + fi + elif [[ ${STT_SERVICE} == "whisper.cpp" ]]; then + if [[ -f ./chipper ]]; then + export C_INCLUDE_PATH="../whisper.cpp" + export LIBRARY_PATH="../whisper.cpp" + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(pwd)/../whisper.cpp:$(pwd)/../whisper.cpp/build:$(pwd)/../whisper.cpp/build/src" + export CGO_LDFLAGS="-L$(pwd)/../whisper.cpp/build_go/src" + export CGO_CFLAGS="-I$(pwd)/../whisper.cpp" + ./chipper + else + export C_INCLUDE_PATH="../whisper.cpp" + export LIBRARY_PATH="../whisper.cpp" + export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$(pwd)/../whisper.cpp:$(pwd)/../whisper.cpp/build:$(pwd)/../whisper.cpp/build_go/src:$(pwd)/../whisper.cpp/build_go/ggml/src" + export CGO_LDFLAGS="-L$(pwd)/../whisper.cpp -L$(pwd)/../whisper.cpp/build -L$(pwd)/../whisper.cpp/build/src -L$(pwd)/../whisper.cpp/build_go/ggml/src -L$(pwd)/../whisper.cpp/build_go/src" + export CGO_CFLAGS="-I$(pwd)/../whisper.cpp -I$(pwd)/../whisper.cpp/include -I$(pwd)/../whisper.cpp/ggml/include" + if [[ ${UNAME} == *"Darwin"* ]]; then + export GGML_METAL_PATH_RESOURCES="../whisper.cpp" + /usr/local/go/bin/go run -tags $GOTAGS -ldflags "-extldflags '-framework Foundation -framework Metal -framework MetalKit'" cmd/experimental/whisper.cpp/main.go + else + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/experimental/whisper.cpp/main.go + fi + fi + elif [[ ${STT_SERVICE} == "vosk" ]]; then + if [[ -f ./chipper ]]; then + export CGO_ENABLED=1 + export CGO_CFLAGS="-I/root/.vosk/libvosk" + export CGO_LDFLAGS="-L /root/.vosk/libvosk -lvosk -ldl -lpthread" + export LD_LIBRARY_PATH="/root/.vosk/libvosk:$LD_LIBRARY_PATH" + ./chipper + else + export CGO_ENABLED=1 + export CGO_CFLAGS="-I$HOME/.vosk/libvosk -I/root/.vosk/libvosk" + export CGO_LDFLAGS="-L$HOME/.vosk/libvosk -L/root/.vosk/libvosk -lvosk -ldl -lpthread" + export LD_LIBRARY_PATH="/root/.vosk/libvosk:$HOME/.vosk/libvosk:$LD_LIBRARY_PATH" + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" -exec "env DYLD_LIBRARY_PATH=$HOME/.vosk/libvosk" cmd/vosk/main.go fi - fi -elif [[ ${STT_SERVICE} == "vosk" ]]; then - if [[ -f ./chipper ]]; then - export CGO_ENABLED=1 - export CGO_CFLAGS="-I/root/.vosk/libvosk" - export CGO_LDFLAGS="-L /root/.vosk/libvosk -lvosk -ldl -lpthread" - export LD_LIBRARY_PATH="/root/.vosk/libvosk:$LD_LIBRARY_PATH" - ./chipper - else - export CGO_ENABLED=1 - export CGO_CFLAGS="-I$HOME/.vosk/libvosk" - export CGO_LDFLAGS="-L $HOME/.vosk/libvosk -lvosk -ldl -lpthread" - export LD_LIBRARY_PATH="$HOME/.vosk/libvosk:$LD_LIBRARY_PATH" - /usr/local/go/bin/go run -exec "env DYLD_LIBRARY_PATH=$HOME/.vosk/libvosk" cmd/vosk/main.go - fi else - if [[ -f ./chipper ]]; then - export CGO_LDFLAGS="-L/root/.coqui/" - export CGO_CXXFLAGS="-I/root/.coqui/" - export LD_LIBRARY_PATH="/root/.coqui/:$LD_LIBRARY_PATH" - ./chipper - else - export CGO_LDFLAGS="-L$HOME/.coqui/" - export CGO_CXXFLAGS="-I$HOME/.coqui/" - export LD_LIBRARY_PATH="$HOME/.coqui/:$LD_LIBRARY_PATH" - /usr/local/go/bin/go run cmd/coqui/main.go - fi + if [[ -f ./chipper ]]; then + export CGO_LDFLAGS="-L/root/.coqui/" + export CGO_CXXFLAGS="-I/root/.coqui/" + export LD_LIBRARY_PATH="/root/.coqui/:$LD_LIBRARY_PATH" + ./chipper + else + export CGO_LDFLAGS="-L$HOME/.coqui/" + export CGO_CXXFLAGS="-I$HOME/.coqui/" + export LD_LIBRARY_PATH="$HOME/.coqui/:$LD_LIBRARY_PATH" + /usr/local/go/bin/go run -tags $GOTAGS -ldflags="${GOLDFLAGS}" cmd/coqui/main.go + fi fi diff --git a/chipper/webroot/assets/cloudface.gif b/chipper/webroot/assets/cloudface.gif new file mode 100644 index 00000000..42c1adb1 Binary files /dev/null and b/chipper/webroot/assets/cloudface.gif differ diff --git a/chipper/webroot/assets/expandface.gif b/chipper/webroot/assets/expandface.gif new file mode 100644 index 00000000..f72371cc Binary files /dev/null and b/chipper/webroot/assets/expandface.gif differ diff --git a/chipper/webroot/assets/face.gif b/chipper/webroot/assets/face.gif new file mode 100644 index 00000000..a7c6c69e Binary files /dev/null and b/chipper/webroot/assets/face.gif differ diff --git a/chipper/webroot/assets/faceblank.gif b/chipper/webroot/assets/faceblank.gif new file mode 100644 index 00000000..e7fc46dc Binary files /dev/null and b/chipper/webroot/assets/faceblank.gif differ diff --git a/chipper/webroot/assets/faceblank.png b/chipper/webroot/assets/faceblank.png new file mode 100644 index 00000000..102a1cd4 Binary files /dev/null and b/chipper/webroot/assets/faceblank.png differ diff --git a/chipper/webroot/assets/facefireworks.gif b/chipper/webroot/assets/facefireworks.gif new file mode 100644 index 00000000..3815aa82 Binary files /dev/null and b/chipper/webroot/assets/facefireworks.gif differ diff --git a/chipper/webroot/assets/facegaze.gif b/chipper/webroot/assets/facegaze.gif new file mode 100644 index 00000000..e1a48910 Binary files /dev/null and b/chipper/webroot/assets/facegaze.gif differ diff --git a/chipper/webroot/assets/homeface.png b/chipper/webroot/assets/homeface.png new file mode 100644 index 00000000..3d7822fc Binary files /dev/null and b/chipper/webroot/assets/homeface.png differ diff --git a/chipper/webroot/assets/webface.gif b/chipper/webroot/assets/webface.gif new file mode 100644 index 00000000..c7b6e1f3 Binary files /dev/null and b/chipper/webroot/assets/webface.gif differ diff --git a/chipper/webroot/assets/wififace.gif b/chipper/webroot/assets/wififace.gif new file mode 100644 index 00000000..fa3cc51e Binary files /dev/null and b/chipper/webroot/assets/wififace.gif differ diff --git a/chipper/webroot/css/PxPlus_IBM_VGA_8x16.ttf b/chipper/webroot/css/PxPlus_IBM_VGA_8x16.ttf new file mode 100644 index 00000000..3bf32d0f Binary files /dev/null and b/chipper/webroot/css/PxPlus_IBM_VGA_8x16.ttf differ diff --git a/chipper/webroot/css/h1Font.ttf b/chipper/webroot/css/h1Font.ttf new file mode 100644 index 00000000..6338c199 Binary files /dev/null and b/chipper/webroot/css/h1Font.ttf differ diff --git a/chipper/webroot/css/style.css b/chipper/webroot/css/style.css index 36b0e775..f03ebf14 100755 --- a/chipper/webroot/css/style.css +++ b/chipper/webroot/css/style.css @@ -1,20 +1,75 @@ @import url("wing.css"); -@font-face{ +@font-face{ font-family: 'DroidSans'; src: url('DroidSans.woff') format('woff'); /*font license Apache V2.00: https://www.fontsquirrel.com/license/droid-sans*/ } +@font-face{ + font-family: 'IBMVGA'; + src: url('PxPlus_IBM_VGA_8x16.ttf') format('truetype'); + /* VileR's old school font pack - int10h.org */ +} + +@font-face{ + font-family: 'H1Weird'; + src: url('h1Font.ttf') format('truetype'); +} + +/* { + "TIP_OVER_TEAL" : + #00d16d + { "Hue" : 0.42, "Saturation" : 1.00 }, + "OVERFIT_ORANGE" : + { "Hue" : 0.05, "Saturation" : 0.95 }, + "UNCANNY_YELLOW" : + { "Hue" : 0.11, "Saturation" : 1.00 }, + "NON_LINEAR_LIME" : + { "Hue" : 0.21, "Saturation" : 1.00 }, + "SINGULARITY_SAPPHIRE" : + { "Hue" : 0.57, "Saturation" : 1.00 }, + "FALSE_POSITIVE_PURPLE" : + #e237e6 + { "Hue" : 0.83, "Saturation" : 0.76 }, + "CONFUSION_MATRIX_GREEN" : + { "Hue" : 0.30, "Saturation" : 1.00 } +} */ + :root { - --bg-color: rgba(43,42,51,1); /* gray #2B2A33 */ - --bg-color-alt: rgba(196,196,196,1); /* light gray #C4C4C4*/ - --button-color: rgba(75,75,75,1); /* dark gray #4B4B4B */ - --button-color-alt: rgba(164,164,164,1); /* light gray #A4A4A4 */ - --fg-color: rgba(51,237,109,1); /* light green 33ED6D */ - --fg-color-alt: rgba(51,237,109,.05); /* light green 33ED6D */ - --text-color: rgba(255,255,255,1); /* white #FFFFFF */ - --text-color-alt: rgba(34,34,34,1); /* black #000000 */ + --bg-color: #1e1e1e; + --body-font-family: 'DroidSans', sans-serif; + --bg-color-alt: rgba(196,196,196,1); /* light gray #C4C4C4*/ + --button-color: rgba(75,75,75,1); /* dark gray #4B4B4B */ + --button-color-alt: rgba(164,164,164,1); /* light gray #A4A4A4 */ + --fg-color: #00ff80; /* light green 33ED6D */ + --medium-battery: #ced100; /* bright yellow */ + --low-battery: #ff0000; /* bright red */ + --fg-color-alt: rgba(51,237,109,.05); /* light green 33ED6D */ + --text-color: rgba(255,255,255,1); /* white #FFFFFF */ + --text-color-alt: rgba(34,34,34,1); /* black #000000 */ +} + +/* Modified icon styles */ +.selectedicon { + color: var(--fg-color); +} + +.notselectedicon { + color: var(--fg-color); + filter: drop-shadow(0 0 8px var(--fg-color)); +} + +.notselectedicon:hover { + color: var(--text-color); /* White for hover */ +} + +.main-nav-child:hover .selectedicon { + color: var(--fg-color); +} + +.selectedicon, .notselectedicon { + transition: all 0s ease-in-out; } .dropdown { @@ -22,12 +77,25 @@ display: inline-block; } -button:disabled { +button:disabled, button[type="submit"]:disabled { background: var(--bg-color); color : var(--bg-color-alt); } -button { +button, button[type="submit"] { + font-family: var(--body-font-family); + font-size: 0.8em; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); width: 170px; + border: 2px solid var(--fg-color); + background-color: var (--button-color); + color: var(--text-color); + transition: border-color 0.3s, background-color 0.3s; +} + +button:hover, button[type="submit"]:disabled { + border-color: var(--fg-color-alt); + background-color: var(--fg-color); + color: var(--text-color-alt); } .dropdown-content { @@ -44,24 +112,111 @@ button { display: block; } +input[type="text"], input[type="file"], select { + background-color: #2d2d2d; + color: #ffffff; + font-family: var(--body-font-family); + font-size: 1.0em; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + text-align: center; + border: 2px solid var(--fg-color); + box-sizing: border-box; + transition: border-color 0.3s; +} + +input[type="text"]:hover, input[type="file"]:hover, select:hover, +input[type="text"]:focus, input[type="file"]:focus, select:focus { + border-color: white; + border: 2px solid; + box-sizing: border-box; + transition: border-color 0.3s; +} + +input[type='text']:disabled { + cursor: not-allowed; +} + +.small-hr { + border: 1px solid #3e3e3e; + width: 30%; +} + +.log-hr { + margin-bottom: 50px; +} + +input[type="file"] { + width: auto; +} + +/* input[type="text"] { + width: ; +} */ + +select { + width: auto; +} + body { background-color: var(--bg-color); - /*font-family: Arial, Helvetica, sans-serif;*/ - font-family: "DroidSans"; + font-family: var(--body-font-family); + font-size: 1.8em; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + margin: 0; + flex-wrap: wrap; + text-align: center; + padding-top: env(safe-area-inset-top); } + h1, h2, h3, h4, small, p, li, label { + font-family: var(--body-font-family); color: var(--text-color); } +h2, h3 { + margin-top: 0px; + margin-bottom: 0px; +} + +.desc { + color: var(--bg-color-alt); +} + h1 { + color: var(--fg-color); + font-family: "H1Weird", sans-serif; + font-size: 3em; + font-weight: bold; + letter-spacing: 0em; text-align: center; - text-transform: uppercase; } -p, small { +textarea { + background-color: #2d2d2d; + color: #ffffff; + font-family: var(--body-font-family); + font-size: 0.9em; + height: 280px; + border: 2px solid var(--fg-color); + box-sizing: border-box; + transition: border-color 0.3s; } +textarea:hover, textarea:focus { + border-color: white; + border: 2px solid; + box-sizing: border-box; + transition: border-color 0.3s; +} + +/* p { + font-size: 1.2em; +} */ + .content { display: none; } @@ -72,9 +227,8 @@ p, small { } hr { - border: 0; - height: 1px; - background-image: linear-gradient(to right, var(--fg-color-alt), var(--fg-color), var(--fg-color-alt)); + border: 2px solid #3e3e3e; + /* background-image: linear-gradient(to right, var(--fg-color-alt), var(--fg-color), var(--fg-color-alt)); */ } a { @@ -84,37 +238,45 @@ a:hover { text-decoration: none; color: var(--fg-color); } - +/* ul { list-style-type:none; -} +} */ ul, li { - list-style-type: none; + /* list-style-type: none; */ list-style-position:inside; margin:0; padding:0; } .tinput { - width: 100px !important; + width: 315px !important; background-color: var(--button-color) !important; color: var(--text-color) !important; + border: 2px solid var(--fg-color); /* accent color */ } .tinputauth { width: 499px !important; background-color: var(--button-color) !important; color: var(--text-color) !important; + border: 2px solid var(--fg-color); /* accent color */ +} + +canvas{ + width:750px !important; + height:400px !important; } /* RADIO BUTTON ######################### */ input[type='radio'] { appearance: none; - top: 13.33333px; + top: 13.333333px; right: 50%; - bottom: 0; + bottom: 0px; left: 0; + margin-top: 0px; height: 40px; width: 40px; transition: all .2s ease; @@ -137,43 +299,218 @@ input[type='radio']:checked:after { display: inline-block; } +input[type='radio']:disabled { + cursor: not-allowed; +} + +input[type='radio']:disabled:after { + background-color: var(--button-color-alt); + cursor: not-allowed; +} + /* ###################################### */ #username, #password { width: 499px !important; background-color: var(--bg-color-alt) !important; color: var(--text-color) !important; + border: 2px solid var(--fg-color); /* accent color */ +} + +#botAuth { + color: var(--text-color); } #content { - width: 800px !important; - /*word-break:break-all !important;*/ - /*word-wrap: break-all !important;*/ + width: 950px !important; + /* word-break:break-all !important; */ + /* word-wrap: break-all !important; */ white-space:normal !important; } #outer { width: 100%; display: flex; justify-content: center; + height: 100vh; /* Keeps things at the top*/ + align-items: start; +} + +#botStats { + width: 100%; + height: 50px; + display: flex; + justify-content: center; + align-items: center; + gap: 30px; +} + +/* a simple gif loading from /assets/cloudface.gif and is 60x60px */ +.botLoader { + background-image: url('/assets/cloudface.gif'); + background-size: 60px 60px; + background-position: center; + background-clip: border-box; + background-repeat: no-repeat; + width: 60px; + height: 60px; + margin: 0 auto; +} + +.batteryContainer { + display: flex; + justify-content: center; + align-items: center; + margin: 0 auto; + height: 60px; + padding-bottom: 15px; + gap: 15px; + cursor: pointer; +} + +.batteryContainer:hover .tooltip { + display: block; +} + +.tooltip { + position: absolute; + display: none; + background-color: var(--bg-color); + color: var(--text-color); + border-radius: 5px; + padding: 5px; + z-index: 1000; + margin-top: 145px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.8); +} + +.batteryOutline { + outline: 2px solid var(--text-color); + outline-offset: 2px; + border-radius: 5px; + width: 100px; + height: 40px; + position: relative; +} +.batteryOutline::before { + content: ''; + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: var(--button-color); + border-radius: 5px; +} +.batteryOutline::after { + content: ''; + position: absolute; + top: 12px; + left: 102px; + width: 6px; + height: 18px; + background-color: var(--text-color); +} + +.vectorFace { + height: 60px; + width: 60px; + background-position: center; + background-size: contain; + background-repeat: no-repeat; +} + +.batteryLevel { + position: absolute; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + background-color: var(--button-color-alt); + border-radius: 5px; +} + +.battery0 { + background-color: var(--low-battery); +} + +.battery1 { + background-color: var(--medium-battery); +} + +.battery3, .battery2 { + background-color: var(--fg-color); +} + +.charging { + position: absolute; + top: 0px; + left: 30px; + width: 40px; + height: 40px; + /* content is an svg of a lightning bolt */ + content: url('data:image/svg+xml;utf8,'); + /* svg drop shadow */ + filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.4)); +} + +.chargeTimeRemaining { + position: absolute; + bottom: 0px; + left: 3px; + font-size: 0.6em; + color: white; + text-shadow: 0 0 4px rgba(0, 0, 0, 1); } .main-nav-parent { - /*border: 1px solid black;*/ - margin: 1rem; - padding: 2rem 2rem; + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 20px; + margin: 1rem auto; + padding: 1rem; text-align: center; + width: 100%; + max-width: 950px; } .main-nav-child { - display: inline-block; - /*border: 1px solid white;*/ - padding: 0rem 0rem 2rem 0rem; + display: flex; + flex-direction: column; + align-items: center; + border-radius: 12px; + border: 1px solid white; + padding: 8px 12px; vertical-align: top; + width: auto; + background: rgba(30, 30, 30, 0.6); + border: 1px solid rgba(255, 255, 255, 0.1); + min-width: 85px; + transition: all 0.3s ease-in-out; width: 85px; } +.main-nav-child:has(.selectedicon) { + background: rgba(25, 25, 25, 0.9); + border: 1px solid var(--fg-color); + box-shadow: 0 0 15px rgba(0, 255, 128, 0.1); +} + +.main-nav-child:hover { + transform: translateY(-2px); + border-color: white; + box-shadow: 0 0 15px rgba(255, 255, 255, 0.1) +} + +.main-nav-child i { + font-size: 32px; + margin-bottom: 8px; + transition: all 0.3s ease-in-out; +} + .main-nav-child a { + width: 100%; text-decoration: none; - font-size: 75%; + font-size: 100%; color: var(--bg-color-alt); } @@ -190,5 +527,69 @@ input[type='radio']:checked:after { } .main-nav-child a img { - width:50%; + width: 40px; + height: 40px; + margin-bottom: 8px; + object-fit: contain; +} + +/* no horizontal scroll on mobile, grid instead of line*/ + +@media screen and (max-width: 768px) { + .main-nav-parent { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); + gap: 15px; + padding: 0.5rem; + margin: 0; + width: 100%; + } + + #outer { + max-width: 100vw; + overflow-x: hidden; + box-sizing: border-box; + } + + #content { + width: 100% !important; + max-width: 100vw; + padding: 0 10px; + box-sizing: border-box; + } +} + +input[type='checkbox'] { + appearance: none; + width: 25px; + height: 25px; + background-color: var(--button-color); + border: 2px solid var(--fg-color); /* accent color */ + border-radius: 5px; + cursor: pointer; + position: relative; + display: inline-block; + vertical-align: middle; +} + +input[type='checkbox']:checked { + background-color: var(--fg-color); +} + +input[type='checkbox']:checked:after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 6px; + height: 12px; + border: solid var(--text-color); + border-width: 0 2px 2px 0; + transform: translate(-50%, -60%) rotate(45deg); +} + +.checkbox-label { + padding-top: 3px; + margin-left: 5px; + word-break: break-word; } diff --git a/chipper/webroot/index.html b/chipper/webroot/index.html index 67dab63d..4e183310 100755 --- a/chipper/webroot/index.html +++ b/chipper/webroot/index.html @@ -1,141 +1,227 @@ + - Wire-Pod Interface - - - + Wire-Pod + + + + + + + + + + +
-

Wire-Pod Interface

-
- - -
- +

Wire-Pod

+ +
+
-