From 885efd959ae9d61e001e530b5efe4f09b8857409 Mon Sep 17 00:00:00 2001 From: arzumify Date: Sun, 20 Oct 2024 17:12:11 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 + LICENSE.md | 23 + build.sh | 9 + go.mod | 52 +++ go.sum | 139 ++++++ lib/main.go | 38 ++ main.go | 818 ++++++++++++++++++++++++++++++++++ plugins-src/bet/build.sh | 2 + plugins-src/bet/main.go | 252 +++++++++++ plugins-src/diceroll/build.sh | 2 + plugins-src/diceroll/main.go | 86 ++++ static/js/jwt.min.js | 8 + templates/admin.html | 99 ++++ templates/index.html | 140 ++++++ templates/login.html | 170 +++++++ templates/plugin.html | 62 +++ templates/privacy.html | 9 + templates/tos.html | 26 ++ 18 files changed, 1939 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100755 build.sh create mode 100644 go.mod create mode 100644 go.sum create mode 100644 lib/main.go create mode 100644 main.go create mode 100755 plugins-src/bet/build.sh create mode 100644 plugins-src/bet/main.go create mode 100755 plugins-src/diceroll/build.sh create mode 100644 plugins-src/diceroll/main.go create mode 100644 static/js/jwt.min.js create mode 100644 templates/admin.html create mode 100644 templates/index.html create mode 100644 templates/login.html create mode 100644 templates/plugin.html create mode 100644 templates/privacy.html create mode 100644 templates/tos.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71b1582 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +shoGambler +plugins +config.json diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..83b750d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,23 @@ +# The Restrictive Non-Commercial License + +## License Text + +1. **Use and Redistribution**: You are permitted to use and redistribute and create derivative works of this software, for non-commercial purposes only, provided that you comply with the terms of this license. + +2. **Commercial Use**: This software may not be used for any commercial purposes without explicit permission from the author. + +3. **Redistribution Terms**: Redistribution of this software is only permitted under the terms of this license. + +4. **Copyright**: All derivative works remain under the copyright of the original author, as stated in the copyright notice below. + +5. **License for Derivative Works**: All derivative works must be licensed under the same terms as this license, including the same copyright notice. + +6. **Permission Waiver**: Any of these conditions may be waived if you have obtained explicit permission from the author. + +7. **Cease of Use**: Upon request by the copyright holder, you must immediately cease all use of the software and refrain from any future use, distribution, or creation of derivative works unless otherwise agreed upon. + +8. **Disclaimer**: This software is provided "as is," without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement, to the extent permitted by law. + +## Copyright Notice + +Copyright (c) 2024, Arzumify \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..f9f9af1 --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh +go build -ldflags "-s -w" +cd plugins-src/diceroll || exit +./build.sh +mv diceroll.so ../../plugins/diceroll.so +cd ../bet || exit +./build.sh +mv bet.so ../../plugins/bet.so +echo Done diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..897c09c --- /dev/null +++ b/go.mod @@ -0,0 +1,52 @@ +module shoGambler + +go 1.23.0 + +require ( + github.com/MicahParks/keyfunc/v3 v3.3.5 + github.com/gin-gonic/gin v1.10.0 + github.com/golang-jwt/jwt/v5 v5.2.0 + modernc.org/sqlite v1.32.0 +) + +require ( + github.com/MicahParks/jwkset v0.5.19 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.55.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..68cda32 --- /dev/null +++ b/go.sum @@ -0,0 +1,139 @@ +github.com/MicahParks/jwkset v0.5.19 h1:XZCsgJv05DBCvxEHYEHlSafqiuVn5ESG0VRB331Fxhw= +github.com/MicahParks/jwkset v0.5.19/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY= +github.com/MicahParks/keyfunc/v3 v3.3.5 h1:7ceAJLUAldnoueHDNzF8Bx06oVcQ5CfJnYwNt1U3YYo= +github.com/MicahParks/keyfunc/v3 v3.3.5/go.mod h1:SdCCyMJn/bYqWDvARspC6nCT8Sk74MjuAY22C7dCST8= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +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= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= +github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= +modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/lib/main.go b/lib/main.go new file mode 100644 index 0000000..9789d37 --- /dev/null +++ b/lib/main.go @@ -0,0 +1,38 @@ +package lib + +import ( + "math/big" + + "github.com/gin-gonic/gin" +) + +type Date struct { + DaysSinceEpoch uint64 +} + +type PluginData struct { + Name string + CanReturnPoints bool + CanAddPoints bool + CanCheckMod bool + OnDataReturn string + CanAcceptArbitraryPointAmount bool + RecommendedPoints *big.Int + PluginHTML string + PluginScript string + ApiCode func(*gin.Context, ApiInput) (*big.Int, error) + HasExtraAPI bool + ExtraAPICode func(*gin.Context) +} + +type ApiInput struct { + InputPoints *big.Int + AddPointsFunction func(string, *big.Int) + ChannelID string + OptionalData string +} + +type DateAndStream struct { + Date Date + Stream *big.Int +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..ec44dc5 --- /dev/null +++ b/main.go @@ -0,0 +1,818 @@ +package main + +import ( + "shoGambler/lib" + + "errors" + "log" + "os" + "plugin" + "strconv" + "strings" + "time" + + "crypto/rand" + "crypto/sha256" + "database/sql" + "encoding/base64" + "encoding/hex" + "encoding/json" + "html/template" + "io/fs" + "math/big" + "net/http" + "path/filepath" + + "github.com/MicahParks/keyfunc/v3" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + _ "modernc.org/sqlite" +) + +func getYTChID(token string) (string, int, error) { + log.Println("[WARN] Scary, we are expending a Google API credit!") + + // Ask Google for the user's channel ID + request, err := http.NewRequest("GET", "https://www.googleapis.com/youtube/v3/channels?mine=true", nil) + if err != nil { + return "", 500, errors.New("error creating Google auth request") + } + + // Set the Authorization header + request.Header.Set("Authorization", "Bearer "+token) + + // Send the request + response, err := http.DefaultClient.Do(request) + if err != nil { + return "", 500, errors.New("error sending Google auth request") + } + + // Check the status code + if response.StatusCode != 200 { + return "", response.StatusCode, errors.New("error getting Google auth response") + } + + // Read the response + var responseJSON map[string]interface{} + err = json.NewDecoder(response.Body).Decode(&responseJSON) + if err != nil { + return "", 500, errors.New("error decoding Google auth response") + } + + // Get the user's channel ID + channelID, ok := responseJSON["items"].([]interface{})[0].(map[string]interface{})["id"].(string) + if !ok { + return "", 400, errors.New("error getting channel ID") + } + + return channelID, 200, nil +} + +func giveUserPoints(channelID string, points *big.Int) { + // Add the points to the userPoints + var existingPoints []byte + err := conn.QueryRow("SELECT points FROM users WHERE channelID = ?", channelID).Scan(&existingPoints) + if errors.Is(err, sql.ErrNoRows) { + _, err := conn.Exec("INSERT INTO users (channelID, points) VALUES (?, ?)", channelID, points.Bytes()) + if err != nil { + log.Fatal("[FATAL] Error adding user to database: ", err) + } + } else if err != nil { + log.Fatal("[FATAL] Error querying database: ", err) + } else { + _, err := conn.Exec("UPDATE users SET points = ? WHERE channelID = ?", new(big.Int).Add(points, new(big.Int).SetBytes(existingPoints)).Bytes(), channelID) + if err != nil { + log.Fatal("[FATAL] Error updating user points: ", err) + } + } +} + +func subtractUserPoints(channelID string, points *big.Int) { + // Subtract the points from the userPoints + var existingPoints []byte + err := conn.QueryRow("SELECT points FROM users WHERE channelID = ?", channelID).Scan(&existingPoints) + if errors.Is(err, sql.ErrNoRows) { + _, err := conn.Exec("INSERT INTO users (channelID, points) VALUES (?, ?)", channelID, new(big.Int).Neg(points).Bytes()) + if err != nil { + log.Fatal("[FATAL] Error adding user to database: ", err) + } + } else if err != nil { + log.Fatal("[FATAL] Error querying database: ", err) + } else { + _, err := conn.Exec("UPDATE users SET points = ? WHERE channelID = ?", new(big.Int).Add(new(big.Int).SetBytes(existingPoints), new(big.Int).Neg(points)).Bytes(), channelID) + if err != nil { + log.Fatal("[FATAL] Error updating user points: ", err) + } + } +} + +func getUserPoints(channelID string) *big.Int { + // Get the user's points + var points []byte + err := conn.QueryRow("SELECT points FROM users WHERE channelID = ?", channelID).Scan(&points) + if errors.Is(err, sql.ErrNoRows) { + return big.NewInt(0) + } else if err != nil { + log.Fatal("[FATAL] Error querying database: ", err) + } + + return new(big.Int).SetBytes(points) +} + +func userIsModerator(accessToken string) bool { + // Get the channel ID + channelID, ok := userSessions[accessToken] + if !ok { + return false + } + + if channelID != "UCHlTEt24Yb4ylJFuWz7hXIw" { + // Check if the user is a moderator + var channelIDCheck string + err := conn.QueryRow("SELECT channelID FROM moderators WHERE channelID = ?", channelID).Scan(&channelIDCheck) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false + } else { + log.Fatal("[FATAL] Error querying database: ", err) + return false + } + } else { + return channelID == channelIDCheck + } + } else { + // Bro it's literally shounic, of course they're a moderator + return true + } +} + +func checkForChatMessages(liveChatID string) { + for { + if streaming == true { + // Check for chat messages + log.Println("[INFO] Scary, we are expending a Google API credit (on stream)!") + + // Keeping this in a comment so the compiler doesn't remember it: + key, err := base64.StdEncoding.DecodeString(configFile.ApiKey) + if err != nil { + log.Println("[ERROR] Error decoding API key: ", err) + } + + response, err := http.Get("https://www.googleapis.com/youtube/v3/liveChat/messages?liveChatId=" + liveChatID + "&part=snippet,authorDetails&key=" + string(key)) + if err != nil { + log.Println("[ERROR] Error getting chat messages: ", err) + return + } + + // Read the response + var responseJSON map[string]interface{} + err = json.NewDecoder(response.Body).Decode(&responseJSON) + if err != nil { + log.Println("[ERROR] Error decoding chat messages: ", err) + return + } + + // Check the status code + if response.StatusCode != 200 { + log.Println("[ERROR] Error getting chat messages: ", response.Status, responseJSON) + return + } else { + // Iterate through each live chat message + for _, item := range responseJSON["items"].([]interface{}) { + log.Println("[INFO] Processing message from ", item.(map[string]interface{})["authorDetails"].(map[string]interface{})["displayName"].(string)) + earliestSentMessage, ok := earliestSentMsg[item.(map[string]interface{})["authorDetails"].(map[string]interface{})["channelId"].(string)] + publishedTime, err := time.Parse(time.RFC3339Nano, item.(map[string]interface{})["snippet"].(map[string]interface{})["publishedAt"].(string)) + if err != nil { + log.Println("[ERROR] Error parsing time: ", err) + } else if !ok || publishedTime.Before(earliestSentMessage) { + if publishedTime.After(streamingSince) { + log.Println("[INFO] New message from ", item.(map[string]interface{})["authorDetails"].(map[string]interface{})["displayName"].(string)) + earliestSentMsg[item.(map[string]interface{})["authorDetails"].(map[string]interface{})["channelId"].(string)] = publishedTime + } + } + } + + // Close the response body + err := response.Body.Close() + if err != nil { + log.Println("[ERROR] Error closing response body: ", err) + } + + // Wait for the rate because Google likes to screw us over + time.Sleep(time.Second * time.Duration(configFile.Rate)) + } + } else { + return + } + } +} + +var ( + plugins []lib.PluginData + conn *sql.DB + earliestSentMsg = make(map[string]time.Time) + userSessions = make(map[string]string) + streamingSince time.Time + configFile config + streaming bool +) + +type config struct { + ApiKey string `json:"key"` + Rate int `json:"rate"` +} + +func main() { + // Connect to the database + var err error + conn, err = sql.Open("sqlite", "database.db") + if err != nil { + log.Fatal("[FATAL] Error connecting to database: ", err) + } + + // Read in config.json + configBytes, err := os.ReadFile("config.json") + if err != nil { + log.Fatal("[FATAL] Error reading config.json: ", err) + } + + // Parse the JSON + err = json.Unmarshal(configBytes, &configFile) + if err != nil { + log.Fatal("[FATAL] Error parsing config.json: ", err) + } + + // Create the blacklist table if it doesn't exist + _, err = conn.Exec("CREATE TABLE IF NOT EXISTS blacklist (nonce TEXT NOT NULL UNIQUE)") + if err != nil { + log.Fatal("[FATAL] Error creating blacklist table: ", err) + } + + // Create the plugins table if it doesn't exist + _, err = conn.Exec("CREATE TABLE IF NOT EXISTS plugins (pluginName TEXT UNIQUE, pointsOverride BLOB)") + if err != nil { + log.Fatal("[FATAL] Error creating plugins table: ", err) + } + + // Create the moderator table if it doesn't exist + _, err = conn.Exec("CREATE TABLE IF NOT EXISTS moderators (channelID TEXT NOT NULL UNIQUE, isStreamer BOOLEAN NOT NULL DEFAULT FALSE)") + if err != nil { + log.Fatal("[FATAL] Error creating moderators table: ", err) + } + + // Create the user table if it doesn't exist + _, err = conn.Exec("CREATE TABLE IF NOT EXISTS users (channelID TEXT NOT NULL UNIQUE, points BLOB NOT NULL, sub TEXT NOT NULL UNIQUE)") + if err != nil { + log.Fatal("[FATAL] Error creating users table: ", err) + } + + // Set up the JWT verification + keyVerifyFunction, err := keyfunc.NewDefault([]string{"https://www.googleapis.com/oauth2/v3/certs"}) + if err != nil { + log.Fatal("[FATAL] Error setting up JWT verification: ", err) + } + + // Set up plugins + err = filepath.WalkDir("plugins", func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + + // Ignore directories + if d.IsDir() { + return nil + } + + // Load the plugin + gamblePlugin, err := plugin.Open(path) + if err != nil { + return err + } + + // Run plugin.Metadata + metadata, err := gamblePlugin.Lookup("Metadata") + if err != nil { + return err + } + + // Declare it as a function + metadataFunc, ok := metadata.(func() (lib.PluginData, error)) + if !ok { + return errors.New("metadata is not a function") + } + + // Call the function + data, err := metadataFunc() + + // Give them the add points function if they want it + if data.CanAddPoints { + addPoints, err := gamblePlugin.Lookup("SetAddPointsFunc") + if err != nil { + return err + } + + addPointsFunc, ok := addPoints.(func(func(string, *big.Int))) + if !ok { + return errors.New("addPoints is not a function") + } + + addPointsFunc(giveUserPoints) + } + + // Give them the check moderator function if they want it + if data.CanCheckMod { + checkMod, err := gamblePlugin.Lookup("SetCheckModFunc") + if err != nil { + return err + } + + checkModFunc, ok := checkMod.(func(func(string) bool)) + if !ok { + return errors.New("checkMod is not a function") + } + + checkModFunc(userIsModerator) + } + + // Append the plugin data to the plugins slice + plugins = append(plugins, data) + + return nil + }) + if err != nil { + log.Fatal("[FATAL] Error setting up plugins: ", err) + } + + // Set up the router + gin.SetMode(gin.ReleaseMode) + router := gin.New() + + // Set up the routes + router.Static("/static", "./static") + router.LoadHTMLGlob("./templates/*") + + // Define routes for each plugin + for _, pluginData := range plugins { + log.Println("[INFO] Setting up plugin", pluginData.Name) + + // Try to see if there is a point cost override + pointCost := pluginData.RecommendedPoints + var pointCostBytes []byte + err := conn.QueryRow("SELECT pointsOverride FROM plugins WHERE pluginName = ?", pluginData.Name).Scan(&pointCostBytes) + if err == nil { + pointCost = new(big.Int).SetBytes(pointCostBytes) + } else if !errors.Is(err, sql.ErrNoRows) { + log.Fatal("[FATAL] Error querying database: ", err) + } + + router.GET("/"+pluginData.Name, func(c *gin.Context) { + var costsSupported string + if pluginData.CanAcceptArbitraryPointAmount { + costsSupported = "true" + } else { + costsSupported = "false" + } + + var canReturn string + if pluginData.CanReturnPoints { + canReturn = "true" + } else { + canReturn = "false" + } + + c.HTML(200, "plugin.html", gin.H{ + "Name": pluginData.Name, + "Cost": pointCost, + "PluginHTML": template.HTML(pluginData.PluginHTML), + "PluginScript": template.JS(pluginData.PluginScript), + "CanReturn": canReturn, + "MultipleCostsSupported": costsSupported, + "OnDataReturn": template.JS(pluginData.OnDataReturn), + }) + }) + + router.POST("/api/"+pluginData.Name, func(c *gin.Context) { + var data map[string]interface{} + err := c.BindJSON(&data) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + // Get the user's access token + accessToken := c.GetHeader("Authorization") + if accessToken == "" { + c.JSON(400, gin.H{"error": "No token provided"}) + return + } + + // Get the user's channel ID + channelID, ok := userSessions[accessToken] + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + var inputPoints *big.Int + if pluginData.CanAcceptArbitraryPointAmount { + // Get the points + points, ok := data["points"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + // Parse the points + inputPoints, ok = new(big.Int).SetString(points, 10) + if !ok { + c.JSON(400, gin.H{"error": "Invalid points"}) + return + } + + // Subtract the point cost from the user's points + userPointAmount := getUserPoints(channelID) + if userPointAmount == big.NewInt(0) { + c.JSON(400, gin.H{"error": "No points"}) + return + } + + remaining := new(big.Int).Sub(userPointAmount, inputPoints) + if remaining.Cmp(big.NewInt(0)) == 1 { + subtractUserPoints(channelID, inputPoints) + } else { + c.JSON(400, gin.H{"error": "Not enough points, want " + inputPoints.String() + " have " + userPointAmount.String() + ", would leave you with " + remaining.String()}) + return + } + } else { + // Subtract the point cost from the user's points + userPointAmount := getUserPoints(channelID) + if userPointAmount == big.NewInt(0) { + c.JSON(400, gin.H{"error": "No points"}) + return + } + + remaining := new(big.Int).Sub(userPointAmount, pointCost) + if remaining.Cmp(big.NewInt(0)) == 1 { + subtractUserPoints(channelID, pointCost) + } else { + c.JSON(400, gin.H{"error": "Not enough points, want " + pointCost.String() + " have " + userPointAmount.String() + ", would leave you with " + remaining.String()}) + return + } + } + + optionalData, ok := data["optional"].(string) + if !ok { + optionalData = "none" + } + + if pluginData.CanReturnPoints { + profit, err := pluginData.ApiCode(c, lib.ApiInput{ + InputPoints: inputPoints, + OptionalData: optionalData, + ChannelID: channelID, + }) + if err != nil { + c.JSON(424, gin.H{"error": err.Error()}) + return + } + giveUserPoints(channelID, profit) + c.JSON(200, gin.H{"profit": profit.String()}) + } else { + _, err := pluginData.ApiCode(c, lib.ApiInput{ + InputPoints: inputPoints, + OptionalData: optionalData, + ChannelID: channelID, + }) + if err != nil { + c.JSON(424, gin.H{"error": err.Error()}) + return + } + c.JSON(200, gin.H{"success": "true"}) + } + + }) + + if pluginData.HasExtraAPI { + router.POST("/api/extra/"+pluginData.Name, pluginData.ExtraAPICode) + } + } + + // Define the route for /api/claimUnclaimedPoints + router.POST("/api/claimUnclaimedPoints", func(c *gin.Context) { + // Look for the token in the userMap + accessToken := c.GetHeader("Authorization") + id, ok := userSessions[accessToken] + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Get the users earliest sent message + earliestSentMessage, ok := earliestSentMsg[id] + if !ok { + c.JSON(400, gin.H{"error": "No messages sent"}) + return + } + + // Get the second difference between the earliest sent message and now + secondDifference := int64(time.Now().Sub(earliestSentMessage).Seconds()) + + // Clear the earliest sent message + delete(earliestSentMsg, id) + + // Issue the points + giveUserPoints(id, big.NewInt(secondDifference)) + + // Return the points + c.JSON(200, gin.H{"points": strconv.FormatInt(secondDifference, 10)}) + }) + + // Define the route for /api/getUnclaimedPoints + router.GET("/api/getUnclaimedPoints", func(c *gin.Context) { + // Look for the token in the userMap + accessToken := c.GetHeader("Authorization") + id, ok := userSessions[accessToken] + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Get the users earliest sent message + earliestSentMessage, ok := earliestSentMsg[id] + if !ok { + c.JSON(200, gin.H{"points": "0"}) + return + } + + // Get the second difference between the earliest sent message and now + secondDifference := int64(time.Now().Sub(earliestSentMessage).Seconds()) + + // Return the points + c.JSON(200, gin.H{"points": strconv.FormatInt(secondDifference, 10)}) + }) + + // Define the route for /api/getPlugins + router.GET("/api/getPlugins", func(c *gin.Context) { + var pluginJSON []map[string]interface{} + for _, pluginData := range plugins { + // Try to see if there is a point cost override + pointCost := pluginData.RecommendedPoints + var pointCostBytes []byte + err := conn.QueryRow("SELECT pointsOverride FROM plugins WHERE pluginName = ?", pluginData.Name).Scan(&pointCostBytes) + if err == nil { + pointCost = new(big.Int).SetBytes(pointCostBytes) + } else if !errors.Is(err, sql.ErrNoRows) { + log.Fatal("[FATAL] Error querying database: ", err) + } + + // Append the plugin data to the pluginJSON slice + var costsSupported string + if pluginData.CanAcceptArbitraryPointAmount { + costsSupported = "true" + } else { + costsSupported = "false" + } + + pluginJSON = append(pluginJSON, map[string]interface{}{ + "Name": pluginData.Name, + "CanReturnPoints": pluginData.CanReturnPoints, + "Cost": pointCost, + "MultipleCostsSupported": costsSupported, + }) + } + c.JSON(200, pluginJSON) + }) + + // Define the route for /api/getPoints + router.GET("/api/getPoints", func(c *gin.Context) { + // Look for the token in the userMap + accessToken := c.GetHeader("Authorization") + id, ok := userSessions[accessToken] + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Get the user's points + points := getUserPoints(id) + + c.JSON(200, gin.H{"points": points.String()}) + }) + + // Ugh, why do we have to do this legally + router.POST("/api/delete", func(c *gin.Context) { + // Look for the token in the userMap + accessToken := c.GetHeader("Authorization") + id, ok := userSessions[accessToken] + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Delete everything from every map with the user's channel ID + delete(earliestSentMsg, id) + _, err := conn.Exec("DELETE FROM users WHERE channelID = ?", id) + if err != nil { + log.Fatal("[FATAL] Error deleting user: ", err) + } + + // Delete all access tokens with the user's channel ID + for key, value := range userSessions { + if value == id { + delete(userSessions, key) + } + } + + c.JSON(200, "Data will be deleted in the time it takes for the garbage collector to do its thing") + }) + + // Define the route for /api/startStream + router.POST("/api/startStream", func(c *gin.Context) { + var data map[string]interface{} + err := c.BindJSON(&data) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + // Authenticate the user + accessToken := c.GetHeader("Authorization") + if accessToken == "" { + c.JSON(400, gin.H{"error": "No token provided"}) + return + } + + // Check if the user is a moderator + if !userIsModerator(accessToken) { + c.JSON(403, gin.H{"error": "You must be a moderator to start a stream"}) + return + } + + // Set the live status + streaming = true + streamingSince = time.Now() + liveChatID, ok := data["liveChatID"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + // Start the chat message checker + log.Println("Starting chat message checker: ", liveChatID) + go checkForChatMessages(liveChatID) + + // Return 200 + c.JSON(200, gin.H{"message": "Stream started"}) + }) + + // Define the route for /api/endStream + router.POST("/api/endStream", func(c *gin.Context) { + // Authenticate the user + accessToken := c.GetHeader("Authorization") + if accessToken == "" { + c.JSON(400, gin.H{"error": "No token provided"}) + return + } + + // Check if the user is a moderator + if !userIsModerator(accessToken) { + c.JSON(403, gin.H{"error": "You must be a moderator to end a stream"}) + return + } + + // Set the live status + streaming = false + + // Clear all unclaimed points + for key := range earliestSentMsg { + delete(earliestSentMsg, key) + } + + // Return 200 + c.JSON(200, gin.H{"message": "Stream ended"}) + }) + + router.POST("/api/authorize", func(c *gin.Context) { + var data map[string]interface{} + err := c.BindJSON(&data) + if err != nil { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + // Check if it's a valid google token via JWT + accessToken, ok := data["idToken"].(string) + if !ok { + c.JSON(400, gin.H{"error": "Invalid JSON"}) + return + } + + parsedToken, err := jwt.Parse(accessToken, keyVerifyFunction.Keyfunc) + if err != nil { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Get the claims + claims, ok := parsedToken.Claims.(jwt.MapClaims) + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Check if the user is already registered + var channelID string + err = conn.QueryRow("SELECT channelID FROM users WHERE sub = ?", claims["sub"]).Scan(&channelID) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + // Get the at_hash + atHash, ok := claims["at_hash"].(string) + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Get the access token + accessToken, ok = data["accessToken"].(string) + if !ok { + c.JSON(403, gin.H{"error": "Invalid token"}) + return + } + + // Hash the access token + hashedAccessToken := sha256.Sum256([]byte(accessToken)) + + // Check if the hash matches the at_hash + if strings.ReplaceAll(base64.URLEncoding.EncodeToString(hashedAccessToken[:16]), "=", "") != atHash { + c.JSON(403, gin.H{"error": "Non-matching access token"}) + return + } + + // Get the user's channel ID + channelID, response, err := getYTChID(accessToken) + if err != nil { + c.JSON(response, gin.H{"error": "Error getting channel ID"}) + return + } + + _, err = conn.Exec("INSERT INTO users (channelID, sub, points) VALUES (?, ?, ?)", channelID, claims["sub"], big.NewInt(0).Bytes()) + if err != nil { + log.Fatal("[FATAL] Error registering user: ", err) + } + } else { + log.Fatal("[FATAL] Error querying database: ", err) + } + } + + // Create a new random session token + sessionToken := make([]byte, 32) + _, err = rand.Read(sessionToken) + if err != nil { + log.Fatal("[FATAL] Error generating session token: ", err) + } + + // Hex encode the session token + sessionTokenHex := hex.EncodeToString(sessionToken) + + // Add the session token to the userSessions map + userSessions[sessionTokenHex] = channelID + + // Return the session token + c.JSON(200, gin.H{"sessionToken": sessionTokenHex}) + }) + + // Now some static routes + router.GET("/", func(c *gin.Context) { + c.HTML(200, "index.html", gin.H{}) + }) + + router.GET("/admin", func(c *gin.Context) { + c.HTML(200, "admin.html", gin.H{}) + }) + + router.GET("/login", func(c *gin.Context) { + c.HTML(200, "login.html", gin.H{}) + }) + + router.GET("/privacy", func(c *gin.Context) { + c.HTML(200, "privacy.html", gin.H{}) + }) + + router.GET("/tos", func(c *gin.Context) { + c.HTML(200, "tos.html", gin.H{}) + }) + + // Start the server + var address string + if len(os.Args) < 2 { + address = ":8080" + } else { + address = os.Args[1] + } + + log.Println("[INFO] Start server on " + address) + err = router.Run(address) + if err != nil { + log.Fatal("[FATAL] Error starting server: ", err) + } +} diff --git a/plugins-src/bet/build.sh b/plugins-src/bet/build.sh new file mode 100755 index 0000000..dbf25f0 --- /dev/null +++ b/plugins-src/bet/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +go build -ldflags "-s -w" -buildmode=plugin -o bet.so main.go \ No newline at end of file diff --git a/plugins-src/bet/main.go b/plugins-src/bet/main.go new file mode 100644 index 0000000..ffb94b3 --- /dev/null +++ b/plugins-src/bet/main.go @@ -0,0 +1,252 @@ +package main + +import ( + "errors" + "math/big" + "shoGambler/lib" + "time" + + "github.com/gin-gonic/gin" +) + +type bet struct { + amount *big.Int + answer string +} + +var ( + bets = make(map[string]bet) + possibleAnswers []string + bettingIsOpen = false + question string + addPoints func(string, *big.Int) + userIsModerator func(string) bool + timeToBet time.Time +) + +func Metadata() (lib.PluginData, error) { + return lib.PluginData{ + Name: "bet-on-it", + CanReturnPoints: false, + RecommendedPoints: big.NewInt(10), + CanAcceptArbitraryPointAmount: true, + PluginHTML: ` +

