Skip to content

Commit

Permalink
[report]: add daily report
Browse files Browse the repository at this point in the history
  • Loading branch information
vkopitsa committed Aug 11, 2023
1 parent d6bb567 commit 40dfc33
Show file tree
Hide file tree
Showing 17 changed files with 481 additions and 109 deletions.
2 changes: 2 additions & 0 deletions .env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ TELEGRAM_TOKEN=
MONO_TOKENS=
TELEGRAM_ADMINS=
TELEGRAM_CHATS=
# <minute> <hour> <day> <month> <weekday>
SCHEDULE_TIME= 0 21 * * *

# More info https://github.com/rs/zerolog#leveled-logging
LOG_LEVEL=info
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.21.x

- uses: actions/checkout@v3

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18.x
go-version: 1.21.x
- uses: actions/checkout@v3

- uses: actions/cache@v3
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# builder
FROM golang:1.18-alpine as builder
FROM golang:1.21-alpine as builder

WORKDIR /

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ A simple telegram bot, written in Go with the [telegram-bot-api](https://github.

![mono_personal_tgbot](Resources/screenshot.png)

![mono_personal_tgbot](Resources/screenshot1.png)

## Usage

Run `mono_personal_tgbot` execution file in your terminal with following env variables
Expand All @@ -19,6 +21,7 @@ Run `mono_personal_tgbot` execution file in your terminal with following env var
`TELEGRAM_TOKEN` | [How to get telegram bot token](https://core.telegram.org/bots#3-how-do-i-create-a-bot)
`TELEGRAM_ADMINS` | ids of the trusted user, example: `1234567,1234567`
`TELEGRAM_CHATS` | ids of the trusted chats, example: `-1234567,-1234567`
`SCHEDULE_TIME` | set time for daily report, example: `0 21 * * *`
`MONO_TOKENS` | [How to get monobank token](https://api.monobank.ua/)

### Telegram commands
Expand Down
Binary file added Resources/screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,23 @@ func main() {
log.Panic().Err(err)
}

// init Schedule Report
isScheduleReportEnabled := os.Getenv("SCHEDULE_TIME") != ""
var scheduleReport *ScheduleReport
if isScheduleReportEnabled {
scheduleReport, err = NewScheduleReport(os.Getenv("SCHEDULE_TIME"))
if err != nil {
log.Panic().Err(err)
}
}

go bot.TelegramStart(os.Getenv("TELEGRAM_TOKEN"))
go bot.ProcessingStart()

if isScheduleReportEnabled {
go scheduleReport.Start(bot.ScheduleReport)
}

// run http server
bot.WebhookStart()
}
53 changes: 32 additions & 21 deletions bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
Expand All @@ -11,6 +12,7 @@ import (
"reflect"
"strconv"
"strings"
"time"

"github.com/rs/zerolog/log"

Expand All @@ -32,6 +34,7 @@ type Bot interface {
TelegramStart(token string)
WebhookStart()
ProcessingStart()
ScheduleReport(ctx context.Context) (int, error)
}

// bot is implementation the Bot interface
Expand All @@ -47,6 +50,8 @@ type bot struct {
statementTmpl *template.Template
balanceTmpl *template.Template
webhookTmpl *template.Template

mono *Mono
}

// New returns a bot object.
Expand Down Expand Up @@ -76,6 +81,7 @@ func New(telegramAdmins, telegramChats string) Bot {
statementTmpl: statementTmpl,
balanceTmpl: balanceTmpl,
webhookTmpl: webhookTmpl,
mono: NewMono(),
}

return &b
Expand All @@ -90,7 +96,7 @@ func (b *bot) InitMonoClients(monoTokens string) error {
clients := make([]Client, 0, len(monoTokensArr))
for _, monoToken := range monoTokensArr {

client := NewClient(monoToken)
client := NewClient(monoToken, b.mono)
if err := client.Init(); err != nil {
return err
}
Expand Down Expand Up @@ -448,7 +454,12 @@ func (b *bot) WebhookStart() {
fmt.Fprintf(w, "Ok!")
})

err := http.ListenAndServe(":8080", nil)
server := &http.Server{
Addr: ":8080",
ReadHeaderTimeout: 5 * time.Minute,
}

err := server.ListenAndServe()
if err != nil {
log.Panic().Err(err).Msg("[webhook] serve")
}
Expand All @@ -457,23 +468,6 @@ func (b *bot) WebhookStart() {
// ProcessingStart starts processing data that received from chennal.
func (b *bot) ProcessingStart() {

sendTo := func(chatIds, message string) error {
ids := strings.Split(strings.Trim(chatIds, " "), ",")
for _, id := range ids {
chatID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return err
}

_, err = b.BotAPI.Send(tgbotapi.NewMessage(chatID, message))
if err != nil {
return err
}
}

return nil
}

for {
statementItemData := <-b.ch

Expand Down Expand Up @@ -508,14 +502,14 @@ func (b *bot) ProcessingStart() {
message := tpl.String()

// to chat
err = sendTo(b.telegramChats, message)
err = b.sendTo(b.telegramChats, message)
if err != nil {
log.Error().Err(err).Msg("[processing] send to chat")
continue
}

// to admin
err = sendTo(b.telegramAdmins, message)
err = b.sendTo(b.telegramAdmins, message)
if err != nil {
log.Error().Err(err).Msg("[processing] send to admin")
continue
Expand Down Expand Up @@ -634,6 +628,23 @@ func (b *bot) sendBalanceByClient(client Client, tgMessage *tgbotapi.Message) er
return err
}

func (b *bot) sendTo(chatIds, message string) error {
ids := strings.Split(strings.Trim(chatIds, " "), ",")
for _, id := range ids {
chatID, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return err
}

_, err = b.BotAPI.Send(tgbotapi.NewMessage(chatID, message))
if err != nil {
return err
}
}

return nil
}

func sendAccountButtonsEditMessage(prefix string, client Client, message tgbotapi.Message) (*tgbotapi.EditMessageTextConfig, error) {
messageConfig, inlineKeyboardMarkup, _ := buildAccountButtons[tgbotapi.EditMessageTextConfig](prefix, client, message)
messageConfig.Text = fmt.Sprintf("%s\nВиберіть рахунок:", client.GetName())
Expand Down
93 changes: 11 additions & 82 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,9 @@ package main

import (
"errors"
"fmt"
"hash/fnv"
"net/http"
"strings"
"time"

"github.com/rs/zerolog/log"

"golang.org/x/time/rate"
)

// StatementItem is a statement data
Expand Down Expand Up @@ -73,26 +67,27 @@ type client struct {
Info *ClientInfo
id uint32
token string
limiter *rate.Limiter
reports map[string]Report
mono *Mono
}

// NewClient returns a client object.
func NewClient(token string) Client {
func NewClient(token string, mono *Mono) Client {

h := fnv.New32a()
h.Write([]byte(token))

return &client{
limiter: rate.NewLimiter(rate.Every(time.Second*30), 1),
token: token,
id: h.Sum32(),
reports: make(map[string]Report),
mono: mono,
}
}

func (c *client) Init() error {
_, err := c.GetInfo()
info, err := c.GetInfo()
c.Info = &info
return err
}

Expand All @@ -109,19 +104,14 @@ func (c *client) GetReport(accountId string) Report {
}

func (c *client) GetInfo() (ClientInfo, error) {
if c.limiter.Allow() {
log.Debug().Msg("[monoapi] get info")
info, err := c.getClientInfo()
c.Info = &info
return info, err
}

if c.Info != nil {
return *c.Info, nil
}

log.Warn().Msg("[monoapi] get info, waiting")
return ClientInfo{}, errors.New("please waiting and then try again")
log.Debug().Msg("[monoapi] get info")
info, err := c.mono.GetClientInfo(c.token)
c.Info = &info
return *c.Info, err
}

// GetName return name of the client
Expand All @@ -134,20 +124,7 @@ func (c client) GetName() string {

// SetWebHook is a function set up the monobank webhook.
func (c client) SetWebHook(url string) (WebHookResponse, error) {
response := WebHookResponse{}

payload := strings.NewReader(fmt.Sprintf("{\"webHookUrl\": \"%s\"}", url))

req, err := http.NewRequest("POST", "https://api.monobank.ua/personal/webhook", payload)
if err != nil {
log.Error().Err(err).Msg("[monoapi] webhook, NewRequest")
return response, err
}

req.Header.Add("X-Token", c.token)
req.Header.Add("content-type", "application/json")

return DoRequest(response, req)
return c.mono.SetWebHook(url, c.token)
}

func (c *client) GetAccountByID(id string) (*Account, error) {
Expand All @@ -167,53 +144,5 @@ func (c *client) ResetReport(accountId string) {
}

func (c client) GetStatement(command string, accountId string) ([]StatementItem, error) {
if c.limiter.Allow() {
return c.getStatement(command, accountId)
}

log.Warn().Msg("[monoapi] statement, waiting")
return []StatementItem{}, errors.New("please waiting and then try again")
}

func (c client) getStatement(command, account string) ([]StatementItem, error) {

statementItems := []StatementItem{}

from, to, err := getTimeRangeByPeriod(command)
if err != nil {
log.Error().Err(err).Msg("[monoapi] statements, range")
return statementItems, err
}

log.Debug().Msgf("[monoapi] statements, range from: %d, to: %d", from, to)

url := fmt.Sprintf("https://api.monobank.ua/personal/statement/%s/%d", account, from)
if to > 0 {
url = fmt.Sprintf("%s/%d", url, to)
}

req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error().Err(err).Msg("[monoapi] statements, NewRequest")
return statementItems, err
}

req.Header.Add("x-token", c.token)

return DoRequest(statementItems, req)
}

func (c client) getClientInfo() (ClientInfo, error) {
var clientInfo ClientInfo

url := "https://api.monobank.ua/personal/client-info"
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Error().Err(err).Msg("[monoapi] client info, create request")
return clientInfo, err
}

req.Header.Add("x-token", c.token)

return DoRequest(clientInfo, req)
return c.mono.GetStatement(command, accountId, c.token)
}
7 changes: 6 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ services:
dockerfile: Dockerfile
environment:
- TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
- MONO_TOKEN=${MONO_TOKEN}
- MONO_TOKENS=${MONO_TOKENS}
- TELEGRAM_ADMINS=${TELEGRAM_ADMINS}
- TELEGRAM_CHATS=${TELEGRAM_CHATS}
- LOG_LEVEL=${LOG_LEVEL}
- SCHEDULE_TIME=${SCHEDULE_TIME}
ports:
- ${APP_PORT}:8080
logging:
options:
max-file: 5
max-size: 15m
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ require (
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
)

require (
github.com/adhocore/gronx v1.6.5 // indirect
github.com/samber/lo v1.38.1 // indirect
)

require (
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/adhocore/gronx v1.6.5 h1:/pryEagBKz3WqUgpgvtL51eBN2rJLXowuW7rpS+jrew=
github.com/adhocore/gronx v1.6.5/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
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=
Expand All @@ -11,6 +13,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/snabb/isoweek v1.0.1 h1:B4IsN2GU8lCNVkaUUgOzaVpPkKC2DdY9zcnxz5yc0qg=
github.com/snabb/isoweek v1.0.1/go.mod h1:CAijAxH7NMgjqGc9baHMDE4sTHMt4B/f6X/XLiEE1iA=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
Expand Down
Loading

0 comments on commit 40dfc33

Please sign in to comment.