Rewrite everything with new framework
This commit is contained in:
+52
-20
@@ -1,38 +1,70 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
"github.com/go-telegram/bot"
|
||||||
"github.com/nightnoryu/anon3anon/pkg/app"
|
"github.com/go-telegram/bot/models"
|
||||||
"github.com/nightnoryu/anon3anon/pkg/infrastructure"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
conf, err := parseEnv()
|
conf, err := parseEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bot, err := tgbotapi.NewBotAPI(conf.TelegramBotToken)
|
opts := []bot.Option{
|
||||||
if err != nil {
|
bot.WithDebug(),
|
||||||
log.Panic(err)
|
bot.WithMessageTextHandler("/start", bot.MatchTypeExact, getStartHandler()),
|
||||||
|
bot.WithDefaultHandler(getDefaultHandler(conf)),
|
||||||
}
|
}
|
||||||
log.Printf("Authorized on account %s", bot.Self.UserName)
|
|
||||||
|
|
||||||
errorsChan := make(chan error)
|
b, err := bot.New(conf.TelegramBotToken, opts...)
|
||||||
botAPI := infrastructure.NewBotAPI(bot, conf.OwnerChatID)
|
if err != nil {
|
||||||
|
|
||||||
commandHandler := app.NewCommandHandler(botAPI)
|
|
||||||
|
|
||||||
service := app.NewAnonymousQuestionsService(errorsChan, commandHandler, botAPI)
|
|
||||||
go func() {
|
|
||||||
for err := range errorsChan {
|
|
||||||
log.Println(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if err = service.ServeMessages(); err != nil {
|
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.Start(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStartHandler() bot.HandlerFunc {
|
||||||
|
return func(ctx context.Context, b *bot.Bot, update *models.Update) {
|
||||||
|
params := &bot.SendMessageParams{
|
||||||
|
ChatID: update.Message.Chat.ID,
|
||||||
|
Text: "Жду твоих сообщений!!\nОтветы будут в канале @meme_me_a_meme",
|
||||||
|
}
|
||||||
|
_, err := b.SendMessage(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultHandler(conf *config) bot.HandlerFunc {
|
||||||
|
return func(ctx context.Context, b *bot.Bot, update *models.Update) {
|
||||||
|
params := &bot.CopyMessageParams{
|
||||||
|
ChatID: conf.OwnerChatID,
|
||||||
|
FromChatID: update.Message.Chat.ID,
|
||||||
|
MessageID: update.Message.ID,
|
||||||
|
}
|
||||||
|
_, err := b.CopyMessage(ctx, params)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendParams := &bot.SendMessageParams{
|
||||||
|
ChatID: update.Message.Chat.ID,
|
||||||
|
Text: "Сообщение отправлено!",
|
||||||
|
}
|
||||||
|
_, err = b.SendMessage(ctx, sendParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ module github.com/nightnoryu/anon3anon
|
|||||||
go 1.22.5
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
|
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/go-telegram/bot v1.13.3
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
|
github.com/go-telegram/bot v1.13.3 h1:r2erpHI5rMQsR5TFWJ/XVqWHq9R228fcaejLFvXJsmM=
|
||||||
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
|
github.com/go-telegram/bot v1.13.3/go.mod h1:i2TRs7fXWIeaceF3z7KzsMt/he0TwkVC680mvdTFYeM=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
func NewAnonymousQuestionsService(
|
|
||||||
errorsChan chan error,
|
|
||||||
commandHandler CommandHandler,
|
|
||||||
api BotAPI,
|
|
||||||
) AnonymousMessagesService {
|
|
||||||
return &anonymousMessagesService{
|
|
||||||
errorsChan: errorsChan,
|
|
||||||
commandHandler: commandHandler,
|
|
||||||
api: api,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
messageSentReply = "*Сообщение отправлено!*"
|
|
||||||
unsupportedMessageReply = "*Такое сообщение не поддерживается :(*"
|
|
||||||
|
|
||||||
newMessageNotification = "Новое анонимное сообщение!"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AnonymousMessagesService interface {
|
|
||||||
ServeMessages() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type anonymousMessagesService struct {
|
|
||||||
errorsChan chan error
|
|
||||||
commandHandler CommandHandler
|
|
||||||
api BotAPI
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *anonymousMessagesService) ServeMessages() error {
|
|
||||||
return s.api.HandleUpdates(func(update MessageUpdate) {
|
|
||||||
if update.Command != nil {
|
|
||||||
err := s.commandHandler.HandleCommand(update)
|
|
||||||
if err != nil {
|
|
||||||
s.errorsChan <- err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if update.Message.Sticker != nil {
|
|
||||||
err := s.api.SendMessage(update.FromChatID, Message{
|
|
||||||
Text: unsupportedMessageReply,
|
|
||||||
UseMarkdown: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
s.errorsChan <- err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationText := newMessageNotification + "\n\n" + update.Message.Text
|
|
||||||
err := s.api.SendMessageToOwner(Message{
|
|
||||||
Text: notificationText,
|
|
||||||
Image: update.Message.Image,
|
|
||||||
Audio: update.Message.Audio,
|
|
||||||
Video: update.Message.Video,
|
|
||||||
Voice: update.Message.Voice,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
s.errorsChan <- err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.api.SendMessage(update.FromChatID, Message{
|
|
||||||
Text: messageSentReply,
|
|
||||||
UseMarkdown: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
s.errorsChan <- err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
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
|
|
||||||
UseMarkdown bool
|
|
||||||
Image *Image
|
|
||||||
Audio *Audio
|
|
||||||
Video *Video
|
|
||||||
Sticker *Sticker
|
|
||||||
Voice *Voice
|
|
||||||
}
|
|
||||||
|
|
||||||
type Image struct {
|
|
||||||
FileID string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Audio struct {
|
|
||||||
FileID string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Video struct {
|
|
||||||
FileID string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Sticker struct {
|
|
||||||
FileID string
|
|
||||||
Emoji string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Voice struct {
|
|
||||||
FileID string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Command int
|
|
||||||
|
|
||||||
const (
|
|
||||||
UnknownCommand Command = iota
|
|
||||||
StartCommand
|
|
||||||
InfoCommand
|
|
||||||
)
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
func NewCommandHandler(api BotAPI) CommandHandler {
|
|
||||||
return &commandHandler{
|
|
||||||
api: api,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CommandHandler interface {
|
|
||||||
HandleCommand(update MessageUpdate) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type commandHandler struct {
|
|
||||||
api BotAPI
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *commandHandler) HandleCommand(update MessageUpdate) error {
|
|
||||||
if update.Command == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var msgText string
|
|
||||||
switch *update.Command {
|
|
||||||
case StartCommand:
|
|
||||||
msgText = "Жду твоих вопросов!!\nОтветы будут в канале @meme_me_a_meme (>ᴗ•)"
|
|
||||||
case InfoCommand:
|
|
||||||
msgText = "Бот привязан к каналу @meme_me_a_meme, так что ответы ищи там!!\nНа данный момент поддерживаются текст, фото и видео („• ᴗ •„)"
|
|
||||||
case UnknownCommand:
|
|
||||||
msgText = "Неизвестная команда!"
|
|
||||||
}
|
|
||||||
|
|
||||||
return h.api.SendMessage(update.FromChatID, Message{Text: msgText})
|
|
||||||
}
|
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
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"
|
|
||||||
infoCommand = "info"
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if message.Audio != nil {
|
|
||||||
return api.sendAudioMessage(chatID, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
if message.Video != nil {
|
|
||||||
return api.sendVideoMessage(chatID, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
if message.Voice != nil {
|
|
||||||
return api.sendVoiceMessage(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),
|
|
||||||
Audio: api.hydrateAudio(msg.Audio),
|
|
||||||
Video: api.hydrateVideo(msg.Video),
|
|
||||||
Sticker: api.hydrateSticker(msg.Sticker),
|
|
||||||
Voice: api.hydrateVoice(msg.Voice),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) sendTextMessage(chatID int64, message app.Message) error {
|
|
||||||
msg := tgbotapi.NewMessage(
|
|
||||||
chatID,
|
|
||||||
message.Text,
|
|
||||||
)
|
|
||||||
|
|
||||||
if message.UseMarkdown {
|
|
||||||
msg.ParseMode = tgbotapi.ModeMarkdown
|
|
||||||
}
|
|
||||||
|
|
||||||
_, 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) sendAudioMessage(chatID int64, message app.Message) error {
|
|
||||||
audios := api.prepareAudio(message)
|
|
||||||
mediaMsg := tgbotapi.NewMediaGroup(chatID, audios)
|
|
||||||
|
|
||||||
_, err := api.bot.Send(mediaMsg)
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) sendVideoMessage(chatID int64, message app.Message) error {
|
|
||||||
video := api.prepareVideo(message)
|
|
||||||
mediaMsg := tgbotapi.NewMediaGroup(chatID, video)
|
|
||||||
|
|
||||||
_, err := api.bot.Send(mediaMsg)
|
|
||||||
return errors.WithStack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) sendVoiceMessage(chatID int64, message app.Message) error {
|
|
||||||
voiceMsg := tgbotapi.NewVoice(chatID, tgbotapi.FileID(message.Voice.FileID))
|
|
||||||
|
|
||||||
voiceMsg.Caption = message.Text
|
|
||||||
if message.UseMarkdown {
|
|
||||||
voiceMsg.ParseMode = tgbotapi.ModeMarkdown
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := api.bot.Send(voiceMsg)
|
|
||||||
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
|
|
||||||
case infoCommand:
|
|
||||||
cmd = app.InfoCommand
|
|
||||||
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) hydrateAudio(audio *tgbotapi.Audio) *app.Audio {
|
|
||||||
if audio == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app.Audio{
|
|
||||||
FileID: audio.FileID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) hydrateVideo(video *tgbotapi.Video) *app.Video {
|
|
||||||
if video == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app.Video{
|
|
||||||
FileID: video.FileID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) hydrateSticker(sticker *tgbotapi.Sticker) *app.Sticker {
|
|
||||||
if sticker == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app.Sticker{
|
|
||||||
FileID: sticker.FileID,
|
|
||||||
Emoji: sticker.Emoji,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) hydrateVoice(voice *tgbotapi.Voice) *app.Voice {
|
|
||||||
if voice == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app.Voice{
|
|
||||||
FileID: voice.FileID,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) preparePhotos(message app.Message) []interface{} {
|
|
||||||
photo := tgbotapi.NewInputMediaPhoto(tgbotapi.FileID(message.Image.FileID))
|
|
||||||
photo.Caption = message.Text
|
|
||||||
|
|
||||||
if message.UseMarkdown {
|
|
||||||
photo.ParseMode = tgbotapi.ModeMarkdown
|
|
||||||
}
|
|
||||||
|
|
||||||
var photos []interface{}
|
|
||||||
photos = append(photos, photo)
|
|
||||||
|
|
||||||
return photos
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) prepareAudio(message app.Message) []interface{} {
|
|
||||||
audio := tgbotapi.NewInputMediaAudio(tgbotapi.FileID(message.Audio.FileID))
|
|
||||||
audio.Caption = message.Text
|
|
||||||
|
|
||||||
if message.UseMarkdown {
|
|
||||||
audio.ParseMode = tgbotapi.ModeMarkdown
|
|
||||||
}
|
|
||||||
|
|
||||||
return []interface{}{audio}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *botAPI) prepareVideo(message app.Message) []interface{} {
|
|
||||||
video := tgbotapi.NewInputMediaVideo(tgbotapi.FileID(message.Video.FileID))
|
|
||||||
video.Caption = message.Text
|
|
||||||
|
|
||||||
if message.UseMarkdown {
|
|
||||||
video.ParseMode = tgbotapi.ModeMarkdown
|
|
||||||
}
|
|
||||||
|
|
||||||
return []interface{}{video}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user