Bet on it

+

Vote for what you think will happen

+

If you lose the bet, you lose your points

+

If you win the bet, you get double your points back

+

+

+

+ + `, + PluginScript: ` + async function updateTimer(time) { + while (true) { + let timeLeft = time - Math.floor(Date.now() / 1000); + if (timeLeft <= 0) { + document.getElementById("timer").innerText = "Betting is now closed"; + document.getElementById("bet").disabled = true; + } else { + document.getElementById("timer").innerText = "Betting closes in " + timeLeft + " seconds"; + } + await new Promise(r => setTimeout(r, 1000)); + } + } + + let data + fetch("/api/extra/bet-on-it", { + method: "POST", + body: JSON.stringify({ + Action: "getCurrentBet", + }), + headers: { + "Content-Type": "application/json", + }, + }) + .then(async (response) => { + if (response.status == 206) { + alert("There is not a running bet"); + window.location.href = "/"; + } else if (response.status != 200) { + alert("Error: " + response.statusText); + window.location.href = "/"; + } + data = await response.json(); + document.getElementById("question").innerText = data["question"]; + document.getElementById("possible").innerText = data["possible"].join(", "); + updateTimer(data["timeToBet"]); + document.getElementById("bet").disabled = false; + }) + document.getElementById("bet").addEventListener("click", async () => { + let points = BigInt(0); + try { + points = BigInt(prompt("How many points do you want to spend?")); + } catch (e) { + alert("Invalid number"); + return; + } + let candidate = prompt(data["question"] + data["possible"].join(", ") + "(case sensitive)"); + if (data["possible"].includes(candidate)) { + sendCost(points, candidate); + } else { + alert("That's not an option!") + } + }) + + `, + ApiCode: ApiCode, + HasExtraAPI: true, + ExtraAPICode: ExtraAPICode, + OnDataReturn: "alert('Bet placed. Good luck!')", + CanAddPoints: true, + CanCheckMod: true, + }, nil +} + +func ApiCode(_ *gin.Context, input lib.ApiInput) (*big.Int, error) { + if time.Now().Before(timeToBet) { + // See which option the user bet on + candidate := input.OptionalData + + // Add the user's bet to the map + validBet := false + for _, possibleAnswer := range possibleAnswers { + if candidate == possibleAnswer { + validBet = true + break + } + } + + if !validBet { + return nil, errors.New("invalid bet") + } else { + bets[input.ChannelID] = bet{ + amount: input.InputPoints, + answer: candidate, + } + return nil, nil + } + } else { + return nil, errors.New("betting is closed") + } +} + +func ExtraAPICode(c *gin.Context) { + var data map[string]interface{} + err := c.BindJSON(&data) + if err != nil { + c.JSON(400, gin.H{ + "error": "Invalid JSON", + }) + return + } + action, ok := data["Action"].(string) + if !ok { + c.JSON(400, gin.H{ + "error": "Invalid action", + }) + return + } + switch action { + case "getCurrentBet": + if bettingIsOpen { + c.JSON(200, gin.H{ + "question": question, + "possible": possibleAnswers, + "timeToBet": timeToBet.Unix(), + }) + return + } else { + c.JSON(206, gin.H{ + "error": "There is not a running bet", + }) + return + } + case "startBet": + if bettingIsOpen { + c.JSON(206, gin.H{ + "error": "There is already a running bet", + }) + return + } else { + accessToken := c.GetHeader("Authorization") + if !userIsModerator(accessToken) || accessToken == "" { + c.JSON(403, gin.H{ + "error": "You must be a moderator to start a bet", + }) + return + } + question, ok = data["question"].(string) + if !ok { + c.JSON(400, gin.H{ + "error": "Invalid question", + }) + return + } + possibleAnswersJSON, ok := data["possible"].([]interface{}) + if !ok { + c.JSON(400, gin.H{ + "error": "Invalid possible answers", + }) + return + } + possibleAnswers = make([]string, len(possibleAnswersJSON)) + for i, possibleAnswer := range possibleAnswersJSON { + possibleAnswers[i], ok = possibleAnswer.(string) + if !ok { + c.JSON(400, gin.H{ + "error": "Invalid possible answer", + }) + return + } + } + + timeToBet = time.Unix(int64(data["timeToBet"].(float64)), 0) + bettingIsOpen = true + c.JSON(200, gin.H{ + "success": true, + }) + return + } + case "endBet": + if bettingIsOpen { + accessToken := c.GetHeader("Authorization") + if !userIsModerator(accessToken) || accessToken == "" { + c.JSON(403, gin.H{ + "error": "You must be a moderator to end a bet", + }) + return + } + bettingIsOpen = false + // Give the winners double their points + for channelID, bet := range bets { + if bet.answer == data["answer"].(string) { + addPoints(channelID, new(big.Int).Mul(bet.amount, big.NewInt(2))) + } + } + // Clear the bets + bets = make(map[string]bet) + question = "" + possibleAnswers = nil + // Return success + c.JSON(200, gin.H{ + "success": true, + }) + return + } + default: + c.JSON(400, gin.H{ + "error": "Invalid action", + }) + return + } +} + +func SetAddPointsFunc(addPointsFunc func(string, *big.Int)) { + addPoints = addPointsFunc +} + +func SetCheckModFunc(userIsModeratorFunc func(string) bool) { + userIsModerator = userIsModeratorFunc +} diff --git a/plugins-src/diceroll/build.sh b/plugins-src/diceroll/build.sh new file mode 100755 index 0000000..ce121c1 --- /dev/null +++ b/plugins-src/diceroll/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +go build -ldflags "-s -w" -buildmode=plugin -o diceroll.so main.go \ No newline at end of file diff --git a/plugins-src/diceroll/main.go b/plugins-src/diceroll/main.go new file mode 100644 index 0000000..327ffd4 --- /dev/null +++ b/plugins-src/diceroll/main.go @@ -0,0 +1,86 @@ +package main + +import ( + "crypto/rand" + "errors" + "fmt" + "math/big" + + "shoGambler/lib" + + "github.com/gin-gonic/gin" +) + +func Metadata() (lib.PluginData, error) { + return lib.PluginData{ + Name: "dice-roll", + // It does return points + CanReturnPoints: true, + // It does not add points outside out /api/dice-roll + CanAddPoints: false, + // It does not need an arbitrary API, it's fine within the constraints of the plugin API + HasExtraAPI: false, + // It does not need an arbitrary API, so this is nil + ExtraAPICode: nil, + // It recommends 10 points to be spent + RecommendedPoints: big.NewInt(10), + // You can input arbitrary point amounts + CanAcceptArbitraryPointAmount: true, + // Very simple HTML + PluginHTML: ` +

