diff --git a/go.mod b/go.mod index 2dde275..80eb968 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/openai/openai-go v0.1.0-alpha.41 google.golang.org/api v0.214.0 google.golang.org/protobuf v1.36.1 + googlemaps.github.io/maps v1.7.0 ) require ( @@ -41,6 +42,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect + go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect diff --git a/go.sum b/go.sum index d8711f0..8704f69 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.117.0 h1:Z5TNFfQxj7WG2FgOGX1ekC5RiXrYgms6QscOm32M/4s= cloud.google.com/go v0.117.0/go.mod h1:ZbwhVTb1DBGt2Iwb3tNO6SEK4q+cplHZmLWH+DelYYc= cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= @@ -14,17 +15,25 @@ cloud.google.com/go/iam v1.3.0 h1:4Wo2qTaGKFtajbLpF6I4mywg900u3TLlHDb6mriLDPU= cloud.google.com/go/iam v1.3.0/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng= cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/GoogleCloudPlatform/functions-framework-go v1.9.0 h1:Fq0sKuCyyFFVFm1r6fEQJ4TRnbbhXP9Q6MEUX+UAd/0= github.com/GoogleCloudPlatform/functions-framework-go v1.9.0/go.mod h1:8Ww7VHPCGKqCfZOCT9INIiakNgGQPGRfL4U4yy5F5Kc= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc= github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -32,15 +41,37 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= @@ -51,8 +82,12 @@ github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10C github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= 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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= @@ -66,10 +101,20 @@ github.com/openai/openai-go v0.1.0-alpha.41 h1:OPRT5YfNKlENfipMtolMWnKbCR1iQDc9h github.com/openai/openai-go v0.1.0-alpha.41/go.mod h1:3SdE6BffOX9HPEQv8IL/fi3LYZ5TUpRYaqGQZbyk11A= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= 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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -86,6 +131,7 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= @@ -110,34 +156,96 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8 h1:e26eS1K69yxjjNNHYqjN49y95kcaQLJ3TL5h68dcA1E= google.golang.org/genproto v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:i5btTErZyoKCCubju3HS5LVho4nZd3yFnEp6moqeUjE= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8 h1:st3LcW/BPi75W4q1jJTEor/QWwbNlPlDG0JTn6XhZu0= google.golang.org/genproto/googleapis/api v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:klhJGKFyG8Tn50enBn7gizg4nXGXJ+jqEREdCWaPcV4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +googlemaps.github.io/maps v1.7.0 h1:9yAEgaAyg6bWn+TpY8PmNJ0C+YfUBtN9KjJypjCOioo= +googlemaps.github.io/maps v1.7.0/go.mod h1:cCq0JKYAnnCRSdiaBi7Ex9CW15uxIAk7oPi8V/xEh6s= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/app/command.go b/internal/app/command.go index 2bf2d3d..531a89b 100644 --- a/internal/app/command.go +++ b/internal/app/command.go @@ -17,15 +17,16 @@ const ( Version Command = "/version" // Show the version // Hidden user commands - MissingNote Command = "/missing_note" // Ask to put a note from the previous text - Timezone Command = "/timezone" // Change the timezone - DownloadData Command = "/download_data" // Download the user data (all notes) - DeleteAccount Command = "/delete_account" // Ask to delete the account - ForceDeleteAccount Command = "/force_delete_account" // Force delete the account - Support Command = "/support" // Give feedback or ask for support - NoteCount Command = "/note_count" // Count of the current user notes - SleepAnalysis Command = "/sleep_analysis" // Sleep analysis of last note - WeeklyAnalysis Command = "/weekly_analysis" // Weekly analysis of the user's journal entries for last week + MissingNote Command = "/missing_note" // Ask to put a note from the previous text + DownloadData Command = "/download_data" // Download the user data (all notes) + DeleteAccount Command = "/delete_account" // Ask to delete the account + ForceDeleteAccount Command = "/force_delete_account" // Force delete the account + Support Command = "/support" // Give feedback or ask for support + NoteCount Command = "/note_count" // Count of the current user notes + SleepAnalysis Command = "/sleep_analysis" // Sleep analysis of last note + WeeklyAnalysis Command = "/weekly_analysis" // Weekly analysis of the user's journal entries for last week + + // Reminder commands Reminders Command = "/reminders" // Set reminders EnableAllReminders Command = "/enable_all_reminders" // Enable all reminders DisableAllReminders Command = "/disable_all_reminders" // Disable all reminders @@ -39,6 +40,10 @@ const ( SetEveningReminderTime Command = "/set_evening_reminder_time" // Set evening reminder time SkipReminders Command = "/skip_reminders" // Skip the reminders (during onboarding) + // Timezone commands + Timezone Command = "/timezone" // Change the timezone (manually) + AskForCity Command = "/ask_for_city" // Ask for the city to set the timezone + // Admin commands TotalUserCount Command = "/total_user_count" // Get the total number of users TotalActiveUserCount Command = "/total_active_user_count" // Get the number of active users diff --git a/internal/app/reminders.go b/internal/app/reminders.go index 5ee87af..26f7949 100644 --- a/internal/app/reminders.go +++ b/internal/app/reminders.go @@ -44,7 +44,7 @@ func handleReminders(session *Session) { var timezoneButton botservice.BotResultTextButton = botservice.BotResultTextButton{ TextID: "timezone", Locale: session.Locale(), - Callback: string(Timezone), + Callback: string(AskForCity), } var morningReminderButton botservice.BotResultTextButton = botservice.BotResultTextButton{ TextID: "morning_reminder_button", diff --git a/internal/app/reminders_test.go b/internal/app/reminders_test.go index 25b3983..cc0ef9f 100644 --- a/internal/app/reminders_test.go +++ b/internal/app/reminders_test.go @@ -51,8 +51,8 @@ func TestRemindersOnHandler(t *testing.T) { if session.Job.Output[0].Buttons[1].TextID != "timezone" { t.Error("Expected 'timezone', got", session.Job.Output[0].Buttons[1].TextID) } - if session.Job.Output[0].Buttons[1].Callback != "/timezone" { - t.Error("Expected '/timezone', got", session.Job.Output[0].Buttons[1].Callback) + if session.Job.Output[0].Buttons[1].Callback != "/ask_for_city" { + t.Error("Expected '/ask_for_city', got", session.Job.Output[0].Buttons[1].Callback) } if session.Job.Output[0].Buttons[2].TextID != "morning_reminder_button" { t.Error("Expected 'morning_reminder_button', got", session.Job.Output[0].Buttons[2].TextID) @@ -237,8 +237,8 @@ func TestAllRemindersEnabler(t *testing.T) { if session.Job.Output[0].TextID != "reminders_enabled" { t.Error("Expected 'reminders_enabled', got", session.Job.Output[0].TextID) } - if session.Job.Output[1].TextID != "timezone_select" { - t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + if session.Job.Output[1].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[1].TextID) } } @@ -269,8 +269,8 @@ func TestMorningReminderEnabled(t *testing.T) { if session.Job.Output[0].TextID != "reminder_set" { t.Error("Expected 'reminder_set', got", session.Job.Output[0].TextID) } - if session.Job.Output[1].TextID != "timezone_select" { - t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + if session.Job.Output[1].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[1].TextID) } } @@ -298,8 +298,8 @@ func TestEveningReminderEnabler(t *testing.T) { if session.Job.Output[0].TextID != "reminder_set" { t.Error("Expected 'reminder_set', got", session.Job.Output[0].TextID) } - if session.Job.Output[1].TextID != "timezone_select" { - t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + if session.Job.Output[1].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[1].TextID) } } @@ -330,8 +330,8 @@ func TestMorningReminderOffset(t *testing.T) { if session.Job.Output[0].TextID != "reminder_set" { t.Error("Expected 'reminder_set', got", session.Job.Output[0].TextID) } - if session.Job.Output[1].TextID != "timezone_select" { - t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + if session.Job.Output[1].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[1].TextID) } } @@ -349,8 +349,8 @@ func TestEveningReminderOffset(t *testing.T) { if session.Job.Output[0].TextID != "reminder_set" { t.Error("Expected 'reminder_set', got", session.Job.Output[0].TextID) } - if session.Job.Output[1].TextID != "timezone_select" { - t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + if session.Job.Output[1].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[1].TextID) } } diff --git a/internal/app/session.go b/internal/app/session.go index f3c53a2..a113b2e 100644 --- a/internal/app/session.go +++ b/internal/app/session.go @@ -85,6 +85,8 @@ func handleSession(session *Session) { handleLanguage(session) case Timezone: handleTimezone(session, settingsStorage) + case AskForCity: + requestTimezone(session) case Reminders: handleReminders(session) case MorningReminder: @@ -145,6 +147,8 @@ func handleSession(session *Session) { finishNote(*session.Job.Input, session, noteStorage) case Support: finishFeedback(session, feedbackStorage) + case AskForCity, EnableAllReminders, EnableMorningReminder, EnableEveningReminder, SetMorningReminderTime, SetEveningReminderTime: + finishCityRequest(session, mapsService, settingsStorage) default: // If the user is typing and the last command is not recognized handleHelp(session) diff --git a/internal/app/timezone.go b/internal/app/timezone.go index 3ea48ea..6c8630f 100644 --- a/internal/app/timezone.go +++ b/internal/app/timezone.go @@ -1,18 +1,22 @@ package app import ( + "fmt" "log" "strconv" + "time" "github.com/capymind/internal/botservice" "github.com/capymind/internal/database" + "github.com/capymind/internal/mapsservice" + "github.com/capymind/internal/translator" "github.com/capymind/internal/utils" ) // Handle the timezone command func handleTimezone(session *Session, settingsStorage database.SettingsStorage) { if len(session.Job.Parameters) == 0 { - requestTimezone(session) + requestTimezoneManually(session) } else { setupTimezone(session, settingsStorage) } @@ -40,8 +44,8 @@ func setupTimezone(session *Session, settingsStorage database.SettingsStorage) { session.SaveSettings(*session.Settings, settingsStorage) } -// Set the timezone -func requestTimezone(session *Session) { +// Set the timezone manually +func requestTimezoneManually(session *Session) { var buttons []botservice.BotResultTextButton timeZones := utils.GetTimeZones() for _, tz := range timeZones { @@ -55,3 +59,44 @@ func requestTimezone(session *Session) { } setOutputTextWithButtons("timezone_select", buttons, session) } + +func requestTimezone(session *Session) { + session.User.IsTyping = true + setOutputText("ask_for_city", session) +} + +func finishCityRequest(session *Session, mapsService mapsservice.MapsService, settingsStorage database.SettingsStorage) { + session.User.IsTyping = false + + city := *session.Job.Input + secondsFromUTC := mapsService.GetTimezone(city) + if secondsFromUTC == nil { + setOutputText("timezone_not_found", session) + requestTimezoneManually(session) + return + } + + session.Settings.Location = &city + session.SaveSettings(*session.Settings, settingsStorage) + + text := translator.Translate(session.Locale(), "is_this_your_time") + text = text + currentTimeString(time.Now(), *secondsFromUTC) + + var yesButton botservice.BotResultTextButton = botservice.BotResultTextButton{ + TextID: "yes", + Locale: session.Locale(), + Callback: string(Timezone) + fmt.Sprintf(" %d", *secondsFromUTC), + } + var noButton botservice.BotResultTextButton = botservice.BotResultTextButton{ + TextID: "no", + Locale: session.Locale(), + Callback: string(Timezone), + } + + setOutputTextWithButtons(text, []botservice.BotResultTextButton{yesButton, noButton}, session) +} + +func currentTimeString(currentTime time.Time, offset int) string { + utcTime := currentTime.UTC().Add(time.Duration(offset) * time.Second) + return utcTime.Format("15:04") +} diff --git a/internal/app/timezone_test.go b/internal/app/timezone_test.go index 6aea97e..179352c 100644 --- a/internal/app/timezone_test.go +++ b/internal/app/timezone_test.go @@ -2,6 +2,7 @@ package app import ( "testing" + "time" "github.com/capymind/internal/database" "github.com/capymind/internal/mocks" @@ -48,3 +49,72 @@ func TestTimezoneHandlerWithParamOnboarded(t *testing.T) { t.Error("Expected '0', got", false) } } + +func TestRequestTimezoneHandler(t *testing.T) { + session := createSession(&Job{Command: "/ask_for_city"}, &database.User{}, nil, nil) + requestTimezone(session) + + if session.Job.Output[0].TextID != "ask_for_city" { + t.Error("Expected 'ask_for_city', got", session.Job.Output[0].TextID) + } + if session.User.IsTyping != true { + t.Error("Expected 'true', got", session.User.IsTyping) + } +} + +func TestFinishCityInvalidRequestHandler(t *testing.T) { + city := "new york" + session := createSession(&Job{Command: "", Input: &city}, &database.User{}, &database.Settings{}, nil) + mapsService := &mocks.InvalidMapsServiceMock{} + settingsStorage := &mocks.EmptySettingsStorageMock{} + finishCityRequest(session, mapsService, settingsStorage) + + if session.User.IsTyping != false { + t.Error("Expected 'false', got", session.User.IsTyping) + } + if session.Job.Output[0].TextID != "timezone_not_found" { + t.Error("Expected 'timezone_not_found', got", session.Job.Output[0].TextID) + } + if session.Job.Output[1].TextID != "timezone_select" { + t.Error("Expected 'timezone_select', got", session.Job.Output[1].TextID) + } +} + +func TestFinishCityRequestHandler(t *testing.T) { + city := "portland" + session := createSession(&Job{Command: "", Input: &city}, &database.User{}, &database.Settings{}, nil) + mapsService := &mocks.MapsServiceMock{} + settingsStorage := &mocks.EmptySettingsStorageMock{} + finishCityRequest(session, mapsService, settingsStorage) + + if session.User.IsTyping != false { + t.Error("Expected 'false', got", session.User.IsTyping) + } + if *session.Settings.Location != city { + t.Error("Expected 'portland', got", *session.Settings.Location) + } + if len(session.Job.Output[0].Buttons) != 2 { + t.Error("Expected '2', got", len(session.Job.Output[0].Buttons)) + } + if session.Job.Output[0].Buttons[0].TextID != "yes" { + t.Error("Expected 'yes', got", session.Job.Output[0].Buttons[0].TextID) + } + if session.Job.Output[0].Buttons[0].Callback != "/timezone 7200" { + t.Error("Expected '/timezone 7200', got", session.Job.Output[0].Buttons[0].Callback) + } + if session.Job.Output[0].Buttons[1].TextID != "no" { + t.Error("Expected 'no', got", session.Job.Output[0].Buttons[1].TextID) + } + if session.Job.Output[0].Buttons[1].Callback != "/timezone" { + t.Error("Expected '/timezone', got", session.Job.Output[0].Buttons[1].Callback) + } +} + +func TestCurrentTimeString(t *testing.T) { + time := time.Date(2021, 1, 1, 9, 35, 0, 0, time.UTC) + + timeString := currentTimeString(time, 7200) + if timeString != "11:35" { + t.Error("Expected '11:35', got", timeString) + } +} diff --git a/internal/app/vars.go b/internal/app/vars.go index 08f9cef..cfd52c1 100644 --- a/internal/app/vars.go +++ b/internal/app/vars.go @@ -5,6 +5,7 @@ package app import ( "github.com/capymind/third_party/firestore" "github.com/capymind/third_party/googledrive" + "github.com/capymind/third_party/googlemaps" "github.com/capymind/third_party/openai" "github.com/capymind/third_party/telegram" ) @@ -20,3 +21,5 @@ var adminStorage firestore.AdminStorage var feedbackStorage firestore.FeedbackStorage var fileStorage googledrive.GoogleDrive + +var mapsService googlemaps.GoogleMapsService diff --git a/internal/database/settings.go b/internal/database/settings.go index 6e3204c..bd3a079 100644 --- a/internal/database/settings.go +++ b/internal/database/settings.go @@ -3,11 +3,12 @@ package database import "context" type Settings struct { - SecondsFromUTC *int `json:"secondsFromUTC"` - HasMorningReminder *bool `json:"hasMorningReminder"` - HasEveningReminder *bool `json:"hasEveningReminder"` - MorningReminderOffset *int `json:"morningReminderOffset"` - EveningReminderOffset *int `json:"eveningReminderOffset"` + SecondsFromUTC *int `json:"secondsFromUTC"` + HasMorningReminder *bool `json:"hasMorningReminder"` + HasEveningReminder *bool `json:"hasEveningReminder"` + MorningReminderOffset *int `json:"morningReminderOffset"` + EveningReminderOffset *int `json:"eveningReminderOffset"` + Location *string `json:"location"` } type SettingsStorage interface { diff --git a/internal/mapsservice/mapsservice.go b/internal/mapsservice/mapsservice.go new file mode 100644 index 0000000..bbee302 --- /dev/null +++ b/internal/mapsservice/mapsservice.go @@ -0,0 +1,7 @@ +//coverage:ignore file + +package mapsservice + +type MapsService interface { + GetTimezone(city string) *int +} diff --git a/internal/mocks/mapsservice_mock.go b/internal/mocks/mapsservice_mock.go new file mode 100644 index 0000000..0281812 --- /dev/null +++ b/internal/mocks/mapsservice_mock.go @@ -0,0 +1,16 @@ +//coverage:ignore file + +package mocks + +type MapsServiceMock struct{} + +func (service MapsServiceMock) GetTimezone(city string) *int { + timezone := 7200 + return &timezone +} + +type InvalidMapsServiceMock struct{} + +func (service InvalidMapsServiceMock) GetTimezone(city string) *int { + return nil +} diff --git a/internal/translator/translations.go b/internal/translator/translations.go index 73c2de3..2b72f66 100644 --- a/internal/translator/translations.go +++ b/internal/translator/translations.go @@ -80,7 +80,12 @@ const translationsJSON = `{ "morning_reminder_descr": "Set a reminder to make a note in the journal every morning 🌅", "evening_reminder_descr": "Set a reminder to make a note in the journal every evening 🌙", "onboarding_reminders": "Would you like to turn on reminders to make journal entries in the morning and evening? 🌅🌙", - "continue": "Continue ➡️" + "continue": "Continue ➡️", + "ask_for_city": "Please enter the name of the city you are currently in 🌍", + "timezone_not_found": "The time zone for the city you entered could not be found. Please set up your time zone manually.", + "is_this_your_time": "Is this your current time? 🕒\n", + "yes": "Yes", + "no": "No" }, "uk": { "welcome": "Ласкаво просимо до CapyMind 👋 Ваш особистий журнал для записів про психічне здоров'я тут, щоб допомогти вам на вашому шляху. Рефлексуйте над своїми думками та емоціями, використовуйте нагадування, щоб залишатися на шляху, та досліджуйте інсайти терапії, щоб поглибити свою самосвідомість.", @@ -159,6 +164,11 @@ const translationsJSON = `{ "morning_reminder_descr": "Встановіть нагадування зробити запис у журнал кожного ранку 🌅", "evening_reminder_descr": "Встановіть нагадування зробити запис у журнал кожного вечора 🌙", "onboarding_reminders": "Бажаєте увімкнути нагадування для ведення записів у журналі вранці та ввечері? 🌅🌙", - "continue": "Продовжити ➡️" + "continue": "Продовжити ➡️", + "ask_for_city": "Будь ласка, введіть назву міста, в якому ви знаходитесь 🌍", + "timezone_not_found": "Часовий пояс для введеного вами міста не вдалося знайти. Будь ласка, встановіть свій часовий пояс вручну.", + "is_this_your_time": "Це ваш поточний час? 🕒\n", + "yes": "Так", + "no": "Ні" } }` diff --git a/scripts/deploy_functions.sh b/scripts/deploy_functions.sh index bbb4aa1..1551204 100755 --- a/scripts/deploy_functions.sh +++ b/scripts/deploy_functions.sh @@ -25,7 +25,7 @@ done ENV_VARS=${ENV_VARS%,} # Set the secret environment variables -SECRET_PARAMS=("CAPY_TELEGRAM_BOT_TOKEN=telegram_bot_token" "CAPY_AI_KEY=ai_key") +SECRET_PARAMS=("CAPY_TELEGRAM_BOT_TOKEN=telegram_bot_token" "CAPY_AI_KEY=ai_key" "CAPY_GOOGLE_MAPS_API_KEY=maps_key") SECRETS="" for PARAM in "${SECRET_PARAMS[@]}"; do SECRETS+="$PARAM:latest," diff --git a/third_party/googlemaps/googlemaps.go b/third_party/googlemaps/googlemaps.go new file mode 100644 index 0000000..43ed690 --- /dev/null +++ b/third_party/googlemaps/googlemaps.go @@ -0,0 +1,68 @@ +//coverage:ignore file + +package googlemaps + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "googlemaps.github.io/maps" +) + +type GoogleMapsService struct{} + +var client *maps.Client + +func createClient() { + if client != nil { + return + } + + var err error + client, err = maps.NewClient(maps.WithAPIKey(os.Getenv("CAPY_GOOGLE_MAPS_API_KEY"))) + if err != nil { + fmt.Println("Error creating client: ", err) + } +} + +func (service GoogleMapsService) GetTimezone(city string) *int { + createClient() + context := context.Background() + + r := &maps.GeocodingRequest{ + Address: city, + } + results, err := client.Geocode(context, r) + if err != nil { + log.Printf("Error getting geocode for city %s: %v\n", city, err) + return nil + } + + if len(results) == 0 { + log.Printf("No results for city %s\n", city) + return nil + } + + location := results[0].Geometry.Location + + timezoneRequest := &maps.TimezoneRequest{ + Location: &maps.LatLng{Lat: location.Lat, Lng: location.Lng}, + Timestamp: time.Now(), + } + + timezoneResult, err := client.Timezone(context, timezoneRequest) + if err != nil { + log.Printf("Error getting timezone for city %s: %v\n", city, err) + return nil + } + + if timezoneResult == nil { + log.Printf("No timezone result for city %s\n", city) + return nil + } + + return &timezoneResult.RawOffset +}