Rework architecture, fix env parsing
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func parseEnv() (*config, error) {
|
||||
c := new(config)
|
||||
if err := envconfig.Process("", c); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse env")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
type config struct {
|
||||
TelegramBotToken string `envconfig:"telegram_bot_token"`
|
||||
OwnerChatID int64 `envconfig:"owner_chat_id"`
|
||||
}
|
||||
+18
-37
@@ -2,54 +2,35 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/nightnoryu/anon3anon/pkg/app"
|
||||
"github.com/nightnoryu/anon3anon/pkg/infrastructure"
|
||||
)
|
||||
|
||||
const (
|
||||
telegramBotTokenEnvKey = "TELEGRAM_BOT_TOKEN"
|
||||
ownerChatIDEnvKey = "OWNER_CHAT_ID"
|
||||
)
|
||||
|
||||
type config struct {
|
||||
Token string
|
||||
OwnerChatID int64
|
||||
}
|
||||
|
||||
func getConfig() (config, error) {
|
||||
token := os.Getenv(telegramBotTokenEnvKey)
|
||||
ownerChatID, err := strconv.ParseInt(os.Getenv(ownerChatIDEnvKey), 10, 64)
|
||||
if err != nil {
|
||||
return config{}, err
|
||||
}
|
||||
|
||||
return config{
|
||||
Token: token,
|
||||
OwnerChatID: ownerChatID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
config, err := getConfig()
|
||||
conf, err := parseEnv()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Println(conf)
|
||||
bot, err := tgbotapi.NewBotAPI(conf.TelegramBotToken)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
bot, err := tgbotapi.NewBotAPI(config.Token)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
//bot.Debug = true
|
||||
|
||||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
||||
|
||||
service := app.NewAnonymousQuestionsService(bot, config.OwnerChatID)
|
||||
botAPI := infrastructure.NewBotAPI(bot, conf.OwnerChatID)
|
||||
errorsChan := make(chan error)
|
||||
service := app.NewAnonymousQuestionsService(botAPI, errorsChan)
|
||||
go func() {
|
||||
for err := range errorsChan {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := service.ListenForMessages(); err != nil {
|
||||
log.Panic(err)
|
||||
if err = service.ServeMessages(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ module github.com/nightnoryu/anon3anon
|
||||
go 1.22.5
|
||||
|
||||
require (
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
||||
@@ -1,66 +1,72 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"log"
|
||||
func NewAnonymousQuestionsService(api BotAPI, errorsChan chan error) AnonymousMessagesService {
|
||||
return &anonymousMessagesService{
|
||||
api: api,
|
||||
errorsChan: errorsChan,
|
||||
}
|
||||
}
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
const (
|
||||
messageSentReply = "*Сообщение отправлено!*"
|
||||
newMessageNotification = "*Новое анонимное сообщение!*"
|
||||
)
|
||||
|
||||
func NewAnonymousQuestionsService(bot *tgbotapi.BotAPI, ownerChatID int64) AnonymousMessagesService {
|
||||
return &anonymousMessagesService{
|
||||
bot: bot,
|
||||
ownerChatID: ownerChatID,
|
||||
}
|
||||
}
|
||||
|
||||
type AnonymousMessagesService interface {
|
||||
ListenForMessages() error
|
||||
ServeMessages() error
|
||||
}
|
||||
|
||||
type anonymousMessagesService struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
ownerChatID int64
|
||||
api BotAPI
|
||||
errorsChan chan error
|
||||
}
|
||||
|
||||
func (s *anonymousMessagesService) ListenForMessages() error {
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = 60
|
||||
|
||||
updates := s.bot.GetUpdatesChan(u)
|
||||
for update := range updates {
|
||||
if update.Message == nil {
|
||||
continue
|
||||
func (s *anonymousMessagesService) ServeMessages() error {
|
||||
return s.api.HandleUpdates(func(update MessageUpdate) {
|
||||
if update.Command != nil {
|
||||
err := s.handleCommand(update)
|
||||
if err != nil {
|
||||
s.errorsChan <- err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[%s] %s", update.Message.From.UserName, update.Message.Text)
|
||||
|
||||
if update.Message.Text == "/start" {
|
||||
reply := tgbotapi.NewMessage(update.Message.Chat.ID, "Жду твоих вопросов!")
|
||||
s.bot.Send(reply)
|
||||
continue
|
||||
err := s.handleMessage(update.Message)
|
||||
if err != nil {
|
||||
s.errorsChan <- err
|
||||
}
|
||||
|
||||
if len(update.Message.Photo) > 0 {
|
||||
var photos []interface{}
|
||||
photo := tgbotapi.NewInputMediaPhoto(tgbotapi.FileID(update.Message.Photo[0].FileID))
|
||||
photo.Caption = "*Новое анонимное сообщение!*"
|
||||
photo.ParseMode = tgbotapi.ModeMarkdown
|
||||
photos = append(photos, photo)
|
||||
|
||||
mediaMsg := tgbotapi.NewMediaGroup(s.ownerChatID, photos)
|
||||
|
||||
s.bot.Send(mediaMsg)
|
||||
} else {
|
||||
msg := tgbotapi.NewMessage(s.ownerChatID, "*Новое анонимное сообщение!*\n\n"+update.Message.Text)
|
||||
msg.ParseMode = tgbotapi.ModeMarkdown
|
||||
|
||||
s.bot.Send(msg)
|
||||
}
|
||||
|
||||
reply := tgbotapi.NewMessage(update.Message.Chat.ID, "Сообщение отправлено!")
|
||||
|
||||
s.bot.Send(reply)
|
||||
err = s.pingClient(update.FromChatID)
|
||||
if err != nil {
|
||||
s.errorsChan <- err
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *anonymousMessagesService) handleCommand(update MessageUpdate) error {
|
||||
if update.Command == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var msgText string
|
||||
switch *update.Command {
|
||||
case StartCommand:
|
||||
msgText = "Жду твоих вопросов!"
|
||||
case UnknownCommand:
|
||||
msgText = "Неизвестная команда!"
|
||||
}
|
||||
|
||||
return s.api.SendMessage(update.FromChatID, Message{Text: msgText})
|
||||
}
|
||||
|
||||
func (s *anonymousMessagesService) pingClient(chatID int64) error {
|
||||
return s.api.SendMessage(chatID, Message{Text: messageSentReply})
|
||||
}
|
||||
|
||||
func (s *anonymousMessagesService) handleMessage(message Message) error {
|
||||
msgText := newMessageNotification + "\n\n" + message.Text
|
||||
return s.api.SendMessageToOwner(Message{
|
||||
Text: msgText,
|
||||
Image: message.Image,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package app
|
||||
|
||||
type BotAPI interface {
|
||||
HandleUpdates(handler MessageUpdateHandler) error
|
||||
SendMessage(chatID int64, message Message) error
|
||||
SendMessageToOwner(message Message) error
|
||||
}
|
||||
|
||||
type MessageUpdateHandler func(MessageUpdate)
|
||||
|
||||
type MessageUpdate struct {
|
||||
Message
|
||||
UpdateID int
|
||||
FromChatID int64
|
||||
Command *Command
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Text string
|
||||
Image *Image
|
||||
}
|
||||
|
||||
type Command int
|
||||
|
||||
const (
|
||||
UnknownCommand Command = iota
|
||||
StartCommand
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
FileID string
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package infrastructure
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/nightnoryu/anon3anon/pkg/app"
|
||||
)
|
||||
|
||||
const (
|
||||
updateTimeoutInSeconds = 60
|
||||
startCommand = "/start"
|
||||
messageParseMode = tgbotapi.ModeMarkdown
|
||||
)
|
||||
|
||||
type fileInfo struct {
|
||||
FileID string
|
||||
Size int
|
||||
}
|
||||
|
||||
func NewBotAPI(bot *tgbotapi.BotAPI, ownerChatID int64) app.BotAPI {
|
||||
return &botAPI{
|
||||
bot: bot,
|
||||
ownerChatID: ownerChatID,
|
||||
}
|
||||
}
|
||||
|
||||
type botAPI struct {
|
||||
bot *tgbotapi.BotAPI
|
||||
ownerChatID int64
|
||||
}
|
||||
|
||||
func (api *botAPI) HandleUpdates(handler app.MessageUpdateHandler) error {
|
||||
u := tgbotapi.NewUpdate(0)
|
||||
u.Timeout = updateTimeoutInSeconds
|
||||
|
||||
updates := api.bot.GetUpdatesChan(u)
|
||||
for update := range updates {
|
||||
if update.Message == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("%+v\n", update.Message)
|
||||
|
||||
messageUpdate := app.MessageUpdate{
|
||||
Message: api.hydrateMessage(update.Message),
|
||||
UpdateID: update.UpdateID,
|
||||
FromChatID: update.FromChat().ID,
|
||||
Command: api.hydrateCommand(update.Message),
|
||||
}
|
||||
|
||||
handler(messageUpdate)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *botAPI) SendMessage(chatID int64, message app.Message) error {
|
||||
if message.Image != nil {
|
||||
return api.sendPhotoMessage(chatID, message)
|
||||
}
|
||||
|
||||
return api.sendTextMessage(chatID, message)
|
||||
}
|
||||
|
||||
func (api *botAPI) SendMessageToOwner(message app.Message) error {
|
||||
return api.SendMessage(api.ownerChatID, message)
|
||||
}
|
||||
|
||||
func (api *botAPI) hydrateMessage(msg *tgbotapi.Message) app.Message {
|
||||
text := msg.Text
|
||||
if len(text) == 0 {
|
||||
text = msg.Caption
|
||||
}
|
||||
|
||||
return app.Message{
|
||||
Text: text,
|
||||
Image: api.hydrateImage(msg.Photo),
|
||||
}
|
||||
}
|
||||
|
||||
func (api *botAPI) sendTextMessage(chatID int64, message app.Message) error {
|
||||
msg := tgbotapi.NewMessage(
|
||||
chatID,
|
||||
message.Text,
|
||||
)
|
||||
msg.ParseMode = messageParseMode
|
||||
|
||||
_, err := api.bot.Send(msg)
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
func (api *botAPI) sendPhotoMessage(chatID int64, message app.Message) error {
|
||||
photos := api.preparePhotos(message)
|
||||
mediaMsg := tgbotapi.NewMediaGroup(chatID, photos)
|
||||
|
||||
_, err := api.bot.Send(mediaMsg)
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
func (api *botAPI) hydrateCommand(msg *tgbotapi.Message) *app.Command {
|
||||
if !msg.IsCommand() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var cmd app.Command
|
||||
switch msg.Command() {
|
||||
case startCommand:
|
||||
cmd = app.StartCommand
|
||||
default:
|
||||
cmd = app.UnknownCommand
|
||||
}
|
||||
|
||||
return &cmd
|
||||
}
|
||||
|
||||
func (api *botAPI) hydrateImage(photos []tgbotapi.PhotoSize) *app.Image {
|
||||
if len(photos) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var originalFileID string
|
||||
var originalFileSize int
|
||||
for _, photo := range photos {
|
||||
if photo.FileSize > originalFileSize {
|
||||
originalFileID = photo.FileID
|
||||
originalFileSize = photo.FileSize
|
||||
}
|
||||
}
|
||||
|
||||
return &app.Image{
|
||||
FileID: originalFileID,
|
||||
}
|
||||
}
|
||||
|
||||
func (api *botAPI) preparePhotos(message app.Message) []interface{} {
|
||||
photo := tgbotapi.NewInputMediaPhoto(tgbotapi.FileID(message.Image.FileID))
|
||||
photo.Caption = message.Text
|
||||
photo.ParseMode = messageParseMode
|
||||
|
||||
var photos []interface{}
|
||||
photos = append(photos, photo)
|
||||
|
||||
return photos
|
||||
}
|
||||
Reference in New Issue
Block a user