Dice Roll

+

Roll a dice

+ + `, + // Very simple script + PluginScript: ` + document.getElementById("roll").addEventListener("click", async () => { + let points = BigInt(0); + try { + points = BigInt(prompt("How many points do you want to spend?")); + } catch (e) { + alert("Invalid number"); + return; + } + sendCost(points); + }) + + `, + // The API code is the function ApiCode + ApiCode: ApiCode, + // If the plugin html says the plugin has returned data rather than points, it's an error + OnDataReturn: "alert('Error: this plugin should be returning points'); throw new Error('Error: this plugin should be returning points')", + }, nil +} + +func ApiCode(_ *gin.Context, input lib.ApiInput) (*big.Int, error) { + // Roll a die + diceRoll, err := rand.Int(rand.Reader, big.NewInt(6)) + if err != nil { + return nil, err + } + + // 1, 2 and 3 - lose all points + // 4 and 5 - keep points + // 6 - win 125% points + + if input.InputPoints != nil { + switch diceRoll.Uint64() + 1 { + case 1, 2, 3: + // Lose all points + return big.NewInt(0), nil + case 4, 5: + // Keep points + return input.InputPoints, nil + case 6: + // Win 125% points + result, _ := new(big.Float).Mul(new(big.Float).SetInt(input.InputPoints), big.NewFloat(1.25)).Int(nil) + return result, nil + } + + return nil, errors.New("dice roll out of range: " + diceRoll.String()) + } else { + fmt.Println(input.InputPoints) + return nil, errors.New("input points is nil") + } +} diff --git a/static/js/jwt.min.js b/static/js/jwt.min.js new file mode 100644 index 0000000..c9bedf6 --- /dev/null +++ b/static/js/jwt.min.js @@ -0,0 +1,8 @@ +// github.com/panva/jose +// This code is licensed under the MIT License (see LICENSE.md file in the repository for more details +// Copyright (c) 2018 Filip Skokan +(function(g,f){typeof exports==='object'&&typeof module!=='undefined'?f(exports):typeof define==='function'&&define.amd?define(['exports'],f):(g=typeof globalThis!=='undefined'?globalThis:g||self,f(g.jose={}));})(this,(function(exports){'use strict';var or=Object.defineProperty;var ut=(e,t)=>{for(var r in t)or(e,r,{get:t[r],enumerable:!0});};var f=crypto,_=e=>e instanceof CryptoKey;var ar=async(e,t)=>{let r=`SHA-${e.slice(-3)}`;return new Uint8Array(await f.subtle.digest(r,t))},He=ar;var w=new TextEncoder,x=new TextDecoder,Ce=2**32;function v(...e){let t=e.reduce((o,{length:a})=>o+a,0),r=new Uint8Array(t),n=0;for(let o of e)r.set(o,n),n+=o.length;return r}function ht(e,t){return v(w.encode(e),new Uint8Array([0]),t)}function $e(e,t,r){if(t<0||t>=Ce)throw new RangeError(`value must be >= 0 and <= ${Ce-1}. Received ${t}`);e.set([t>>>24,t>>>16,t>>>8,t&255],r);}function Pe(e){let t=Math.floor(e/Ce),r=e%Ce,n=new Uint8Array(8);return $e(n,t,0),$e(n,r,4),n}function ve(e){let t=new Uint8Array(4);return $e(t,e),t}function We(e){return v(ve(e.length),e)}async function lt(e,t,r){let n=Math.ceil((t>>3)/32),o=new Uint8Array(n*32);for(let a=0;a>3)}var Je=e=>{let t=e;typeof t=="string"&&(t=w.encode(t));let r=32768,n=[];for(let o=0;oJe(e).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"),Be=e=>{let t=atob(e),r=new Uint8Array(t.length);for(let n=0;n{let t=e;t instanceof Uint8Array&&(t=x.decode(t)),t=t.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"");try{return Be(t)}catch{throw new TypeError("The input to be decoded is not correctly encoded.")}};var yt={};ut(yt,{JOSEAlgNotAllowed:()=>N,JOSEError:()=>H,JOSENotSupported:()=>h,JWEDecryptionFailed:()=>M,JWEInvalid:()=>c,JWKInvalid:()=>pe,JWKSInvalid:()=>oe,JWKSMultipleMatchingKeys:()=>fe,JWKSNoMatchingKey:()=>z,JWKSTimeout:()=>ue,JWSInvalid:()=>m,JWSSignatureVerificationFailed:()=>X,JWTClaimValidationFailed:()=>C,JWTExpired:()=>ne,JWTInvalid:()=>K});var H=class extends Error{static get code(){return "ERR_JOSE_GENERIC"}constructor(t){super(t),this.code="ERR_JOSE_GENERIC",this.name=this.constructor.name,Error.captureStackTrace?.(this,this.constructor);}},C=class extends H{static get code(){return "ERR_JWT_CLAIM_VALIDATION_FAILED"}constructor(t,r,n="unspecified",o="unspecified"){super(t),this.code="ERR_JWT_CLAIM_VALIDATION_FAILED",this.claim=n,this.reason=o,this.payload=r;}},ne=class extends H{static get code(){return "ERR_JWT_EXPIRED"}constructor(t,r,n="unspecified",o="unspecified"){super(t),this.code="ERR_JWT_EXPIRED",this.claim=n,this.reason=o,this.payload=r;}},N=class extends H{constructor(){super(...arguments),this.code="ERR_JOSE_ALG_NOT_ALLOWED";}static get code(){return "ERR_JOSE_ALG_NOT_ALLOWED"}},h=class extends H{constructor(){super(...arguments),this.code="ERR_JOSE_NOT_SUPPORTED";}static get code(){return "ERR_JOSE_NOT_SUPPORTED"}},M=class extends H{constructor(){super(...arguments),this.code="ERR_JWE_DECRYPTION_FAILED",this.message="decryption operation failed";}static get code(){return "ERR_JWE_DECRYPTION_FAILED"}},c=class extends H{constructor(){super(...arguments),this.code="ERR_JWE_INVALID";}static get code(){return "ERR_JWE_INVALID"}},m=class extends H{constructor(){super(...arguments),this.code="ERR_JWS_INVALID";}static get code(){return "ERR_JWS_INVALID"}},K=class extends H{constructor(){super(...arguments),this.code="ERR_JWT_INVALID";}static get code(){return "ERR_JWT_INVALID"}},pe=class extends H{constructor(){super(...arguments),this.code="ERR_JWK_INVALID";}static get code(){return "ERR_JWK_INVALID"}},oe=class extends H{constructor(){super(...arguments),this.code="ERR_JWKS_INVALID";}static get code(){return "ERR_JWKS_INVALID"}},z=class extends H{constructor(){super(...arguments),this.code="ERR_JWKS_NO_MATCHING_KEY",this.message="no applicable key found in the JSON Web Key Set";}static get code(){return "ERR_JWKS_NO_MATCHING_KEY"}},fe=class extends H{constructor(){super(...arguments),this.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS",this.message="multiple matching keys found in the JSON Web Key Set";}static get code(){return "ERR_JWKS_MULTIPLE_MATCHING_KEYS"}},ue=class extends H{constructor(){super(...arguments),this.code="ERR_JWKS_TIMEOUT",this.message="request timed out";}static get code(){return "ERR_JWKS_TIMEOUT"}},X=class extends H{constructor(){super(...arguments),this.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED",this.message="signature verification failed";}static get code(){return "ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}};var k=f.getRandomValues.bind(f);function Ge(e){switch(e){case"A128GCM":case"A128GCMKW":case"A192GCM":case"A192GCMKW":case"A256GCM":case"A256GCMKW":return 96;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return 128;default:throw new h(`Unsupported JWE Algorithm: ${e}`)}}var wt=e=>k(new Uint8Array(Ge(e)>>3));var ir=(e,t)=>{if(t.length<<3!==Ge(e))throw new c("Invalid Initialization Vector length")},Ie=ir;var sr=(e,t)=>{let r=e.byteLength<<3;if(r!==t)throw new c(`Invalid Content Encryption Key length. Expected ${t} bits, got ${r} bits`)},ae=sr;var cr=(e,t)=>{if(!(e instanceof Uint8Array))throw new TypeError("First argument must be a buffer");if(!(t instanceof Uint8Array))throw new TypeError("Second argument must be a buffer");if(e.length!==t.length)throw new TypeError("Input buffers must have the same length");let r=e.length,n=0,o=-1;for(;++oe.usages.includes(r))){let r="CryptoKey does not support this operation, its usages must include ";if(t.length>2){let n=t.pop();r+=`one of ${t.join(", ")}, or ${n}.`;}else t.length===2?r+=`one of ${t[0]} or ${t[1]}.`:r+=`${t[0]}.`;throw new TypeError(r)}}function St(e,t,...r){switch(t){case"HS256":case"HS384":case"HS512":{if(!L(e.algorithm,"HMAC"))throw W("HMAC");let n=parseInt(t.slice(2),10);if(Te(e.algorithm.hash)!==n)throw W(`SHA-${n}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!L(e.algorithm,"RSASSA-PKCS1-v1_5"))throw W("RSASSA-PKCS1-v1_5");let n=parseInt(t.slice(2),10);if(Te(e.algorithm.hash)!==n)throw W(`SHA-${n}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!L(e.algorithm,"RSA-PSS"))throw W("RSA-PSS");let n=parseInt(t.slice(2),10);if(Te(e.algorithm.hash)!==n)throw W(`SHA-${n}`,"algorithm.hash");break}case"EdDSA":{if(e.algorithm.name!=="Ed25519"&&e.algorithm.name!=="Ed448")throw W("Ed25519 or Ed448");break}case"ES256":case"ES384":case"ES512":{if(!L(e.algorithm,"ECDSA"))throw W("ECDSA");let n=dr(t);if(e.algorithm.namedCurve!==n)throw W(n,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}gt(e,r);}function T(e,t,...r){switch(t){case"A128GCM":case"A192GCM":case"A256GCM":{if(!L(e.algorithm,"AES-GCM"))throw W("AES-GCM");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw W(n,"algorithm.length");break}case"A128KW":case"A192KW":case"A256KW":{if(!L(e.algorithm,"AES-KW"))throw W("AES-KW");let n=parseInt(t.slice(1,4),10);if(e.algorithm.length!==n)throw W(n,"algorithm.length");break}case"ECDH":{switch(e.algorithm.name){case"ECDH":case"X25519":case"X448":break;default:throw W("ECDH, X25519, or X448")}break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":if(!L(e.algorithm,"PBKDF2"))throw W("PBKDF2");break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(!L(e.algorithm,"RSA-OAEP"))throw W("RSA-OAEP");let n=parseInt(t.slice(9),10)||1;if(Te(e.algorithm.hash)!==n)throw W(`SHA-${n}`,"algorithm.hash");break}default:throw new TypeError("CryptoKey does not support this operation")}gt(e,r);}function At(e,t,...r){if(r.length>2){let n=r.pop();e+=`one of type ${r.join(", ")}, or ${n}.`;}else r.length===2?e+=`one of type ${r[0]} or ${r[1]}.`:e+=`of type ${r[0]}.`;return t==null?e+=` Received ${t}`:typeof t=="function"&&t.name?e+=` Received function ${t.name}`:typeof t=="object"&&t!=null&&t.constructor?.name&&(e+=` Received an instance of ${t.constructor.name}`),e}var b=(e,...t)=>At("Key must be ",e,...t);function Fe(e,t,...r){return At(`Key for the ${e} algorithm must be `,t,...r)}var Ve=e=>_(e)?!0:e?.[Symbol.toStringTag]==="KeyObject",A=["CryptoKey"];async function pr(e,t,r,n,o,a){if(!(t instanceof Uint8Array))throw new TypeError(b(t,"Uint8Array"));let i=parseInt(e.slice(1,4),10),s=await f.subtle.importKey("raw",t.subarray(i>>3),"AES-CBC",!1,["decrypt"]),d=await f.subtle.importKey("raw",t.subarray(0,i>>3),{hash:`SHA-${i<<1}`,name:"HMAC"},!1,["sign"]),p=v(a,n,r,Pe(a.length<<3)),u=new Uint8Array((await f.subtle.sign("HMAC",d,p)).slice(0,i>>3)),l;try{l=Et(o,u);}catch{}if(!l)throw new M;let I;try{I=new Uint8Array(await f.subtle.decrypt({iv:n,name:"AES-CBC"},s,r));}catch{}if(!I)throw new M;return I}async function fr(e,t,r,n,o,a){let i;t instanceof Uint8Array?i=await f.subtle.importKey("raw",t,"AES-GCM",!1,["decrypt"]):(T(t,e,"decrypt"),i=t);try{return new Uint8Array(await f.subtle.decrypt({additionalData:a,iv:n,name:"AES-GCM",tagLength:128},i,v(r,o)))}catch{throw new M}}var ur=async(e,t,r,n,o,a)=>{if(!_(t)&&!(t instanceof Uint8Array))throw new TypeError(b(t,...A,"Uint8Array"));if(!n)throw new c("JWE Initialization Vector missing");if(!o)throw new c("JWE Authentication Tag missing");switch(Ie(e,n),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return t instanceof Uint8Array&&ae(t,parseInt(e.slice(-3),10)),pr(e,t,r,n,o,a);case"A128GCM":case"A192GCM":case"A256GCM":return t instanceof Uint8Array&&ae(t,parseInt(e.slice(1,4),10)),fr(e,t,r,n,o,a);default:throw new h("Unsupported JWE Content Encryption Algorithm")}},Re=ur;var hr=(...e)=>{let t=e.filter(Boolean);if(t.length===0||t.length===1)return !0;let r;for(let n of t){let o=Object.keys(n);if(!r||r.size===0){r=new Set(o);continue}for(let a of o){if(r.has(a))return !1;r.add(a);}}return !0},R=hr;function lr(e){return typeof e=="object"&&e!==null}function y(e){if(!lr(e)||Object.prototype.toString.call(e)!=="[object Object]")return !1;if(Object.getPrototypeOf(e)===null)return !0;let t=e;for(;Object.getPrototypeOf(t)!==null;)t=Object.getPrototypeOf(t);return Object.getPrototypeOf(e)===t}var mr=[{hash:"SHA-256",name:"HMAC"},!0,["sign"]],ie=mr;function bt(e,t){if(e.algorithm.length!==parseInt(t.slice(1,4),10))throw new TypeError(`Invalid key size for alg: ${t}`)}function _t(e,t,r){if(_(e))return T(e,t,r),e;if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"AES-KW",!0,[r]);throw new TypeError(b(e,...A,"Uint8Array"))}var he=async(e,t,r)=>{let n=await _t(t,e,"wrapKey");bt(n,e);let o=await f.subtle.importKey("raw",r,...ie);return new Uint8Array(await f.subtle.wrapKey("raw",o,n,"AES-KW"))},le=async(e,t,r)=>{let n=await _t(t,e,"unwrapKey");bt(n,e);let o=await f.subtle.unwrapKey("raw",r,n,"AES-KW",...ie);return new Uint8Array(await f.subtle.exportKey("raw",o))};async function Oe(e,t,r,n,o=new Uint8Array(0),a=new Uint8Array(0)){if(!_(e))throw new TypeError(b(e,...A));if(T(e,"ECDH"),!_(t))throw new TypeError(b(t,...A));T(t,"ECDH","deriveBits");let i=v(We(w.encode(r)),We(o),We(a),ve(n)),s;e.algorithm.name==="X25519"?s=256:e.algorithm.name==="X448"?s=448:s=Math.ceil(parseInt(e.algorithm.namedCurve.substr(-3),10)/8)<<3;let d=new Uint8Array(await f.subtle.deriveBits({name:e.algorithm.name,public:e},t,s));return lt(d,n,i)}async function xt(e){if(!_(e))throw new TypeError(b(e,...A));return f.subtle.generateKey(e.algorithm,!0,["deriveBits"])}function Ue(e){if(!_(e))throw new TypeError(b(e,...A));return ["P-256","P-384","P-521"].includes(e.algorithm.namedCurve)||e.algorithm.name==="X25519"||e.algorithm.name==="X448"}function ze(e){if(!(e instanceof Uint8Array)||e.length<8)throw new c("PBES2 Salt Input must be 8 or more octets")}function yr(e,t){if(e instanceof Uint8Array)return f.subtle.importKey("raw",e,"PBKDF2",!1,["deriveBits"]);if(_(e))return T(e,t,"deriveBits","deriveKey"),e;throw new TypeError(b(e,...A,"Uint8Array"))}async function Ht(e,t,r,n){ze(e);let o=ht(t,e),a=parseInt(t.slice(13,16),10),i={hash:`SHA-${t.slice(8,11)}`,iterations:r,name:"PBKDF2",salt:o},s={length:a,name:"AES-KW"},d=await yr(n,t);if(d.usages.includes("deriveBits"))return new Uint8Array(await f.subtle.deriveBits(i,d,a));if(d.usages.includes("deriveKey"))return f.subtle.deriveKey(i,d,s,!1,["wrapKey","unwrapKey"]);throw new TypeError('PBKDF2 key "usages" must include "deriveBits" or "deriveKey"')}var Ct=async(e,t,r,n=2048,o=k(new Uint8Array(16)))=>{let a=await Ht(o,e,n,t);return {encryptedKey:await he(e.slice(-6),a,r),p2c:n,p2s:E(o)}},Pt=async(e,t,r,n,o)=>{let a=await Ht(o,e,n,t);return le(e.slice(-6),a,r)};function se(e){switch(e){case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":return "RSA-OAEP";default:throw new h(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}var Y=(e,t)=>{if(e.startsWith("RS")||e.startsWith("PS")){let{modulusLength:r}=t.algorithm;if(typeof r!="number"||r<2048)throw new TypeError(`${e} requires key modulusLength to be 2048 bits or larger`)}};var vt=async(e,t,r)=>{if(!_(t))throw new TypeError(b(t,...A));if(T(t,e,"encrypt","wrapKey"),Y(e,t),t.usages.includes("encrypt"))return new Uint8Array(await f.subtle.encrypt(se(e),t,r));if(t.usages.includes("wrapKey")){let n=await f.subtle.importKey("raw",r,...ie);return new Uint8Array(await f.subtle.wrapKey("raw",n,t,se(e)))}throw new TypeError('RSA-OAEP key "usages" must include "encrypt" or "wrapKey" for this operation')},Wt=async(e,t,r)=>{if(!_(t))throw new TypeError(b(t,...A));if(T(t,e,"decrypt","unwrapKey"),Y(e,t),t.usages.includes("decrypt"))return new Uint8Array(await f.subtle.decrypt(se(e),t,r));if(t.usages.includes("unwrapKey")){let n=await f.subtle.unwrapKey("raw",r,t,se(e),...ie);return new Uint8Array(await f.subtle.exportKey("raw",n))}throw new TypeError('RSA-OAEP key "usages" must include "decrypt" or "unwrapKey" for this operation')};function wr(e){let t,r;switch(e.kty){case"RSA":{switch(e.alg){case"PS256":case"PS384":case"PS512":t={name:"RSA-PSS",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":t={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":t={name:"RSA-OAEP",hash:`SHA-${parseInt(e.alg.slice(-3),10)||1}`},r=e.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new h('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"EC":{switch(e.alg){case"ES256":t={name:"ECDSA",namedCurve:"P-256"},r=e.d?["sign"]:["verify"];break;case"ES384":t={name:"ECDSA",namedCurve:"P-384"},r=e.d?["sign"]:["verify"];break;case"ES512":t={name:"ECDSA",namedCurve:"P-521"},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:"ECDH",namedCurve:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new h('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}case"OKP":{switch(e.alg){case"EdDSA":t={name:e.crv},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new h('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break}default:throw new h('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return {algorithm:t,keyUsages:r}}var Er=async e=>{if(!e.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');let{algorithm:t,keyUsages:r}=wr(e),n=[t,e.ext??!1,e.key_ops??r],o={...e};return delete o.alg,delete o.use,f.subtle.importKey("jwk",o,...n)},De=Er;var Jt=e=>S(e),Xe,Ye,It=e=>e?.[Symbol.toStringTag]==="KeyObject",Tt=async(e,t,r,n)=>{let o=e.get(t);if(o?.[n])return o[n];let a=await De({...r,alg:n});return o?o[n]=a:e.set(t,{[n]:a}),a},gr=(e,t)=>{if(It(e)){let r=e.export({format:"jwk"});return delete r.d,delete r.dp,delete r.dq,delete r.p,delete r.q,delete r.qi,r.k?Jt(r.k):(Ye||(Ye=new WeakMap),Tt(Ye,e,r,t))}return e},Sr=(e,t)=>{if(It(e)){let r=e.export({format:"jwk"});return r.k?Jt(r.k):(Xe||(Xe=new WeakMap),Tt(Xe,e,r,t))}return e},q={normalizePublicKey:gr,normalizePrivateKey:Sr};function me(e){switch(e){case"A128GCM":return 128;case"A192GCM":return 192;case"A256GCM":case"A128CBC-HS256":return 256;case"A192CBC-HS384":return 384;case"A256CBC-HS512":return 512;default:throw new h(`Unsupported JWE Algorithm: ${e}`)}}var O=e=>k(new Uint8Array(me(e)>>3));var qe=(e,t)=>{let r=(e.match(/.{1,64}/g)||[]).join(` +`);return `-----BEGIN ${t}----- +${r} +-----END ${t}-----`};var Ut=async(e,t,r)=>{if(!_(r))throw new TypeError(b(r,...A));if(!r.extractable)throw new TypeError("CryptoKey is not extractable");if(r.type!==e)throw new TypeError(`key is not a ${e} key`);return qe(Je(new Uint8Array(await f.subtle.exportKey(t,r))),`${e.toUpperCase()} KEY`)},Dt=e=>Ut("public","spki",e),Mt=e=>Ut("private","pkcs8",e),$=(e,t,r=0)=>{r===0&&(t.unshift(t.length),t.unshift(6));let n=e.indexOf(t[0],r);if(n===-1)return !1;let o=e.subarray(n,n+t.length);return o.length!==t.length?!1:o.every((a,i)=>a===t[i])||$(e,t,n+1)},Rt=e=>{switch(!0){case $(e,[42,134,72,206,61,3,1,7]):return "P-256";case $(e,[43,129,4,0,34]):return "P-384";case $(e,[43,129,4,0,35]):return "P-521";case $(e,[43,101,110]):return "X25519";case $(e,[43,101,111]):return "X448";case $(e,[43,101,112]):return "Ed25519";case $(e,[43,101,113]):return "Ed448";default:throw new h("Invalid or unsupported EC Key Curve or OKP Key Sub Type")}},Nt=async(e,t,r,n,o)=>{let a,i,s=new Uint8Array(atob(r.replace(e,"")).split("").map(p=>p.charCodeAt(0))),d=t==="spki";switch(n){case"PS256":case"PS384":case"PS512":a={name:"RSA-PSS",hash:`SHA-${n.slice(-3)}`},i=d?["verify"]:["sign"];break;case"RS256":case"RS384":case"RS512":a={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${n.slice(-3)}`},i=d?["verify"]:["sign"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":a={name:"RSA-OAEP",hash:`SHA-${parseInt(n.slice(-3),10)||1}`},i=d?["encrypt","wrapKey"]:["decrypt","unwrapKey"];break;case"ES256":a={name:"ECDSA",namedCurve:"P-256"},i=d?["verify"]:["sign"];break;case"ES384":a={name:"ECDSA",namedCurve:"P-384"},i=d?["verify"]:["sign"];break;case"ES512":a={name:"ECDSA",namedCurve:"P-521"},i=d?["verify"]:["sign"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{let p=Rt(s);a=p.startsWith("P-")?{name:"ECDH",namedCurve:p}:{name:p},i=d?[]:["deriveBits"];break}case"EdDSA":a={name:Rt(s)},i=d?["verify"]:["sign"];break;default:throw new h('Invalid or unsupported "alg" (Algorithm) value')}return f.subtle.importKey(t,s,a,o?.extractable??!1,i)},kt=(e,t,r)=>Nt(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g,"pkcs8",e,t,r),Ze=(e,t,r)=>Nt(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g,"spki",e,t,r);function Ot(e){let t=[],r=0;for(;r=128;)r=r*128+e[t]-128,t++;r=r*128+e[t]-128,t++;}let n=0;if(e[t]<128)n=e[t],t++;else if(n===128){for(n=0;e[t+n]!==0||e[t+n+1]!==0;){if(n>e.byteLength)throw new TypeError("invalid indefinite form length");n++;}let a=t+n+2;return {byteLength:a,contents:e.subarray(t,t+n),raw:e.subarray(0,a)}}else {let a=e[t]&127;t++,n=0;for(let i=0;i{let n;try{n=br(e);}catch(o){throw new TypeError("Failed to parse the X.509 certificate",{cause:o})}return Ze(n,t,r)};async function _r(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PUBLIC KEY-----")!==0)throw new TypeError('"spki" must be SPKI formatted string');return Ze(e,t,r)}async function xr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN CERTIFICATE-----")!==0)throw new TypeError('"x509" must be X.509 formatted string');return $t(e,t,r)}async function Kr(e,t,r){if(typeof e!="string"||e.indexOf("-----BEGIN PRIVATE KEY-----")!==0)throw new TypeError('"pkcs8" must be PKCS#8 formatted string');return kt(e,t,r)}async function Z(e,t){if(!y(e))throw new TypeError("JWK must be an object");switch(t||(t=e.alg),e.kty){case"oct":if(typeof e.k!="string"||!e.k)throw new TypeError('missing "k" (Key Value) Parameter value');return S(e.k);case"RSA":if(e.oth!==void 0)throw new h('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');case"EC":case"OKP":return De({...e,alg:t});default:throw new h('Unsupported "kty" (Key Type) Parameter value')}}var ce=e=>e?.[Symbol.toStringTag],Hr=(e,t)=>{if(!(t instanceof Uint8Array)){if(!Ve(t))throw new TypeError(Fe(e,t,...A,"Uint8Array"));if(t.type!=="secret")throw new TypeError(`${ce(t)} instances for symmetric algorithms must be of type "secret"`)}},Cr=(e,t,r)=>{if(!Ve(t))throw new TypeError(Fe(e,t,...A));if(t.type==="secret")throw new TypeError(`${ce(t)} instances for asymmetric algorithms must not be of type "secret"`);if(r==="sign"&&t.type==="public")throw new TypeError(`${ce(t)} instances for asymmetric algorithm signing must be of type "private"`);if(r==="decrypt"&&t.type==="public")throw new TypeError(`${ce(t)} instances for asymmetric algorithm decryption must be of type "private"`);if(t.algorithm&&r==="verify"&&t.type==="private")throw new TypeError(`${ce(t)} instances for asymmetric algorithm verifying must be of type "public"`);if(t.algorithm&&r==="encrypt"&&t.type==="private")throw new TypeError(`${ce(t)} instances for asymmetric algorithm encryption must be of type "public"`)},Pr=(e,t,r)=>{e.startsWith("HS")||e==="dir"||e.startsWith("PBES2")||/^A\d{3}(?:GCM)?KW$/.test(e)?Hr(e,t):Cr(e,t,r);},B=Pr;async function vr(e,t,r,n,o){if(!(r instanceof Uint8Array))throw new TypeError(b(r,"Uint8Array"));let a=parseInt(e.slice(1,4),10),i=await f.subtle.importKey("raw",r.subarray(a>>3),"AES-CBC",!1,["encrypt"]),s=await f.subtle.importKey("raw",r.subarray(0,a>>3),{hash:`SHA-${a<<1}`,name:"HMAC"},!1,["sign"]),d=new Uint8Array(await f.subtle.encrypt({iv:n,name:"AES-CBC"},i,t)),p=v(o,n,d,Pe(o.length<<3)),u=new Uint8Array((await f.subtle.sign("HMAC",s,p)).slice(0,a>>3));return {ciphertext:d,tag:u,iv:n}}async function Wr(e,t,r,n,o){let a;r instanceof Uint8Array?a=await f.subtle.importKey("raw",r,"AES-GCM",!1,["encrypt"]):(T(r,e,"encrypt"),a=r);let i=new Uint8Array(await f.subtle.encrypt({additionalData:o,iv:n,name:"AES-GCM",tagLength:128},a,t)),s=i.slice(-16);return {ciphertext:i.slice(0,-16),tag:s,iv:n}}var Jr=async(e,t,r,n,o)=>{if(!_(r)&&!(r instanceof Uint8Array))throw new TypeError(b(r,...A,"Uint8Array"));switch(n?Ie(e,n):n=wt(e),e){case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return r instanceof Uint8Array&&ae(r,parseInt(e.slice(-3),10)),vr(e,t,r,n,o);case"A128GCM":case"A192GCM":case"A256GCM":return r instanceof Uint8Array&&ae(r,parseInt(e.slice(1,4),10)),Wr(e,t,r,n,o);default:throw new h("Unsupported JWE Content Encryption Algorithm")}},Me=Jr;async function Bt(e,t,r,n){let o=e.slice(0,7),a=await Me(o,r,t,n,new Uint8Array(0));return {encryptedKey:a.ciphertext,iv:E(a.iv),tag:E(a.tag)}}async function Gt(e,t,r,n,o){let a=e.slice(0,7);return Re(a,t,r,n,o,new Uint8Array(0))}async function Ir(e,t,r,n,o){switch(B(e,t,"decrypt"),t=await q.normalizePrivateKey?.(t,e)||t,e){case"dir":{if(r!==void 0)throw new c("Encountered unexpected JWE Encrypted Key");return t}case"ECDH-ES":if(r!==void 0)throw new c("Encountered unexpected JWE Encrypted Key");case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!y(n.epk))throw new c('JOSE Header "epk" (Ephemeral Public Key) missing or invalid');if(!Ue(t))throw new h("ECDH with the provided key is not allowed or not supported by your javascript runtime");let a=await Z(n.epk,e),i,s;if(n.apu!==void 0){if(typeof n.apu!="string")throw new c('JOSE Header "apu" (Agreement PartyUInfo) invalid');try{i=S(n.apu);}catch{throw new c("Failed to base64url decode the apu")}}if(n.apv!==void 0){if(typeof n.apv!="string")throw new c('JOSE Header "apv" (Agreement PartyVInfo) invalid');try{s=S(n.apv);}catch{throw new c("Failed to base64url decode the apv")}}let d=await Oe(a,t,e==="ECDH-ES"?n.enc:e,e==="ECDH-ES"?me(n.enc):parseInt(e.slice(-5,-2),10),i,s);if(e==="ECDH-ES")return d;if(r===void 0)throw new c("JWE Encrypted Key missing");return le(e.slice(-6),d,r)}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{if(r===void 0)throw new c("JWE Encrypted Key missing");return Wt(e,t,r)}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{if(r===void 0)throw new c("JWE Encrypted Key missing");if(typeof n.p2c!="number")throw new c('JOSE Header "p2c" (PBES2 Count) missing or invalid');let a=o?.maxPBES2Count||1e4;if(n.p2c>a)throw new c('JOSE Header "p2c" (PBES2 Count) out is of acceptable bounds');if(typeof n.p2s!="string")throw new c('JOSE Header "p2s" (PBES2 Salt) missing or invalid');let i;try{i=S(n.p2s);}catch{throw new c("Failed to base64url decode the p2s")}return Pt(e,t,r,n.p2c,i)}case"A128KW":case"A192KW":case"A256KW":{if(r===void 0)throw new c("JWE Encrypted Key missing");return le(e,t,r)}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{if(r===void 0)throw new c("JWE Encrypted Key missing");if(typeof n.iv!="string")throw new c('JOSE Header "iv" (Initialization Vector) missing or invalid');if(typeof n.tag!="string")throw new c('JOSE Header "tag" (Authentication Tag) missing or invalid');let a;try{a=S(n.iv);}catch{throw new c("Failed to base64url decode the iv")}let i;try{i=S(n.tag);}catch{throw new c("Failed to base64url decode the tag")}return Gt(e,t,r,a,i)}default:throw new h('Invalid or unsupported "alg" (JWE Algorithm) header value')}}var Ft=Ir;function Tr(e,t,r,n,o){if(o.crit!==void 0&&n?.crit===void 0)throw new e('"crit" (Critical) Header Parameter MUST be integrity protected');if(!n||n.crit===void 0)return new Set;if(!Array.isArray(n.crit)||n.crit.length===0||n.crit.some(i=>typeof i!="string"||i.length===0))throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let a;r!==void 0?a=new Map([...Object.entries(r),...t.entries()]):a=t;for(let i of n.crit){if(!a.has(i))throw new h(`Extension Header Parameter "${i}" is not recognized`);if(o[i]===void 0)throw new e(`Extension Header Parameter "${i}" is missing`);if(a.get(i)&&n[i]===void 0)throw new e(`Extension Header Parameter "${i}" MUST be integrity protected`)}return new Set(n.crit)}var U=Tr;var Rr=(e,t)=>{if(t!==void 0&&(!Array.isArray(t)||t.some(r=>typeof r!="string")))throw new TypeError(`"${e}" option must be an array of strings`);if(t)return new Set(t)},ye=Rr;async function we(e,t,r){if(!y(e))throw new c("Flattened JWE must be an object");if(e.protected===void 0&&e.header===void 0&&e.unprotected===void 0)throw new c("JOSE Header missing");if(e.iv!==void 0&&typeof e.iv!="string")throw new c("JWE Initialization Vector incorrect type");if(typeof e.ciphertext!="string")throw new c("JWE Ciphertext missing or incorrect type");if(e.tag!==void 0&&typeof e.tag!="string")throw new c("JWE Authentication Tag incorrect type");if(e.protected!==void 0&&typeof e.protected!="string")throw new c("JWE Protected Header incorrect type");if(e.encrypted_key!==void 0&&typeof e.encrypted_key!="string")throw new c("JWE Encrypted Key incorrect type");if(e.aad!==void 0&&typeof e.aad!="string")throw new c("JWE AAD incorrect type");if(e.header!==void 0&&!y(e.header))throw new c("JWE Shared Unprotected Header incorrect type");if(e.unprotected!==void 0&&!y(e.unprotected))throw new c("JWE Per-Recipient Unprotected Header incorrect type");let n;if(e.protected)try{let re=S(e.protected);n=JSON.parse(x.decode(re));}catch{throw new c("JWE Protected Header is invalid")}if(!R(n,e.header,e.unprotected))throw new c("JWE Protected, JWE Unprotected Header, and JWE Per-Recipient Unprotected Header Parameter names must be disjoint");let o={...n,...e.header,...e.unprotected};if(U(c,new Map,r?.crit,n,o),o.zip!==void 0)throw new h('JWE "zip" (Compression Algorithm) Header Parameter is not supported.');let{alg:a,enc:i}=o;if(typeof a!="string"||!a)throw new c("missing JWE Algorithm (alg) in JWE Header");if(typeof i!="string"||!i)throw new c("missing JWE Encryption Algorithm (enc) in JWE Header");let s=r&&ye("keyManagementAlgorithms",r.keyManagementAlgorithms),d=r&&ye("contentEncryptionAlgorithms",r.contentEncryptionAlgorithms);if(s&&!s.has(a)||!s&&a.startsWith("PBES2"))throw new N('"alg" (Algorithm) Header Parameter value not allowed');if(d&&!d.has(i))throw new N('"enc" (Encryption Algorithm) Header Parameter value not allowed');let p;if(e.encrypted_key!==void 0)try{p=S(e.encrypted_key);}catch{throw new c("Failed to base64url decode the encrypted_key")}let u=!1;typeof t=="function"&&(t=await t(n,e),u=!0);let l;try{l=await Ft(a,t,p,o,r);}catch(re){if(re instanceof TypeError||re instanceof c||re instanceof h)throw re;l=O(i);}let I,J;if(e.iv!==void 0)try{I=S(e.iv);}catch{throw new c("Failed to base64url decode the iv")}if(e.tag!==void 0)try{J=S(e.tag);}catch{throw new c("Failed to base64url decode the tag")}let g=w.encode(e.protected??""),P;e.aad!==void 0?P=v(g,w.encode("."),w.encode(e.aad)):P=g;let Ke;try{Ke=S(e.ciphertext);}catch{throw new c("Failed to base64url decode the ciphertext")}let te={plaintext:await Re(i,l,Ke,I,J,P)};if(e.protected!==void 0&&(te.protectedHeader=n),e.aad!==void 0)try{te.additionalAuthenticatedData=S(e.aad);}catch{throw new c("Failed to base64url decode the aad")}return e.unprotected!==void 0&&(te.sharedUnprotectedHeader=e.unprotected),e.header!==void 0&&(te.unprotectedHeader=e.header),u?{...te,key:t}:te}async function Qe(e,t,r){if(e instanceof Uint8Array&&(e=x.decode(e)),typeof e!="string")throw new c("Compact JWE must be a string or Uint8Array");let{0:n,1:o,2:a,3:i,4:s,length:d}=e.split(".");if(d!==5)throw new c("Invalid Compact JWE");let p=await we({ciphertext:i,iv:a||void 0,protected:n,tag:s||void 0,encrypted_key:o||void 0},t,r),u={plaintext:p.plaintext,protectedHeader:p.protectedHeader};return typeof t=="function"?{...u,key:p.key}:u}async function Or(e,t,r){if(!y(e))throw new c("General JWE must be an object");if(!Array.isArray(e.recipients)||!e.recipients.every(y))throw new c("JWE Recipients missing or incorrect type");if(!e.recipients.length)throw new c("JWE Recipients has no members");for(let n of e.recipients)try{return await we({aad:e.aad,ciphertext:e.ciphertext,encrypted_key:n.encrypted_key,header:n.header,iv:e.iv,protected:e.protected,tag:e.tag,unprotected:e.unprotected},t,r)}catch{}throw new M}var Ne=Symbol();var Ur=async e=>{if(e instanceof Uint8Array)return {kty:"oct",k:E(e)};if(!_(e))throw new TypeError(b(e,...A,"Uint8Array"));if(!e.extractable)throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");let{ext:t,key_ops:r,alg:n,use:o,...a}=await f.subtle.exportKey("jwk",e);return a},Vt=Ur;async function Dr(e){return Dt(e)}async function Mr(e){return Mt(e)}async function je(e){return Vt(e)}async function Nr(e,t,r,n,o={}){let a,i,s;switch(B(e,r,"encrypt"),r=await q.normalizePublicKey?.(r,e)||r,e){case"dir":{s=r;break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{if(!Ue(r))throw new h("ECDH with the provided key is not allowed or not supported by your javascript runtime");let{apu:d,apv:p}=o,{epk:u}=o;u||(u=(await xt(r)).privateKey);let{x:l,y:I,crv:J,kty:g}=await je(u),P=await Oe(r,u,e==="ECDH-ES"?t:e,e==="ECDH-ES"?me(t):parseInt(e.slice(-5,-2),10),d,p);if(i={epk:{x:l,crv:J,kty:g}},g==="EC"&&(i.epk.y=I),d&&(i.apu=E(d)),p&&(i.apv=E(p)),e==="ECDH-ES"){s=P;break}s=n||O(t);let Ke=e.slice(-6);a=await he(Ke,P,s);break}case"RSA1_5":case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":{s=n||O(t),a=await vt(e,r,s);break}case"PBES2-HS256+A128KW":case"PBES2-HS384+A192KW":case"PBES2-HS512+A256KW":{s=n||O(t);let{p2c:d,p2s:p}=o;({encryptedKey:a,...i}=await Ct(e,r,s,d,p));break}case"A128KW":case"A192KW":case"A256KW":{s=n||O(t),a=await he(e,r,s);break}case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":{s=n||O(t);let{iv:d}=o;({encryptedKey:a,...i}=await Bt(e,r,s,d));break}default:throw new h('Invalid or unsupported "alg" (JWE Algorithm) header value')}return {cek:s,encryptedKey:a,parameters:i}}var ke=Nr;var G=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("plaintext must be an instance of Uint8Array");this._plaintext=t;}setKeyManagementParameters(t){if(this._keyManagementParameters)throw new TypeError("setKeyManagementParameters can only be called once");return this._keyManagementParameters=t,this}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._sharedUnprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._sharedUnprotectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}setContentEncryptionKey(t){if(this._cek)throw new TypeError("setContentEncryptionKey can only be called once");return this._cek=t,this}setInitializationVector(t){if(this._iv)throw new TypeError("setInitializationVector can only be called once");return this._iv=t,this}async encrypt(t,r){if(!this._protectedHeader&&!this._unprotectedHeader&&!this._sharedUnprotectedHeader)throw new c("either setProtectedHeader, setUnprotectedHeader, or sharedUnprotectedHeader must be called before #encrypt()");if(!R(this._protectedHeader,this._unprotectedHeader,this._sharedUnprotectedHeader))throw new c("JWE Protected, JWE Shared Unprotected and JWE Per-Recipient Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader,...this._sharedUnprotectedHeader};if(U(c,new Map,r?.crit,this._protectedHeader,n),n.zip!==void 0)throw new h('JWE "zip" (Compression Algorithm) Header Parameter is not supported.');let{alg:o,enc:a}=n;if(typeof o!="string"||!o)throw new c('JWE "alg" (Algorithm) Header Parameter missing or invalid');if(typeof a!="string"||!a)throw new c('JWE "enc" (Encryption Algorithm) Header Parameter missing or invalid');let i;if(this._cek&&(o==="dir"||o==="ECDH-ES"))throw new TypeError(`setContentEncryptionKey cannot be called with JWE "alg" (Algorithm) Header ${o}`);let s;{let P;(({cek:s,encryptedKey:i,parameters:P}=await ke(o,a,t,this._cek,this._keyManagementParameters))),P&&(r&&Ne in r?this._unprotectedHeader?this._unprotectedHeader={...this._unprotectedHeader,...P}:this.setUnprotectedHeader(P):this._protectedHeader?this._protectedHeader={...this._protectedHeader,...P}:this.setProtectedHeader(P));}let d,p,u;this._protectedHeader?p=w.encode(E(JSON.stringify(this._protectedHeader))):p=w.encode(""),this._aad?(u=E(this._aad),d=v(p,w.encode("."),w.encode(u))):d=p;let{ciphertext:l,tag:I,iv:J}=await Me(a,this._plaintext,s,this._iv,d),g={ciphertext:E(l)};return J&&(g.iv=E(J)),I&&(g.tag=E(I)),i&&(g.encrypted_key=E(i)),u&&(g.aad=u),this._protectedHeader&&(g.protected=x.decode(p)),this._sharedUnprotectedHeader&&(g.unprotected=this._sharedUnprotectedHeader),this._unprotectedHeader&&(g.header=this._unprotectedHeader),g}};var et=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n;}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addRecipient(...t){return this.parent.addRecipient(...t)}encrypt(...t){return this.parent.encrypt(...t)}done(){return this.parent}},tt=class{constructor(t){this._recipients=[],this._plaintext=t;}addRecipient(t,r){let n=new et(this,t,{crit:r?.crit});return this._recipients.push(n),n}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setSharedUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setSharedUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}setAdditionalAuthenticatedData(t){return this._aad=t,this}async encrypt(){if(!this._recipients.length)throw new c("at least one recipient must be added");if(this._recipients.length===1){let[o]=this._recipients,a=await new G(this._plaintext).setAdditionalAuthenticatedData(this._aad).setProtectedHeader(this._protectedHeader).setSharedUnprotectedHeader(this._unprotectedHeader).setUnprotectedHeader(o.unprotectedHeader).encrypt(o.key,{...o.options}),i={ciphertext:a.ciphertext,iv:a.iv,recipients:[{}],tag:a.tag};return a.aad&&(i.aad=a.aad),a.protected&&(i.protected=a.protected),a.unprotected&&(i.unprotected=a.unprotected),a.encrypted_key&&(i.recipients[0].encrypted_key=a.encrypted_key),a.header&&(i.recipients[0].header=a.header),i}let t;for(let o=0;o>3};case"RS256":case"RS384":case"RS512":return {hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return {hash:r,name:"ECDSA",namedCurve:t.namedCurve};case"EdDSA":return {name:t.name};default:throw new h(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}async function ge(e,t,r){if(r==="sign"&&(t=await q.normalizePrivateKey(t,e)),r==="verify"&&(t=await q.normalizePublicKey(t,e)),_(t))return St(t,e,r),t;if(t instanceof Uint8Array){if(!e.startsWith("HS"))throw new TypeError(b(t,...A));return f.subtle.importKey("raw",t,{hash:`SHA-${e.slice(-3)}`,name:"HMAC"},!1,[r])}throw new TypeError(b(t,...A,"Uint8Array"))}var kr=async(e,t,r,n)=>{let o=await ge(e,t,"verify");Y(e,o);let a=Ee(e,o.algorithm);try{return await f.subtle.verify(a,o,r,n)}catch{return !1}},zt=kr;async function Se(e,t,r){if(!y(e))throw new m("Flattened JWS must be an object");if(e.protected===void 0&&e.header===void 0)throw new m('Flattened JWS must have either of the "protected" or "header" members');if(e.protected!==void 0&&typeof e.protected!="string")throw new m("JWS Protected Header incorrect type");if(e.payload===void 0)throw new m("JWS Payload missing");if(typeof e.signature!="string")throw new m("JWS Signature missing or incorrect type");if(e.header!==void 0&&!y(e.header))throw new m("JWS Unprotected Header incorrect type");let n={};if(e.protected)try{let P=S(e.protected);n=JSON.parse(x.decode(P));}catch{throw new m("JWS Protected Header is invalid")}if(!R(n,e.header))throw new m("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let o={...n,...e.header},a=U(m,new Map([["b64",!0]]),r?.crit,n,o),i=!0;if(a.has("b64")&&(i=n.b64,typeof i!="boolean"))throw new m('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:s}=o;if(typeof s!="string"||!s)throw new m('JWS "alg" (Algorithm) Header Parameter missing or invalid');let d=r&&ye("algorithms",r.algorithms);if(d&&!d.has(s))throw new N('"alg" (Algorithm) Header Parameter value not allowed');if(i){if(typeof e.payload!="string")throw new m("JWS Payload must be a string")}else if(typeof e.payload!="string"&&!(e.payload instanceof Uint8Array))throw new m("JWS Payload must be a string or an Uint8Array instance");let p=!1;typeof t=="function"&&(t=await t(n,e),p=!0),B(s,t,"verify");let u=v(w.encode(e.protected??""),w.encode("."),typeof e.payload=="string"?w.encode(e.payload):e.payload),l;try{l=S(e.signature);}catch{throw new m("Failed to base64url decode the signature")}if(!await zt(s,t,l,u))throw new X;let J;if(i)try{J=S(e.payload);}catch{throw new m("Failed to base64url decode the payload")}else typeof e.payload=="string"?J=w.encode(e.payload):J=e.payload;let g={payload:J};return e.protected!==void 0&&(g.protectedHeader=n),e.header!==void 0&&(g.unprotectedHeader=e.header),p?{...g,key:t}:g}async function rt(e,t,r){if(e instanceof Uint8Array&&(e=x.decode(e)),typeof e!="string")throw new m("Compact JWS must be a string or Uint8Array");let{0:n,1:o,2:a,length:i}=e.split(".");if(i!==3)throw new m("Invalid Compact JWS");let s=await Se({payload:o,protected:n,signature:a},t,r),d={payload:s.payload,protectedHeader:s.protectedHeader};return typeof t=="function"?{...d,key:s.key}:d}async function Lr(e,t,r){if(!y(e))throw new m("General JWS must be an object");if(!Array.isArray(e.signatures)||!e.signatures.every(y))throw new m("JWS Signatures missing or incorrect type");for(let n of e.signatures)try{return await Se({header:n.header,payload:e.payload,protected:n.protected,signature:n.signature},t,r)}catch{}throw new X}var D=e=>Math.floor(e.getTime()/1e3);var $r=/^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i,Q=e=>{let t=$r.exec(e);if(!t||t[4]&&t[1])throw new TypeError("Invalid time period format");let r=parseFloat(t[2]),n=t[3].toLowerCase(),o;switch(n){case"sec":case"secs":case"second":case"seconds":case"s":o=Math.round(r);break;case"minute":case"minutes":case"min":case"mins":case"m":o=Math.round(r*60);break;case"hour":case"hours":case"hr":case"hrs":case"h":o=Math.round(r*3600);break;case"day":case"days":case"d":o=Math.round(r*86400);break;case"week":case"weeks":case"w":o=Math.round(r*604800);break;default:o=Math.round(r*31557600);break}return t[1]==="-"||t[4]==="ago"?-o:o};var Xt=e=>e.toLowerCase().replace(/^application\//,""),Br=(e,t)=>typeof e=="string"?t.includes(e):Array.isArray(e)?t.some(Set.prototype.has.bind(new Set(e))):!1,de=(e,t,r={})=>{let n;try{n=JSON.parse(x.decode(t));}catch{}if(!y(n))throw new K("JWT Claims Set must be a top-level JSON object");let{typ:o}=r;if(o&&(typeof e.typ!="string"||Xt(e.typ)!==Xt(o)))throw new C('unexpected "typ" JWT header value',n,"typ","check_failed");let{requiredClaims:a=[],issuer:i,subject:s,audience:d,maxTokenAge:p}=r,u=[...a];p!==void 0&&u.push("iat"),d!==void 0&&u.push("aud"),s!==void 0&&u.push("sub"),i!==void 0&&u.push("iss");for(let g of new Set(u.reverse()))if(!(g in n))throw new C(`missing required "${g}" claim`,n,g,"missing");if(i&&!(Array.isArray(i)?i:[i]).includes(n.iss))throw new C('unexpected "iss" claim value',n,"iss","check_failed");if(s&&n.sub!==s)throw new C('unexpected "sub" claim value',n,"sub","check_failed");if(d&&!Br(n.aud,typeof d=="string"?[d]:d))throw new C('unexpected "aud" claim value',n,"aud","check_failed");let l;switch(typeof r.clockTolerance){case"string":l=Q(r.clockTolerance);break;case"number":l=r.clockTolerance;break;case"undefined":l=0;break;default:throw new TypeError("Invalid clockTolerance option type")}let{currentDate:I}=r,J=D(I||new Date);if((n.iat!==void 0||p)&&typeof n.iat!="number")throw new C('"iat" claim must be a number',n,"iat","invalid");if(n.nbf!==void 0){if(typeof n.nbf!="number")throw new C('"nbf" claim must be a number',n,"nbf","invalid");if(n.nbf>J+l)throw new C('"nbf" claim timestamp check failed',n,"nbf","check_failed")}if(n.exp!==void 0){if(typeof n.exp!="number")throw new C('"exp" claim must be a number',n,"exp","invalid");if(n.exp<=J-l)throw new ne('"exp" claim timestamp check failed',n,"exp","check_failed")}if(p){let g=J-n.iat,P=typeof p=="number"?p:Q(p);if(g-l>P)throw new ne('"iat" claim timestamp check failed (too far in the past)',n,"iat","check_failed");if(g<0-l)throw new C('"iat" claim timestamp check failed (it should be in the past)',n,"iat","check_failed")}return n};async function Gr(e,t,r){let n=await rt(e,t,r);if(n.protectedHeader.crit?.includes("b64")&&n.protectedHeader.b64===!1)throw new K("JWTs MUST NOT use unencoded payload");let a={payload:de(n.protectedHeader,n.payload,r),protectedHeader:n.protectedHeader};return typeof t=="function"?{...a,key:n.key}:a}async function Fr(e,t,r){let n=await Qe(e,t,r),o=de(n.protectedHeader,n.plaintext,r),{protectedHeader:a}=n;if(a.iss!==void 0&&a.iss!==o.iss)throw new C('replicated "iss" claim header parameter mismatch',o,"iss","mismatch");if(a.sub!==void 0&&a.sub!==o.sub)throw new C('replicated "sub" claim header parameter mismatch',o,"sub","mismatch");if(a.aud!==void 0&&JSON.stringify(a.aud)!==JSON.stringify(o.aud))throw new C('replicated "aud" claim header parameter mismatch',o,"aud","mismatch");let i={payload:o,protectedHeader:a};return typeof t=="function"?{...i,key:n.key}:i}var Ae=class{constructor(t){this._flattened=new G(t);}setContentEncryptionKey(t){return this._flattened.setContentEncryptionKey(t),this}setInitializationVector(t){return this._flattened.setInitializationVector(t),this}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}setKeyManagementParameters(t){return this._flattened.setKeyManagementParameters(t),this}async encrypt(t,r){let n=await this._flattened.encrypt(t,r);return [n.protected,n.encrypted_key,n.iv,n.ciphertext,n.tag].join(".")}};var Vr=async(e,t,r)=>{let n=await ge(e,t,"sign");Y(e,n);let o=await f.subtle.sign(Ee(e,n.algorithm),n,r);return new Uint8Array(o)},Yt=Vr;var j=class{constructor(t){if(!(t instanceof Uint8Array))throw new TypeError("payload must be an instance of Uint8Array");this._payload=t;}setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setUnprotectedHeader(t){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=t,this}async sign(t,r){if(!this._protectedHeader&&!this._unprotectedHeader)throw new m("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");if(!R(this._protectedHeader,this._unprotectedHeader))throw new m("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");let n={...this._protectedHeader,...this._unprotectedHeader},o=U(m,new Map([["b64",!0]]),r?.crit,this._protectedHeader,n),a=!0;if(o.has("b64")&&(a=this._protectedHeader.b64,typeof a!="boolean"))throw new m('The "b64" (base64url-encode payload) Header Parameter must be a boolean');let{alg:i}=n;if(typeof i!="string"||!i)throw new m('JWS "alg" (Algorithm) Header Parameter missing or invalid');B(i,t,"sign");let s=this._payload;a&&(s=w.encode(E(s)));let d;this._protectedHeader?d=w.encode(E(JSON.stringify(this._protectedHeader))):d=w.encode("");let p=v(d,w.encode("."),s),u=await Yt(i,t,p),l={signature:E(u),payload:""};return a&&(l.payload=x.decode(s)),this._unprotectedHeader&&(l.header=this._unprotectedHeader),this._protectedHeader&&(l.protected=x.decode(d)),l}};var be=class{constructor(t){this._flattened=new j(t);}setProtectedHeader(t){return this._flattened.setProtectedHeader(t),this}async sign(t,r){let n=await this._flattened.sign(t,r);if(n.payload===void 0)throw new TypeError("use the flattened module for creating JWS with b64: false");return `${n.protected}.${n.payload}.${n.signature}`}};var nt=class{constructor(t,r,n){this.parent=t,this.key=r,this.options=n;}setProtectedHeader(t){if(this.protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this.protectedHeader=t,this}setUnprotectedHeader(t){if(this.unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this.unprotectedHeader=t,this}addSignature(...t){return this.parent.addSignature(...t)}sign(...t){return this.parent.sign(...t)}done(){return this.parent}},ot=class{constructor(t){this._signatures=[],this._payload=t;}addSignature(t,r){let n=new nt(this,t,r);return this._signatures.push(n),n}async sign(){if(!this._signatures.length)throw new m("at least one signature must be added");let t={signatures:[],payload:""};for(let r=0;r"u"?this._payload={...this._payload,iat:D(new Date)}:t instanceof Date?this._payload={...this._payload,iat:ee("setIssuedAt",D(t))}:typeof t=="string"?this._payload={...this._payload,iat:ee("setIssuedAt",D(new Date)+Q(t))}:this._payload={...this._payload,iat:ee("setIssuedAt",t)},this}};var at=class extends F{setProtectedHeader(t){return this._protectedHeader=t,this}async sign(t,r){let n=new be(w.encode(JSON.stringify(this._payload)));if(n.setProtectedHeader(this._protectedHeader),Array.isArray(this._protectedHeader?.crit)&&this._protectedHeader.crit.includes("b64")&&this._protectedHeader.b64===!1)throw new K("JWTs MUST NOT use unencoded payload");return n.sign(t,r)}};var it=class extends F{setProtectedHeader(t){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=t,this}setKeyManagementParameters(t){if(this._keyManagementParameters)throw new TypeError("setKeyManagementParameters can only be called once");return this._keyManagementParameters=t,this}setContentEncryptionKey(t){if(this._cek)throw new TypeError("setContentEncryptionKey can only be called once");return this._cek=t,this}setInitializationVector(t){if(this._iv)throw new TypeError("setInitializationVector can only be called once");return this._iv=t,this}replicateIssuerAsHeader(){return this._replicateIssuerAsHeader=!0,this}replicateSubjectAsHeader(){return this._replicateSubjectAsHeader=!0,this}replicateAudienceAsHeader(){return this._replicateAudienceAsHeader=!0,this}async encrypt(t,r){let n=new Ae(w.encode(JSON.stringify(this._payload)));return this._replicateIssuerAsHeader&&(this._protectedHeader={...this._protectedHeader,iss:this._payload.iss}),this._replicateSubjectAsHeader&&(this._protectedHeader={...this._protectedHeader,sub:this._payload.sub}),this._replicateAudienceAsHeader&&(this._protectedHeader={...this._protectedHeader,aud:this._payload.aud}),n.setProtectedHeader(this._protectedHeader),this._iv&&n.setInitializationVector(this._iv),this._cek&&n.setContentEncryptionKey(this._cek),this._keyManagementParameters&&n.setKeyManagementParameters(this._keyManagementParameters),n.encrypt(t,r)}};var V=(e,t)=>{if(typeof e!="string"||!e)throw new pe(`${t} missing or invalid`)};async function qt(e,t){if(!y(e))throw new TypeError("JWK must be an object");if(t??(t="sha256"),t!=="sha256"&&t!=="sha384"&&t!=="sha512")throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');let r;switch(e.kty){case"EC":V(e.crv,'"crv" (Curve) Parameter'),V(e.x,'"x" (X Coordinate) Parameter'),V(e.y,'"y" (Y Coordinate) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x,y:e.y};break;case"OKP":V(e.crv,'"crv" (Subtype of Key Pair) Parameter'),V(e.x,'"x" (Public Key) Parameter'),r={crv:e.crv,kty:e.kty,x:e.x};break;case"RSA":V(e.e,'"e" (Exponent) Parameter'),V(e.n,'"n" (Modulus) Parameter'),r={e:e.e,kty:e.kty,n:e.n};break;case"oct":V(e.k,'"k" (Key Value) Parameter'),r={k:e.k,kty:e.kty};break;default:throw new h('"kty" (Key Type) Parameter missing or unsupported')}let n=w.encode(JSON.stringify(r));return E(await He(t,n))}async function zr(e,t){t??(t="sha256");let r=await qt(e,t);return `urn:ietf:params:oauth:jwk-thumbprint:sha-${t.slice(-3)}:${r}`}async function Xr(e,t){let r={...e,...t?.header};if(!y(r.jwk))throw new m('"jwk" (JSON Web Key) Header Parameter must be a JSON object');let n=await Z({...r.jwk,ext:!0},r.alg);if(n instanceof Uint8Array||n.type!=="public")throw new m('"jwk" (JSON Web Key) Header Parameter must be a public key');return n}function Yr(e){switch(typeof e=="string"&&e.slice(0,2)){case"RS":case"PS":return "RSA";case"ES":return "EC";case"Ed":return "OKP";default:throw new h('Unsupported "alg" value for a JSON Web Key Set')}}function qr(e){return e&&typeof e=="object"&&Array.isArray(e.keys)&&e.keys.every(Zr)}function Zr(e){return y(e)}function Qt(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}var st=class{constructor(t){if(this._cached=new WeakMap,!qr(t))throw new oe("JSON Web Key Set malformed");this._jwks=Qt(t);}async getKey(t,r){let{alg:n,kid:o}={...t,...r?.header},a=Yr(n),i=this._jwks.keys.filter(p=>{let u=a===p.kty;if(u&&typeof o=="string"&&(u=o===p.kid),u&&typeof p.alg=="string"&&(u=n===p.alg),u&&typeof p.use=="string"&&(u=p.use==="sig"),u&&Array.isArray(p.key_ops)&&(u=p.key_ops.includes("verify")),u&&n==="EdDSA"&&(u=p.crv==="Ed25519"||p.crv==="Ed448"),u)switch(n){case"ES256":u=p.crv==="P-256";break;case"ES256K":u=p.crv==="secp256k1";break;case"ES384":u=p.crv==="P-384";break;case"ES512":u=p.crv==="P-521";break}return u}),{0:s,length:d}=i;if(d===0)throw new z;if(d!==1){let p=new fe,{_cached:u}=this;throw p[Symbol.asyncIterator]=async function*(){for(let l of i)try{yield await Zt(u,l,n);}catch{}},p}return Zt(this._cached,s,n)}};async function Zt(e,t,r){let n=e.get(t)||e.set(t,{}).get(t);if(n[r]===void 0){let o=await Z({...t,ext:!0},r);if(o instanceof Uint8Array||o.type!=="public")throw new oe("JSON Web Key Set members must be public keys");n[r]=o;}return n[r]}function Le(e){let t=new st(e),r=async(n,o)=>t.getKey(n,o);return Object.defineProperties(r,{jwks:{value:()=>Qt(t._jwks),enumerable:!0,configurable:!1,writable:!1}}),r}var Qr=async(e,t,r)=>{let n,o,a=!1;typeof AbortController=="function"&&(n=new AbortController,o=setTimeout(()=>{a=!0,n.abort();},t));let i=await fetch(e.href,{signal:n?n.signal:void 0,redirect:"manual",headers:r.headers}).catch(s=>{throw a?new ue:s});if(o!==void 0&&clearTimeout(o),i.status!==200)throw new H("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await i.json()}catch{throw new H("Failed to parse the JSON Web Key Set HTTP response as JSON")}},jt=Qr;function jr(){return typeof WebSocketPair<"u"||typeof navigator<"u"&&navigator.userAgent==="Cloudflare-Workers"||typeof EdgeRuntime<"u"&&EdgeRuntime==="vercel"}var ct;(typeof navigator>"u"||!navigator.userAgent?.startsWith?.("Mozilla/5.0 "))&&(ct="jose/v5.8.0");var _e=Symbol();function en(e,t){return !(typeof e!="object"||e===null||!("uat"in e)||typeof e.uat!="number"||Date.now()-e.uat>=t||!("jwks"in e)||!y(e.jwks)||!Array.isArray(e.jwks.keys)||!Array.prototype.every.call(e.jwks.keys,y))}var dt=class{constructor(t,r){if(!(t instanceof URL))throw new TypeError("url must be an instance of URL");this._url=new URL(t.href),this._options={agent:r?.agent,headers:r?.headers},this._timeoutDuration=typeof r?.timeoutDuration=="number"?r?.timeoutDuration:5e3,this._cooldownDuration=typeof r?.cooldownDuration=="number"?r?.cooldownDuration:3e4,this._cacheMaxAge=typeof r?.cacheMaxAge=="number"?r?.cacheMaxAge:6e5,r?.[_e]!==void 0&&(this._cache=r?.[_e],en(r?.[_e],this._cacheMaxAge)&&(this._jwksTimestamp=this._cache.uat,this._local=Le(this._cache.jwks)));}coolingDown(){return typeof this._jwksTimestamp=="number"?Date.now(){this._local=Le(r),this._cache&&(this._cache.uat=Date.now(),this._cache.jwks=r),this._jwksTimestamp=Date.now(),this._pendingFetch=void 0;}).catch(r=>{throw this._pendingFetch=void 0,r})),await this._pendingFetch;}};function tn(e,t){let r=new dt(e,t),n=async(o,a)=>r.getKey(o,a);return Object.defineProperties(n,{coolingDown:{get:()=>r.coolingDown(),enumerable:!0,configurable:!1},fresh:{get:()=>r.fresh(),enumerable:!0,configurable:!1},reload:{value:()=>r.reload(),enumerable:!0,configurable:!1,writable:!1},reloading:{get:()=>!!r._pendingFetch,enumerable:!0,configurable:!1},jwks:{value:()=>r._local?.jwks(),enumerable:!0,configurable:!1,writable:!1}}),n}var rn=_e;var pt=class extends F{encode(){let t=E(JSON.stringify({alg:"none"})),r=E(JSON.stringify(this._payload));return `${t}.${r}.`}static decode(t,r){if(typeof t!="string")throw new K("Unsecured JWT must be a string");let{0:n,1:o,2:a,length:i}=t.split(".");if(i!==3||a!=="")throw new K("Invalid Unsecured JWT");let s;try{if(s=JSON.parse(x.decode(S(n))),s.alg!=="none")throw new Error}catch{throw new K("Invalid Unsecured JWT")}return {payload:de(s,S(o),r),header:s}}};var er={};ut(er,{decode:()=>xe,encode:()=>nn});var nn=E,xe=S;function on(e){let t;if(typeof e=="string"){let r=e.split(".");(r.length===3||r.length===5)&&([t]=r);}else if(typeof e=="object"&&e)if("protected"in e)t=e.protected;else throw new TypeError("Token does not contain a Protected Header");try{if(typeof t!="string"||!t)throw new Error;let r=JSON.parse(x.decode(xe(t)));if(!y(r))throw new Error;return r}catch{throw new TypeError("Invalid Token or Protected Header formatting")}}function an(e){if(typeof e!="string")throw new K("JWTs must use Compact JWS serialization, JWT must be a string");let{1:t,length:r}=e.split(".");if(r===5)throw new K("Only JWTs using Compact JWS serialization can be decoded");if(r!==3)throw new K("Invalid JWT");if(!t)throw new K("JWTs must contain a payload");let n;try{n=xe(t);}catch{throw new K("Failed to base64url decode the payload")}let o;try{o=JSON.parse(x.decode(n));}catch{throw new K("Failed to parse the decoded payload as JSON")}if(!y(o))throw new K("Invalid JWT Claims Set");return o}async function tr(e,t){let r,n,o;switch(e){case"HS256":case"HS384":case"HS512":r=parseInt(e.slice(-3),10),n={name:"HMAC",hash:`SHA-${r}`,length:r},o=["sign","verify"];break;case"A128CBC-HS256":case"A192CBC-HS384":case"A256CBC-HS512":return r=parseInt(e.slice(-3),10),k(new Uint8Array(r>>3));case"A128KW":case"A192KW":case"A256KW":r=parseInt(e.slice(1,4),10),n={name:"AES-KW",length:r},o=["wrapKey","unwrapKey"];break;case"A128GCMKW":case"A192GCMKW":case"A256GCMKW":case"A128GCM":case"A192GCM":case"A256GCM":r=parseInt(e.slice(1,4),10),n={name:"AES-GCM",length:r},o=["encrypt","decrypt"];break;default:throw new h('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return f.subtle.generateKey(n,t?.extractable??!1,o)}function ft(e){let t=e?.modulusLength??2048;if(typeof t!="number"||t<2048)throw new h("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used");return t}async function rr(e,t){let r,n;switch(e){case"PS256":case"PS384":case"PS512":r={name:"RSA-PSS",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:ft(t)},n=["sign","verify"];break;case"RS256":case"RS384":case"RS512":r={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:ft(t)},n=["sign","verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":r={name:"RSA-OAEP",hash:`SHA-${parseInt(e.slice(-3),10)||1}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:ft(t)},n=["decrypt","unwrapKey","encrypt","wrapKey"];break;case"ES256":r={name:"ECDSA",namedCurve:"P-256"},n=["sign","verify"];break;case"ES384":r={name:"ECDSA",namedCurve:"P-384"},n=["sign","verify"];break;case"ES512":r={name:"ECDSA",namedCurve:"P-521"},n=["sign","verify"];break;case"EdDSA":{n=["sign","verify"];let o=t?.crv??"Ed25519";switch(o){case"Ed25519":case"Ed448":r={name:o};break;default:throw new h("Invalid or unsupported crv option provided")}break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{n=["deriveKey","deriveBits"];let o=t?.crv??"P-256";switch(o){case"P-256":case"P-384":case"P-521":{r={name:"ECDH",namedCurve:o};break}case"X25519":case"X448":r={name:o};break;default:throw new h("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448")}break}default:throw new h('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return f.subtle.generateKey(r,t?.extractable??!1,n)}async function sn(e,t){return rr(e,t)}async function cn(e,t){return tr(e,t)}var nr="WebCryptoAPI";var dn=nr; + exports.CompactEncrypt=Ae;exports.CompactSign=be;exports.EmbeddedJWK=Xr;exports.EncryptJWT=it;exports.FlattenedEncrypt=G;exports.FlattenedSign=j;exports.GeneralEncrypt=tt;exports.GeneralSign=ot;exports.SignJWT=at;exports.UnsecuredJWT=pt;exports.base64url=er;exports.calculateJwkThumbprint=qt;exports.calculateJwkThumbprintUri=zr;exports.compactDecrypt=Qe;exports.compactVerify=rt;exports.createLocalJWKSet=Le;exports.createRemoteJWKSet=tn;exports.cryptoRuntime=dn;exports.decodeJwt=an;exports.decodeProtectedHeader=on;exports.errors=yt;exports.experimental_jwksCache=rn;exports.exportJWK=je;exports.exportPKCS8=Mr;exports.exportSPKI=Dr;exports.flattenedDecrypt=we;exports.flattenedVerify=Se;exports.generalDecrypt=Or;exports.generalVerify=Lr;exports.generateKeyPair=sn;exports.generateSecret=cn;exports.importJWK=Z;exports.importPKCS8=Kr;exports.importSPKI=_r;exports.importX509=xr;exports.jwksCache=_e;exports.jwtDecrypt=Fr;exports.jwtVerify=Gr;})); \ No newline at end of file diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..724eebc --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,99 @@ + + + + + Admin Panel + + +

Admin Panel

+

Note: this admin panel will only work for Shounic!

+ + + + + + + \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7c6b308 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,140 @@ + + + + Gamble + + + +

Shounic's un-styled ultra-ugly gambling site

+

Loading...

+

Click the button to claim unclaimed points

+ +

Loading...

+

Here are the current activities:

+
    + +
+ Privacy Policy + Terms of Service + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..e52623e --- /dev/null +++ b/templates/login.html @@ -0,0 +1,170 @@ + + + + + + Log in with Google + + +

Log in with Google

+ + +

By logging in, you agree to our Privacy Policy and Terms of Service

+ + + diff --git a/templates/plugin.html b/templates/plugin.html new file mode 100644 index 0000000..5c264b9 --- /dev/null +++ b/templates/plugin.html @@ -0,0 +1,62 @@ + + + + + {{ .Name }} + + + + + + + + + + {{ .PluginHTML }} +

Loading...

+ + + \ No newline at end of file diff --git a/templates/privacy.html b/templates/privacy.html new file mode 100644 index 0000000..e515900 --- /dev/null +++ b/templates/privacy.html @@ -0,0 +1,9 @@ + + + + + Privacy Policy + + + We do not sell your data. Full stop. Your personal information never leaves your device. The only thing we ever use your google account for is to get your YouTube channel ID, and to display your name. We never store your email address, and your name never gets on our servers. If you would like us to delete all your data, immediately, simply press the delete account button on the login page (this may not work during outages). + \ No newline at end of file diff --git a/templates/tos.html b/templates/tos.html new file mode 100644 index 0000000..a3400f0 --- /dev/null +++ b/templates/tos.html @@ -0,0 +1,26 @@ + + + + + Privacy Policy + + +

Hey there! Here’s the deal with your data:

+ +

We’re all about keeping things simple. We only use your Google account to:

+
    +
  • Grab your YouTube channel ID so we can make our service better.
  • +
  • Show your name on the client-side, so you can see it. That’s it!
  • +
+ +

We don’t store your email address, and your name never touches our servers. It’s all on your device and stays there.

+ +

Want to delete your data? Hit the "Delete Account" button on the login page. It might not work during outages, but we’re doing our best.

+ +

We are very sorry, but I don't own servers in the EU and therefore cannot comply with the EU GDPR. It is compliant with the UK one and everything except the location thing in the EU one. Very sorry, but we cannot support any EU users at this time. If you use this service from within the EU, you are doing so at your own risk.

+ +

If you’ve got any questions or just want to chat, hit us up at @arzumify on discord.

+ +

Thanks for reading! Now go enjoy the app without worrying about your data.

+ +