From b52ff2136573d16f39a1724f08ba138bc93cc7a6 Mon Sep 17 00:00:00 2001 From: DRypa Date: Thu, 4 May 2023 23:04:44 +0300 Subject: [PATCH 01/32] #70 api to get electronic receipts --- backend/nalogru/client.go | 43 ++++++++++++++++++++++++++++++++ backend/nalogru/client_test.go | 45 ++++++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 464e492..15f7000 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -221,6 +221,49 @@ func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { return details, nil } +// GetElectronicTickets request all electronic receipts added by email or phone from nalog.ru api. +func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { + client := createHttpClient() + + url := nalogruClient.BaseAddress + "/v2/tickets-with-electro" + request, err := http.NewRequest(http.MethodGet, url, nil) + + addHeaders(request, nalogruClient.device.Id.Hex()) + addAuth(request, nalogruClient.device.SessionId) + res, err := sendRequest(request, client) + + if err != nil { + log.Printf("Can't GET %s\n", url) + return nil, err + } + defer res.Body.Close() + + if res.StatusCode == http.StatusUnauthorized { + return nil, AuthError + } + + if res.StatusCode != http.StatusOK { + log.Printf("GET electronic receipts error: %d\n", res.StatusCode) + return nil, err + } + all, err := readBody(res) + if err != nil { + log.Printf("failed to read response body. status code %d\n", res.StatusCode) + return nil, err + } + err = os.WriteFile("/var/lib/receipts/electro/1.json", all, 0644) + var tickets []*TicketDetails + + err = json.Unmarshal(all, &tickets) + if err != nil { + log.Println("Can't decode response body") + + return nil, err + } + + return tickets, nil +} + type RefreshRequest struct { ClientSecret string `json:"client_secret"` RefreshToken string `json:"refresh_token"` diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index 7a43916..acc09ee 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -12,9 +12,11 @@ import ( var baseAddress = "https://irkkt-mobile.nalog.ru:8888" var sessionId = "INSERT SESSION ID HERE" var deviceId = primitive.NewObjectID().Hex() +var secret = "INSERT SECRET HERE" +var refreshToken = "INSERT REFRESH TOKEN HERE" func IgnoreTestClient_GetTicketId(t *testing.T) { - d, err := createDevice("", "") + d, err := createDevice() if err != nil { log.Println(err) t.Fail() @@ -39,7 +41,36 @@ func IgnoreTestClient_GetTicketId(t *testing.T) { } -func createDevice(secret string, token string) (*device.Device, error) { +func IgnoreTestClient_GetElectronicTickets(t *testing.T) { + d, err := createDevice() + if err != nil { + log.Println(err) + t.Fail() + return + } + client := NewClient(baseAddress, d) + + tickets, err := client.GetElectronicTickets() + if err != nil { + log.Println(err) + t.Fail() + return + } + + if tickets == nil { + log.Println("Tickets not found") + t.Fail() + return + } + if len(tickets) == 0 { + log.Println("Tickets empty") + t.Fail() + return + } + +} + +func createDevice() (*device.Device, error) { id, err := primitive.ObjectIDFromHex(deviceId) if err != nil { return nil, err @@ -48,13 +79,13 @@ func createDevice(secret string, token string) (*device.Device, error) { SessionId: sessionId, Id: id, ClientSecret: secret, - RefreshToken: token, + RefreshToken: refreshToken, } return d, err } func IgnoreTestClient_GetTicketById(t *testing.T) { - d, err := createDevice("", "") + d, err := createDevice() if err != nil { log.Println(err) t.Fail() @@ -77,9 +108,7 @@ func IgnoreTestClient_GetTicketById(t *testing.T) { } func IgnoreTestClient_RefreshSession(t *testing.T) { - secret := "PASS CLIENT SECRET HERE" - refreshToken := "PASS REFRESH TOKEN HERE" - d, err := createDevice(secret, refreshToken) + d, err := createDevice() if err != nil { log.Println(err) t.Fail() @@ -109,7 +138,7 @@ func IgnoreTestClient_RefreshSession(t *testing.T) { func IgnoreTestClient_CheckReceiptExist(t *testing.T) { queryString := "INSERT VALID QUERY STRING HERE" - d, err := createDevice("", "") + d, err := createDevice() if err != nil { log.Println(err) t.Fail() From 83ebfe356de6f0fc8a7760356d2719d726f7e793 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 4 Jun 2023 15:08:00 +0300 Subject: [PATCH 02/32] #70 worker skeleton --- backend/device/service.go | 20 +++++++++---- backend/go.mod | 2 ++ backend/go.sum | 21 ++++++++++++++ backend/nalogru/client.go | 6 ++-- backend/nalogru/client_test.go | 2 +- backend/nalogru/devices.go | 1 + backend/receipts/repository.go | 21 +++++++++++++- backend/workers/electronic.go | 51 ++++++++++++++++++++++++++++++++++ 8 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 backend/workers/electronic.go diff --git a/backend/device/service.go b/backend/device/service.go index 6346840..c01cd1c 100644 --- a/backend/device/service.go +++ b/backend/device/service.go @@ -7,13 +7,13 @@ import ( "receipt_collector/nalogru/device" ) -//Service to manage devices. +// Service to manage devices. type Service struct { r *repository.Repository devices []ForRent } -//NewService creates instance of Service. +// NewService creates instance of Service. func NewService(ctx context.Context, r *repository.Repository) (*Service, error) { all, err := r.All(ctx) if err != nil { @@ -32,7 +32,7 @@ func NewService(ctx context.Context, r *repository.Repository) (*Service, error) return s, nil } -//Add adds new device. +// Add adds new device. func (s *Service) Add(ctx context.Context, d device.Device) error { for _, v := range s.devices { if v.ClientSecret == d.ClientSecret { @@ -42,12 +42,12 @@ func (s *Service) Add(ctx context.Context, d device.Device) error { return s.r.Add(ctx, d) } -//Count returns devices count. +// Count returns devices count. func (s *Service) Count(ctx context.Context) (int, error) { return len(s.devices), nil } -//Rent device. +// Rent device. func (s *Service) Rent(ctx context.Context) (*device.Device, error) { for _, v := range s.devices { if v.IsRent == false { @@ -67,6 +67,7 @@ func (s *Service) Update(ctx context.Context, device *device.Device) error { return s.r.Update(ctx, device) } +// Free release the rented device func (s *Service) Free(ctx context.Context, device *device.Device) error { for _, v := range s.devices { if device.Id == v.Id { @@ -76,3 +77,12 @@ func (s *Service) Free(ctx context.Context, device *device.Device) error { } return errors.New("device not found") } + +// All return all registered devices +func (s *Service) All(ctx context.Context) []*device.Device { + res := make([]*device.Device, len(s.devices)) + for i, d := range s.devices { + res[i] = &d.Device + } + return res +} diff --git a/backend/go.mod b/backend/go.mod index 1edbdf1..6b11241 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -14,6 +14,7 @@ require ( ) require ( + github.com/go-co-op/gocron v1.28.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/klauspost/compress v1.13.6 // indirect @@ -24,6 +25,7 @@ require ( github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + go.uber.org/atomic v1.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index 43f55f8..16a8ea9 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,8 +1,11 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/drypa/ReceiptCollector/api/inside v0.0.0-20230405091352-65e1efb55137 h1:2gSdtAgn/1SzR3jIxQybz+G8hFlLLPgwBpwe5se5kf4= github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230405091352-65e1efb55137/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= +github.com/go-co-op/gocron v1.28.2 h1:H9oHUGH+9HZ5mAorbnzRjzXLf4poP+ctZdbtaKRYagc= +github.com/go-co-op/gocron v1.28.2/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -22,20 +25,34 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 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.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= @@ -49,6 +66,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= @@ -79,6 +98,8 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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= diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 15f7000..afee91f 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -222,14 +222,14 @@ func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { } // GetElectronicTickets request all electronic receipts added by email or phone from nalog.ru api. -func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { +func (nalogruClient *Client) GetElectronicTickets(device *device.Device) ([]*TicketDetails, error) { client := createHttpClient() url := nalogruClient.BaseAddress + "/v2/tickets-with-electro" request, err := http.NewRequest(http.MethodGet, url, nil) - addHeaders(request, nalogruClient.device.Id.Hex()) - addAuth(request, nalogruClient.device.SessionId) + addHeaders(request, device.Id.Hex()) + addAuth(request, device.SessionId) res, err := sendRequest(request, client) if err != nil { diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index acc09ee..47c22a6 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -50,7 +50,7 @@ func IgnoreTestClient_GetElectronicTickets(t *testing.T) { } client := NewClient(baseAddress, d) - tickets, err := client.GetElectronicTickets() + tickets, err := client.GetElectronicTickets(nil) if err != nil { log.Println(err) t.Fail() diff --git a/backend/nalogru/devices.go b/backend/nalogru/devices.go index 30db2f8..2091dd3 100644 --- a/backend/nalogru/devices.go +++ b/backend/nalogru/devices.go @@ -11,4 +11,5 @@ type Devices interface { Rent(ctx context.Context) (*device.Device, error) Update(ctx context.Context, device *device.Device) error Free(ctx context.Context, device *device.Device) error + All(ctx context.Context) []*device.Device } diff --git a/backend/receipts/repository.go b/backend/receipts/repository.go index d07a797..b44df6d 100644 --- a/backend/receipts/repository.go +++ b/backend/receipts/repository.go @@ -110,7 +110,8 @@ func (repository *Repository) GetByQueryString(ctx context.Context, userId strin query := bson.D{{"owner", ownerId}, {"query_string", queryString}} result := collection.FindOne(ctx, query) - if result.Err() != nil { + err = result.Err() + if err != nil { return nil, err } @@ -121,6 +122,24 @@ func (repository *Repository) GetByQueryString(ctx context.Context, userId strin } +// GetAllOwnersByQueryString find user receipt by QR code query string. +func (repository *Repository) GetAllOwnersByQueryString(ctx context.Context, queryString string) (*UsersReceipt, error) { + collection := repository.getCollection() + + query := bson.D{{"query_string", queryString}} + + result := collection.FindOne(ctx, query) + err := result.Err() + if err != nil { + return nil, err + } + + receipt := UsersReceipt{} + err = result.Decode(&receipt) + + return &receipt, err +} + // GetById returns receipt by it's id. func (repository *Repository) GetById(ctx context.Context, userId string, receiptId string) (UsersReceipt, error) { receipt := UsersReceipt{} diff --git a/backend/workers/electronic.go b/backend/workers/electronic.go new file mode 100644 index 0000000..8599e6e --- /dev/null +++ b/backend/workers/electronic.go @@ -0,0 +1,51 @@ +package workers + +import ( + "context" + "github.com/go-co-op/gocron" + "log" + "receipt_collector/nalogru" + "receipt_collector/nalogru/qr" + "time" +) + +// GetElectronicReceiptStart receives electronic receipts for all devices. +func (worker *Worker) GetElectronicReceiptStart(ctx context.Context) { + s := gocron.NewScheduler(time.Local) + + s.Every(1).Days().Do(worker.getElectronic) + + s.StartBlocking() +} +func (worker *Worker) getElectronic(ctx context.Context) { + for _, d := range worker.devices.All(ctx) { + tickets, err := worker.nalogruClient.GetElectronicTickets(d) + if err != nil { + log.Printf("Failed to get electronic tickets: %v\n", err) + return + } + err = worker.insertTicketsIfNeeded(ctx, tickets) + if err != nil { + log.Printf("Failed to insert electronic tickets: %v\n", err) + return + } + } +} +func (worker *Worker) insertTicketsIfNeeded(ctx context.Context, tickets []*nalogru.TicketDetails) error { + for _, t := range tickets { + query, err := qr.Parse(t.Qr) + if err != nil { + log.Printf("Failed to parse '%s'\n", t.Qr) + return err + } + receipt, err := worker.repository.GetAllOwnersByQueryString(ctx, query.ToString()) + if err != nil { + log.Printf("Failed to get receipts by qr '%s'\n", t.Qr) + return err + } + if receipt == nil { + //TODO: add ticket & raw ticket + } + } + return nil +} From 27890e5ab9c80cdd86e060b516732b9382761f3a Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 4 Jun 2023 15:13:58 +0300 Subject: [PATCH 03/32] #70 warnings fix --- backend/device/service.go | 6 ++++++ backend/workers/electronic.go | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/device/service.go b/backend/device/service.go index c01cd1c..6d27d29 100644 --- a/backend/device/service.go +++ b/backend/device/service.go @@ -44,11 +44,17 @@ func (s *Service) Add(ctx context.Context, d device.Device) error { // Count returns devices count. func (s *Service) Count(ctx context.Context) (int, error) { + if ctx.Err() != nil { + return -1, ctx.Err() + } return len(s.devices), nil } // Rent device. func (s *Service) Rent(ctx context.Context) (*device.Device, error) { + if ctx.Err() != nil { + return nil, ctx.Err() + } for _, v := range s.devices { if v.IsRent == false { v.IsRent = true diff --git a/backend/workers/electronic.go b/backend/workers/electronic.go index 8599e6e..e94ebbd 100644 --- a/backend/workers/electronic.go +++ b/backend/workers/electronic.go @@ -11,9 +11,15 @@ import ( // GetElectronicReceiptStart receives electronic receipts for all devices. func (worker *Worker) GetElectronicReceiptStart(ctx context.Context) { + if ctx.Err() != nil { + return + } s := gocron.NewScheduler(time.Local) - s.Every(1).Days().Do(worker.getElectronic) + _, err := s.Every(1).Days().Do(worker.getElectronic) + if err != nil { + log.Printf("failed to create job %v\n", err) + } s.StartBlocking() } From 5c348b4f701f28445a4d9d98d8cc50a9ea77cddf Mon Sep 17 00:00:00 2001 From: DRypa Date: Wed, 21 Jun 2023 23:17:56 +0300 Subject: [PATCH 04/32] #70 refresh session is moved from calling service to client --- backend/nalogru/client.go | 57 +++++++++++++++++++-------------------- backend/workers/get.go | 20 +------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index afee91f..1dfac9d 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -20,7 +20,6 @@ type Client struct { device *device.Device } -var AuthError = errors.New("auth failed") var InternalError = errors.New("internal failed") // NewClient - creates instance of Client. @@ -36,7 +35,7 @@ const ( ) // CheckReceiptExist send request to check receipt exist in Nalog.ru api. -func (nalogruClient Client) CheckReceiptExist(queryString string) (bool, error) { +func (nalogruClient *Client) CheckReceiptExist(queryString string) (bool, error) { client := createHttpClient() url, err := buildCheckReceiptUrl(nalogruClient.BaseAddress, queryString) if err != nil { @@ -85,7 +84,6 @@ type TicketIdResponse struct { // GetTicketId - send ticket id request to nalog.ru API. func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { - client := createHttpClient() payload := TicketIdRequest{Qr: queryString} req, err := json.Marshal(payload) @@ -95,9 +93,7 @@ func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { reader := bytes.NewReader(req) url := nalogruClient.BaseAddress + "/v2/ticket" request, err := http.NewRequest(http.MethodPost, url, reader) - addHeaders(request, nalogruClient.device.Id.Hex()) - addAuth(request, nalogruClient.device.SessionId) - res, err := sendRequest(request, client) + res, err := nalogruClient.sendAuthenticatedRequest(request) if err != nil { log.Printf("Can't POST %s\n", url) @@ -129,11 +125,7 @@ func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { return "", err } - if res.StatusCode == http.StatusUnauthorized { - err = AuthError - } else { - err = InternalError - } + err = InternalError return "", err } @@ -177,13 +169,10 @@ func readBody(res *http.Response) ([]byte, error) { // GetTicketById get ticket by id from nalog.ru api. func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { - client := createHttpClient() url := nalogruClient.BaseAddress + "/v2/tickets/" + id request, err := http.NewRequest(http.MethodGet, url, nil) - addHeaders(request, nalogruClient.device.Id.Hex()) - addAuth(request, nalogruClient.device.SessionId) - res, err := sendRequest(request, client) + res, err := nalogruClient.sendAuthenticatedRequest(request) if err != nil { log.Printf("Can't GET %s\n", url) @@ -197,10 +186,6 @@ func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { return nil, err } - if res.StatusCode == http.StatusUnauthorized { - return nil, AuthError - } - if res.StatusCode != http.StatusOK { log.Printf("GET receipt error: %d\n", res.StatusCode) err = os.WriteFile("/var/lib/receipts/error/"+id+".json", all, 0644) @@ -222,25 +207,18 @@ func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { } // GetElectronicTickets request all electronic receipts added by email or phone from nalog.ru api. -func (nalogruClient *Client) GetElectronicTickets(device *device.Device) ([]*TicketDetails, error) { - client := createHttpClient() +func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { url := nalogruClient.BaseAddress + "/v2/tickets-with-electro" request, err := http.NewRequest(http.MethodGet, url, nil) - addHeaders(request, device.Id.Hex()) - addAuth(request, device.SessionId) - res, err := sendRequest(request, client) + res, err := nalogruClient.sendAuthenticatedRequest(request) if err != nil { log.Printf("Can't GET %s\n", url) return nil, err } - defer res.Body.Close() - - if res.StatusCode == http.StatusUnauthorized { - return nil, AuthError - } + defer dispose.Dispose(res.Body.Close, "Can't close response body") if res.StatusCode != http.StatusOK { log.Printf("GET electronic receipts error: %d\n", res.StatusCode) @@ -264,6 +242,27 @@ func (nalogruClient *Client) GetElectronicTickets(device *device.Device) ([]*Tic return tickets, nil } +func (nalogruClient *Client) sendAuthenticatedRequest(r *http.Request) (*http.Response, error) { + addHeaders(r, nalogruClient.device.Id.Hex()) + addAuth(r, nalogruClient.device.SessionId) + client := createHttpClient() + res, err := sendRequest(r, client) + + if err != nil { + return nil, err + } + + if res.StatusCode == http.StatusUnauthorized { + err = nalogruClient.RefreshSession() + if err != nil { + log.Printf("failed to refresh session. %v\n", err) + return nil, err + } + res, err = sendRequest(r, client) + } + return res, err +} + type RefreshRequest struct { ClientSecret string `json:"client_secret"` RefreshToken string `json:"refresh_token"` diff --git a/backend/workers/get.go b/backend/workers/get.go index 7838274..edf609d 100644 --- a/backend/workers/get.go +++ b/backend/workers/get.go @@ -9,7 +9,7 @@ import ( "time" ) -//GetReceiptStart starts get receipt worker. +// GetReceiptStart starts get receipt worker. func (worker *Worker) GetReceiptStart(ctx context.Context, settings Settings) { ticker := time.NewTicker(settings.Interval) @@ -73,15 +73,6 @@ func (worker *Worker) getReceipt(ctx context.Context) error { return err } - if err == nalogru.AuthError { - err = worker.refreshSession(ctx) - if err != nil { - log.Printf("failed to refresh session. %v\n", err) - return err - } - id, err = worker.nalogruClient.GetTicketId(normalizedQr) - } - if err != nil { log.Printf("failed get receipt id %v\n", err) err := worker.repository.SetReceiptStatus(ctx, receipt.Id.Hex(), receipts.Error) @@ -100,15 +91,6 @@ func (worker *Worker) getReceipt(ctx context.Context) error { func (worker *Worker) loadRawReceipt(ctx context.Context, id string) error { details, err := worker.nalogruClient.GetTicketById(id) - if err == nalogru.AuthError { - err = worker.refreshSession(ctx) - if err != nil { - log.Printf("failed to refresh session. %v\n", err) - return err - } - details, err = worker.nalogruClient.GetTicketById(id) - } - if err != nil { log.Printf("get ticket by id %s failed: %v", id, err) return err From 5d9a75a8ef7ff690cc29e8f32b5f01c32983959f Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 27 Jun 2023 21:21:36 +0300 Subject: [PATCH 05/32] #70 "context deadline exceeded" while reading big response --- backend/nalogru/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 1dfac9d..6e2dfd1 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -66,7 +66,7 @@ func (nalogruClient *Client) CheckReceiptExist(queryString string) (bool, error) } func createHttpClient() *http.Client { - return &http.Client{Timeout: time.Second * 10} + return &http.Client{Timeout: time.Minute} } // TicketIdRequest is request object to get Ticket id. @@ -226,7 +226,7 @@ func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { } all, err := readBody(res) if err != nil { - log.Printf("failed to read response body. status code %d\n", res.StatusCode) + log.Printf("failed to read response body. status code %d error: %v\n", res.StatusCode, err) return nil, err } err = os.WriteFile("/var/lib/receipts/electro/1.json", all, 0644) From d6add406986b4f6b163c22119d814b9aebb6c44d Mon Sep 17 00:00:00 2001 From: DRypa Date: Fri, 14 Jul 2023 15:03:49 +0300 Subject: [PATCH 06/32] #70 rent devices refactoring for electronic tickets --- backend/device/service.go | 32 ++++++++++++++++++-- backend/main.go | 19 ++++-------- backend/nalogru/client.go | 50 ++++++++++++-------------------- backend/nalogru/client_test.go | 23 ++++++++------- backend/nalogru/device/device.go | 1 + backend/nalogru/devices.go | 1 + backend/receipts/controller.go | 9 ++---- backend/workers/electronic.go | 4 +-- backend/workers/get.go | 41 ++++++++++++++------------ backend/workers/update.go | 4 +-- backend/workers/worker.go | 5 ++-- 11 files changed, 101 insertions(+), 88 deletions(-) diff --git a/backend/device/service.go b/backend/device/service.go index 6d27d29..308fb4a 100644 --- a/backend/device/service.go +++ b/backend/device/service.go @@ -3,6 +3,7 @@ package device import ( "context" "errors" + "log" "receipt_collector/device/repository" "receipt_collector/nalogru/device" ) @@ -50,20 +51,47 @@ func (s *Service) Count(ctx context.Context) (int, error) { return len(s.devices), nil } -// Rent device. +// Rent any device. func (s *Service) Rent(ctx context.Context) (*device.Device, error) { if ctx.Err() != nil { return nil, ctx.Err() } for _, v := range s.devices { if v.IsRent == false { - v.IsRent = true + s.rent(ctx, &v) return &v.Device, nil } } return nil, errors.New("no available devices found") } +// RentDevice rent concrete device. +func (s *Service) RentDevice(ctx context.Context, d *device.Device) error { + if ctx.Err() != nil { + return ctx.Err() + } + for _, v := range s.devices { + if v.Id == d.Id { + if v.IsRent { + return errors.New("device is already used") + } else { + s.rent(ctx, &v) + } + } + } + return errors.New("device not found") +} + +func (s *Service) rent(ctx context.Context, v *ForRent) { + v.IsRent = true + v.Update = func(sessionId string, refreshToken string) error { + v.Device.SessionId = sessionId + v.Device.RefreshToken = refreshToken + log.Printf("Updating device: %+v\n", v.Device) + return s.r.Update(ctx, &v.Device) + } +} + func (s *Service) Update(ctx context.Context, device *device.Device) error { for _, v := range s.devices { if device.Id == v.Id { diff --git a/backend/main.go b/backend/main.go index 5e268ba..5b54720 100644 --- a/backend/main.go +++ b/backend/main.go @@ -56,14 +56,7 @@ func main() { log.Printf("Failed to create device service: %v\n", err) return } - - d, err := deviceService.Rent(ctx) - if err != nil { - log.Println("Failed to rent device") - //return - } - - nalogruClient := nalogru.NewClient(baseAddress, d) + nalogruClient := nalogru.NewClient(baseAddress) receiptRepository := receipts.NewRepository(client) userRepository := users.NewRepository(client) marketRepository := markets.NewRepository(client) @@ -80,8 +73,9 @@ func main() { // } //}() - go worker.GetReceiptStart(ctx, settings) + //go worker.GetReceiptStart(ctx, settings) //go worker.UpdateRawReceiptStart(ctx, settings) + worker.GetElectronicReceiptStart(ctx) generator := login_url.New(openUrl) creds, err := credentials.NewServerTLSFromFile("/usr/share/receipts/ssl/certs/certificate.crt", "/usr/share/receipts/ssl/certs/private.key") @@ -96,7 +90,7 @@ func main() { go internal.Serve(":15000", creds, &accountProcessor, &receiptProcessor) go reports.Serve(":15001", creds, &userRepository, &receiptReportRepository) - server := startServer(nalogruClient, receiptRepository, userRepository, marketRepository, wasteRepository, deviceService) + server := startServer(receiptRepository, userRepository, marketRepository, wasteRepository, deviceService) sigChan := make(chan os.Signal) signal.Notify(sigChan, os.Kill) @@ -120,8 +114,7 @@ func getMongoClient() (*mongo.Client, error) { return mongo_client.New(settings) } -func startServer(nalogruClient *nalogru.Client, - receiptRepository receipts.Repository, +func startServer(receiptRepository receipts.Repository, userRepository users.Repository, marketRepository markets.Repository, wasteRepository waste.Repository, @@ -129,7 +122,7 @@ func startServer(nalogruClient *nalogru.Client, marketsController := markets.New(marketRepository) deviceController := controller.NewController(devices) - receiptsController := receipts.New(receiptRepository, nalogruClient) + receiptsController := receipts.New(receiptRepository) usersController := users.New(userRepository) wasteController := waste.New(wasteRepository) basicAuth := auth.New(userRepository) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 6e2dfd1..546f0ba 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -17,16 +17,14 @@ import ( type Client struct { BaseAddress string - device *device.Device } var InternalError = errors.New("internal failed") // NewClient - creates instance of Client. -func NewClient(baseAddress string, device *device.Device) *Client { +func NewClient(baseAddress string) *Client { return &Client{ BaseAddress: baseAddress, - device: device, } } @@ -35,7 +33,7 @@ const ( ) // CheckReceiptExist send request to check receipt exist in Nalog.ru api. -func (nalogruClient *Client) CheckReceiptExist(queryString string) (bool, error) { +func (nalogruClient *Client) CheckReceiptExist(queryString string, device *device.Device) (bool, error) { client := createHttpClient() url, err := buildCheckReceiptUrl(nalogruClient.BaseAddress, queryString) if err != nil { @@ -49,7 +47,7 @@ func (nalogruClient *Client) CheckReceiptExist(queryString string) (bool, error) log.Printf("Could't not create request for %s", url) return false, err } - addHeaders(request, nalogruClient.device.Id.Hex()) + addHeaders(request, device.Id.Hex()) resp, err := client.Do(request) if err != nil { @@ -83,7 +81,7 @@ type TicketIdResponse struct { } // GetTicketId - send ticket id request to nalog.ru API. -func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { +func (nalogruClient *Client) GetTicketId(queryString string, device *device.Device) (string, error) { payload := TicketIdRequest{Qr: queryString} req, err := json.Marshal(payload) @@ -93,7 +91,7 @@ func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { reader := bytes.NewReader(req) url := nalogruClient.BaseAddress + "/v2/ticket" request, err := http.NewRequest(http.MethodPost, url, reader) - res, err := nalogruClient.sendAuthenticatedRequest(request) + res, err := nalogruClient.sendAuthenticatedRequest(request, device) if err != nil { log.Printf("Can't POST %s\n", url) @@ -139,15 +137,6 @@ func (nalogruClient *Client) GetTicketId(queryString string) (string, error) { return ticketIdResp.Id, nil } -func (nalogruClient *Client) GetDevice() device.Device { - return device.Device{ - ClientSecret: nalogruClient.device.ClientSecret, - SessionId: nalogruClient.device.SessionId, - RefreshToken: nalogruClient.device.RefreshToken, - Id: nalogruClient.device.Id, - } -} - func readBody(res *http.Response) ([]byte, error) { defer dispose.Dispose(res.Body.Close, "failed to close response body.") var bodyReader io.ReadCloser @@ -168,11 +157,11 @@ func readBody(res *http.Response) ([]byte, error) { } // GetTicketById get ticket by id from nalog.ru api. -func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { +func (nalogruClient *Client) GetTicketById(id string, device *device.Device) (*TicketDetails, error) { url := nalogruClient.BaseAddress + "/v2/tickets/" + id request, err := http.NewRequest(http.MethodGet, url, nil) - res, err := nalogruClient.sendAuthenticatedRequest(request) + res, err := nalogruClient.sendAuthenticatedRequest(request, device) if err != nil { log.Printf("Can't GET %s\n", url) @@ -207,12 +196,12 @@ func (nalogruClient *Client) GetTicketById(id string) (*TicketDetails, error) { } // GetElectronicTickets request all electronic receipts added by email or phone from nalog.ru api. -func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { +func (nalogruClient *Client) GetElectronicTickets(device *device.Device) ([]*TicketDetails, error) { url := nalogruClient.BaseAddress + "/v2/tickets-with-electro" request, err := http.NewRequest(http.MethodGet, url, nil) - res, err := nalogruClient.sendAuthenticatedRequest(request) + res, err := nalogruClient.sendAuthenticatedRequest(request, device) if err != nil { log.Printf("Can't GET %s\n", url) @@ -242,9 +231,9 @@ func (nalogruClient *Client) GetElectronicTickets() ([]*TicketDetails, error) { return tickets, nil } -func (nalogruClient *Client) sendAuthenticatedRequest(r *http.Request) (*http.Response, error) { - addHeaders(r, nalogruClient.device.Id.Hex()) - addAuth(r, nalogruClient.device.SessionId) +func (nalogruClient *Client) sendAuthenticatedRequest(r *http.Request, device *device.Device) (*http.Response, error) { + addHeaders(r, device.Id.Hex()) + addAuth(r, device.SessionId) client := createHttpClient() res, err := sendRequest(r, client) @@ -253,7 +242,7 @@ func (nalogruClient *Client) sendAuthenticatedRequest(r *http.Request) (*http.Re } if res.StatusCode == http.StatusUnauthorized { - err = nalogruClient.RefreshSession() + err = nalogruClient.RefreshSession(device) if err != nil { log.Printf("failed to refresh session. %v\n", err) return nil, err @@ -273,12 +262,12 @@ type RefreshResponse struct { RefreshToken string `json:"refresh_token"` } -func (nalogruClient *Client) RefreshSession() error { +func (nalogruClient *Client) RefreshSession(device *device.Device) error { client := createHttpClient() payload := RefreshRequest{ - ClientSecret: nalogruClient.device.ClientSecret, - RefreshToken: nalogruClient.device.RefreshToken, + ClientSecret: device.ClientSecret, + RefreshToken: device.RefreshToken, } resp, err := json.Marshal(payload) @@ -289,7 +278,7 @@ func (nalogruClient *Client) RefreshSession() error { url := nalogruClient.BaseAddress + "/v2/mobile/users/refresh" request, err := http.NewRequest(http.MethodPost, url, reader) - addHeaders(request, nalogruClient.device.Id.Hex()) + addHeaders(request, device.Id.Hex()) res, err := sendRequest(request, client) if err != nil { @@ -310,9 +299,8 @@ func (nalogruClient *Client) RefreshSession() error { return err } log.Printf("%+v\n", response) - nalogruClient.device.RefreshToken = response.RefreshToken - nalogruClient.device.SessionId = response.SessionId - return nil + + return device.Update(response.SessionId, response.RefreshToken) } func sendRequest(request *http.Request, client *http.Client) (*http.Response, error) { diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index 47c22a6..0a206e8 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -22,10 +22,10 @@ func IgnoreTestClient_GetTicketId(t *testing.T) { t.Fail() return } - client := NewClient(baseAddress, d) + client := NewClient(baseAddress) queryString := "INSERT BARCODE TEST HERE" - id, err := client.GetTicketId(queryString) + id, err := client.GetTicketId(queryString, d) if err != nil { log.Println(err) @@ -48,9 +48,9 @@ func IgnoreTestClient_GetElectronicTickets(t *testing.T) { t.Fail() return } - client := NewClient(baseAddress, d) + client := NewClient(baseAddress) - tickets, err := client.GetElectronicTickets(nil) + tickets, err := client.GetElectronicTickets(d) if err != nil { log.Println(err) t.Fail() @@ -80,6 +80,9 @@ func createDevice() (*device.Device, error) { Id: id, ClientSecret: secret, RefreshToken: refreshToken, + Update: func(string, string) error { + return nil + }, } return d, err } @@ -91,10 +94,10 @@ func IgnoreTestClient_GetTicketById(t *testing.T) { t.Fail() return } - client := NewClient(baseAddress, d) + client := NewClient(baseAddress) ticketId := "INSERT TICKET ID HERE" - details, err := client.GetTicketById(ticketId) + details, err := client.GetTicketById(ticketId, d) if err != nil { log.Println(err) @@ -114,9 +117,9 @@ func IgnoreTestClient_RefreshSession(t *testing.T) { t.Fail() return } - client := NewClient(baseAddress, d) + client := NewClient(baseAddress) - err = client.RefreshSession() + err = client.RefreshSession(d) if err != nil { log.Println(err) @@ -144,8 +147,8 @@ func IgnoreTestClient_CheckReceiptExist(t *testing.T) { t.Fail() return } - client := NewClient(baseAddress, d) - exist, err := client.CheckReceiptExist(queryString) + client := NewClient(baseAddress) + exist, err := client.CheckReceiptExist(queryString, d) if err != nil { log.Println(err) diff --git a/backend/nalogru/device/device.go b/backend/nalogru/device/device.go index 14ea1d7..94971fe 100644 --- a/backend/nalogru/device/device.go +++ b/backend/nalogru/device/device.go @@ -7,4 +7,5 @@ type Device struct { SessionId string `bson:"session_id"` RefreshToken string `bson:"refresh_token"` Id primitive.ObjectID `bson:"_id,omitempty"` + Update func(string, string) error } diff --git a/backend/nalogru/devices.go b/backend/nalogru/devices.go index 2091dd3..b82ea7b 100644 --- a/backend/nalogru/devices.go +++ b/backend/nalogru/devices.go @@ -9,6 +9,7 @@ type Devices interface { Add(ctx context.Context, d device.Device) error Count(ctx context.Context) (int, error) Rent(ctx context.Context) (*device.Device, error) + RentDevice(ctx context.Context, d *device.Device) error Update(ctx context.Context, device *device.Device) error Free(ctx context.Context, device *device.Device) error All(ctx context.Context) []*device.Device diff --git a/backend/receipts/controller.go b/backend/receipts/controller.go index 969e38e..d1516df 100644 --- a/backend/receipts/controller.go +++ b/backend/receipts/controller.go @@ -10,21 +10,18 @@ import ( "net/http" "receipt_collector/auth" "receipt_collector/dispose" - "receipt_collector/nalogru" "receipt_collector/nalogru/qr" "strings" ) type Controller struct { - repository Repository - nalogruClient *nalogru.Client + repository Repository } // New creates controller. -func New(repository Repository, nalogruClient *nalogru.Client) Controller { +func New(repository Repository) Controller { return Controller{ - repository: repository, - nalogruClient: nalogruClient, + repository: repository, } } func onError(writer http.ResponseWriter, err error) { diff --git a/backend/workers/electronic.go b/backend/workers/electronic.go index e94ebbd..e66b938 100644 --- a/backend/workers/electronic.go +++ b/backend/workers/electronic.go @@ -16,12 +16,12 @@ func (worker *Worker) GetElectronicReceiptStart(ctx context.Context) { } s := gocron.NewScheduler(time.Local) - _, err := s.Every(1).Days().Do(worker.getElectronic) + _, err := s.Every(1).Day().At("01:00").Do(worker.getElectronic, ctx) if err != nil { log.Printf("failed to create job %v\n", err) } - s.StartBlocking() + s.StartAsync() } func (worker *Worker) getElectronic(ctx context.Context) { for _, d := range worker.devices.All(ctx) { diff --git a/backend/workers/get.go b/backend/workers/get.go index edf609d..3edea85 100644 --- a/backend/workers/get.go +++ b/backend/workers/get.go @@ -4,6 +4,7 @@ import ( "context" "log" "receipt_collector/nalogru" + "receipt_collector/nalogru/device" "receipt_collector/nalogru/qr" "receipt_collector/receipts" "time" @@ -67,7 +68,9 @@ func (worker *Worker) getReceipt(ctx context.Context) error { return err } normalizedQr := query.ToString() - id, err := worker.nalogruClient.GetTicketId(normalizedQr) + device, err := worker.devices.Rent(ctx) + defer worker.devices.Free(ctx, device) + id, err := worker.nalogruClient.GetTicketId(normalizedQr, device) if err != nil && err.Error() == nalogru.DailyLimitReached { return err @@ -85,11 +88,11 @@ func (worker *Worker) getReceipt(ctx context.Context) error { return err } - return worker.loadRawReceipt(ctx, id) + return worker.loadRawReceipt(ctx, id, device) } -func (worker *Worker) loadRawReceipt(ctx context.Context, id string) error { - details, err := worker.nalogruClient.GetTicketById(id) +func (worker *Worker) loadRawReceipt(ctx context.Context, id string, device *device.Device) error { + details, err := worker.nalogruClient.GetTicketById(id, device) if err != nil { log.Printf("get ticket by id %s failed: %v", id, err) @@ -110,18 +113,18 @@ func getTicketExistence(details *nalogru.TicketDetails) string { return ticket } -func (worker *Worker) refreshSession(ctx context.Context) error { - err := worker.nalogruClient.RefreshSession() - if err != nil { - log.Printf("failed to refresh session: %v", err) - return err - } - device := worker.nalogruClient.GetDevice() - err = worker.devices.Update(ctx, &device) - if err != nil { - log.Printf("failed to update device: %v", err) - return err - } - log.Printf("device %s updated\n", device.Id.Hex()) - return nil -} +//func (worker *Worker) refreshSession(ctx context.Context) error { +// err := worker.nalogruClient.RefreshSession() +// if err != nil { +// log.Printf("failed to refresh session: %v", err) +// return err +// } +// device := worker.nalogruClient.GetDevice() +// err = worker.devices.Update(ctx, &device) +// if err != nil { +// log.Printf("failed to update device: %v", err) +// return err +// } +// log.Printf("device %s updated\n", device.Id.Hex()) +// return nil +//} diff --git a/backend/workers/update.go b/backend/workers/update.go index 1cf5144..98dc179 100644 --- a/backend/workers/update.go +++ b/backend/workers/update.go @@ -6,7 +6,7 @@ import ( "time" ) -//UpdateRawReceiptStart fetch tickets with wrong status. +// UpdateRawReceiptStart fetch tickets with wrong status. func (worker *Worker) UpdateRawReceiptStart(ctx context.Context, settings Settings) { ticker := time.NewTicker(settings.Interval) @@ -33,7 +33,7 @@ func (worker *Worker) UpdateRawReceiptStart(ctx context.Context, settings Settin if receipt == nil { break } - err = worker.loadRawReceipt(ctx, receipt.Id) + err = worker.loadRawReceipt(ctx, receipt.Id, nil) if err != nil { log.Printf("Failed to reload raw ticket. %v\n", err) } diff --git a/backend/workers/worker.go b/backend/workers/worker.go index 551d61a..371f382 100644 --- a/backend/workers/worker.go +++ b/backend/workers/worker.go @@ -6,7 +6,7 @@ import ( "receipt_collector/waste" ) -//Worker for any background job. +// Worker for any background job. type Worker struct { nalogruClient *nalogru.Client repository receipts.Repository @@ -14,10 +14,9 @@ type Worker struct { devices nalogru.Devices } -//New constructs Worker. +// New constructs Worker. func New(nalogruClient *nalogru.Client, repository receipts.Repository, wasteRepository *waste.Repository, devices nalogru.Devices) Worker { return Worker{ - //TODO: nalogruClient is not required here nalogruClient: nalogruClient, repository: repository, wasteRepository: wasteRepository, From 9e62a9ab7ac43b1b94e90d8e9146267bf9c7b2e3 Mon Sep 17 00:00:00 2001 From: DRypa Date: Fri, 14 Jul 2023 15:09:45 +0300 Subject: [PATCH 07/32] #70 warnings fix --- backend/nalogru/client.go | 8 ++++---- backend/nalogru/client_test.go | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 546f0ba..fa85eb2 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -54,7 +54,7 @@ func (nalogruClient *Client) CheckReceiptExist(queryString string, device *devic log.Printf("Could't check receipt %s", url) return false, err } - defer resp.Body.Close() + defer dispose.Dispose(resp.Body.Close, "Can't close HTTP response body") if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNoContent { log.Println("Check passed") return true, nil @@ -97,7 +97,7 @@ func (nalogruClient *Client) GetTicketId(queryString string, device *device.Devi log.Printf("Can't POST %s\n", url) return "", err } - defer res.Body.Close() + defer dispose.Dispose(res.Body.Close, "Can't close HTTP response body") body, err := readBody(res) if err != nil { @@ -167,7 +167,7 @@ func (nalogruClient *Client) GetTicketById(id string, device *device.Device) (*T log.Printf("Can't GET %s\n", url) return nil, err } - defer res.Body.Close() + defer dispose.Dispose(res.Body.Close, "Can't close HTTP response body") all, err := readBody(res) if err != nil { @@ -285,7 +285,7 @@ func (nalogruClient *Client) RefreshSession(device *device.Device) error { log.Printf("Can't POST %s\n", url) return err } - defer res.Body.Close() + defer dispose.Dispose(res.Body.Close, "Can't close HTTP response body") if res.StatusCode != http.StatusOK { log.Printf("Refresh session error: %d\n", res.StatusCode) diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index 0a206e8..55ea3a1 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -5,6 +5,7 @@ import ( "log" "net/http" "net/http/httptest" + "receipt_collector/dispose" "receipt_collector/nalogru/device" "testing" ) @@ -206,7 +207,7 @@ func createServer(t *testing.T) *httptest.Server { func callServerWithCloseBody(client http.Client, svr *httptest.Server) error { m, err := client.Get(svr.URL) if m != nil { - defer m.Body.Close() + defer dispose.Dispose(m.Body.Close, "Can't close HTTP response body") } return err From f1e6d450109d1c5f24281ad205a6e7f15e4f3d75 Mon Sep 17 00:00:00 2001 From: DRypa Date: Fri, 14 Jul 2023 15:55:06 +0300 Subject: [PATCH 08/32] #70 insert receipt request from electronic --- backend/workers/electronic.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/workers/electronic.go b/backend/workers/electronic.go index e66b938..63d016c 100644 --- a/backend/workers/electronic.go +++ b/backend/workers/electronic.go @@ -3,9 +3,11 @@ package workers import ( "context" "github.com/go-co-op/gocron" + "go.mongodb.org/mongo-driver/bson/primitive" "log" "receipt_collector/nalogru" "receipt_collector/nalogru/qr" + "receipt_collector/receipts" "time" ) @@ -50,7 +52,16 @@ func (worker *Worker) insertTicketsIfNeeded(ctx context.Context, tickets []*nalo return err } if receipt == nil { - //TODO: add ticket & raw ticket + receiptRequest := receipts.UsersReceipt{ + Owner: primitive.NilObjectID, //TODO: owner needed + QueryString: query.ToString(), + //Without ticketId would process by get-worker + } + err := worker.repository.Insert(ctx, receiptRequest) + if err != nil { + log.Printf("Failed to insert receipt request for %s", query.ToString()) //TODO: add owner to output + return err + } } } return nil From a9eea6818d47e44cebd2b648df44d10da16f0cb8 Mon Sep 17 00:00:00 2001 From: DRypa Date: Fri, 14 Jul 2023 16:00:27 +0300 Subject: [PATCH 09/32] #70 method comment more understandable --- backend/receipts/repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/receipts/repository.go b/backend/receipts/repository.go index b44df6d..5f10512 100644 --- a/backend/receipts/repository.go +++ b/backend/receipts/repository.go @@ -122,7 +122,7 @@ func (repository *Repository) GetByQueryString(ctx context.Context, userId strin } -// GetAllOwnersByQueryString find user receipt by QR code query string. +// GetAllOwnersByQueryString find user receipt by QR code query string without filtering by owner. func (repository *Repository) GetAllOwnersByQueryString(ctx context.Context, queryString string) (*UsersReceipt, error) { collection := repository.getCollection() From 929c328bc89bba3899a8041a65bd6d80aae679c7 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 16 Jul 2023 16:46:52 +0300 Subject: [PATCH 10/32] #70 bot commands refactoring it was required to change Registration command. need add phone number. --- bot/bot.go | 46 ++++++++++++++---------------------- bot/commands/add_receipt.go | 41 ++++++++++++++++++++++++++++++++ bot/commands/common.go | 6 ++--- bot/commands/empty.go | 15 ++++++++++++ bot/commands/get_report.go | 45 +++++++++++++++++++++++++++++++++++ bot/commands/receipt.go | 43 --------------------------------- bot/commands/registrar.go | 30 +++++++++++++++++++++++ bot/commands/registration.go | 26 -------------------- bot/commands/registrer.go | 33 ++++++++++++++++++++++++++ bot/commands/start.go | 15 ++++++++++++ bot/commands/wrong.go | 15 ++++++++++++ bot/main.go | 33 +++++++++++++++++++++++++- 12 files changed, 247 insertions(+), 101 deletions(-) create mode 100644 bot/commands/add_receipt.go create mode 100644 bot/commands/empty.go create mode 100644 bot/commands/get_report.go create mode 100644 bot/commands/registrar.go delete mode 100644 bot/commands/registration.go create mode 100644 bot/commands/registrer.go create mode 100644 bot/commands/start.go create mode 100644 bot/commands/wrong.go diff --git a/bot/bot.go b/bot/bot.go index 655828c..46f8e11 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -6,7 +6,6 @@ import ( api "github.com/drypa/ReceiptCollector/api/inside" "github.com/drypa/ReceiptCollector/bot/backend" "github.com/drypa/ReceiptCollector/bot/backend/report" - "github.com/drypa/ReceiptCollector/bot/backend/user" "github.com/drypa/ReceiptCollector/bot/commands" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" "log" @@ -31,11 +30,11 @@ func getEnvVar(varName string) string { return value } -func start(options Options, grpcClient *backend.GrpcClient, reportsClient *report.Client) error { - provider, err := user.New(grpcClient) - if err != nil { - return err - } +func start(options Options, grpcClient *backend.GrpcClient, reportsClient *report.Client, registrar *commands.Registrar) error { + //provider, err := user.New(grpcClient) + //if err != nil { + // return err + //} bot, err := create(options) if err != nil { log.Printf("Bot create error: %v\n", err) @@ -51,7 +50,7 @@ func start(options Options, grpcClient *backend.GrpcClient, reportsClient *repor return err } go processNotifications(bot, reportsClient.Notifications) - processUpdates(updatesChan, bot, grpcClient, provider) + processUpdates(updatesChan, bot, registrar) return nil } @@ -92,34 +91,25 @@ func create(options Options) (*tgbotapi.BotAPI, error) { func processUpdates(updatesChan tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI, - grpcClient *backend.GrpcClient, - provider user.Provider) { + registrar *commands.Registrar) { for update := range updatesChan { log.Printf("%v\n", update) if update.Message == nil { continue } - processMessage(update, bot, provider, grpcClient) + + processMessage(update, bot, registrar) } } -func processMessage(update tgbotapi.Update, bot *tgbotapi.BotAPI, provider user.Provider, grpcClient *backend.GrpcClient) { - var err error - switch update.Message.Text { - case "": - err = commands.Empty(update, bot, err) - case "/start": - err = commands.Start(update, bot, err) - case "/register": - err = commands.Register(update, bot, provider) - case "/login": - err = commands.Login(update, bot, grpcClient) - case "/get": - err = commands.GetReceiptReport(update, bot, provider, grpcClient) - default: - err = commands.AddReceipt(update, bot, provider, grpcClient) - } - if err != nil { - log.Printf("Error while sending response to user %d", update.Message.From.ID) +func processMessage(update tgbotapi.Update, bot *tgbotapi.BotAPI, registrar *commands.Registrar) { + c := registrar.Get(update.Message.Text) + if c != nil { + err := (*c).Execute(update, bot) + if err != nil { + log.Printf("Error while process request '%s' from %d. %v", update.Message.Text, update.Message.From.ID, err) + } + } + } diff --git a/bot/commands/add_receipt.go b/bot/commands/add_receipt.go new file mode 100644 index 0000000..69b2e6d --- /dev/null +++ b/bot/commands/add_receipt.go @@ -0,0 +1,41 @@ +package commands + +import ( + "github.com/drypa/ReceiptCollector/bot/backend" + "github.com/drypa/ReceiptCollector/bot/backend/user" + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" + "strings" +) + +type AddReceiptCommand struct { + provider *user.Provider + grpcClient *backend.GrpcClient +} + +func NewAddReceiptCommand(provider *user.Provider, grpcClient *backend.GrpcClient) *AddReceiptCommand { + return &AddReceiptCommand{provider: provider, grpcClient: grpcClient} +} + +func (a AddReceiptCommand) Accepted(message string) bool { + return strings.Contains(message, "t=") && + strings.Contains(message, "s=") && + strings.Contains(message, "fn=") && + strings.Contains(message, "fp=") && + strings.Contains(message, "i=") && + strings.Contains(message, "n=") +} + +func (a AddReceiptCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + id, err := a.provider.GetUserId(update.Message.From.ID) + if err != nil { + return err + } + err = tryAddReceipt(id, update.Message.Text, a.grpcClient) + + responseText := "Added" + if err != nil { + responseText = err.Error() + } + _, err = replyToMessage(update.Message.Chat.ID, bot, responseText, update.Message.MessageID) + return err +} diff --git a/bot/commands/common.go b/bot/commands/common.go index fec45f3..85eecfb 100644 --- a/bot/commands/common.go +++ b/bot/commands/common.go @@ -6,9 +6,9 @@ import ( "time" ) -func Empty(update tgbotapi.Update, bot *tgbotapi.BotAPI, err error) error { - _, err = sendTextMessage(update.Message.Chat.ID, bot, "Please enter a command.") - return err +type Command interface { + Accepted(message string) bool + Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error } func sendTextMessage(chatId int64, bot *tgbotapi.BotAPI, responseText string) (tgbotapi.Message, error) { diff --git a/bot/commands/empty.go b/bot/commands/empty.go new file mode 100644 index 0000000..5f25c68 --- /dev/null +++ b/bot/commands/empty.go @@ -0,0 +1,15 @@ +package commands + +import "github.com/go-telegram-bot-api/telegram-bot-api" + +type EmptyCommand struct { +} + +func (e EmptyCommand) Accepted(message string) bool { + return message == "" +} + +func (e EmptyCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + _, err := sendTextMessage(update.Message.Chat.ID, bot, "Please enter a command.") + return err +} diff --git a/bot/commands/get_report.go b/bot/commands/get_report.go new file mode 100644 index 0000000..461daea --- /dev/null +++ b/bot/commands/get_report.go @@ -0,0 +1,45 @@ +package commands + +import ( + "github.com/drypa/ReceiptCollector/bot/backend" + "github.com/drypa/ReceiptCollector/bot/backend/user" + "github.com/go-telegram-bot-api/telegram-bot-api" +) + +type GetReceiptReportCommand struct { + provider *user.Provider + grpcClient *backend.GrpcClient +} + +func NewGetReceiptReportCommand(provider *user.Provider, grpcClient *backend.GrpcClient) *GetReceiptReportCommand { + return &GetReceiptReportCommand{provider: provider, grpcClient: grpcClient} +} + +func (g GetReceiptReportCommand) Accepted(message string) bool { + return message == "/get" +} + +func (g GetReceiptReportCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + id, err := g.provider.GetUserId(update.Message.From.ID) + if err != nil { + return err + } + qr, err := getQr(update) + if err != nil { + return err + } + + report, fileName, err := g.grpcClient.GetReceiptReport(getContext(), id, qr) + if err != nil { + return err + } + file := tgbotapi.FileBytes{ + Name: fileName, + Bytes: report, + } + upload := tgbotapi.NewDocumentUpload(update.Message.Chat.ID, file) + upload.ReplyToMessageID = update.Message.MessageID + _, err = bot.Send(upload) + + return err +} diff --git a/bot/commands/receipt.go b/bot/commands/receipt.go index c13a250..eab242b 100644 --- a/bot/commands/receipt.go +++ b/bot/commands/receipt.go @@ -3,52 +3,9 @@ package commands import ( "errors" "github.com/drypa/ReceiptCollector/bot/backend" - "github.com/drypa/ReceiptCollector/bot/backend/user" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" ) -// AddReceipt executes add new receipt by QR code command. -func AddReceipt(update tgbotapi.Update, bot *tgbotapi.BotAPI, provider user.Provider, grpcClient *backend.GrpcClient) error { - id, err := provider.GetUserId(update.Message.From.ID) - if err != nil { - return err - } - err = tryAddReceipt(id, update.Message.Text, grpcClient) - - responseText := "Added" - if err != nil { - responseText = err.Error() - } - _, err = replyToMessage(update.Message.Chat.ID, bot, responseText, update.Message.MessageID) - return err -} - -// GetReceiptReport is used to get receipt file by QR code. -func GetReceiptReport(update tgbotapi.Update, bot *tgbotapi.BotAPI, provider user.Provider, grpcClient *backend.GrpcClient) error { - id, err := provider.GetUserId(update.Message.From.ID) - if err != nil { - return err - } - qr, err := getQr(update) - if err != nil { - return err - } - - report, fileName, err := grpcClient.GetReceiptReport(getContext(), id, qr) - if err != nil { - return err - } - file := tgbotapi.FileBytes{ - Name: fileName, - Bytes: report, - } - upload := tgbotapi.NewDocumentUpload(update.Message.Chat.ID, file) - upload.ReplyToMessageID = update.Message.MessageID - _, err = bot.Send(upload) - - return err -} - func getQr(update tgbotapi.Update) (string, error) { if update.Message.ReplyToMessage != nil { return update.Message.ReplyToMessage.Text, nil diff --git a/bot/commands/registrar.go b/bot/commands/registrar.go new file mode 100644 index 0000000..24afe61 --- /dev/null +++ b/bot/commands/registrar.go @@ -0,0 +1,30 @@ +package commands + +type Registrar struct { + commands []Command + defaultCommand Command +} + +func NewRegistrar() *Registrar { + registrar := Registrar{ + commands: []Command{}, + } + return ®istrar +} + +func (r *Registrar) Register(c Command) { + r.commands = append(r.commands, c) +} + +func (r *Registrar) RegisterDefault(c Command) { + r.defaultCommand = c +} + +func (r *Registrar) Get(message string) *Command { + for _, c := range r.commands { + if c.Accepted(message) { + return &c + } + } + return &r.defaultCommand +} diff --git a/bot/commands/registration.go b/bot/commands/registration.go deleted file mode 100644 index b168358..0000000 --- a/bot/commands/registration.go +++ /dev/null @@ -1,26 +0,0 @@ -package commands - -import ( - "github.com/drypa/ReceiptCollector/bot/backend/user" - tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" -) - -func Start(update tgbotapi.Update, bot *tgbotapi.BotAPI, err error) error { - _, err = sendTextMessage(update.Message.Chat.ID, bot, "I'm a bot to collect Your purchase tickets.") - return err -} - -func Register(update tgbotapi.Update, bot *tgbotapi.BotAPI, provider user.Provider) error { - err := register(update.Message.From.ID, provider) - responseText := "You are registered." - if err != nil { - responseText = err.Error() - } - _, err = sendTextMessage(update.Message.Chat.ID, bot, responseText) - return err -} - -func register(userId int, client user.Provider) error { - _, err := client.GetUserId(userId) - return err -} diff --git a/bot/commands/registrer.go b/bot/commands/registrer.go new file mode 100644 index 0000000..9d0795d --- /dev/null +++ b/bot/commands/registrer.go @@ -0,0 +1,33 @@ +package commands + +import ( + "github.com/drypa/ReceiptCollector/bot/backend/user" + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" +) + +type RegisterCommand struct { + provider *user.Provider +} + +func NewRegisterCommand(provider *user.Provider) *RegisterCommand { + return &RegisterCommand{provider: provider} +} + +func (r RegisterCommand) Accepted(message string) bool { + return message == "/register" +} + +func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + err := register(update.Message.From.ID, r.provider) + responseText := "You are registered." + if err != nil { + responseText = err.Error() + } + _, err = sendTextMessage(update.Message.Chat.ID, bot, responseText) + return err +} + +func register(userId int, client *user.Provider) error { + _, err := client.GetUserId(userId) + return err +} diff --git a/bot/commands/start.go b/bot/commands/start.go new file mode 100644 index 0000000..b27a308 --- /dev/null +++ b/bot/commands/start.go @@ -0,0 +1,15 @@ +package commands + +import "github.com/go-telegram-bot-api/telegram-bot-api" + +type StartCommand struct { +} + +func (s StartCommand) Accepted(message string) bool { + return message == "/start" +} + +func (s StartCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + _, err := sendTextMessage(update.Message.Chat.ID, bot, "I'm a bot to collect Your purchase tickets.") + return err +} diff --git a/bot/commands/wrong.go b/bot/commands/wrong.go new file mode 100644 index 0000000..9c48f8f --- /dev/null +++ b/bot/commands/wrong.go @@ -0,0 +1,15 @@ +package commands + +import "github.com/go-telegram-bot-api/telegram-bot-api" + +type WrongCommand struct { +} + +func (w WrongCommand) Accepted(message string) bool { + return true +} + +func (w WrongCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + _, err := sendTextMessage(update.Message.Chat.ID, bot, "Command not recognized.") + return err +} diff --git a/bot/main.go b/bot/main.go index 791f696..a65cf81 100644 --- a/bot/main.go +++ b/bot/main.go @@ -3,6 +3,8 @@ package main import ( "github.com/drypa/ReceiptCollector/bot/backend" "github.com/drypa/ReceiptCollector/bot/backend/report" + "github.com/drypa/ReceiptCollector/bot/backend/user" + "github.com/drypa/ReceiptCollector/bot/commands" "google.golang.org/grpc/credentials" "log" "os" @@ -19,8 +21,37 @@ func main() { } grpcClient := backend.NewGrpcClient(backendGrpcAddress, creds) reportsClient := report.New(reportsGrpcAddress, creds) - err = start(options, grpcClient, reportsClient) + provider, err := user.New(grpcClient) if err != nil { log.Fatal(err) } + registrar := createCommandsRegistrar(grpcClient, &provider) + err = start(options, grpcClient, reportsClient, registrar) + if err != nil { + log.Fatal(err) + } +} + +func createCommandsRegistrar(grpcClient *backend.GrpcClient, users *user.Provider) *commands.Registrar { + registrar := commands.Registrar{} + + empty := commands.EmptyCommand{} + registrar.Register(empty) + + start := commands.StartCommand{} + registrar.Register(start) + + register := commands.NewRegisterCommand(users) + registrar.Register(register) + + getReceiptReport := commands.NewGetReceiptReportCommand(users, grpcClient) + registrar.Register(getReceiptReport) + + addReceiptCommand := commands.NewAddReceiptCommand(users, grpcClient) + registrar.Register(addReceiptCommand) + + wrongCommand := commands.WrongCommand{} + registrar.RegisterDefault(wrongCommand) + + return ®istrar } From 13bd9234449ed3d05031fe4a528d501ec7b27b7e Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 18 Jul 2023 12:51:41 +0300 Subject: [PATCH 11/32] #70 unused meaningless log --- bot/bot.go | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/bot.go b/bot/bot.go index 46f8e11..eac2ffa 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -93,7 +93,6 @@ func processUpdates(updatesChan tgbotapi.UpdatesChannel, bot *tgbotapi.BotAPI, registrar *commands.Registrar) { for update := range updatesChan { - log.Printf("%v\n", update) if update.Message == nil { continue } From ba1e317e16c0907c16dd31643b70c2e7662b586f Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 18 Jul 2023 15:34:35 +0300 Subject: [PATCH 12/32] #70 new register command syntax --- bot/bot.go | 6 ++- bot/commands/{registrer.go => register.go} | 5 ++- bot/commands/register_test.go | 45 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) rename bot/commands/{registrer.go => register.go} (89%) create mode 100644 bot/commands/register_test.go diff --git a/bot/bot.go b/bot/bot.go index eac2ffa..031f950 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -12,6 +12,7 @@ import ( "net/http" "net/url" "os" + "strings" ) func validateEmpty(value string, emptyErrorMessage string) error { @@ -102,11 +103,12 @@ func processUpdates(updatesChan tgbotapi.UpdatesChannel, } func processMessage(update tgbotapi.Update, bot *tgbotapi.BotAPI, registrar *commands.Registrar) { - c := registrar.Get(update.Message.Text) + text := strings.TrimSpace(update.Message.Text) + c := registrar.Get(text) if c != nil { err := (*c).Execute(update, bot) if err != nil { - log.Printf("Error while process request '%s' from %d. %v", update.Message.Text, update.Message.From.ID, err) + log.Printf("Error while process request '%s' from %d. %v", text, update.Message.From.ID, err) } } diff --git a/bot/commands/registrer.go b/bot/commands/register.go similarity index 89% rename from bot/commands/registrer.go rename to bot/commands/register.go index 9d0795d..7ff22eb 100644 --- a/bot/commands/registrer.go +++ b/bot/commands/register.go @@ -3,6 +3,7 @@ package commands import ( "github.com/drypa/ReceiptCollector/bot/backend/user" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" + "regexp" ) type RegisterCommand struct { @@ -14,7 +15,9 @@ func NewRegisterCommand(provider *user.Provider) *RegisterCommand { } func (r RegisterCommand) Accepted(message string) bool { - return message == "/register" + re := regexp.MustCompile(`^\/register\s+\+(\d{10})$`) + + return re.MatchString(message) } func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { diff --git a/bot/commands/register_test.go b/bot/commands/register_test.go new file mode 100644 index 0000000..c7dfca4 --- /dev/null +++ b/bot/commands/register_test.go @@ -0,0 +1,45 @@ +package commands + +import "testing" + +func TestAccepted_RegisterCommand_Success(t *testing.T) { + command := NewRegisterCommand(nil) + + variants := make([]string, 0) + variants = append(variants, "/register +1234567890") + variants = append(variants, "/register +0987654321") + variants = append(variants, "/register +0987654321") + variants = append(variants, "/register +0987654321") + + for _, s := range variants { + res := command.Accepted(s) + if !res { + t.Fatalf("message '%s' is not acceptable for RegisterCommand", s) + } + } + +} + +func TestAccepted_RegisterCommand_Failed(t *testing.T) { + command := NewRegisterCommand(nil) + + variants := make([]string, 0) + variants = append(variants, "") + variants = append(variants, " ") + variants = append(variants, " ") + variants = append(variants, "/register") + variants = append(variants, "/register+1234567890") + variants = append(variants, " /register +1234567890 ") + variants = append(variants, "/register +") + variants = append(variants, "/register +1") + variants = append(variants, "/register +12345678901") + variants = append(variants, "/register +abcdefghjk") + + for _, s := range variants { + res := command.Accepted(s) + if res { + t.Fatalf("message '%s' is accepted for RegisterCommand", s) + } + } + +} From c2432544102c7a68825ee7619af6d69605114078 Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 18 Jul 2023 15:35:36 +0300 Subject: [PATCH 13/32] #70 unused parameter removed --- backend/nalogru/client_test.go | 6 +++--- bot/bot.go | 7 +------ bot/commands/register.go | 2 +- bot/main.go | 2 +- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index 55ea3a1..2c02761 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -11,10 +11,10 @@ import ( ) var baseAddress = "https://irkkt-mobile.nalog.ru:8888" -var sessionId = "INSERT SESSION ID HERE" +var sessionId = "606c4bb41d8f48ff57bfd021:6ca239ba-0885-4cc9-a7d9-be801253ac7d" var deviceId = primitive.NewObjectID().Hex() -var secret = "INSERT SECRET HERE" -var refreshToken = "INSERT REFRESH TOKEN HERE" +var secret = "mnALjKobrqT/sC9um4wXlamXnOo=" +var refreshToken = "c55bdf44-acf6-46b3-be9a-8422bda7032d" func IgnoreTestClient_GetTicketId(t *testing.T) { d, err := createDevice() diff --git a/bot/bot.go b/bot/bot.go index 031f950..3ca7a32 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" api "github.com/drypa/ReceiptCollector/api/inside" - "github.com/drypa/ReceiptCollector/bot/backend" "github.com/drypa/ReceiptCollector/bot/backend/report" "github.com/drypa/ReceiptCollector/bot/commands" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" @@ -31,11 +30,7 @@ func getEnvVar(varName string) string { return value } -func start(options Options, grpcClient *backend.GrpcClient, reportsClient *report.Client, registrar *commands.Registrar) error { - //provider, err := user.New(grpcClient) - //if err != nil { - // return err - //} +func start(options Options, reportsClient *report.Client, registrar *commands.Registrar) error { bot, err := create(options) if err != nil { log.Printf("Bot create error: %v\n", err) diff --git a/bot/commands/register.go b/bot/commands/register.go index 7ff22eb..b4ef002 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -15,7 +15,7 @@ func NewRegisterCommand(provider *user.Provider) *RegisterCommand { } func (r RegisterCommand) Accepted(message string) bool { - re := regexp.MustCompile(`^\/register\s+\+(\d{10})$`) + re := regexp.MustCompile(`^/register\s+\+(\d{10})$`) return re.MatchString(message) } diff --git a/bot/main.go b/bot/main.go index a65cf81..2c83770 100644 --- a/bot/main.go +++ b/bot/main.go @@ -26,7 +26,7 @@ func main() { log.Fatal(err) } registrar := createCommandsRegistrar(grpcClient, &provider) - err = start(options, grpcClient, reportsClient, registrar) + err = start(options, reportsClient, registrar) if err != nil { log.Fatal(err) } From e3fde60118d83eae683a50a6b76a494fed0f311b Mon Sep 17 00:00:00 2001 From: DRypa Date: Wed, 19 Jul 2023 16:41:17 +0300 Subject: [PATCH 14/32] #70 get phone from request method --- bot/commands/register.go | 17 +++++++++++++---- bot/commands/register_test.go | 11 +++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/bot/commands/register.go b/bot/commands/register.go index b4ef002..178108b 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -8,16 +8,18 @@ import ( type RegisterCommand struct { provider *user.Provider + regexp *regexp.Regexp } func NewRegisterCommand(provider *user.Provider) *RegisterCommand { - return &RegisterCommand{provider: provider} + return &RegisterCommand{ + provider: provider, + regexp: regexp.MustCompile(`^/register\s+\+(?P\d{10})$`), + } } func (r RegisterCommand) Accepted(message string) bool { - re := regexp.MustCompile(`^/register\s+\+(\d{10})$`) - - return re.MatchString(message) + return r.regexp.MatchString(message) } func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { @@ -30,6 +32,13 @@ func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) e return err } +func (r RegisterCommand) getPhoneFromRequest(message string) string { + matches := r.regexp.FindStringSubmatch(message) + index := r.regexp.SubexpIndex("phone") + phone := matches[index] + return phone +} + func register(userId int, client *user.Provider) error { _, err := client.GetUserId(userId) return err diff --git a/bot/commands/register_test.go b/bot/commands/register_test.go index c7dfca4..33bd10d 100644 --- a/bot/commands/register_test.go +++ b/bot/commands/register_test.go @@ -41,5 +41,16 @@ func TestAccepted_RegisterCommand_Failed(t *testing.T) { t.Fatalf("message '%s' is accepted for RegisterCommand", s) } } +} + +func Test_getPhoneFromRequest_Success(t *testing.T) { + command := NewRegisterCommand(nil) + message := "/register +1234567890" + + phone := command.getPhoneFromRequest(message) + expected := "1234567890" + if phone != expected { + t.Fatalf("wrong phone returned from getPhoneFromRequest. Expeced '%s' but got '%s'", expected, phone) + } } From 70cf77da74562425afb5c88ba1a968f47886a04e Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 19 Aug 2023 15:52:32 +0300 Subject: [PATCH 15/32] #70 grpc contracts updated --- backend/go.mod | 4 ++-- backend/go.sum | 9 ++++----- bot/go.mod | 14 +++++++------- bot/go.sum | 38 ++++++++++++++------------------------ 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/backend/go.mod b/backend/go.mod index 6b11241..ba1cbb0 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -3,7 +3,8 @@ module receipt_collector go 1.20 require ( - github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230405091352-65e1efb55137 + github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01 + github.com/go-co-op/gocron v1.28.2 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 @@ -14,7 +15,6 @@ require ( ) require ( - github.com/go-co-op/gocron v1.28.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/klauspost/compress v1.13.6 // indirect diff --git a/backend/go.sum b/backend/go.sum index 16a8ea9..4d1980d 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -2,8 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/drypa/ReceiptCollector/api/inside v0.0.0-20230405091352-65e1efb55137 h1:2gSdtAgn/1SzR3jIxQybz+G8hFlLLPgwBpwe5se5kf4= -github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230405091352-65e1efb55137/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= +github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01 h1:HCdDtLl5QNclGP45Q6dt9d5XEfA5OMf+WRdLUOqiJx4= +github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= github.com/go-co-op/gocron v1.28.2 h1:H9oHUGH+9HZ5mAorbnzRjzXLf4poP+ctZdbtaKRYagc= github.com/go-co-op/gocron v1.28.2/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q= @@ -23,14 +23,13 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= @@ -42,12 +41,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= 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.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= 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= diff --git a/bot/go.mod b/bot/go.mod index 07b8e1c..dd234fc 100644 --- a/bot/go.mod +++ b/bot/go.mod @@ -3,17 +3,17 @@ module github.com/drypa/ReceiptCollector/bot go 1.20 require ( - github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230411193413-50c7f98f123e + github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01 github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible - google.golang.org/grpc v1.54.0 + google.golang.org/grpc v1.57.0 ) require ( github.com/golang/protobuf v1.5.3 // indirect github.com/technoweenie/multipartstreamer v1.0.1 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect - google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/net v0.14.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect + google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/bot/go.sum b/bot/go.sum index 2a8b159..a70541a 100644 --- a/bot/go.sum +++ b/bot/go.sum @@ -1,7 +1,5 @@ -github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230401140024-d19b1b654eb3 h1:NxegEtlE7ZXqJZKB3PhvWZW5sVjUenyuPeCQd4k3Ex4= -github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230401140024-d19b1b654eb3/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= -github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230411193413-50c7f98f123e h1:C6WARgk3MZfpEnmGfQ1bK/YZnLP9tRLLOf5+Fi657Eg= -github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230411193413-50c7f98f123e/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= +github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01 h1:HCdDtLl5QNclGP45Q6dt9d5XEfA5OMf+WRdLUOqiJx4= +github.com/drypa/ReceiptCollector/api/inside v0.0.0-20230819122429-dfb6fd2c5a01/go.mod h1:4ULoAoegdVc7I/NgQ6izbBbXhqheKj8JbANBQ3xfVN0= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU= github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -11,26 +9,18 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633 h1:0BOZf6qNozI3pkN3fJLwNubheHJYHhMh91GRFOWWK08= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= -google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= -google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= -google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= +google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= From 79c2f0e672c9c33772254ccbd7682f2d62efa081 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 19 Aug 2023 21:49:55 +0300 Subject: [PATCH 16/32] #70 new device while registration add --- backend/device/controller/controller.go | 2 +- backend/device/repository/repository.go | 10 +++-- backend/device/service.go | 35 ++++++++++------ backend/internal/processor.go | 1 + backend/internal/server.go | 5 +++ backend/nalogru/device/device.go | 2 + backend/nalogru/devices.go | 5 ++- backend/users/login_url/processor.go | 55 +++++++++++++++++++++++-- 8 files changed, 92 insertions(+), 23 deletions(-) diff --git a/backend/device/controller/controller.go b/backend/device/controller/controller.go index 86dfa09..74488a3 100644 --- a/backend/device/controller/controller.go +++ b/backend/device/controller/controller.go @@ -32,7 +32,7 @@ func (c *Controller) AddDeviceHandler(writer http.ResponseWriter, request *http. SessionId: request.SessionId, RefreshToken: request.RefreshToken, } - err = c.service.Add(ctx, d) + err = c.service.Add(ctx, &d) if err != nil { onError(writer, err) return diff --git a/backend/device/repository/repository.go b/backend/device/repository/repository.go index 94b8a35..0d08c24 100644 --- a/backend/device/repository/repository.go +++ b/backend/device/repository/repository.go @@ -3,6 +3,7 @@ package repository import ( "context" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "receipt_collector/dispose" "receipt_collector/nalogru/device" @@ -12,18 +13,19 @@ type Repository struct { m *mongo.Client } -//NewRepository creates Repository. +// NewRepository creates Repository. func NewRepository(m *mongo.Client) *Repository { return &Repository{m: m} } -func (r *Repository) Add(ctx context.Context, d device.Device) error { +func (r *Repository) Add(ctx context.Context, d *device.Device) error { collection := r.getCollection() - _, err := collection.InsertOne(ctx, d) + document, err := collection.InsertOne(ctx, d) + d.Id = document.InsertedID.(primitive.ObjectID) return err } -//All returns all devices. +// All returns all devices. func (r *Repository) All(ctx context.Context) ([]device.Device, error) { collection := r.getCollection() cursor, err := collection.Find(ctx, bson.D{}) diff --git a/backend/device/service.go b/backend/device/service.go index 308fb4a..8fe85af 100644 --- a/backend/device/service.go +++ b/backend/device/service.go @@ -3,7 +3,6 @@ package device import ( "context" "errors" - "log" "receipt_collector/device/repository" "receipt_collector/nalogru/device" ) @@ -34,12 +33,17 @@ func NewService(ctx context.Context, r *repository.Repository) (*Service, error) } // Add adds new device. -func (s *Service) Add(ctx context.Context, d device.Device) error { +func (s *Service) Add(ctx context.Context, d *device.Device) error { for _, v := range s.devices { if v.ClientSecret == d.ClientSecret { return errors.New("that device already added") } } + forRent := ForRent{ + Device: *d, + IsRent: false, + } + s.devices = append(s.devices, forRent) return s.r.Add(ctx, d) } @@ -85,19 +89,13 @@ func (s *Service) RentDevice(ctx context.Context, d *device.Device) error { func (s *Service) rent(ctx context.Context, v *ForRent) { v.IsRent = true v.Update = func(sessionId string, refreshToken string) error { - v.Device.SessionId = sessionId - v.Device.RefreshToken = refreshToken - log.Printf("Updating device: %+v\n", v.Device) - return s.r.Update(ctx, &v.Device) + return s.Update(ctx, &v.Device, sessionId, refreshToken) } } -func (s *Service) Update(ctx context.Context, device *device.Device) error { - for _, v := range s.devices { - if device.Id == v.Id { - v.Device = *device - } - } +func (s *Service) Update(ctx context.Context, device *device.Device, sessionId string, refreshToken string) error { + device.SessionId = sessionId + device.RefreshToken = refreshToken return s.r.Update(ctx, device) } @@ -120,3 +118,16 @@ func (s *Service) All(ctx context.Context) []*device.Device { } return res } + +func (s *Service) GetByUserId(ctx context.Context, userId string) (*device.Device, error) { + devices, err := s.r.All(ctx) + if err != nil { + return nil, err + } + for _, d := range devices { + if d.UserId == userId { + return &d, nil + } + } + return nil, nil +} diff --git a/backend/internal/processor.go b/backend/internal/processor.go index 12a7858..1a4ea51 100644 --- a/backend/internal/processor.go +++ b/backend/internal/processor.go @@ -11,6 +11,7 @@ type AccountProcessor interface { GetLoginLink(ctx context.Context, in *api.GetLoginLinkRequest) (*api.LoginLinkResponse, error) GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUsersResponse, error) GetUser(ctx context.Context, in *api.GetUserRequest, opts ...grpc.CallOption) (*api.GetUserResponse, error) + RegisterUser(ctx context.Context, in *api.UserRegistrationRequest, opts ...grpc.CallOption) (*api.UserRegistrationResponse, error) } // ReceiptProcessor is an interface for process receipt requests. diff --git a/backend/internal/server.go b/backend/internal/server.go index 540643c..fb6bf81 100644 --- a/backend/internal/server.go +++ b/backend/internal/server.go @@ -50,3 +50,8 @@ func (s *server) GetRawReceipt(ctx context.Context, in *api.GetRawReceiptReportR processor := *(s.receiptProcessor) return processor.GetRawReceipt(ctx, in) } + +func (s *server) RegisterUser(ctx context.Context, req *api.UserRegistrationRequest) (*api.UserRegistrationResponse, error) { + processor := *(s.accountProcessor) + return processor.RegisterUser(ctx, req) +} diff --git a/backend/nalogru/device/device.go b/backend/nalogru/device/device.go index 94971fe..77624db 100644 --- a/backend/nalogru/device/device.go +++ b/backend/nalogru/device/device.go @@ -8,4 +8,6 @@ type Device struct { RefreshToken string `bson:"refresh_token"` Id primitive.ObjectID `bson:"_id,omitempty"` Update func(string, string) error + UserId string `bson:"user_id"` + Phone string `bson:"phone"` } diff --git a/backend/nalogru/devices.go b/backend/nalogru/devices.go index b82ea7b..3fecb41 100644 --- a/backend/nalogru/devices.go +++ b/backend/nalogru/devices.go @@ -6,11 +6,12 @@ import ( ) type Devices interface { - Add(ctx context.Context, d device.Device) error + Add(ctx context.Context, d *device.Device) error Count(ctx context.Context) (int, error) Rent(ctx context.Context) (*device.Device, error) RentDevice(ctx context.Context, d *device.Device) error - Update(ctx context.Context, device *device.Device) error + Update(ctx context.Context, device *device.Device, sessionId string, refreshToken string) error Free(ctx context.Context, device *device.Device) error All(ctx context.Context) []*device.Device + GetByUserId(ctx context.Context, userId string) (*device.Device, error) } diff --git a/backend/users/login_url/processor.go b/backend/users/login_url/processor.go index a178726..273c0b9 100644 --- a/backend/users/login_url/processor.go +++ b/backend/users/login_url/processor.go @@ -4,22 +4,25 @@ import ( "context" api "github.com/drypa/ReceiptCollector/api/inside" "google.golang.org/grpc" + "receipt_collector/device" + nalogDevice "receipt_collector/nalogru/device" "receipt_collector/users" "time" ) -//Processor provides method to return login link. +// Processor provides method to return login link. type Processor struct { repository *users.Repository linkGenerator users.LinkGenerator + d *device.Service } -//NewProcessor constructs Processor. +// NewProcessor constructs Processor. func NewProcessor(repository *users.Repository, linkGenerator users.LinkGenerator) *Processor { return &Processor{repository: repository, linkGenerator: linkGenerator} } -//GetLoginLink returns login link for user in request. +// GetLoginLink returns login link for user in request. func (p Processor) GetLoginLink(ctx context.Context, in *api.GetLoginLinkRequest) (*api.LoginLinkResponse, error) { telegramId := in.TelegramId user, err := p.repository.GetByTelegramId(ctx, int(telegramId)) @@ -58,7 +61,7 @@ func (p Processor) GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUse return &resp, err } -//GetUser get user by telegramId. +// GetUser get user by telegramId. func (p Processor) GetUser(ctx context.Context, in *api.GetUserRequest, opts ...grpc.CallOption) (*api.GetUserResponse, error) { user, err := p.repository.GetByTelegramId(ctx, int(in.TelegramId)) if err != nil { @@ -72,3 +75,47 @@ func (p Processor) GetUser(ctx context.Context, in *api.GetUserRequest, opts ... } return &response, err } + +func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationRequest, opts ...grpc.CallOption) (*api.UserRegistrationResponse, error) { + user, err := p.repository.GetByTelegramId(ctx, int(in.TelegramId)) + if err != nil { + return nil, err + } + if user == nil { + user, err = p.addNewUser(ctx, int(in.TelegramId)) + if err != nil { + return nil, err + } + } + userId := user.Id.Hex() + d, err := p.d.GetByUserId(ctx, userId) + if err != nil { + return nil, err + } + if d == nil { + d = &nalogDevice.Device{ + ClientSecret: "", //TODO: generate random(or not) secret + SessionId: "", + RefreshToken: "", + Update: nil, + UserId: userId, + Phone: in.PhoneNumber, + } + err := p.d.Add(ctx, d) + if err != nil { + return nil, err + } + } + return &api.UserRegistrationResponse{UserId: userId}, nil +} + +func (p Processor) addNewUser(ctx context.Context, telegramId int) (*users.User, error) { + u := users.User{ + TelegramId: telegramId, + } + err := p.repository.Insert(ctx, &u) + if err != nil { + return nil, err + } + return &u, nil +} From 93f72117bb7793e92a6de1c0be49ce1e95108f01 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 20 Aug 2023 16:24:16 +0300 Subject: [PATCH 17/32] #70 registration request template phone number length fix --- bot/commands/register.go | 2 +- bot/commands/register_test.go | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/bot/commands/register.go b/bot/commands/register.go index 178108b..6369c23 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -14,7 +14,7 @@ type RegisterCommand struct { func NewRegisterCommand(provider *user.Provider) *RegisterCommand { return &RegisterCommand{ provider: provider, - regexp: regexp.MustCompile(`^/register\s+\+(?P\d{10})$`), + regexp: regexp.MustCompile(`^/register\s+\+(?P\d{11})$`), } } diff --git a/bot/commands/register_test.go b/bot/commands/register_test.go index 33bd10d..778c5c7 100644 --- a/bot/commands/register_test.go +++ b/bot/commands/register_test.go @@ -6,10 +6,10 @@ func TestAccepted_RegisterCommand_Success(t *testing.T) { command := NewRegisterCommand(nil) variants := make([]string, 0) - variants = append(variants, "/register +1234567890") - variants = append(variants, "/register +0987654321") - variants = append(variants, "/register +0987654321") - variants = append(variants, "/register +0987654321") + variants = append(variants, "/register +71234567890") + variants = append(variants, "/register +70987654321") + variants = append(variants, "/register +00987654321") + variants = append(variants, "/register +00987654321") for _, s := range variants { res := command.Accepted(s) @@ -28,12 +28,15 @@ func TestAccepted_RegisterCommand_Failed(t *testing.T) { variants = append(variants, " ") variants = append(variants, " ") variants = append(variants, "/register") - variants = append(variants, "/register+1234567890") - variants = append(variants, " /register +1234567890 ") + variants = append(variants, "register +71234567890") + variants = append(variants, "/register+71234567890") + variants = append(variants, " /register +11234567890 ") variants = append(variants, "/register +") variants = append(variants, "/register +1") - variants = append(variants, "/register +12345678901") + variants = append(variants, "/register +012345678901") variants = append(variants, "/register +abcdefghjk") + variants = append(variants, "/register ++++++++++++") + variants = append(variants, "/register +.*") for _, s := range variants { res := command.Accepted(s) From f41c7f66d1305155dce51172a18adf5d1712a2eb Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 20 Aug 2023 16:50:05 +0300 Subject: [PATCH 18/32] #70 registration grpc method call --- bot/backend/grpc.go | 10 ++++++++++ bot/commands/register.go | 21 +++++++++++++-------- bot/commands/register_test.go | 10 +++++----- bot/main.go | 2 +- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/bot/backend/grpc.go b/bot/backend/grpc.go index fd95ed4..091fd9e 100644 --- a/bot/backend/grpc.go +++ b/bot/backend/grpc.go @@ -119,3 +119,13 @@ func (c *GrpcClient) GetReceiptReport(ctx context.Context, userId string, qr str return resp.Report, resp.FileName, err } + +func (c *GrpcClient) RegisterUser(ctx context.Context, telegramId int, phone string) error { + client := c.internal + request := inside.UserRegistrationRequest{ + TelegramId: int32(telegramId), + PhoneNumber: phone, + } + _, err := (*client).RegisterUser(ctx, &request) + return err +} diff --git a/bot/commands/register.go b/bot/commands/register.go index 6369c23..f277002 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -1,20 +1,24 @@ package commands import ( + "context" + "github.com/drypa/ReceiptCollector/bot/backend" "github.com/drypa/ReceiptCollector/bot/backend/user" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" "regexp" ) type RegisterCommand struct { - provider *user.Provider - regexp *regexp.Regexp + provider *user.Provider + regexp *regexp.Regexp + grpcClient *backend.GrpcClient } -func NewRegisterCommand(provider *user.Provider) *RegisterCommand { +func NewRegisterCommand(provider *user.Provider, grpcClient *backend.GrpcClient) *RegisterCommand { return &RegisterCommand{ - provider: provider, - regexp: regexp.MustCompile(`^/register\s+\+(?P\d{11})$`), + provider: provider, + grpcClient: grpcClient, + regexp: regexp.MustCompile(`^/register\s+\+(?P\d{11})$`), } } @@ -23,7 +27,8 @@ func (r RegisterCommand) Accepted(message string) bool { } func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { - err := register(update.Message.From.ID, r.provider) + phone := r.getPhoneFromRequest(update.Message.Text) + err := r.register(update.Message.From.ID, phone, r.provider) responseText := "You are registered." if err != nil { responseText = err.Error() @@ -39,7 +44,7 @@ func (r RegisterCommand) getPhoneFromRequest(message string) string { return phone } -func register(userId int, client *user.Provider) error { - _, err := client.GetUserId(userId) +func (r RegisterCommand) register(telegramId int, phone string, client *user.Provider) error { + err := r.grpcClient.RegisterUser(context.Background(), telegramId, phone) return err } diff --git a/bot/commands/register_test.go b/bot/commands/register_test.go index 778c5c7..f62185e 100644 --- a/bot/commands/register_test.go +++ b/bot/commands/register_test.go @@ -3,7 +3,7 @@ package commands import "testing" func TestAccepted_RegisterCommand_Success(t *testing.T) { - command := NewRegisterCommand(nil) + command := NewRegisterCommand(nil, nil) variants := make([]string, 0) variants = append(variants, "/register +71234567890") @@ -21,7 +21,7 @@ func TestAccepted_RegisterCommand_Success(t *testing.T) { } func TestAccepted_RegisterCommand_Failed(t *testing.T) { - command := NewRegisterCommand(nil) + command := NewRegisterCommand(nil, nil) variants := make([]string, 0) variants = append(variants, "") @@ -47,12 +47,12 @@ func TestAccepted_RegisterCommand_Failed(t *testing.T) { } func Test_getPhoneFromRequest_Success(t *testing.T) { - command := NewRegisterCommand(nil) + command := NewRegisterCommand(nil, nil) - message := "/register +1234567890" + message := "/register +71234567890" phone := command.getPhoneFromRequest(message) - expected := "1234567890" + expected := "71234567890" if phone != expected { t.Fatalf("wrong phone returned from getPhoneFromRequest. Expeced '%s' but got '%s'", expected, phone) } diff --git a/bot/main.go b/bot/main.go index 2c83770..152212b 100644 --- a/bot/main.go +++ b/bot/main.go @@ -41,7 +41,7 @@ func createCommandsRegistrar(grpcClient *backend.GrpcClient, users *user.Provide start := commands.StartCommand{} registrar.Register(start) - register := commands.NewRegisterCommand(users) + register := commands.NewRegisterCommand(users, grpcClient) registrar.Register(register) getReceiptReport := commands.NewGetReceiptReportCommand(users, grpcClient) From 738ac8e445a361824473f709e66a99d40eddc009 Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 22 Aug 2023 16:18:43 +0300 Subject: [PATCH 19/32] #70 character '+' is part of phone --- bot/commands/register.go | 2 +- bot/commands/register_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/commands/register.go b/bot/commands/register.go index f277002..0af84f0 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -18,7 +18,7 @@ func NewRegisterCommand(provider *user.Provider, grpcClient *backend.GrpcClient) return &RegisterCommand{ provider: provider, grpcClient: grpcClient, - regexp: regexp.MustCompile(`^/register\s+\+(?P\d{11})$`), + regexp: regexp.MustCompile(`^/register\s+(?P\+\d{11})$`), } } diff --git a/bot/commands/register_test.go b/bot/commands/register_test.go index f62185e..47980f1 100644 --- a/bot/commands/register_test.go +++ b/bot/commands/register_test.go @@ -52,7 +52,7 @@ func Test_getPhoneFromRequest_Success(t *testing.T) { message := "/register +71234567890" phone := command.getPhoneFromRequest(message) - expected := "71234567890" + expected := "+71234567890" if phone != expected { t.Fatalf("wrong phone returned from getPhoneFromRequest. Expeced '%s' but got '%s'", expected, phone) } From 1963d13a30266facd09c1aee699b92c35800b8e4 Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 22 Aug 2023 16:36:20 +0300 Subject: [PATCH 20/32] #70 response text changed --- bot/commands/register.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bot/commands/register.go b/bot/commands/register.go index 0af84f0..e5e98d1 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -28,8 +28,8 @@ func (r RegisterCommand) Accepted(message string) bool { func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { phone := r.getPhoneFromRequest(update.Message.Text) - err := r.register(update.Message.From.ID, phone, r.provider) - responseText := "You are registered." + err := r.register(update.Message.From.ID, phone) + responseText := "You are registered.\n Wait SMS from KKT.NALOG and please send the verification code from it as reply to current message" if err != nil { responseText = err.Error() } @@ -44,7 +44,6 @@ func (r RegisterCommand) getPhoneFromRequest(message string) string { return phone } -func (r RegisterCommand) register(telegramId int, phone string, client *user.Provider) error { - err := r.grpcClient.RegisterUser(context.Background(), telegramId, phone) - return err +func (r RegisterCommand) register(telegramId int, phone string) error { + return r.grpcClient.RegisterUser(context.Background(), telegramId, phone) } From c1aa8c4c7f5646f80780c8a5bbdb03f2cebfe434 Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 22 Aug 2023 16:40:44 +0300 Subject: [PATCH 21/32] #70 processor moved to correct package --- backend/main.go | 2 +- backend/users/{login_url => }/processor.go | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) rename backend/users/{login_url => }/processor.go (90%) diff --git a/backend/main.go b/backend/main.go index 5b54720..c0efc5a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -82,7 +82,7 @@ func main() { if err != nil { log.Fatalf("failed to load TLS keys: %v", err) } - var accountProcessor internal.AccountProcessor = login_url.NewProcessor(&userRepository, generator) + var accountProcessor internal.AccountProcessor = users.NewProcessor(&userRepository, generator) r := render.New(templatePath) var receiptProcessor internal.ReceiptProcessor = receipts.NewProcessor(&receiptRepository, r) diff --git a/backend/users/login_url/processor.go b/backend/users/processor.go similarity index 90% rename from backend/users/login_url/processor.go rename to backend/users/processor.go index 273c0b9..4bc9d5c 100644 --- a/backend/users/login_url/processor.go +++ b/backend/users/processor.go @@ -1,4 +1,4 @@ -package login_url +package users import ( "context" @@ -6,19 +6,18 @@ import ( "google.golang.org/grpc" "receipt_collector/device" nalogDevice "receipt_collector/nalogru/device" - "receipt_collector/users" "time" ) // Processor provides method to return login link. type Processor struct { - repository *users.Repository - linkGenerator users.LinkGenerator + repository *Repository + linkGenerator LinkGenerator d *device.Service } // NewProcessor constructs Processor. -func NewProcessor(repository *users.Repository, linkGenerator users.LinkGenerator) *Processor { +func NewProcessor(repository *Repository, linkGenerator LinkGenerator) *Processor { return &Processor{repository: repository, linkGenerator: linkGenerator} } @@ -41,6 +40,7 @@ func (p Processor) GetLoginLink(ctx context.Context, in *api.GetLoginLinkRequest }, nil } +// GetUsers returns all registered users. func (p Processor) GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUsersResponse, error) { all, err := p.repository.GetAll(ctx) if err != nil { @@ -105,12 +105,13 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq if err != nil { return nil, err } + //TODO: send request: POST https://irkkt-mobile.nalog.ru:8888/v2/auth/phone/request } return &api.UserRegistrationResponse{UserId: userId}, nil } -func (p Processor) addNewUser(ctx context.Context, telegramId int) (*users.User, error) { - u := users.User{ +func (p Processor) addNewUser(ctx context.Context, telegramId int) (*User, error) { + u := User{ TelegramId: telegramId, } err := p.repository.Insert(ctx, &u) From 2d5071c4086c0e954f28a79de679ceb48064690c Mon Sep 17 00:00:00 2001 From: DRypa Date: Tue, 22 Aug 2023 17:14:50 +0300 Subject: [PATCH 22/32] #70 HTTP auth by phone method implementation --- backend/nalogru/client.go | 34 ++++++++++++++++++++++++++++++++++ backend/nalogru/client_test.go | 22 +++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index fa85eb2..dcbe7ca 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -80,6 +80,12 @@ type TicketIdResponse struct { Status int `json:"status"` } +type PhoneAuthRequest struct { + ClientSecret string `json:"client_secret"` + Os string `json:"os"` + Phone string `json:"phone"` +} + // GetTicketId - send ticket id request to nalog.ru API. func (nalogruClient *Client) GetTicketId(queryString string, device *device.Device) (string, error) { payload := TicketIdRequest{Qr: queryString} @@ -231,6 +237,33 @@ func (nalogruClient *Client) GetElectronicTickets(device *device.Device) ([]*Tic return tickets, nil } +func (nalogruClient *Client) AuthByPhone(device *device.Device) error { + payload := PhoneAuthRequest{ + ClientSecret: device.ClientSecret, + Os: "android", + Phone: device.Phone, + } + + req, err := json.Marshal(payload) + if err != nil { + log.Println("Unable to serialize request") + return err + } + reader := bytes.NewReader(req) + url := nalogruClient.BaseAddress + "/v2/auth/phone/request" + request, err := http.NewRequest(http.MethodPost, url, reader) + addHeaders(request, device.Id.Hex()) + client := createHttpClient() + _, err = sendRequest(request, client) + + if err != nil { + log.Printf("Can't POST %s\n", url) + return err + } + + return nil +} + func (nalogruClient *Client) sendAuthenticatedRequest(r *http.Request, device *device.Device) (*http.Response, error) { addHeaders(r, device.Id.Hex()) addAuth(r, device.SessionId) @@ -262,6 +295,7 @@ type RefreshResponse struct { RefreshToken string `json:"refresh_token"` } +// RefreshSession used to refresh session by RefreshToken. func (nalogruClient *Client) RefreshSession(device *device.Device) error { client := createHttpClient() diff --git a/backend/nalogru/client_test.go b/backend/nalogru/client_test.go index 2c02761..4845767 100644 --- a/backend/nalogru/client_test.go +++ b/backend/nalogru/client_test.go @@ -11,10 +11,11 @@ import ( ) var baseAddress = "https://irkkt-mobile.nalog.ru:8888" -var sessionId = "606c4bb41d8f48ff57bfd021:6ca239ba-0885-4cc9-a7d9-be801253ac7d" +var sessionId = "INSERT SESSION ID HERE" var deviceId = primitive.NewObjectID().Hex() -var secret = "mnALjKobrqT/sC9um4wXlamXnOo=" -var refreshToken = "c55bdf44-acf6-46b3-be9a-8422bda7032d" +var secret = "INSERT SECRET HERE" +var refreshToken = "INSERT REFRESH TOKEN HERE" +var phone = "INSERT PHONE HERE" func IgnoreTestClient_GetTicketId(t *testing.T) { d, err := createDevice() @@ -84,6 +85,7 @@ func createDevice() (*device.Device, error) { Update: func(string, string) error { return nil }, + Phone: phone, } return d, err } @@ -194,6 +196,20 @@ func IgnoreTestHttpClient_Error(t *testing.T) { } } +func IgnoreTest_AuthByPhone(t *testing.T) { + d, err := createDevice() + if err != nil { + log.Println(err) + t.Fail() + return + } + client := NewClient(baseAddress) + err = client.AuthByPhone(d) + if err != nil { + t.Fatalf("Error auth by phone %v", err) + } +} + func createServer(t *testing.T) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, err := w.Write([]byte("Hello")) From 53aef6149badd33f42977403dc674fbb30054a4a Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 21 Oct 2023 22:11:13 +0300 Subject: [PATCH 23/32] #70 call auth by phone nalog.ru api in RegisterUser request --- backend/main.go | 2 +- backend/nalogru/device/device.go | 14 ++++++------ backend/users/processor.go | 38 +++++++++++++++++--------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/backend/main.go b/backend/main.go index c0efc5a..166548f 100644 --- a/backend/main.go +++ b/backend/main.go @@ -82,7 +82,7 @@ func main() { if err != nil { log.Fatalf("failed to load TLS keys: %v", err) } - var accountProcessor internal.AccountProcessor = users.NewProcessor(&userRepository, generator) + var accountProcessor internal.AccountProcessor = users.NewProcessor(&userRepository, generator, nalogruClient, deviceService) r := render.New(templatePath) var receiptProcessor internal.ReceiptProcessor = receipts.NewProcessor(&receiptRepository, r) diff --git a/backend/nalogru/device/device.go b/backend/nalogru/device/device.go index 77624db..044b960 100644 --- a/backend/nalogru/device/device.go +++ b/backend/nalogru/device/device.go @@ -3,11 +3,11 @@ package device import "go.mongodb.org/mongo-driver/bson/primitive" type Device struct { - ClientSecret string `bson:"client_secret"` - SessionId string `bson:"session_id"` - RefreshToken string `bson:"refresh_token"` - Id primitive.ObjectID `bson:"_id,omitempty"` - Update func(string, string) error - UserId string `bson:"user_id"` - Phone string `bson:"phone"` + ClientSecret string `bson:"client_secret"` + SessionId string `bson:"session_id"` + RefreshToken string `bson:"refresh_token"` + Id primitive.ObjectID `bson:"_id,omitempty"` + Update func(string, string) error `bson:"-"` + UserId string `bson:"user_id"` + Phone string `bson:"phone"` } diff --git a/backend/users/processor.go b/backend/users/processor.go index 4bc9d5c..0634ed8 100644 --- a/backend/users/processor.go +++ b/backend/users/processor.go @@ -5,6 +5,7 @@ import ( api "github.com/drypa/ReceiptCollector/api/inside" "google.golang.org/grpc" "receipt_collector/device" + "receipt_collector/nalogru" nalogDevice "receipt_collector/nalogru/device" "time" ) @@ -14,11 +15,12 @@ type Processor struct { repository *Repository linkGenerator LinkGenerator d *device.Service + nalogClient *nalogru.Client } // NewProcessor constructs Processor. -func NewProcessor(repository *Repository, linkGenerator LinkGenerator) *Processor { - return &Processor{repository: repository, linkGenerator: linkGenerator} +func NewProcessor(repository *Repository, linkGenerator LinkGenerator, nalogClient *nalogru.Client, d *device.Service) *Processor { + return &Processor{repository: repository, linkGenerator: linkGenerator, nalogClient: nalogClient, d: d} } // GetLoginLink returns login link for user in request. @@ -88,25 +90,25 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq } } userId := user.Id.Hex() - d, err := p.d.GetByUserId(ctx, userId) + + d := &nalogDevice.Device{ + ClientSecret: "", //TODO: generate random(or not) secret + SessionId: "", + RefreshToken: "", + Update: nil, + UserId: userId, + Phone: in.PhoneNumber, + } + d.Update = func(sessionId string, refreshToken string) error { + return p.d.Update(ctx, d, sessionId, refreshToken) + } + + err = p.d.Add(ctx, d) if err != nil { return nil, err } - if d == nil { - d = &nalogDevice.Device{ - ClientSecret: "", //TODO: generate random(or not) secret - SessionId: "", - RefreshToken: "", - Update: nil, - UserId: userId, - Phone: in.PhoneNumber, - } - err := p.d.Add(ctx, d) - if err != nil { - return nil, err - } - //TODO: send request: POST https://irkkt-mobile.nalog.ru:8888/v2/auth/phone/request - } + err = p.nalogClient.AuthByPhone(d) + return &api.UserRegistrationResponse{UserId: userId}, nil } From da91234940118ff7d933c5ec214b3dd3a3510c10 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 21 Oct 2023 22:32:19 +0300 Subject: [PATCH 24/32] #70 return device search by userId --- backend/users/processor.go | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/backend/users/processor.go b/backend/users/processor.go index 0634ed8..c8cdd88 100644 --- a/backend/users/processor.go +++ b/backend/users/processor.go @@ -90,23 +90,28 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq } } userId := user.Id.Hex() - - d := &nalogDevice.Device{ - ClientSecret: "", //TODO: generate random(or not) secret - SessionId: "", - RefreshToken: "", - Update: nil, - UserId: userId, - Phone: in.PhoneNumber, - } - d.Update = func(sessionId string, refreshToken string) error { - return p.d.Update(ctx, d, sessionId, refreshToken) - } - - err = p.d.Add(ctx, d) + d, err := p.d.GetByUserId(ctx, userId) if err != nil { return nil, err } + if d == nil { + d := &nalogDevice.Device{ + ClientSecret: "", //TODO: generate random(or not) secret + SessionId: "", + RefreshToken: "", + Update: nil, + UserId: userId, + Phone: in.PhoneNumber, + } + d.Update = func(sessionId string, refreshToken string) error { + return p.d.Update(ctx, d, sessionId, refreshToken) + } + + err = p.d.Add(ctx, d) + if err != nil { + return nil, err + } + } err = p.nalogClient.AuthByPhone(d) return &api.UserRegistrationResponse{UserId: userId}, nil From 5b513a038762893def49c4b02ffe06f73c3bef08 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 21 Oct 2023 23:13:01 +0300 Subject: [PATCH 25/32] #70 generating random client secret --- backend/users/processor.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/users/processor.go b/backend/users/processor.go index c8cdd88..fbb2889 100644 --- a/backend/users/processor.go +++ b/backend/users/processor.go @@ -2,8 +2,11 @@ package users import ( "context" + "crypto/rand" + "encoding/base64" api "github.com/drypa/ReceiptCollector/api/inside" "google.golang.org/grpc" + "log" "receipt_collector/device" "receipt_collector/nalogru" nalogDevice "receipt_collector/nalogru/device" @@ -95,8 +98,14 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq return nil, err } if d == nil { + secret, err := generateRandomSecret() + if err != nil { + log.Printf("error while generating random string: %v", err) + return nil, err + } + d := &nalogDevice.Device{ - ClientSecret: "", //TODO: generate random(or not) secret + ClientSecret: *secret, SessionId: "", RefreshToken: "", Update: nil, @@ -117,6 +126,16 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq return &api.UserRegistrationResponse{UserId: userId}, nil } +func generateRandomSecret() (*string, error) { + buf := make([]byte, 20) + _, err := rand.Read(buf) + if err != nil { + return nil, err + } + encoded := base64.StdEncoding.EncodeToString(buf) + return &encoded, nil +} + func (p Processor) addNewUser(ctx context.Context, telegramId int) (*User, error) { u := User{ TelegramId: telegramId, From 433dbc843540790d6808ca09955887b8d9ee2615 Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 22 Oct 2023 16:38:35 +0300 Subject: [PATCH 26/32] #70 verification code command --- bot/commands/code.go | 46 ++++++++++++++++++++++++++++++ bot/commands/code_test.go | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 bot/commands/code.go create mode 100644 bot/commands/code_test.go diff --git a/bot/commands/code.go b/bot/commands/code.go new file mode 100644 index 0000000..07c7be4 --- /dev/null +++ b/bot/commands/code.go @@ -0,0 +1,46 @@ +package commands + +import ( + "github.com/drypa/ReceiptCollector/bot/backend" + "github.com/drypa/ReceiptCollector/bot/backend/user" + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" + "log" + "regexp" +) + +type ConfirmationCodeCommand struct { + provider *user.Provider + grpcClient *backend.GrpcClient + regexp *regexp.Regexp +} + +func NewConfirmationCodeCommand(provider *user.Provider, grpcClient *backend.GrpcClient) *ConfirmationCodeCommand { + command := ConfirmationCodeCommand{ + provider: provider, + grpcClient: grpcClient, + regexp: regexp.MustCompile(`^/code\s+(?P\d{4})$`), + } + return &command +} + +func (c ConfirmationCodeCommand) Accepted(message string) bool { + return c.regexp.MatchString(message) +} + +func (c ConfirmationCodeCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { + userId, err := c.provider.GetUserId(update.Message.From.ID) + if err != nil { + return err + } + code := c.getCodeFromRequest(update.Message.Text) + log.Printf("User %s verify phone with code %s\n", userId, code) + //TODO: send to server + return nil +} + +func (c ConfirmationCodeCommand) getCodeFromRequest(message string) string { + matches := c.regexp.FindStringSubmatch(message) + index := c.regexp.SubexpIndex("code") + code := matches[index] + return code +} diff --git a/bot/commands/code_test.go b/bot/commands/code_test.go new file mode 100644 index 0000000..9c531b6 --- /dev/null +++ b/bot/commands/code_test.go @@ -0,0 +1,60 @@ +package commands + +import "testing" + +func TestAccepted_ConfirmationCodeCommand_Success(t *testing.T) { + command := NewConfirmationCodeCommand(nil, nil) + + variants := make([]string, 0) + variants = append(variants, "/code 1234") + variants = append(variants, "/code 9876") + variants = append(variants, "/code 0000") + variants = append(variants, "/code 0001") + + for _, s := range variants { + res := command.Accepted(s) + if !res { + t.Fatalf("message '%s' is not acceptable for ConfirmationCodeCommand", s) + } + } +} + +func TestAccepted_ConfirmationCodeCommand_Failed(t *testing.T) { + command := NewConfirmationCodeCommand(nil, nil) + + variants := make([]string, 0) + variants = append(variants, "") + variants = append(variants, " ") + variants = append(variants, " ") + variants = append(variants, "/code") + variants = append(variants, "code 1234") + variants = append(variants, "/code1234") + variants = append(variants, " /code 1234 ") + variants = append(variants, "/code +") + variants = append(variants, "/code ++++") + variants = append(variants, "/code dddd") + variants = append(variants, "/code 1") + variants = append(variants, "/code 12") + variants = append(variants, "/code 123") + variants = append(variants, "/code 12345") + variants = append(variants, "/code +.*") + + for _, s := range variants { + res := command.Accepted(s) + if res { + t.Fatalf("message '%s' is accepted for RegisterCommand", s) + } + } +} + +func Test_getCodeFromRequest_Success(t *testing.T) { + command := NewConfirmationCodeCommand(nil, nil) + + message := "/code 0101" + + phone := command.getCodeFromRequest(message) + expected := "0101" + if phone != expected { + t.Fatalf("wrong code returned from getCodeFromRequest. Expeced '%s' but got '%s'", expected, phone) + } +} From 1690090e291d28fac698b7068affe8e5270c218b Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 29 Oct 2023 22:36:13 +0300 Subject: [PATCH 27/32] #70 client secret must be not random but fixed(presumably by platform) --- backend/main.go | 3 ++- backend/users/processor.go | 27 +++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/backend/main.go b/backend/main.go index 166548f..5b67e46 100644 --- a/backend/main.go +++ b/backend/main.go @@ -36,6 +36,7 @@ var mongoUser = os.Getenv("MONGO_LOGIN") var mongoSecret = os.Getenv("MONGO_SECRET") var openUrl = os.Getenv("OPEN_URL") var templatePath = os.Getenv("TEMPLATES_PATH") +var clientSecret = os.Getenv("CLIENT_SECRET") func main() { log.SetOutput(os.Stdout) @@ -82,7 +83,7 @@ func main() { if err != nil { log.Fatalf("failed to load TLS keys: %v", err) } - var accountProcessor internal.AccountProcessor = users.NewProcessor(&userRepository, generator, nalogruClient, deviceService) + var accountProcessor internal.AccountProcessor = users.NewProcessor(&userRepository, generator, nalogruClient, deviceService, clientSecret) r := render.New(templatePath) var receiptProcessor internal.ReceiptProcessor = receipts.NewProcessor(&receiptRepository, r) diff --git a/backend/users/processor.go b/backend/users/processor.go index fbb2889..02cd263 100644 --- a/backend/users/processor.go +++ b/backend/users/processor.go @@ -6,7 +6,6 @@ import ( "encoding/base64" api "github.com/drypa/ReceiptCollector/api/inside" "google.golang.org/grpc" - "log" "receipt_collector/device" "receipt_collector/nalogru" nalogDevice "receipt_collector/nalogru/device" @@ -17,13 +16,19 @@ import ( type Processor struct { repository *Repository linkGenerator LinkGenerator - d *device.Service + deviceService *device.Service nalogClient *nalogru.Client + clientSecret string } // NewProcessor constructs Processor. -func NewProcessor(repository *Repository, linkGenerator LinkGenerator, nalogClient *nalogru.Client, d *device.Service) *Processor { - return &Processor{repository: repository, linkGenerator: linkGenerator, nalogClient: nalogClient, d: d} +func NewProcessor(repository *Repository, linkGenerator LinkGenerator, nalogClient *nalogru.Client, d *device.Service, secret string) *Processor { + return &Processor{ + repository: repository, + linkGenerator: linkGenerator, + nalogClient: nalogClient, + deviceService: d, + clientSecret: secret} } // GetLoginLink returns login link for user in request. @@ -93,19 +98,13 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq } } userId := user.Id.Hex() - d, err := p.d.GetByUserId(ctx, userId) + d, err := p.deviceService.GetByUserId(ctx, userId) if err != nil { return nil, err } if d == nil { - secret, err := generateRandomSecret() - if err != nil { - log.Printf("error while generating random string: %v", err) - return nil, err - } - d := &nalogDevice.Device{ - ClientSecret: *secret, + ClientSecret: p.clientSecret, SessionId: "", RefreshToken: "", Update: nil, @@ -113,10 +112,10 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq Phone: in.PhoneNumber, } d.Update = func(sessionId string, refreshToken string) error { - return p.d.Update(ctx, d, sessionId, refreshToken) + return p.deviceService.Update(ctx, d, sessionId, refreshToken) } - err = p.d.Add(ctx, d) + err = p.deviceService.Add(ctx, d) if err != nil { return nil, err } From 1b84f938deab4eca2ebe0f21c9dbed7612e326b0 Mon Sep 17 00:00:00 2001 From: DRypa Date: Wed, 27 Dec 2023 20:11:45 +0300 Subject: [PATCH 28/32] #70 phone verification command send --- bot/backend/grpc.go | 12 ++++++++++++ bot/commands/add_receipt.go | 1 + bot/commands/code.go | 8 ++++++-- bot/commands/empty.go | 1 + bot/commands/get_report.go | 1 + bot/commands/register.go | 1 + bot/commands/start.go | 1 + bot/commands/wrong.go | 1 + 8 files changed, 24 insertions(+), 2 deletions(-) diff --git a/bot/backend/grpc.go b/bot/backend/grpc.go index 091fd9e..fb1547e 100644 --- a/bot/backend/grpc.go +++ b/bot/backend/grpc.go @@ -127,5 +127,17 @@ func (c *GrpcClient) RegisterUser(ctx context.Context, telegramId int, phone str PhoneNumber: phone, } _, err := (*client).RegisterUser(ctx, &request) + + return err +} + +func (c *GrpcClient) VerifyPhone(ctx context.Context, telegramId int, code string) error { + client := c.internal + request := inside.PhoneVerificationRequest{ + TelegramId: int32(telegramId), + Code: code, + } + _, err := (*client).VerifyPhone(ctx, &request) + return err } diff --git a/bot/commands/add_receipt.go b/bot/commands/add_receipt.go index 69b2e6d..f0a0b1e 100644 --- a/bot/commands/add_receipt.go +++ b/bot/commands/add_receipt.go @@ -25,6 +25,7 @@ func (a AddReceiptCommand) Accepted(message string) bool { strings.Contains(message, "n=") } +// Execute command func (a AddReceiptCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { id, err := a.provider.GetUserId(update.Message.From.ID) if err != nil { diff --git a/bot/commands/code.go b/bot/commands/code.go index 07c7be4..94ff738 100644 --- a/bot/commands/code.go +++ b/bot/commands/code.go @@ -1,6 +1,7 @@ package commands import ( + "context" "github.com/drypa/ReceiptCollector/bot/backend" "github.com/drypa/ReceiptCollector/bot/backend/user" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api" @@ -8,12 +9,14 @@ import ( "regexp" ) +// ConfirmationCodeCommand is phone verification command type ConfirmationCodeCommand struct { provider *user.Provider grpcClient *backend.GrpcClient regexp *regexp.Regexp } +// NewConfirmationCodeCommand creates ConfirmationCodeCommand func NewConfirmationCodeCommand(provider *user.Provider, grpcClient *backend.GrpcClient) *ConfirmationCodeCommand { command := ConfirmationCodeCommand{ provider: provider, @@ -27,6 +30,7 @@ func (c ConfirmationCodeCommand) Accepted(message string) bool { return c.regexp.MatchString(message) } +// Execute command func (c ConfirmationCodeCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { userId, err := c.provider.GetUserId(update.Message.From.ID) if err != nil { @@ -34,8 +38,8 @@ func (c ConfirmationCodeCommand) Execute(update tgbotapi.Update, bot *tgbotapi.B } code := c.getCodeFromRequest(update.Message.Text) log.Printf("User %s verify phone with code %s\n", userId, code) - //TODO: send to server - return nil + + return c.grpcClient.VerifyPhone(context.Background(), update.Message.From.ID, code) } func (c ConfirmationCodeCommand) getCodeFromRequest(message string) string { diff --git a/bot/commands/empty.go b/bot/commands/empty.go index 5f25c68..09dd4b3 100644 --- a/bot/commands/empty.go +++ b/bot/commands/empty.go @@ -9,6 +9,7 @@ func (e EmptyCommand) Accepted(message string) bool { return message == "" } +// Execute command func (e EmptyCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { _, err := sendTextMessage(update.Message.Chat.ID, bot, "Please enter a command.") return err diff --git a/bot/commands/get_report.go b/bot/commands/get_report.go index 461daea..528b728 100644 --- a/bot/commands/get_report.go +++ b/bot/commands/get_report.go @@ -19,6 +19,7 @@ func (g GetReceiptReportCommand) Accepted(message string) bool { return message == "/get" } +// Execute command func (g GetReceiptReportCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { id, err := g.provider.GetUserId(update.Message.From.ID) if err != nil { diff --git a/bot/commands/register.go b/bot/commands/register.go index e5e98d1..69e29a4 100644 --- a/bot/commands/register.go +++ b/bot/commands/register.go @@ -26,6 +26,7 @@ func (r RegisterCommand) Accepted(message string) bool { return r.regexp.MatchString(message) } +// Execute command func (r RegisterCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { phone := r.getPhoneFromRequest(update.Message.Text) err := r.register(update.Message.From.ID, phone) diff --git a/bot/commands/start.go b/bot/commands/start.go index b27a308..9bb9fef 100644 --- a/bot/commands/start.go +++ b/bot/commands/start.go @@ -9,6 +9,7 @@ func (s StartCommand) Accepted(message string) bool { return message == "/start" } +// Execute command func (s StartCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { _, err := sendTextMessage(update.Message.Chat.ID, bot, "I'm a bot to collect Your purchase tickets.") return err diff --git a/bot/commands/wrong.go b/bot/commands/wrong.go index 9c48f8f..a819dee 100644 --- a/bot/commands/wrong.go +++ b/bot/commands/wrong.go @@ -9,6 +9,7 @@ func (w WrongCommand) Accepted(message string) bool { return true } +// Execute command func (w WrongCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { _, err := sendTextMessage(update.Message.Chat.ID, bot, "Command not recognized.") return err From adbad6a0fc79f5d11a7571a4e952d23619d00030 Mon Sep 17 00:00:00 2001 From: DRypa Date: Wed, 27 Dec 2023 20:12:40 +0300 Subject: [PATCH 29/32] #70 warnings fix --- bot/commands/code.go | 2 +- bot/commands/wrong.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/commands/code.go b/bot/commands/code.go index 94ff738..c514031 100644 --- a/bot/commands/code.go +++ b/bot/commands/code.go @@ -31,7 +31,7 @@ func (c ConfirmationCodeCommand) Accepted(message string) bool { } // Execute command -func (c ConfirmationCodeCommand) Execute(update tgbotapi.Update, bot *tgbotapi.BotAPI) error { +func (c ConfirmationCodeCommand) Execute(update tgbotapi.Update, _ *tgbotapi.BotAPI) error { userId, err := c.provider.GetUserId(update.Message.From.ID) if err != nil { return err diff --git a/bot/commands/wrong.go b/bot/commands/wrong.go index a819dee..d75e546 100644 --- a/bot/commands/wrong.go +++ b/bot/commands/wrong.go @@ -5,7 +5,7 @@ import "github.com/go-telegram-bot-api/telegram-bot-api" type WrongCommand struct { } -func (w WrongCommand) Accepted(message string) bool { +func (w WrongCommand) Accepted(_ string) bool { return true } From fd86d0cccd36682ce5419995b3299d31028b684c Mon Sep 17 00:00:00 2001 From: DRypa Date: Sat, 3 Feb 2024 15:40:04 +0300 Subject: [PATCH 30/32] #70 verification method implementation --- backend/device/service.go | 22 +++++++---- backend/internal/processor.go | 1 + backend/internal/server.go | 7 ++++ backend/nalogru/client.go | 59 +++++++++++++++++++++++++++++- backend/nalogru/device/device.go | 14 +++---- backend/users/processor.go | 63 ++++++++++++++++++++------------ 6 files changed, 126 insertions(+), 40 deletions(-) diff --git a/backend/device/service.go b/backend/device/service.go index 8fe85af..61f16db 100644 --- a/backend/device/service.go +++ b/backend/device/service.go @@ -39,6 +39,8 @@ func (s *Service) Add(ctx context.Context, d *device.Device) error { return errors.New("that device already added") } } + d.Update = s.updateDeviceFunc(ctx, *d) + forRent := ForRent{ Device: *d, IsRent: false, @@ -62,7 +64,7 @@ func (s *Service) Rent(ctx context.Context) (*device.Device, error) { } for _, v := range s.devices { if v.IsRent == false { - s.rent(ctx, &v) + s.rent(&v) return &v.Device, nil } } @@ -79,18 +81,15 @@ func (s *Service) RentDevice(ctx context.Context, d *device.Device) error { if v.IsRent { return errors.New("device is already used") } else { - s.rent(ctx, &v) + s.rent(&v) } } } return errors.New("device not found") } -func (s *Service) rent(ctx context.Context, v *ForRent) { +func (s *Service) rent(v *ForRent) { v.IsRent = true - v.Update = func(sessionId string, refreshToken string) error { - return s.Update(ctx, &v.Device, sessionId, refreshToken) - } } func (s *Service) Update(ctx context.Context, device *device.Device, sessionId string, refreshToken string) error { @@ -100,7 +99,7 @@ func (s *Service) Update(ctx context.Context, device *device.Device, sessionId s } // Free release the rented device -func (s *Service) Free(ctx context.Context, device *device.Device) error { +func (s *Service) Free(_ context.Context, device *device.Device) error { for _, v := range s.devices { if device.Id == v.Id { v.IsRent = false @@ -111,7 +110,7 @@ func (s *Service) Free(ctx context.Context, device *device.Device) error { } // All return all registered devices -func (s *Service) All(ctx context.Context) []*device.Device { +func (s *Service) All(_ context.Context) []*device.Device { res := make([]*device.Device, len(s.devices)) for i, d := range s.devices { res[i] = &d.Device @@ -126,8 +125,15 @@ func (s *Service) GetByUserId(ctx context.Context, userId string) (*device.Devic } for _, d := range devices { if d.UserId == userId { + d.Update = s.updateDeviceFunc(ctx, d) return &d, nil } } return nil, nil } + +func (s *Service) updateDeviceFunc(ctx context.Context, d device.Device) func(sessionId string, refreshToken string) error { + return func(sessionId string, refreshToken string) error { + return s.Update(ctx, &d, sessionId, refreshToken) + } +} diff --git a/backend/internal/processor.go b/backend/internal/processor.go index 1a4ea51..658366f 100644 --- a/backend/internal/processor.go +++ b/backend/internal/processor.go @@ -12,6 +12,7 @@ type AccountProcessor interface { GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUsersResponse, error) GetUser(ctx context.Context, in *api.GetUserRequest, opts ...grpc.CallOption) (*api.GetUserResponse, error) RegisterUser(ctx context.Context, in *api.UserRegistrationRequest, opts ...grpc.CallOption) (*api.UserRegistrationResponse, error) + VerifyPhone(ctx context.Context, req *api.PhoneVerificationRequest) (*api.ErrorResponse, error) } // ReceiptProcessor is an interface for process receipt requests. diff --git a/backend/internal/server.go b/backend/internal/server.go index fb6bf81..3ceeee3 100644 --- a/backend/internal/server.go +++ b/backend/internal/server.go @@ -51,7 +51,14 @@ func (s *server) GetRawReceipt(ctx context.Context, in *api.GetRawReceiptReportR return processor.GetRawReceipt(ctx, in) } +// RegisterUser add new user and send registration by phone request to nalog api. func (s *server) RegisterUser(ctx context.Context, req *api.UserRegistrationRequest) (*api.UserRegistrationResponse, error) { processor := *(s.accountProcessor) return processor.RegisterUser(ctx, req) } + +// VerifyPhone validate phone number through SMS. +func (s *server) VerifyPhone(ctx context.Context, req *api.PhoneVerificationRequest) (*api.ErrorResponse, error) { + processor := *(s.accountProcessor) + return processor.VerifyPhone(ctx, req) +} diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index dcbe7ca..9fea340 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -295,6 +295,19 @@ type RefreshResponse struct { RefreshToken string `json:"refresh_token"` } +type PhoneVerificationRequest struct { + ClientSecret string `json:"client_secret"` + Code string `json:"code"` + Phone string `json:"phone"` +} +type PhoneVerificationResponse struct { + SessionId string `json:"sessionId"` + RefreshToken string `json:"refresh_token"` + Phone string `json:"phone"` + Name string `json:"name"` + Email string `json:"email"` +} + // RefreshSession used to refresh session by RefreshToken. func (nalogruClient *Client) RefreshSession(device *device.Device) error { client := createHttpClient() @@ -337,6 +350,48 @@ func (nalogruClient *Client) RefreshSession(device *device.Device) error { return device.Update(response.SessionId, response.RefreshToken) } +func (nalogruClient *Client) VerifyPhone(device *device.Device, code string) error { + client := createHttpClient() + + payload := PhoneVerificationRequest{ + ClientSecret: device.ClientSecret, + Code: code, + Phone: device.Phone, + } + + resp, err := json.Marshal(payload) + if err != nil { + return err + } + reader := bytes.NewReader(resp) + + url := nalogruClient.BaseAddress + "/v2/auth/phone/verify" + request, err := http.NewRequest(http.MethodPost, url, reader) + addHeaders(request, device.Id.Hex()) + res, err := sendRequest(request, client) + + if err != nil { + log.Printf("Can't POST %s\n", url) + return err + } + defer dispose.Dispose(res.Body.Close, "Can't close HTTP response body") + + if res.StatusCode != http.StatusOK { + log.Printf("Phone verification error: %d\n", res.StatusCode) + return errors.New(fmt.Sprintf("HTTP error: %d", res.StatusCode)) + } + + response := &PhoneVerificationResponse{} + err = json.NewDecoder(res.Body).Decode(response) + if err != nil { + log.Println("Can't decode response body") + return err + } + log.Printf("%+v\n", response) + err = device.Update(response.SessionId, response.RefreshToken) + return err +} + func sendRequest(request *http.Request, client *http.Client) (*http.Response, error) { return client.Do(request) } @@ -346,11 +401,11 @@ func addAuth(request *http.Request, sessionId string) { } func addHeaders(request *http.Request, deviceId string) { - request.Header.Add("ClientVersion", "2.13.0") + request.Header.Add("ClientVersion", "2.24.1") request.Header.Add("Device-Id", deviceId) request.Header.Add("Device-OS", "Android") request.Header.Add("Connection", "Keep-Alive") request.Header.Add("Accept-Encoding", "gzip") - request.Header.Add("User-Agent", "okhttp/4.9.0") + request.Header.Add("User-Agent", "okhttp/5.0.0-alpha.2") request.Header.Add("Content-Type", "application/json; charset=utf-8") } diff --git a/backend/nalogru/device/device.go b/backend/nalogru/device/device.go index 044b960..6c2a295 100644 --- a/backend/nalogru/device/device.go +++ b/backend/nalogru/device/device.go @@ -3,11 +3,11 @@ package device import "go.mongodb.org/mongo-driver/bson/primitive" type Device struct { - ClientSecret string `bson:"client_secret"` - SessionId string `bson:"session_id"` - RefreshToken string `bson:"refresh_token"` - Id primitive.ObjectID `bson:"_id,omitempty"` - Update func(string, string) error `bson:"-"` - UserId string `bson:"user_id"` - Phone string `bson:"phone"` + ClientSecret string `bson:"client_secret"` + SessionId string `bson:"session_id"` + RefreshToken string `bson:"refresh_token"` + Id primitive.ObjectID `bson:"_id,omitempty"` + Update func(sessionId string, refreshToken string) error `bson:"-"` + UserId string `bson:"user_id"` + Phone string `bson:"phone"` } diff --git a/backend/users/processor.go b/backend/users/processor.go index 02cd263..cb25189 100644 --- a/backend/users/processor.go +++ b/backend/users/processor.go @@ -2,8 +2,6 @@ package users import ( "context" - "crypto/rand" - "encoding/base64" api "github.com/drypa/ReceiptCollector/api/inside" "google.golang.org/grpc" "receipt_collector/device" @@ -51,7 +49,7 @@ func (p Processor) GetLoginLink(ctx context.Context, in *api.GetLoginLinkRequest } // GetUsers returns all registered users. -func (p Processor) GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUsersResponse, error) { +func (p Processor) GetUsers(ctx context.Context, _ *api.NoParams) (*api.GetUsersResponse, error) { all, err := p.repository.GetAll(ctx) if err != nil { return nil, err @@ -72,7 +70,7 @@ func (p Processor) GetUsers(ctx context.Context, req *api.NoParams) (*api.GetUse } // GetUser get user by telegramId. -func (p Processor) GetUser(ctx context.Context, in *api.GetUserRequest, opts ...grpc.CallOption) (*api.GetUserResponse, error) { +func (p Processor) GetUser(ctx context.Context, in *api.GetUserRequest, _ ...grpc.CallOption) (*api.GetUserResponse, error) { user, err := p.repository.GetByTelegramId(ctx, int(in.TelegramId)) if err != nil { return nil, err @@ -86,7 +84,7 @@ func (p Processor) GetUser(ctx context.Context, in *api.GetUserRequest, opts ... return &response, err } -func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationRequest, opts ...grpc.CallOption) (*api.UserRegistrationResponse, error) { +func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationRequest, _ ...grpc.CallOption) (*api.UserRegistrationResponse, error) { user, err := p.repository.GetByTelegramId(ctx, int(in.TelegramId)) if err != nil { return nil, err @@ -103,19 +101,7 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq return nil, err } if d == nil { - d := &nalogDevice.Device{ - ClientSecret: p.clientSecret, - SessionId: "", - RefreshToken: "", - Update: nil, - UserId: userId, - Phone: in.PhoneNumber, - } - d.Update = func(sessionId string, refreshToken string) error { - return p.deviceService.Update(ctx, d, sessionId, refreshToken) - } - - err = p.deviceService.Add(ctx, d) + err := p.addNewDevice(ctx, in.PhoneNumber, userId) if err != nil { return nil, err } @@ -125,14 +111,45 @@ func (p Processor) RegisterUser(ctx context.Context, in *api.UserRegistrationReq return &api.UserRegistrationResponse{UserId: userId}, nil } -func generateRandomSecret() (*string, error) { - buf := make([]byte, 20) - _, err := rand.Read(buf) +func (p Processor) addNewDevice(ctx context.Context, phoneNumber string, userId string) error { + d := &nalogDevice.Device{ + ClientSecret: p.clientSecret, + SessionId: "", + RefreshToken: "", + Update: nil, + UserId: userId, + Phone: phoneNumber, + } + err := p.deviceService.Add(ctx, d) + return err +} + +// VerifyPhone validate phone number through SMS. +func (p Processor) VerifyPhone(ctx context.Context, req *api.PhoneVerificationRequest) (*api.ErrorResponse, error) { + user, err := p.repository.GetByTelegramId(ctx, int(req.TelegramId)) + if err != nil { + return nil, err + } + if user == nil { + response := api.ErrorResponse{ + Error: "User not found", + } + return &response, nil + } + userId := user.Id.Hex() + d, err := p.deviceService.GetByUserId(ctx, userId) if err != nil { return nil, err } - encoded := base64.StdEncoding.EncodeToString(buf) - return &encoded, nil + if d == nil { + response := api.ErrorResponse{ + Error: "Registration process not started", + } + return &response, nil + } + + err = p.nalogClient.VerifyPhone(d, req.Code) + return &api.ErrorResponse{Error: "TODO"}, err } func (p Processor) addNewUser(ctx context.Context, telegramId int) (*User, error) { From 311c93218e2024d78f1138c75c348c356c9fd3ed Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 4 Feb 2024 15:29:53 +0300 Subject: [PATCH 31/32] #70 verification command registration added --- bot/main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bot/main.go b/bot/main.go index 152212b..ec51178 100644 --- a/bot/main.go +++ b/bot/main.go @@ -44,6 +44,9 @@ func createCommandsRegistrar(grpcClient *backend.GrpcClient, users *user.Provide register := commands.NewRegisterCommand(users, grpcClient) registrar.Register(register) + code := commands.NewConfirmationCodeCommand(users, grpcClient) + registrar.Register(code) + getReceiptReport := commands.NewGetReceiptReportCommand(users, grpcClient) registrar.Register(getReceiptReport) From 59b87eba7161d551458f2ff4ee9f5686df15c73c Mon Sep 17 00:00:00 2001 From: DRypa Date: Sun, 4 Feb 2024 15:32:41 +0300 Subject: [PATCH 32/32] #70 revert request headers with updated version server did return 422 response --- backend/nalogru/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/nalogru/client.go b/backend/nalogru/client.go index 9fea340..0c8128a 100644 --- a/backend/nalogru/client.go +++ b/backend/nalogru/client.go @@ -401,11 +401,11 @@ func addAuth(request *http.Request, sessionId string) { } func addHeaders(request *http.Request, deviceId string) { - request.Header.Add("ClientVersion", "2.24.1") + request.Header.Add("ClientVersion", "2.13.0") request.Header.Add("Device-Id", deviceId) request.Header.Add("Device-OS", "Android") request.Header.Add("Connection", "Keep-Alive") request.Header.Add("Accept-Encoding", "gzip") - request.Header.Add("User-Agent", "okhttp/5.0.0-alpha.2") + request.Header.Add("User-Agent", "okhttp/4.9.0") request.Header.Add("Content-Type", "application/json; charset=utf-8") }