You've already forked monobank-firefly3-bot
Compare commits
28 Commits
1646d484fb
...
master
Author | SHA1 | Date | |
---|---|---|---|
a5c9403576
|
|||
ebb213a3cc
|
|||
f947606131
|
|||
464093e5bd
|
|||
f96f88d16e
|
|||
686c46bf78
|
|||
38b4e89a02
|
|||
3986e1c9de
|
|||
f0e26a0cd2
|
|||
00657a8660
|
|||
39459202f1
|
|||
896e91e61e
|
|||
950d6649b2
|
|||
a3e2a462a3
|
|||
34dce58d91
|
|||
de69376bff
|
|||
e0d4758540
|
|||
efb5bf0b3b
|
|||
daf5f8fb03
|
|||
b05b1b9b48
|
|||
074c32a8f1
|
|||
917d6d9103
|
|||
540eef981f
|
|||
7c03492676
|
|||
cd31de87e5
|
|||
25212fbd1c
|
|||
fb7796c475
|
|||
e1c9f56921
|
34
.gitea/workflows/build-docker-image.yaml
Normal file
34
.gitea/workflows/build-docker-image.yaml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name: build docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: catthehacker/ubuntu:act-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: gitea.stuzer.link
|
||||||
|
username: ${{ gitea.repository_owner }}
|
||||||
|
password: ${{ secrets.DOCKER_REGISTRY_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64
|
||||||
|
push: true
|
||||||
|
tags: gitea.stuzer.link/stuzer05/monobank-firefly3-bot:latest
|
||||||
|
cache-from: type=registry,ref=gitea.stuzer.link/stuzer05/monobank-firefly3-bot:latest
|
||||||
|
cache-to: type=inline
|
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
FROM golang:1.23.2 AS builder
|
||||||
|
|
||||||
|
# Install certificates
|
||||||
|
RUN apt-get update && apt-get install -y ca-certificates
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY go.mod go.sum .
|
||||||
|
|
||||||
|
RUN go mod download && go mod verify
|
||||||
|
|
||||||
|
# Copy project files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build
|
||||||
|
RUN make
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
COPY --from=builder /app/monobank-firefly3-bot /app
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app"]
|
@ -12,6 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ func ImportTransaction(monobankTransaction monobank.WebHookResponse) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// find matching transaction to delete
|
// find matching transaction to adjust/delete
|
||||||
isDeleted := false
|
isDeleted := false
|
||||||
for _, tRows := range oldTransactions.Data {
|
for _, tRows := range oldTransactions.Data {
|
||||||
if isDeleted {
|
if isDeleted {
|
||||||
@ -69,31 +70,74 @@ func ImportTransaction(monobankTransaction monobank.WebHookResponse) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// read monobank transaction
|
// read monobank transaction
|
||||||
var monobankTransaction monobank.WebHookResponse
|
var monobankTransactionOld monobank.WebHookResponse
|
||||||
err = json.Unmarshal(notesBytes, &monobankTransaction)
|
err = json.Unmarshal(notesBytes, &monobankTransactionOld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse amounts
|
||||||
|
sumNew := int64(math.Abs(math.Round(monobankTransaction.Data.StatementItem.Amount/100))) - int64(math.Abs(math.Round(monobankTransaction.Data.StatementItem.CommissionRate/100)))
|
||||||
|
sumOldFloat, _ := strconv.ParseFloat(tRow.Amount, 64)
|
||||||
|
sumOld := int64(sumOldFloat)
|
||||||
|
|
||||||
// find transaction
|
// find transaction
|
||||||
sum := int(math.Abs(math.Round(monobankTransaction.Data.StatementItem.Amount/100))) - int(math.Abs(math.Round(monobankTransaction.Data.StatementItem.CommissionRate/100)))
|
if slices.Contains(row.Names, monobankTransactionOld.Data.StatementItem.Description) {
|
||||||
sum2, _ := strconv.ParseFloat(tRow.Amount, 64)
|
if sumNew == sumOld {
|
||||||
if slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description) && sum == int(sum2) {
|
|
||||||
// delete transaction
|
// delete transaction
|
||||||
opts := firefly3.TransactionsApiDeleteTransactionOpts{}
|
opts := firefly3.TransactionsApiDeleteTransactionOpts{}
|
||||||
_, err := App().Firefly3Client.TransactionsApi.DeleteTransaction(context.Background(), tRows.Id, &opts)
|
_, err := App().Firefly3Client.TransactionsApi.DeleteTransaction(context.Background(), tRows.Id, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// adjust transaction
|
||||||
|
opts := firefly3.TransactionsApiUpdateTransactionOpts{}
|
||||||
|
body := firefly3.TransactionUpdate{
|
||||||
|
Transactions: []firefly3.TransactionSplitUpdate{
|
||||||
|
{
|
||||||
|
Description: tRow.Description,
|
||||||
|
CategoryId: tRow.CategoryId,
|
||||||
|
DestinationId: tRow.DestinationId,
|
||||||
|
SourceId: tRow.SourceId,
|
||||||
|
CurrencyId: tRow.CurrencyId,
|
||||||
|
ExternalUrl: tRow.ExternalUrl,
|
||||||
|
Date: tRow.Date,
|
||||||
|
DueDate: tRow.DueDate,
|
||||||
|
Tags: tRow.Tags,
|
||||||
|
Notes: tRow.Notes,
|
||||||
|
// Notes: string(monobankTransactionJson),
|
||||||
|
Amount: strconv.FormatInt(sumOld-sumNew, 10),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, _, err := App().Firefly3Client.TransactionsApi.UpdateTransaction(context.Background(), body, tRows.Id, &opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isDeleted = true
|
isDeleted = true // break 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
|
// check name match
|
||||||
|
isDescriptionMatch := false
|
||||||
|
if row.NamesLooseMatch {
|
||||||
|
for _, name := range row.Names {
|
||||||
|
if strings.HasPrefix(monobankTransaction.Data.StatementItem.Description, name) {
|
||||||
|
isDescriptionMatch = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isDescriptionMatch = slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description)
|
||||||
|
}
|
||||||
|
|
||||||
// check name & mcc
|
// check name & mcc
|
||||||
if !(slices.Contains(row.Names, monobankTransaction.Data.StatementItem.Description) || slices.Contains(row.MccCodes, int(monobankTransaction.Data.StatementItem.Mcc))) {
|
if !(isDescriptionMatch || slices.Contains(row.MccCodes, int(monobankTransaction.Data.StatementItem.Mcc))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +179,7 @@ func ImportTransaction(monobankTransaction monobank.WebHookResponse) error {
|
|||||||
|
|
||||||
// when transfer between different currencies, convert
|
// when transfer between different currencies, convert
|
||||||
sourceAccount := App().Config.GetAccountByFirefly3Name(firefly3Transaction.SourceName)
|
sourceAccount := App().Config.GetAccountByFirefly3Name(firefly3Transaction.SourceName)
|
||||||
if sourceAccount.Currency != destAccount.Currency {
|
if len(sourceAccount.Currency) > 0 && sourceAccount.Currency != destAccount.Currency {
|
||||||
// swap amounts
|
// swap amounts
|
||||||
firefly3Transaction.ForeignAmount = firefly3Transaction.Amount
|
firefly3Transaction.ForeignAmount = firefly3Transaction.Amount
|
||||||
firefly3Transaction.Amount = strconv.Itoa(int(math.Abs(math.Round(monobankTransaction.Data.StatementItem.OperationAmount / 100))))
|
firefly3Transaction.Amount = strconv.Itoa(int(math.Abs(math.Round(monobankTransaction.Data.StatementItem.OperationAmount / 100))))
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
@ -20,3 +22,49 @@ func LogString(str string) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LogContainsTransactionID(transactionID string) (bool, error) {
|
||||||
|
if len(os.Getenv("LOG_FILE")) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// open the log file for reading.
|
||||||
|
logFile, err := os.Open(os.Getenv("LOG_FILE"))
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("error opening log file: %w", err)
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
|
||||||
|
// create a new scanner to read the log file line by line.
|
||||||
|
scanner := bufio.NewScanner(logFile)
|
||||||
|
|
||||||
|
// iterate over each line of the log file.
|
||||||
|
for scanner.Scan() {
|
||||||
|
// unmarshal the JSON data from the current line.
|
||||||
|
var transactionData struct {
|
||||||
|
Data struct {
|
||||||
|
StatementItem struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"statementItem"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(scanner.Bytes(), &transactionData)
|
||||||
|
if err != nil {
|
||||||
|
// skip lines that are not valid JSON.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the transaction ID matches the given ID.
|
||||||
|
if transactionData.Data.StatementItem.ID == transactionID {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for any errors that occurred during scanning.
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return false, fmt.Errorf("error scanning log file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// transaction ID not found in the log file.
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Accounts []ConfigAccount `json:"accounts"`
|
Accounts []Account `json:"accounts"`
|
||||||
TransactionTypes []ConfigTransactionTypes `json:"transaction_types"`
|
TransactionTypes []TransactionTypes `json:"transaction_types"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigAccount struct {
|
type Account struct {
|
||||||
Firefly3Name string `json:"firefly3_name,omitempty"`
|
Firefly3Name string `json:"firefly3_name,omitempty"`
|
||||||
MonobankId string `json:"monobank_id,omitempty"`
|
MonobankId string `json:"monobank_id,omitempty"`
|
||||||
Currency string `json:"currency,omitempty"`
|
Currency string `json:"currency,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigTransactionTypes struct {
|
type TransactionTypes struct {
|
||||||
Names []string `json:"names,omitempty"`
|
Names []string `json:"names,omitempty"`
|
||||||
NamesRefund []string `json:"names_refund,omitempty"`
|
NamesRefund []string `json:"names_refund,omitempty"`
|
||||||
|
NamesLooseMatch bool `json:"names_loose_match,omitempty"` // "name%" match
|
||||||
MccCodes []int `json:"mcc_codes,omitempty"`
|
MccCodes []int `json:"mcc_codes,omitempty"`
|
||||||
Firefly3 ConfigTransactionTypeFirefly3 `json:"firefly3,omitempty"`
|
Firefly3 TransactionTypeFirefly3 `json:"firefly3,omitempty"`
|
||||||
SumMax int `json:"sum_max,omitempty"`
|
SumMax int `json:"sum_max,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigTransactionTypeFirefly3 struct {
|
type TransactionTypeFirefly3 struct {
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
Destination string `json:"destination,omitempty"`
|
Destination string `json:"destination,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
func (c *Config) GetAccountByMonobankId(q string) ConfigAccount {
|
func (c *Config) GetAccountByMonobankId(q string) Account {
|
||||||
for _, row := range c.Accounts {
|
for _, row := range c.Accounts {
|
||||||
if row.MonobankId == q {
|
if row.MonobankId == q {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConfigAccount{}
|
return Account{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetAccountByFirefly3Name(q string) ConfigAccount {
|
func (c *Config) GetAccountByFirefly3Name(q string) Account {
|
||||||
for _, row := range c.Accounts {
|
for _, row := range c.Accounts {
|
||||||
if row.Firefly3Name == q {
|
if row.Firefly3Name == q {
|
||||||
return row
|
return row
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ConfigAccount{}
|
return Account{}
|
||||||
}
|
}
|
||||||
|
5
go.mod
5
go.mod
@ -4,11 +4,12 @@ go 1.23
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.stuzer.link/stuzer05/go-firefly3/v2 v2.1.0
|
gitea.stuzer.link/stuzer05/go-firefly3/v2 v2.1.0
|
||||||
gitea.stuzer.link/stuzer05/go-monobank v0.2303.0
|
gitea.stuzer.link/stuzer05/go-monobank v0.2303.1
|
||||||
github.com/antihax/optional v1.0.0
|
github.com/antihax/optional v1.0.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/oauth2 v0.22.0 // indirect
|
github.com/sanity-io/litter v1.5.5 // indirect
|
||||||
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
)
|
)
|
||||||
|
4
go.sum
4
go.sum
@ -2,6 +2,8 @@ gitea.stuzer.link/stuzer05/go-firefly3/v2 v2.1.0 h1:t+FOFg48PPN2n6SO6PwbqVpHGqYK
|
|||||||
gitea.stuzer.link/stuzer05/go-firefly3/v2 v2.1.0/go.mod h1:FNdERhJjtqfkBQqR2EvB7T3h6eGJVid4xcBCUu1/9FU=
|
gitea.stuzer.link/stuzer05/go-firefly3/v2 v2.1.0/go.mod h1:FNdERhJjtqfkBQqR2EvB7T3h6eGJVid4xcBCUu1/9FU=
|
||||||
gitea.stuzer.link/stuzer05/go-monobank v0.2303.0 h1:BDv3h9bk5Fs3iI51W+pXmjPBAQn9T+YZu+B/lmbeBqc=
|
gitea.stuzer.link/stuzer05/go-monobank v0.2303.0 h1:BDv3h9bk5Fs3iI51W+pXmjPBAQn9T+YZu+B/lmbeBqc=
|
||||||
gitea.stuzer.link/stuzer05/go-monobank v0.2303.0/go.mod h1:y/jOBU1U+NNR5umvDH+scrOWk0byZGZrMb4PHEehyrk=
|
gitea.stuzer.link/stuzer05/go-monobank v0.2303.0/go.mod h1:y/jOBU1U+NNR5umvDH+scrOWk0byZGZrMb4PHEehyrk=
|
||||||
|
gitea.stuzer.link/stuzer05/go-monobank v0.2303.1 h1:2IdaL70SVyb3aUqlC6TNNi9VOyliYDdiqKSzhCwHmaE=
|
||||||
|
gitea.stuzer.link/stuzer05/go-monobank v0.2303.1/go.mod h1:sZvm8Jhtwpup3/X0acYuT0b+v+r4X0SnCBrJxQjlqkw=
|
||||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@ -19,3 +21,5 @@ golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
|||||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
|
||||||
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
11
main.go
11
main.go
@ -17,10 +17,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// load .env
|
// load .env
|
||||||
err := godotenv.Load(".env")
|
godotenv.Load(".env")
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("error loading .env file")
|
|
||||||
}
|
|
||||||
|
|
||||||
// init app
|
// init app
|
||||||
app.Init()
|
app.Init()
|
||||||
@ -63,13 +60,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
} else if len(*flagMonobankDoTransaction) > 0 {
|
} else if len(*flagMonobankDoTransaction) > 0 {
|
||||||
var monobankTransaction monobank.WebHookResponse
|
var monobankTransaction monobank.WebHookResponse
|
||||||
err = json.Unmarshal([]byte(*flagMonobankDoTransaction), &monobankTransaction)
|
err := json.Unmarshal([]byte(*flagMonobankDoTransaction), &monobankTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := app.ImportTransaction(monobankTransaction)
|
err = app.ImportTransaction(monobankTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@ -81,7 +78,7 @@ func main() {
|
|||||||
// register monobank webhook
|
// register monobank webhook
|
||||||
_, err := app.App().MonobankClient.Api.PersonalWebhookPost(context.Background(), monobank.SetWebHook{WebHookUrl: webhookUrl}, os.Getenv("MONOBANK_TOKEN"))
|
_, err := app.App().MonobankClient.Api.PersonalWebhookPost(context.Background(), monobank.SetWebHook{WebHookUrl: webhookUrl}, os.Getenv("MONOBANK_TOKEN"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("failed to register monobank webhook")
|
log.Fatalln("failed to register monobank webhook: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// set webhook
|
// set webhook
|
||||||
|
18
webhook.go
18
webhook.go
@ -9,6 +9,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func handleWebhook(w http.ResponseWriter, r *http.Request) {
|
func handleWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Parse URL query parameters
|
||||||
|
queryParams := r.URL.Query()
|
||||||
|
isRetry := queryParams.Get("retry") == "true"
|
||||||
|
|
||||||
// read request body bytes
|
// read request body bytes
|
||||||
body, err := io.ReadAll(r.Body)
|
body, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -18,7 +22,7 @@ func handleWebhook(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log body string
|
// log body string
|
||||||
app.LogString(string(body))
|
defer app.LogString(string(body))
|
||||||
|
|
||||||
// check request empty body
|
// check request empty body
|
||||||
if len(string(body)) == 0 {
|
if len(string(body)) == 0 {
|
||||||
@ -36,6 +40,18 @@ func handleWebhook(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only check for logged transaction if not a retry
|
||||||
|
if !isRetry {
|
||||||
|
isTransactionAlreadyLogged, err := app.LogContainsTransactionID(monobankTransaction.Data.StatementItem.Id)
|
||||||
|
if err != nil {
|
||||||
|
app.LogString(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isTransactionAlreadyLogged {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = app.ImportTransaction(monobankTransaction)
|
err = app.ImportTransaction(monobankTransaction)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.LogString(err.Error())
|
app.LogString(err.Error())
|
||||||
|
Reference in New Issue
Block a user