alpha version
This commit is contained in:
92
workers/telegram/commands.go
Normal file
92
workers/telegram/commands.go
Normal file
@ -0,0 +1,92 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var btnCmd = map[string]int64{
|
||||
"user_add": 1,
|
||||
"user_edit": 2,
|
||||
"user_detete": 3,
|
||||
"users_list": 4,
|
||||
"group_add": 11,
|
||||
"group_edit": 12,
|
||||
"group_delete": 13,
|
||||
"groups_list": 14,
|
||||
"receipt_add": 21,
|
||||
"receipt_edit": 22,
|
||||
"receipt_delete": 23,
|
||||
"receipts_list": 24,
|
||||
"item_add": 31,
|
||||
"item_edit": 32,
|
||||
"item_delete": 33,
|
||||
"items_list": 34,
|
||||
"merchant": 96,
|
||||
"category": 97,
|
||||
"tax": 98,
|
||||
"total": 99,
|
||||
}
|
||||
|
||||
var btnPage = map[string]int64{
|
||||
"prev": 1,
|
||||
"next": 2,
|
||||
}
|
||||
|
||||
// TelegramBotCommand .
|
||||
type TelegramBotCommand struct {
|
||||
Cmd int64 // Internal Command ( user_add | user_edit | user_delete )
|
||||
ID int64 // Internal UserID
|
||||
RefCmd int64 // Internal Reference Command
|
||||
RefID int64 // Internal Reference ID
|
||||
TelegramID int64 // Telegram UserID
|
||||
Pagination int64 // Pagination
|
||||
Extra string // Internal String filed to pass Telegram FileID
|
||||
}
|
||||
|
||||
// Unmarshal .
|
||||
func (t *TelegramBotCommand) Unmarshal(c string) error {
|
||||
r := regexp.MustCompile(`^(?P<Cmd>.*)\|(?P<ID>.*)\|(?P<RefCmd>.*)\|(?P<RefID>.*)\|(?P<TelegramID>.*)\|(?P<Pagination>.*)\|(?P<Extra>.*)$`)
|
||||
m := r.FindStringSubmatch(c)
|
||||
|
||||
res := make(map[string]string)
|
||||
for i, name := range r.SubexpNames() {
|
||||
if i != 0 && name != "" {
|
||||
res[name] = m[i]
|
||||
}
|
||||
}
|
||||
|
||||
for k, val := range res {
|
||||
v := reflect.ValueOf(&t).Elem()
|
||||
f := reflect.Indirect(v).FieldByName(k)
|
||||
|
||||
if !f.IsValid() {
|
||||
continue
|
||||
}
|
||||
if f.Type().Kind() == reflect.Int64 {
|
||||
age, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
f.SetInt(int64(age))
|
||||
} else {
|
||||
f.SetString(val)
|
||||
}
|
||||
}
|
||||
log.Printf("CALLBACK (UNMARSHAL) %d|%d|%d|%d|%d|%d|%s", t.Cmd, t.ID, t.RefCmd, t.RefID, t.TelegramID, t.Pagination, t.Extra)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal .
|
||||
func (t TelegramBotCommand) Marshal() string {
|
||||
return fmt.Sprintf("%d|%d|%d|%d|%d|%d|%s", t.Cmd, t.ID, t.RefCmd, t.RefID, t.TelegramID, t.Pagination, t.Extra)
|
||||
}
|
||||
|
||||
// String .
|
||||
func String(t *TelegramBotCommand) string {
|
||||
return t.Marshal()
|
||||
}
|
213
workers/telegram/telegram.go
Normal file
213
workers/telegram/telegram.go
Normal file
@ -0,0 +1,213 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/alex-savin/go-receipt-tracker/bus"
|
||||
"github.com/alex-savin/go-receipt-tracker/config"
|
||||
tele "gopkg.in/telebot.v4"
|
||||
)
|
||||
|
||||
type Bot struct {
|
||||
id string //
|
||||
opts *config.Telegram //
|
||||
client *tele.Bot //
|
||||
subscriptions map[string]chan bus.Event //
|
||||
bus *bus.Bus //
|
||||
log *slog.Logger //
|
||||
cancel context.CancelFunc //
|
||||
end uint32 // ensure the close methods are only called once
|
||||
}
|
||||
|
||||
func NewTelegramBot(cfg *config.Telegram, bus *bus.Bus) (*Bot, error) {
|
||||
|
||||
bot := &Bot{
|
||||
id: "telegram",
|
||||
opts: cfg,
|
||||
bus: bus,
|
||||
}
|
||||
|
||||
return bot, nil
|
||||
}
|
||||
|
||||
func (b *Bot) ID() string {
|
||||
return b.id
|
||||
}
|
||||
|
||||
func (b *Bot) Type() string {
|
||||
return "telegram"
|
||||
}
|
||||
|
||||
func (b *Bot) Init(log *slog.Logger) error {
|
||||
var err error
|
||||
b.log = log
|
||||
|
||||
pref := tele.Settings{
|
||||
Token: b.opts.ApiKey,
|
||||
Poller: &tele.LongPoller{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
b.client, err = tele.NewBot(pref)
|
||||
if err != nil {
|
||||
b.log.Debug("cannot create telegram bot", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
b.client.Handle(tele.OnText, func(c tele.Context) error {
|
||||
return c.Send("")
|
||||
})
|
||||
|
||||
b.client.Handle(tele.OnPhoto, func(c tele.Context) error {
|
||||
|
||||
photo := c.Message().Photo
|
||||
b.client.Download(&photo.File, "./"+photo.UniqueID+".jpg")
|
||||
|
||||
msg := &bus.Message{
|
||||
Image: &bus.Image{
|
||||
ID: photo.UniqueID,
|
||||
Filename: b.opts.StoragePath + "/" + photo.UniqueID + ".jpg",
|
||||
},
|
||||
ReplyType: "send",
|
||||
TbContext: c,
|
||||
}
|
||||
err := b.bus.Publish("processor:image", msg)
|
||||
if err != nil {
|
||||
b.log.Error("couldn't publish to the channel", "channel", "processor:image", "error", err.Error())
|
||||
}
|
||||
|
||||
return c.Send("")
|
||||
})
|
||||
|
||||
b.client.Handle(tele.OnCallback, func(c tele.Context) error {
|
||||
b.log.Debug("CALLBACK (BEFORE) %s // %s", c.Callback().ID, c.Callback().Data)
|
||||
|
||||
tbc := new(TelegramBotCommand)
|
||||
err := tbc.Unmarshal(c.Callback().Data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.log.Debug("CALLBACK (AFTER) %d|%d|%d|%d|%d|%d|%s", tbc.Cmd, tbc.ID, tbc.RefCmd, tbc.RefID, tbc.TelegramID, tbc.Pagination, tbc.Extra)
|
||||
|
||||
switch tbc.Cmd {
|
||||
case btnCmd["user_add"]:
|
||||
return c.Send("")
|
||||
case btnCmd["receipt_add"]:
|
||||
|
||||
return c.Send("")
|
||||
case btnCmd["receipt_edit"]:
|
||||
return c.Send("")
|
||||
case btnCmd["receipts_list"]:
|
||||
case btnCmd["items_list"]:
|
||||
}
|
||||
cbr := &tele.CallbackResponse{
|
||||
CallbackID: c.Callback().ID,
|
||||
Text: "No unique",
|
||||
ShowAlert: false,
|
||||
}
|
||||
return c.Respond(cbr)
|
||||
})
|
||||
|
||||
b.client.Handle("/start", func(c tele.Context) error {
|
||||
err = b.bus.Publish("processor:start", nil)
|
||||
if err != nil {
|
||||
b.log.Error("couldn't publish to a channel", "channel", "telegram:publish", "error", err.Error())
|
||||
}
|
||||
|
||||
return c.Send("")
|
||||
})
|
||||
|
||||
b.client.Handle("/receipts", func(c tele.Context) error {
|
||||
return c.Send("")
|
||||
})
|
||||
|
||||
b.client.Handle("/reports", func(c tele.Context) error {
|
||||
return c.Send("")
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bot) Serve() {
|
||||
if atomic.LoadUint32(&b.end) == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
b.subscribe("messenger:" + b.ID())
|
||||
|
||||
b.client.Start()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
b.cancel = cancel
|
||||
|
||||
go b.eventLoop(ctx)
|
||||
|
||||
// if atomic.LoadUint32(&b.end) == 0 {
|
||||
// go func() {
|
||||
// if !w.mqtt.IsConnected() {
|
||||
// b.bus.Publish("app:connected", &bus.ConnectionStatus{WorkerID: b.ID(), WorkerType: b.Type(), IsConnected: false})
|
||||
// b.log.Warn(b.ID() + " client is disconnected")
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
}
|
||||
|
||||
func (b *Bot) Stop() {
|
||||
b.client.Stop()
|
||||
}
|
||||
|
||||
func (b *Bot) subscribe(chn string) error {
|
||||
s, err := b.bus.Subscribe(chn, b.Type()+":"+b.ID())
|
||||
if err != nil {
|
||||
b.log.Error("couldn't subscribe to a channel", "channel", chn, "error", err.Error())
|
||||
return err
|
||||
}
|
||||
b.subscriptions[chn] = s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// eventLoop loops forever
|
||||
func (b *Bot) eventLoop(ctx context.Context) {
|
||||
b.log.Debug(b.ID() + " communication event loop started")
|
||||
defer b.log.Debug(b.ID() + " communication event loop halted")
|
||||
|
||||
for {
|
||||
for chn, ch := range b.subscriptions {
|
||||
select {
|
||||
case event := <-ch:
|
||||
b.log.Debug("got a new message to a channel", "channel", chn)
|
||||
for _, msg := range event.Payload.([]*bus.Message) {
|
||||
b.log.Debug("publishing telegram message", "topic", msg)
|
||||
// w.mqtt.Publish(message.Topic, message.QOS, message.Retained, message.Payload)
|
||||
}
|
||||
case <-ctx.Done():
|
||||
b.log.Info("stopping " + b.ID() + " communication event loop")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for {
|
||||
// select {
|
||||
// case event := <-chMQTTPublishStatus:
|
||||
// for _, message := range event.Data.([]*bus.Message) {
|
||||
// b.log.Debug("publishing mqtt message", "topic", message.Topic, "qos", message.QOS, "retained", message.Retained, "payload", message.Payload)
|
||||
// // w.mqtt.Publish(message.Topic, message.QOS, message.Retained, message.Payload)
|
||||
// }
|
||||
// case event := <-chMQTTSubscribeCommand:
|
||||
// for _, message := range event.Data.([]*bus.Message) {
|
||||
// b.log.Debug("subscribing to a topic", "topic", message.Topic, "qos", message.QOS)
|
||||
// // w.mqtt.SubscribeMultiple()
|
||||
// // b.mqtt.Subscribe(message.Topic, message.QOS, w.mqttHandlers.publish)
|
||||
// }
|
||||
// case <-ctx.Done():
|
||||
// b.log.Info("stopping mqtt communication event loop")
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
}
|
Reference in New Issue
Block a user