Compare commits
32 Commits
922e770405
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e9e9702d5 | |||
| 6a6aff0274 | |||
| 615c479179 | |||
| 2e1dbbc6be | |||
| 7b892730f2 | |||
| edfdf4affb | |||
| ddca8c456f | |||
| 09fe5f2324 | |||
| 3f9447f0b8 | |||
| a35a5c8efc | |||
| ada1a97999 | |||
| d1f2c359a2 | |||
| c3d00b5dec | |||
| 12ffc3461f | |||
| f1f2ae721b | |||
| 4509aae6a8 | |||
| 11d3adf8db | |||
| 92634ce744 | |||
| 4ceb63a307 | |||
| 64345c9350 | |||
| b771745c47 | |||
| 9e6681da30 | |||
| bce61e698a | |||
| 823786c3ea | |||
| e3d2ba38a8 | |||
| e24c6813a7 | |||
| 871b6c6dcd | |||
| c52a1f7f40 | |||
| ff9a9ca154 | |||
| d6d2e06a72 | |||
| 87157b2081 | |||
| 01e132628c |
+6
-1
@@ -6,7 +6,12 @@ steps:
|
|||||||
- name: build
|
- name: build
|
||||||
image: golang
|
image: golang
|
||||||
commands:
|
commands:
|
||||||
- go build -o valera_tg_bot main.go
|
- go build -o valera_tg_bot cmd/valera/main.go
|
||||||
|
|
||||||
|
- name: test
|
||||||
|
image: golang
|
||||||
|
commands:
|
||||||
|
- go test ./...
|
||||||
|
|
||||||
- name: scp
|
- name: scp
|
||||||
image: appleboy/drone-scp
|
image: appleboy/drone-scp
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "go",
|
|
||||||
"name": "run",
|
|
||||||
"goExecPath": "/usr/local/go/bin/go",
|
|
||||||
"buildParams": [
|
|
||||||
"$PROJECT_DIR$/main.go",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"valera/internal/config"
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
"valera/internal/states/clear_bot_state"
|
||||||
|
"valera/internal/states/eat_bot_state"
|
||||||
|
"valera/internal/states/go_bot_state"
|
||||||
|
"valera/internal/states/help_bot_state"
|
||||||
|
"valera/internal/states/pause_bot_state"
|
||||||
|
"valera/internal/states/ping_bot_state"
|
||||||
|
"valera/internal/states/start_bot_state"
|
||||||
|
"valera/internal/states/stat_bot_state"
|
||||||
|
"valera/internal/states/weight_bot_state"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
version = "v1.9.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg := config.NewAppConfig()
|
||||||
|
|
||||||
|
bot, err := tgbot.NewBotAPI(cfg.TgConfig.Token)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dataBase, err := db.NewDB(cfg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u := tgbot.NewUpdate(0)
|
||||||
|
u.Timeout = 60
|
||||||
|
|
||||||
|
updates, err := bot.GetUpdatesChan(u)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
botStates := []states.BotState{
|
||||||
|
clear_bot_state.NewClearBotState(bot, dataBase),
|
||||||
|
go_bot_state.NewGoBotState(bot, dataBase),
|
||||||
|
stat_bot_state.NewStatBotState(bot, dataBase),
|
||||||
|
start_bot_state.NewStartBotState(bot, dataBase, version),
|
||||||
|
help_bot_state.NewHelpBotState(bot, dataBase),
|
||||||
|
ping_bot_state.NewPingBotState(bot, dataBase),
|
||||||
|
pause_bot_state.NewPauseBotState(bot, dataBase),
|
||||||
|
eat_bot_state.NewEatBotState(bot, dataBase),
|
||||||
|
weight_bot_state.NewWeightBotState(bot, dataBase),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for _, s := range botStates {
|
||||||
|
http.HandleFunc(s.GetHandler())
|
||||||
|
}
|
||||||
|
log.Println("Server is start up! port", cfg.Port)
|
||||||
|
log.Fatal(http.ListenAndServe(cfg.Port, nil))
|
||||||
|
}()
|
||||||
|
|
||||||
|
for update := range updates {
|
||||||
|
|
||||||
|
if update.Message == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
text := update.Message.Text
|
||||||
|
chatID := update.Message.Chat.ID
|
||||||
|
username := update.Message.From.UserName
|
||||||
|
userInfoDTO, err := dataBase.GetChatInfo(chatID, username)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range botStates {
|
||||||
|
if err := s.Do(text, userInfoDTO); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
type Command string
|
|
||||||
|
|
||||||
const (
|
|
||||||
Start = Command("/start")
|
|
||||||
Help = Command("/help")
|
|
||||||
Ping = Command("/ping")
|
|
||||||
Go = Command("/go")
|
|
||||||
Stat = Command("/stat")
|
|
||||||
Eat = Command("/eat")
|
|
||||||
)
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
MongoURL string
|
|
||||||
DBName string
|
|
||||||
ChatsCollectionName string
|
|
||||||
WorkoutsCollectionName string
|
|
||||||
CaloriesCollectionName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfig(mongoURl string, dbName string) *Config {
|
|
||||||
return &Config{
|
|
||||||
MongoURL: mongoURl,
|
|
||||||
DBName: dbName,
|
|
||||||
ChatsCollectionName: "chats",
|
|
||||||
WorkoutsCollectionName: "workouts",
|
|
||||||
CaloriesCollectionName: "calories",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
const (
|
|
||||||
UserStateNone = UserState("")
|
|
||||||
UserStateGo = UserState("Go")
|
|
||||||
UserStateEat = UserState("Eat")
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserState string
|
|
||||||
|
|
||||||
type Chat struct {
|
|
||||||
ChatID int64 `bson:"chat_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChatInfo struct {
|
|
||||||
ChatID int64 `bson:"chat_id"`
|
|
||||||
Status string `bson:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ChatInfo) GetStatus() UserState {
|
|
||||||
return UserState(c.Status)
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
"log"
|
|
||||||
"time"
|
|
||||||
"valera/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DB struct {
|
|
||||||
cfg *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDB(
|
|
||||||
mongoURL string,
|
|
||||||
dbName string,
|
|
||||||
) *DB {
|
|
||||||
return &DB{
|
|
||||||
cfg: config.NewConfig(mongoURL, dbName),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) AddChat(chatID int64) error {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.ChatsCollectionName)
|
|
||||||
|
|
||||||
result := collection.FindOne(ctx, bson.M{"chat_id": chatID})
|
|
||||||
if result.Err() == mongo.ErrNoDocuments {
|
|
||||||
if _, err := collection.InsertOne(ctx, &Chat{ChatID: chatID}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return result.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) AddWorkout(chatID int64, workout *Workout) error {
|
|
||||||
if workout.Count <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := db.AddChat(chatID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
workout.ChatID = chatID
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.WorkoutsCollectionName)
|
|
||||||
_, err = collection.InsertOne(
|
|
||||||
ctx,
|
|
||||||
workout,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) AddCalories(chatID int64, calories *Calories) error {
|
|
||||||
if calories.Count <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := db.AddChat(chatID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
calories.ChatID = chatID
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.CaloriesCollectionName)
|
|
||||||
_, err = collection.InsertOne(
|
|
||||||
ctx,
|
|
||||||
calories,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) GetChatInfo(chatID int64) (*ChatInfo, error) {
|
|
||||||
if err := db.AddChat(chatID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.ChatsCollectionName)
|
|
||||||
chatInfoDTO := &ChatInfo{}
|
|
||||||
if err := collection.FindOne(ctx, bson.M{"chat_id": chatID}).Decode(chatInfoDTO); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return chatInfoDTO, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) SetStatusToChat(chatID int64, status UserState) error {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.ChatsCollectionName)
|
|
||||||
_, err = collection.UpdateOne(
|
|
||||||
ctx,
|
|
||||||
bson.M{"chat_id": chatID},
|
|
||||||
bson.M{"$set": bson.M{"status": status}},
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) GetStat(chatID int64) (map[string]int, error) {
|
|
||||||
if err := db.AddChat(chatID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
loc, _ := time.LoadLocation("Asia/Novosibirsk")
|
|
||||||
t := time.Now().In(loc).Add(-24 * time.Hour)
|
|
||||||
|
|
||||||
res := map[string]int{}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.WorkoutsCollectionName)
|
|
||||||
cursor, err := collection.Find(
|
|
||||||
ctx,
|
|
||||||
bson.M{
|
|
||||||
"chat_id": chatID,
|
|
||||||
"created_at": bson.M{"$gt": t},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for cursor.Next(context.Background()) {
|
|
||||||
var result Workout
|
|
||||||
if err := cursor.Decode(&result); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
res[result.Name] += result.Count
|
|
||||||
}
|
|
||||||
if err := cursor.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
caloriesCollection := client.Database(db.cfg.DBName).Collection(db.cfg.CaloriesCollectionName)
|
|
||||||
caloriesCursor, err := caloriesCollection.Find(
|
|
||||||
ctx,
|
|
||||||
bson.M{
|
|
||||||
"chat_id": chatID,
|
|
||||||
"created_at": bson.M{"$gt": t},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for caloriesCursor.Next(context.Background()) {
|
|
||||||
var result Calories
|
|
||||||
if err := caloriesCursor.Decode(&result); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
res["Калории"] += result.Count
|
|
||||||
}
|
|
||||||
if err := caloriesCursor.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) GetAllChats() ([]int64, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(db.cfg.MongoURL))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
collection := client.Database(db.cfg.DBName).Collection(db.cfg.ChatsCollectionName)
|
|
||||||
cursor, err := collection.Find(ctx, bson.M{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var res []int64
|
|
||||||
for cursor.Next(context.Background()) {
|
|
||||||
var result ChatInfo
|
|
||||||
if err := cursor.Decode(&result); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
res = append(res, result.ChatID)
|
|
||||||
}
|
|
||||||
if err := cursor.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
module valera
|
module valera
|
||||||
|
|
||||||
go 1.17
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||||
github.com/umputun/go-flags v1.5.1
|
|
||||||
go.mongodb.org/mongo-driver v1.11.2
|
go.mongodb.org/mongo-driver v1.11.2
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,6 +18,7 @@ require (
|
|||||||
github.com/xdg-go/stringprep v1.0.3 // indirect
|
github.com/xdg-go/stringprep v1.0.3 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaEL
|
|||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
@@ -27,8 +27,6 @@ github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQ
|
|||||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||||
github.com/umputun/go-flags v1.5.1 h1:vRauoXV3Ultt1HrxivSxowbintgZLJE+EcBy5ta3/mY=
|
|
||||||
github.com/umputun/go-flags v1.5.1/go.mod h1:nTbvsO/hKqe7Utri/NoyN18GR3+EWf+9RrmsdwdhrEc=
|
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
|
||||||
@@ -41,6 +39,8 @@ go.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNH
|
|||||||
go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
go.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||||
|
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -52,7 +52,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppConfig struct {
|
||||||
|
Port string
|
||||||
|
MongoConfig *MongoConfig
|
||||||
|
TgConfig *TgConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type MongoConfig struct {
|
||||||
|
URL string
|
||||||
|
DBName string
|
||||||
|
ChatsCollectionName string
|
||||||
|
WorkoutsCollectionName string
|
||||||
|
CaloriesCollectionName string
|
||||||
|
WeightCollectionName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type TgConfig struct {
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAppConfig() *AppConfig {
|
||||||
|
mongoURL, err := readFile("mongo_url.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
mongoURL = strings.ReplaceAll(mongoURL, "\n", "")
|
||||||
|
|
||||||
|
dbName, err := readFile("db_name.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
dbName = strings.ReplaceAll(dbName, "\n", "")
|
||||||
|
|
||||||
|
token, err := readFile("token.txt")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
token = strings.ReplaceAll(token, "\n", "")
|
||||||
|
|
||||||
|
return &AppConfig{
|
||||||
|
Port: ":10002",
|
||||||
|
MongoConfig: &MongoConfig{
|
||||||
|
URL: mongoURL,
|
||||||
|
DBName: dbName,
|
||||||
|
ChatsCollectionName: "chats",
|
||||||
|
WorkoutsCollectionName: "workouts",
|
||||||
|
CaloriesCollectionName: "calories",
|
||||||
|
WeightCollectionName: "weights",
|
||||||
|
},
|
||||||
|
TgConfig: &TgConfig{
|
||||||
|
Token: token,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(filename string) (string, error) {
|
||||||
|
b, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str := string(b)
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserStateNone = UserState("")
|
||||||
|
UserStateGo = UserState("Go")
|
||||||
|
UserStateEat = UserState("Eat")
|
||||||
|
UserStatePause = UserState("Pause")
|
||||||
|
UserStateWeight = UserState("Weight")
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserState string
|
||||||
|
|
||||||
|
type Chat struct {
|
||||||
|
ChatID int64 `bson:"chat_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChatInfo struct {
|
||||||
|
ChatID int64 `bson:"chat_id"`
|
||||||
|
Status string `bson:"status"`
|
||||||
|
Username string `bson:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ChatInfo) GetStatus() UserState {
|
||||||
|
return UserState(c.Status)
|
||||||
|
}
|
||||||
@@ -0,0 +1,298 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|
||||||
|
"valera/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
chatsColection *mongo.Collection
|
||||||
|
workoutsColection *mongo.Collection
|
||||||
|
caloriesColection *mongo.Collection
|
||||||
|
weightColection *mongo.Collection
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDB(
|
||||||
|
cfg *config.AppConfig,
|
||||||
|
) (*DB, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
opt := options.Client().
|
||||||
|
ApplyURI(cfg.MongoConfig.URL).
|
||||||
|
SetMinPoolSize(10)
|
||||||
|
|
||||||
|
client, err := mongo.Connect(ctx, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DB{
|
||||||
|
chatsColection: client.Database(cfg.MongoConfig.DBName).Collection(cfg.MongoConfig.ChatsCollectionName),
|
||||||
|
workoutsColection: client.Database(cfg.MongoConfig.DBName).Collection(cfg.MongoConfig.WorkoutsCollectionName),
|
||||||
|
caloriesColection: client.Database(cfg.MongoConfig.DBName).Collection(cfg.MongoConfig.CaloriesCollectionName),
|
||||||
|
weightColection: client.Database(cfg.MongoConfig.DBName).Collection(cfg.MongoConfig.WeightCollectionName),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddChat(chatID int64) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
result := db.chatsColection.FindOne(ctx, bson.M{"chat_id": chatID})
|
||||||
|
if result.Err() == mongo.ErrNoDocuments {
|
||||||
|
if _, err := db.chatsColection.InsertOne(ctx, &Chat{ChatID: chatID}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if result.Err() != nil {
|
||||||
|
return result.Err()
|
||||||
|
}
|
||||||
|
loc, _ := time.LoadLocation("Asia/Novosibirsk")
|
||||||
|
t := time.Now().In(loc)
|
||||||
|
_, err := db.chatsColection.UpdateOne(
|
||||||
|
ctx,
|
||||||
|
bson.M{"chat_id": chatID},
|
||||||
|
bson.M{"$set": bson.M{"last_time": t}},
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddWorkout(chatID int64, workout *Workout) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if workout.Count <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := db.AddChat(chatID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
workout.ChatID = chatID
|
||||||
|
|
||||||
|
_, err := db.workoutsColection.InsertOne(
|
||||||
|
ctx,
|
||||||
|
workout,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddCalories(chatID int64, calories *Calories) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if err := db.AddChat(chatID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
calories.ChatID = chatID
|
||||||
|
|
||||||
|
_, err := db.caloriesColection.InsertOne(
|
||||||
|
ctx,
|
||||||
|
calories,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) AddWeight(chatID int64, weight *Weight) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if weight.Weight <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := db.AddChat(chatID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
weight.ChatID = chatID
|
||||||
|
|
||||||
|
_, err := db.weightColection.InsertOne(
|
||||||
|
ctx,
|
||||||
|
weight,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetChatInfo(chatID int64, username string) (*ChatInfo, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if err := db.AddChat(chatID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chatInfoDTO := &ChatInfo{Username: username}
|
||||||
|
if err := db.chatsColection.FindOne(ctx, bson.M{"chat_id": chatID}).Decode(chatInfoDTO); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return chatInfoDTO, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) SetStatusToChat(chatID int64, status UserState) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
_, err := db.chatsColection.UpdateOne(
|
||||||
|
ctx,
|
||||||
|
bson.M{"chat_id": chatID},
|
||||||
|
bson.M{"$set": bson.M{"status": status}},
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) SetPause(chatID int64, duration time.Duration) error {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
loc, _ := time.LoadLocation("Asia/Novosibirsk")
|
||||||
|
t := time.Now().In(loc).Add(+duration)
|
||||||
|
_, err := db.chatsColection.UpdateOne(
|
||||||
|
ctx,
|
||||||
|
bson.M{"chat_id": chatID},
|
||||||
|
bson.M{"$set": bson.M{"pause_until": t}},
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetStatBetween(chatID int64, startTime time.Time, endTime time.Time) (map[string]float64, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
if err := db.AddChat(chatID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := map[string]float64{}
|
||||||
|
|
||||||
|
cursor, err := db.workoutsColection.Find(
|
||||||
|
ctx,
|
||||||
|
bson.M{
|
||||||
|
"chat_id": chatID,
|
||||||
|
"created_at": bson.M{
|
||||||
|
"$gte": startTime,
|
||||||
|
"$lt": endTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.Find().SetSort(
|
||||||
|
bson.M{
|
||||||
|
"created_at": 1,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for cursor.Next(context.Background()) {
|
||||||
|
var result Workout
|
||||||
|
if err := cursor.Decode(&result); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
res[result.Name] += float64(result.Count)
|
||||||
|
}
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caloriesCursor, err := db.caloriesColection.Find(
|
||||||
|
ctx,
|
||||||
|
bson.M{
|
||||||
|
"chat_id": chatID,
|
||||||
|
"created_at": bson.M{
|
||||||
|
"$gte": startTime,
|
||||||
|
"$lt": endTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.Find().SetSort(
|
||||||
|
bson.M{
|
||||||
|
"created_at": 1,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for caloriesCursor.Next(context.Background()) {
|
||||||
|
var result Calories
|
||||||
|
if err := caloriesCursor.Decode(&result); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
res["Калории"] += float64(result.Count)
|
||||||
|
}
|
||||||
|
if err := caloriesCursor.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
weightCursor, err := db.weightColection.Find(
|
||||||
|
ctx,
|
||||||
|
bson.M{
|
||||||
|
"chat_id": chatID,
|
||||||
|
"created_at": bson.M{
|
||||||
|
"$gte": startTime,
|
||||||
|
"$lt": endTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options.Find().SetSort(
|
||||||
|
bson.M{
|
||||||
|
"created_at": 1,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
firstWeight := 0.0
|
||||||
|
lastWeight := 0.0
|
||||||
|
countWeights := 0
|
||||||
|
for weightCursor.Next(context.Background()) {
|
||||||
|
var result Weight
|
||||||
|
if err := weightCursor.Decode(&result); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
res["Вес кг"] = result.Weight
|
||||||
|
if firstWeight == 0 {
|
||||||
|
firstWeight = result.Weight
|
||||||
|
}
|
||||||
|
lastWeight = result.Weight
|
||||||
|
countWeights++
|
||||||
|
}
|
||||||
|
if err := weightCursor.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if countWeights > 0 {
|
||||||
|
res["Изменение веса"] = lastWeight - firstWeight
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetAllChats() ([]int64, error) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
loc, _ := time.LoadLocation("Asia/Novosibirsk")
|
||||||
|
t := time.Now().In(loc)
|
||||||
|
cursor, err := db.chatsColection.Find(
|
||||||
|
ctx,
|
||||||
|
bson.M{
|
||||||
|
"$or": bson.A{
|
||||||
|
bson.M{
|
||||||
|
"pause_until": bson.M{"$lt": t},
|
||||||
|
},
|
||||||
|
bson.M{
|
||||||
|
"pause_until": bson.M{"$exists": false},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var res []int64
|
||||||
|
for cursor.Next(context.Background()) {
|
||||||
|
var result ChatInfo
|
||||||
|
if err := cursor.Decode(&result); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
res = append(res, result.ChatID)
|
||||||
|
}
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Weight struct {
|
||||||
|
ChatID int64 `bson:"chat_id"`
|
||||||
|
Weight float64 `bson:"weight"`
|
||||||
|
CreatedAt time.Time `bson:"created_at"`
|
||||||
|
Username string `bson:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWeight(
|
||||||
|
weight float64,
|
||||||
|
username string,
|
||||||
|
) *Weight {
|
||||||
|
loc, _ := time.LoadLocation("Asia/Novosibirsk")
|
||||||
|
return &Weight{
|
||||||
|
Weight: weight,
|
||||||
|
Username: username,
|
||||||
|
CreatedAt: time.Now().In(loc),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package times
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
loc *time.Location
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
loc, _ = time.LoadLocation("Asia/Novosibirsk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNow() time.Time {
|
||||||
|
t := time.Now().In(loc)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStartDay() time.Time {
|
||||||
|
t := GetNow()
|
||||||
|
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStartDayMinus(days int) time.Time {
|
||||||
|
t := GetStartDay()
|
||||||
|
for i := 0; i < days; i++ {
|
||||||
|
t = t.Add(-24 * time.Hour)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetStartWeek() time.Time {
|
||||||
|
t := GetStartDay()
|
||||||
|
t = t.Add(-6 * 24 * time.Hour)
|
||||||
|
return t
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package clear_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/c", // en
|
||||||
|
"/с", // ru
|
||||||
|
}
|
||||||
|
|
||||||
|
type clearBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClearBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &clearBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clearBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := tgbot.NewMessage(chatInfo.ChatID, "Чистка")
|
||||||
|
msg.ReplyMarkup = tgbot.NewRemoveKeyboard(false)
|
||||||
|
_, _ = s.bot.Send(msg)
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *clearBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package eat_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
productsMap = map[string]struct {
|
||||||
|
caloriesIn100G int
|
||||||
|
awgWeightG int
|
||||||
|
}{
|
||||||
|
"чай": {caloriesIn100G: 65, awgWeightG: 200},
|
||||||
|
"яблоко": {caloriesIn100G: 52, awgWeightG: 242},
|
||||||
|
"хлеб": {caloriesIn100G: 245, awgWeightG: 35},
|
||||||
|
"огурец": {caloriesIn100G: 15, awgWeightG: 20},
|
||||||
|
"помидор": {caloriesIn100G: 18, awgWeightG: 18},
|
||||||
|
"слива": {caloriesIn100G: 50, awgWeightG: 50},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func calcCalories(text string) (int, error) {
|
||||||
|
product, ok := productsMap[strings.ToLower(text)]
|
||||||
|
if ok {
|
||||||
|
return product.caloriesIn100G * product.awgWeightG / 100, nil
|
||||||
|
}
|
||||||
|
arr := strings.Split(text, " ")
|
||||||
|
if len(arr) == 2 {
|
||||||
|
product, ok := productsMap[strings.ToLower(arr[0])]
|
||||||
|
if ok {
|
||||||
|
count, err := strconv.Atoi(arr[1])
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return product.caloriesIn100G * count / 100, nil
|
||||||
|
}
|
||||||
|
count1, err := strconv.Atoi(arr[0])
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
count2, err := strconv.Atoi(arr[1])
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return count1 * count2 / 100, nil
|
||||||
|
}
|
||||||
|
count, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package eat_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCalcCalories(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
text string
|
||||||
|
want int
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"empty text",
|
||||||
|
"",
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"1 text",
|
||||||
|
"1",
|
||||||
|
1,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"2 5 text",
|
||||||
|
"20 50",
|
||||||
|
10,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"чай text",
|
||||||
|
"чай",
|
||||||
|
130,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"чай 250 text",
|
||||||
|
"чай 250",
|
||||||
|
162,
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
count, err := calcCalories(tt.text)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("error calc calories: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count != tt.want {
|
||||||
|
fmt.Println(count, tt.want)
|
||||||
|
fmt.Println(count, tt.want)
|
||||||
|
t.Errorf("error count: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package eat_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/modules/times"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/eat",
|
||||||
|
}
|
||||||
|
|
||||||
|
type eatBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEatBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &eatBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *eatBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if chatInfo.Status == string(db.UserStateEat) {
|
||||||
|
count, err := calcCalories(text)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := s.dataBase.AddCalories(chatInfo.ChatID, db.NewCalories(count, chatInfo.Username)); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stat, err := s.dataBase.GetStatBetween(chatInfo.ChatID, times.GetStartDay(), times.GetNow())
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, fmt.Sprintf("Калории, фу, %d, записал.\nКалорий сегодня: %v", count, stat["Калории"])))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, "Вижу ты поел, отпишись сколько калорий было"))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateEat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *eatBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package go_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/go",
|
||||||
|
"/го",
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
workoutTypes = []string{
|
||||||
|
"Приседания",
|
||||||
|
"Тяга",
|
||||||
|
"Бицепс",
|
||||||
|
"Пресс",
|
||||||
|
"Отжимания",
|
||||||
|
"Подтягивания",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type goBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGoBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &goBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if chatInfo.Status == string(db.UserStateGo) {
|
||||||
|
if err := s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserState(text)); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
msg := tgbot.NewMessage(chatInfo.ChatID, fmt.Sprintf("%s, отпишись сколько раз ты выполнил упражнение", text))
|
||||||
|
msg.ReplyMarkup = tgbot.NewRemoveKeyboard(false)
|
||||||
|
_, _ = s.bot.Send(msg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
isWorkout := false
|
||||||
|
for _, workoutType := range workoutTypes {
|
||||||
|
if chatInfo.GetStatus() == db.UserState(workoutType) {
|
||||||
|
count, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := s.dataBase.AddWorkout(chatInfo.ChatID, db.NewWorkout(workoutType, count, chatInfo.Username)); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msgText := fmt.Sprintf("Отлично, %s, записал.", text)
|
||||||
|
if count <= 0 {
|
||||||
|
msgText = "Плохо, хочешь быть толстым и не красивым?"
|
||||||
|
}
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, msgText))
|
||||||
|
if err := s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
isWorkout = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isWorkout {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sendGoToChat(chatInfo.ChatID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chats, err := s.dataBase.GetAllChats()
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, chatID := range chats {
|
||||||
|
s.sendGoToChat(chatID)
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"ok"}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *goBotState) sendGoToChat(chatID int64) {
|
||||||
|
msg := tgbot.NewMessage(chatID, "Давай немного разомнемся, выбирай:")
|
||||||
|
rows := make([][]tgbot.KeyboardButton, 0, len(workoutTypes))
|
||||||
|
for _, workoutType := range workoutTypes {
|
||||||
|
rows = append(rows, tgbot.NewKeyboardButtonRow(tgbot.NewKeyboardButton(workoutType)))
|
||||||
|
}
|
||||||
|
msg.ReplyMarkup = tgbot.NewReplyKeyboard(rows...)
|
||||||
|
if _, err := s.bot.Send(msg); err == nil {
|
||||||
|
if err := s.dataBase.SetStatusToChat(chatID, db.UserStateGo); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package help_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/help",
|
||||||
|
}
|
||||||
|
|
||||||
|
type helpBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHelpBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &helpBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *helpBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "Вот что я умею:\n\n1) Предлагать размяться\n - несколько видов упражнений 6 раз в день\n2) Показывать статистику за день\n3) Считать калории\n - введи одно число, я запишу калории\n - введи 2 числа через пробел одно количество грамм другое количество калорий в 100 граммах (порядок не важен), я посчитаю\n - введи название того что ты съел, я посчитаю (если знаю)"
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, msg))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *helpBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package states
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"valera/internal/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BotState interface {
|
||||||
|
Do(text string, chatInfo *db.ChatInfo) error
|
||||||
|
GetHandler() (string, func(http.ResponseWriter, *http.Request))
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package pause_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getDuration(text string) (time.Duration, error) {
|
||||||
|
d := strings.TrimSuffix(text, "ч")
|
||||||
|
d = strings.TrimSuffix(d, "д")
|
||||||
|
count, err := strconv.Atoi(d)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
res := time.Duration(count) * time.Hour
|
||||||
|
if strings.HasSuffix(text, "д") {
|
||||||
|
res *= 24
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package pause_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/pause",
|
||||||
|
}
|
||||||
|
|
||||||
|
type pauseBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPauseBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &pauseBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *pauseBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if chatInfo.Status == string(db.UserStatePause) {
|
||||||
|
duration, err := getDuration(text)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := s.dataBase.SetPause(chatInfo.ChatID, duration); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
msg := tgbot.NewMessage(chatInfo.ChatID, fmt.Sprintf("Поставил паузу %v, отдыхай", duration))
|
||||||
|
msg.ReplyMarkup = tgbot.NewRemoveKeyboard(false)
|
||||||
|
_, _ = s.bot.Send(msg)
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := tgbot.NewMessage(chatInfo.ChatID, "Хочешь отдохнуть? Сколько времени тебе нужно?")
|
||||||
|
msg.ReplyMarkup = tgbot.NewReplyKeyboard([][]tgbot.KeyboardButton{
|
||||||
|
tgbot.NewKeyboardButtonRow(
|
||||||
|
tgbot.NewKeyboardButton("8ч"),
|
||||||
|
tgbot.NewKeyboardButton("32ч"),
|
||||||
|
),
|
||||||
|
}...)
|
||||||
|
_, _ = s.bot.Send(msg)
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStatePause)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *pauseBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package ping_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/ping",
|
||||||
|
}
|
||||||
|
|
||||||
|
type pingBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPingBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &pingBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *pingBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := "Понг"
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, msg))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *pingBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package start_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/states"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/start",
|
||||||
|
}
|
||||||
|
|
||||||
|
type startBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStartBotState(bot *tgbot.BotAPI, dataBase *db.DB, version string) states.BotState {
|
||||||
|
return &startBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
version: version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *startBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("Здорова, я Валера (%s), твой тренер (%d).", s.version, chatInfo.ChatID)
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, msg))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *startBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package stat_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/modules/times"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/stat",
|
||||||
|
"/stat_week",
|
||||||
|
}
|
||||||
|
|
||||||
|
type statBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &statBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "/stat" {
|
||||||
|
s.sendStatToChatAfter(chatInfo.ChatID, "Результаты за сегодня:\n", times.GetStartDay())
|
||||||
|
}
|
||||||
|
|
||||||
|
if text == "/stat_week" {
|
||||||
|
s.sendStatToChatAfter(chatInfo.ChatID, "Результаты за неделю:\n", times.GetStartWeek())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chats, err := s.dataBase.GetAllChats()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, chatID := range chats {
|
||||||
|
t := times.GetStartDay()
|
||||||
|
if err := s.sendStatToChatAfter(chatID, "Напоминаю:\n- Cегодня больше не жрем!\n\nРезультаты за сегодня:\n", times.GetStartDay()); err != nil {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t.Weekday() == time.Sunday {
|
||||||
|
if err := s.sendStatToChatAfter(chatID, "Результаты за неделю:\n", times.GetStartWeek()); err != nil {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"ok"}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statBotState) sendStatToChatAfter(chatID int64, prefix string, t time.Time) error {
|
||||||
|
msgText := prefix
|
||||||
|
stat, err := s.dataBase.GetStatBetween(chatID, t, times.GetNow())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range stat {
|
||||||
|
msgText += fmt.Sprintf("- %s: %v\n", k, v)
|
||||||
|
}
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatID, msgText))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package weight_bot_state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
"valera/internal/db"
|
||||||
|
"valera/internal/modules/times"
|
||||||
|
"valera/internal/states"
|
||||||
|
)
|
||||||
|
|
||||||
|
var names = []string{
|
||||||
|
"/weight",
|
||||||
|
}
|
||||||
|
|
||||||
|
type weightBotState struct {
|
||||||
|
bot *tgbot.BotAPI
|
||||||
|
dataBase *db.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWeightBotState(bot *tgbot.BotAPI, dataBase *db.DB) states.BotState {
|
||||||
|
return &weightBotState{
|
||||||
|
bot: bot,
|
||||||
|
dataBase: dataBase,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *weightBotState) Do(text string, chatInfo *db.ChatInfo) error {
|
||||||
|
if chatInfo.Status == string(db.UserStateWeight) {
|
||||||
|
weight, err := getWeight(text)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if weight <= 0 {
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, "Все фигня, давай по новой"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := s.dataBase.AddWeight(chatInfo.ChatID, db.NewWeight(weight, chatInfo.Username)); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
stat, err := s.dataBase.GetStatBetween(chatInfo.ChatID, times.GetStartDayMinus(1), times.GetStartDay())
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dWeight := 0.0
|
||||||
|
if stat["Вес кг"] != 0 {
|
||||||
|
dWeight = weight - stat["Вес кг"]
|
||||||
|
}
|
||||||
|
_, _ = s.bot.Send(tgbot.NewMessage(chatInfo.ChatID, fmt.Sprintf("%vкг, записал.\nИзменение веса: %vкг", weight, dWeight)))
|
||||||
|
return s.dataBase.SetStatusToChat(chatInfo.ChatID, db.UserStateNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(names, text) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sendWeightToChat(chatInfo.ChatID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *weightBotState) GetHandler() (string, func(http.ResponseWriter, *http.Request)) {
|
||||||
|
return names[0], func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
chats, err := s.dataBase.GetAllChats()
|
||||||
|
if err != nil {
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, chatID := range chats {
|
||||||
|
s.sendWeightToChat(chatID)
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, _ = fmt.Fprintf(w, `{"result":"ok"}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *weightBotState) sendWeightToChat(chatID int64) {
|
||||||
|
msg := tgbot.NewMessage(chatID, "Давай взвешивайся, отпишись сколько кг ты весишь")
|
||||||
|
if _, err := s.bot.Send(msg); err == nil {
|
||||||
|
if err := s.dataBase.SetStatusToChat(chatID, db.UserStateWeight); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWeight(text string) (float64, error) {
|
||||||
|
number := strings.ReplaceAll(text, ",", ".")
|
||||||
|
return strconv.ParseFloat(number, 64)
|
||||||
|
}
|
||||||
@@ -1,274 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
tgbot "github.com/go-telegram-bot-api/telegram-bot-api"
|
|
||||||
"github.com/umputun/go-flags"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"valera/commands"
|
|
||||||
"valera/db"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
workoutTypes = []string{
|
|
||||||
"Отжимания",
|
|
||||||
"Пресс",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
version = "v1.4.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Opts struct {
|
|
||||||
Token string `short:"t" long:"token" description:"Telegram api token"`
|
|
||||||
Name string `short:"n" long:"name" description:"Telegram bot name" default:"@body_weight_loss_bot"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var opts Opts
|
|
||||||
|
|
||||||
func readFile(filename string) (string, error) {
|
|
||||||
b, err := ioutil.ReadFile(filename)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
str := string(b)
|
|
||||||
return str, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() {
|
|
||||||
p := flags.NewParser(&opts, flags.PrintErrors|flags.PassDoubleDash|flags.HelpFlag)
|
|
||||||
p.SubcommandsOptional = true
|
|
||||||
if _, err := p.Parse(); err != nil {
|
|
||||||
if err.(*flags.Error).Type != flags.ErrHelp {
|
|
||||||
log.Println(errors.New("[ERROR] cli error: " + err.Error()))
|
|
||||||
}
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Token == "" {
|
|
||||||
token, err := readFile("token.txt")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
opts.Token = strings.ReplaceAll(token, "\n", "")
|
|
||||||
}
|
|
||||||
fmt.Println(opts.Token)
|
|
||||||
|
|
||||||
bot, err := tgbot.NewBotAPI(opts.Token)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mongoURL, err := readFile("mongo_url.txt")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
mongoURL = strings.ReplaceAll(mongoURL, "\n", "")
|
|
||||||
fmt.Println(mongoURL)
|
|
||||||
|
|
||||||
dbName, err := readFile("db_name.txt")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
dbName = strings.ReplaceAll(dbName, "\n", "")
|
|
||||||
fmt.Println(dbName)
|
|
||||||
|
|
||||||
dataBase := db.NewDB(
|
|
||||||
mongoURL,
|
|
||||||
dbName,
|
|
||||||
)
|
|
||||||
|
|
||||||
u := tgbot.NewUpdate(0)
|
|
||||||
u.Timeout = 60
|
|
||||||
|
|
||||||
updates, err := bot.GetUpdatesChan(u)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
http.HandleFunc("/go", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
chats, err := dataBase.GetAllChats()
|
|
||||||
if err != nil {
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, chatID := range chats {
|
|
||||||
sendGoToChat(bot, dataBase, chatID)
|
|
||||||
}
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
_, _ = fmt.Fprintf(w, `{"result":"ok"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
http.HandleFunc("/stat", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
chats, err := dataBase.GetAllChats()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, chatID := range chats {
|
|
||||||
if err := sendStatToChat(bot, dataBase, chatID, "Напоминаю:\n- Cегодня больше не жрем!\n\n"); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
_, _ = fmt.Fprintf(w, `{"result":"error"}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
|
||||||
w.WriteHeader(http.StatusOK)
|
|
||||||
_, _ = fmt.Fprintf(w, `{"result":"ok"}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
port := ":10002"
|
|
||||||
log.Println("Server is start up! port", port)
|
|
||||||
log.Fatal(http.ListenAndServe(port, nil))
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Println("Run", opts.Name)
|
|
||||||
|
|
||||||
for update := range updates {
|
|
||||||
|
|
||||||
if update.Message == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
text := update.Message.Text
|
|
||||||
chatID := update.Message.Chat.ID
|
|
||||||
username := update.Message.From.UserName
|
|
||||||
|
|
||||||
userInfoDTO, err := dataBase.GetChatInfo(chatID)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
isWorkout := false
|
|
||||||
for _, workoutType := range workoutTypes {
|
|
||||||
if userInfoDTO.GetStatus() == db.UserState(workoutType) {
|
|
||||||
count, err := strconv.Atoi(text)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := dataBase.AddWorkout(chatID, db.NewWorkout(workoutType, count, username)); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
msgText := fmt.Sprintf("Отлично, %s, записал.", text)
|
|
||||||
if count <= 0 {
|
|
||||||
msgText = "Плохо, хочешь быть толстым и не красивым?"
|
|
||||||
}
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, msgText))
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserStateNone); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
isWorkout = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isWorkout {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch userInfoDTO.GetStatus() {
|
|
||||||
case db.UserStateGo:
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserState(text)); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
msg := tgbot.NewMessage(chatID, fmt.Sprintf("%s, отпишись сколько раз ты выполнил упражнение", text))
|
|
||||||
msg.ReplyMarkup = tgbot.NewRemoveKeyboard(false)
|
|
||||||
_, _ = bot.Send(msg)
|
|
||||||
continue
|
|
||||||
case db.UserStateEat:
|
|
||||||
count, err := strconv.Atoi(text)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := dataBase.AddCalories(chatID, db.NewCalories(count, username)); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if count <= 0 {
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, "Все фигня, давай по новой"))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, fmt.Sprintf("Калории, фу, %s, записал.", text)))
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserStateNone); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
command := commands.Command(strings.Replace(text, opts.Name, "", 1))
|
|
||||||
switch command {
|
|
||||||
case commands.Start:
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, fmt.Sprintf("Здорова, я Валера (%s), твой тренер (%d).", version, chatID)))
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserStateNone); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
case commands.Help:
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, "Вот что я умею:\n\n1) Предлагать размяться\n2) Показывать статистику\n3) Считать калории"))
|
|
||||||
case commands.Ping:
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, "pong"))
|
|
||||||
case commands.Go:
|
|
||||||
sendGoToChat(bot, dataBase, chatID)
|
|
||||||
case commands.Stat:
|
|
||||||
if err := sendStatToChat(bot, dataBase, chatID, ""); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case commands.Eat:
|
|
||||||
if _, err := bot.Send(tgbot.NewMessage(chatID, "Вижу ты поел, отпишись сколько калорий было")); err == nil {
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserStateEat); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendGoToChat(bot *tgbot.BotAPI, dataBase *db.DB, chatID int64) {
|
|
||||||
msg := tgbot.NewMessage(chatID, "Давай немного разомнемся, выбирай:")
|
|
||||||
row := tgbot.NewKeyboardButtonRow()
|
|
||||||
for _, workoutType := range workoutTypes {
|
|
||||||
row = append(row, tgbot.NewKeyboardButton(workoutType))
|
|
||||||
}
|
|
||||||
msg.ReplyMarkup = tgbot.NewReplyKeyboard(row)
|
|
||||||
if _, err := bot.Send(msg); err == nil {
|
|
||||||
if err := dataBase.SetStatusToChat(chatID, db.UserStateGo); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendStatToChat(bot *tgbot.BotAPI, dataBase *db.DB, chatID int64, prefix string) error {
|
|
||||||
stat, err := dataBase.GetStat(chatID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
msgText := prefix + "Результаты за сегодня:\n"
|
|
||||||
for k, v := range stat {
|
|
||||||
msgText += fmt.Sprintf("- %s: %d\n", k, v)
|
|
||||||
}
|
|
||||||
_, _ = bot.Send(tgbot.NewMessage(chatID, msgText))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user