Skip to content

Commit

Permalink
Add code docs; Change Error Struct; (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
artem-telnov committed Apr 6, 2023
1 parent b3d353b commit bc8d1a1
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 78 deletions.
7 changes: 4 additions & 3 deletions cmd/dushno_and_tochka_bot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/joho/godotenv"
)

// Точка запуска бота. Инициализирует все основные куски проекта и вызвывает бот поллинг.
func main() {
time.Local = time.UTC
logger := log.GetLogger()
Expand All @@ -23,7 +24,7 @@ func main() {
logger.Error(err)
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)

storages.NewStorage(ctx)

Expand All @@ -34,11 +35,11 @@ func main() {

logger.Info("All Rigth!")

bot, err := bot.New()
tgBot, err := bot.New()

if err != nil {
logger.Fatal(err)
}

bot.StartPolling(cancel)
bot.StartPolling(tgBot, ctx)
}
66 changes: 27 additions & 39 deletions internal/pkg/bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package bot

import (
"context"
"errors"
"os"
"os/signal"
"syscall"
"sync"

"github.com/jackc/pgx/v5/pgxpool"
"github.com/mymmrac/telego"

th "github.com/mymmrac/telego/telegohandler"
Expand All @@ -18,68 +15,56 @@ import (
"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/log"
)

func New() (*Bot, error) {
// Инициализатор Бота. Ожидает что в переменных окржения имеет переменная TELEGRAM_BOT_TOKEN,
// в которой указан токен. Если данной переменной окружения не будет, то вызовется паника
func New() (*telego.Bot, error) {
logger := log.GetLogger()
telegramBotToken := os.Getenv("TELEGRAM_BOT_TOKEN")

if telegramBotToken == "" {
err := errors.New("Telegram Bot Token not found. Please specify TELEGRAM_BOT_TOKEN env.")
return nil, err
logger.Fatal("Telegram Bot Token not found. Please specify TELEGRAM_BOT_TOKEN env.")
}

logger := log.GetLogger()

newApiBot, err := telego.NewBot(telegramBotToken, telego.WithLogger(logger))

if err != nil {
return nil, err
}

dbConnection := dbconnection.GetPoolConnections()
botHandler := &Bot{
bot: newApiBot,
dbConnection: dbConnection,
}

return botHandler, nil

}
return newApiBot, nil

type Bot struct {
bot *telego.Bot
dbConnection *pgxpool.Pool
}

func (b *Bot) StartPolling(cancel context.CancelFunc) {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
func StartPolling(bot *telego.Bot, ctx context.Context) {
logger := log.GetLogger()
done := make(chan struct{}, 1)
var wg sync.WaitGroup

updates, _ := b.bot.UpdatesViaLongPolling(nil)
updates, _ := bot.UpdatesViaLongPolling(nil)
dbpool := dbconnection.GetPoolConnections()

bh, _ := th.NewBotHandler(b.bot, updates)
bh, _ := th.NewBotHandler(bot, updates)
wg.Add(1)

go func() {
<-sigs
go func(wg *sync.WaitGroup) {
<-ctx.Done()

logger.Info("Polling is stoping")
b.bot.StopLongPolling()
bot.StopLongPolling()
bh.Stop()

logger.Info("Long polling stoped")
cancel()

done <- struct{}{}
}()
wg.Done()
}(&wg)

defer bh.Stop()
defer b.bot.StopLongPolling()
defer bot.StopLongPolling()
defer dbpool.Close()

bh.Use(
func(next th.Handler) th.Handler {
return func(bot *telego.Bot, update telego.Update) {
// midleware для оборачивания обработки в горутину.
go func() {
defer func() {
if r := recover(); r != nil {
Expand All @@ -92,6 +77,14 @@ func (b *Bot) StartPolling(cancel context.CancelFunc) {
},
)

initialHandlers(bh)
bh.Start()
wg.Wait()
logger.Info("Long polling is done")
}

// инициализируем все обработчики
func initialHandlers(bh *th.BotHandler) {
bh.Handle(eventprocessor.ProcessStartComand, th.CommandEqual("start"))
bh.Handle(eventprocessor.ProcessHelpComand, th.CommandEqual("help"))
bh.Handle(eventprocessor.ProcessProposeProblemFromMessage, th.CommandEqual("suggest_problem"))
Expand All @@ -100,9 +93,4 @@ func (b *Bot) StartPolling(cancel context.CancelFunc) {
bh.Handle(eventprocessor.ProcessProposeProblemFromMessage, th.TextEqual("Хочу предложить задачу"))
bh.Handle(eventprocessor.ProcessGetLinkFromReply, custompredicates.IsNewProposeTask)
bh.Handle(eventprocessor.ProcessNotSupportedComandsComand, th.AnyCommand())

bh.Start()

<-done
logger.Info("Long polling is done")
}
24 changes: 24 additions & 0 deletions internal/pkg/customerrors/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Пакет определяющий кастомные ошибки. Содержит саму ошбку,
// которая будет отображаться в логах и текст, который будет отправлен пользователю.
package customerrors

import "fmt"

type CustomError error

// перечень кастомных ошибок
var (
ErrNotSupportedURL CustomError = fmt.Errorf("notSupported")
ErrNotUniqSuggestion CustomError = fmt.Errorf("notUniqSuggestion")
)

// перечень сообщений, которые будут отправлены пользователю.
const (
ErrNotSupportedURLMessage string = "Данный URL не поддерживается"
ErrNotUniqSuggestionMessage string = "Вы уже предлагали данную задачу."
)

var CustomErrors = map[CustomError]string{
ErrNotSupportedURL: ErrNotSupportedURLMessage,
ErrNotUniqSuggestion: ErrNotUniqSuggestionMessage,
}
12 changes: 11 additions & 1 deletion internal/pkg/custompredicates/custompredicates.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@ package custompredicates

import "github.com/mymmrac/telego"

// Сообщения которые будут использоваться в кастомных условиях для Handlers.
type CustomPredicateMessage string

const (
GetMeProblemLink CustomPredicateMessage = "Укажите ссылку на задачу."
)

// кастомное правило для обработчиков бота
// позволяет определить, что бот написал сообщение "Укажите ссылку на задачу.",
// а пользователь на него ответил;
func IsNewProposeTask(update telego.Update) bool {
if update.Message != nil &&
update.Message.ReplyToMessage != nil &&
update.Message.ReplyToMessage.From.IsBot {
return update.Message.ReplyToMessage.Text == "Укажите ссылку на задачу."
return update.Message.ReplyToMessage.Text == string(GetMeProblemLink)
}
return false
}
5 changes: 5 additions & 0 deletions internal/pkg/dbconnection/dbconnectio.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@ import (
"github.com/jackc/pgx/v5/pgxpool"
)

// объект синглтона для пула конекторов
var pgPoolConnection *pgxpool.Pool

// создает пул коннекторов.
// Требуется что бы были объявлены переменные окружения
// POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DB_PORT, DB_HOST
func newPoolConnections() {
logger := log.GetLogger()
dbUser := os.Getenv("POSTGRES_USER")
Expand All @@ -37,6 +41,7 @@ func newPoolConnections() {
pgPoolConnection = dbpool
}

// инициализирует один раз пул конекторов и перекладывает его в синглтон
func GetPoolConnections() *pgxpool.Pool {
var once sync.Once
once.Do(newPoolConnections)
Expand Down
27 changes: 22 additions & 5 deletions internal/pkg/eventprocessor/eventprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package eventprocessor
import (
"fmt"

"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/customerrors"
"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/custompredicates"
"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/log"
"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/models"
"github.com/artem-telnov/dushno_and_tochka_bot/internal/pkg/storages"
"github.com/mymmrac/telego"
tu "github.com/mymmrac/telego/telegoutil"
)

// Обработчик команды "/start". Пишет приветственное сообщение и добавляет новых пользователей в БД.
func ProcessStartComand(bot *telego.Bot, update telego.Update) {

storage := storages.GetStorage()
Expand All @@ -31,6 +34,7 @@ func ProcessStartComand(bot *telego.Bot, update telego.Update) {
_, _ = bot.SendMessage(message)
}

// Обработчик команды "/help". Пишет информационное сообщение о возможностях бота.
func ProcessHelpComand(bot *telego.Bot, update telego.Update) {

message := tu.Message(
Expand All @@ -46,6 +50,7 @@ func ProcessHelpComand(bot *telego.Bot, update telego.Update) {
_, _ = bot.SendMessage(message)
}

// Обработчик не известных команд.
func ProcessNotSupportedComandsComand(bot *telego.Bot, update telego.Update) {
message := tu.Message(
tu.ID(update.Message.Chat.ID),
Expand All @@ -55,6 +60,7 @@ func ProcessNotSupportedComandsComand(bot *telego.Bot, update telego.Update) {
_, _ = bot.SendMessage(message)
}

// Обработчик предложенной задачи. Вычитывает ссылку из сообщения, валидирует ее и добавляет в БД.
func ProcessGetLinkFromReply(bot *telego.Bot, update telego.Update) {
logger := log.GetLogger()
answer := update.Message.Text
Expand Down Expand Up @@ -98,6 +104,7 @@ func ProcessGetLinkFromReply(bot *telego.Bot, update telego.Update) {
_, _ = bot.SendMessage(message)
}

// Обработчик команды "/show_top_suggestions". Выбирает ТОП предложений из бд и формирует сообщение.
func ProcessShowAllProposeProblems(bot *telego.Bot, update telego.Update) {

var message string
Expand All @@ -122,13 +129,17 @@ func ProcessShowAllProposeProblems(bot *telego.Bot, update telego.Update) {
`
botMessage = tu.Message(tu.ID(update.Message.Chat.ID), message)
} else {
// Формируем сообщение с ссылками на Problems.

// инитим список сообщений
var entityMessages []tu.MessageEntityCollection
entityMessages = append(entityMessages, tu.Entity("ТОП предложениями являются:\n"))

var problemUrl string

for problem, count := range suggstions {
problemUrl = problem.GetUrl()
problemUrl = problem.GetOriginalUrl()
// собираем каждую строчку отдельно
entityMessages = append(entityMessages, tu.Entity("\n- Задача "))
if problemUrl != "" {
entityMessages = append(entityMessages, tu.Entity(string(problem.Name)).TextLink(problemUrl))
Expand All @@ -137,13 +148,15 @@ func ProcessShowAllProposeProblems(bot *telego.Bot, update telego.Update) {
}
entityMessages = append(entityMessages, tu.Entity(fmt.Sprintf(" была предложена %v раз.\n", int(*count))))
}
// скармливаем собранный список сообщений и формируем целостное сообщение.
botMessage = tu.MessageWithEntities(tu.ID(update.Message.Chat.ID), entityMessages...)

}

_, _ = bot.SendMessage(botMessage)
}

// Обработчик команды "/show_my_suggestion". Показывает предложения пользователя.
func ProcessShowMyProposeProblem(bot *telego.Bot, update telego.Update) {
var botMessage *telego.SendMessageParams

Expand All @@ -165,6 +178,7 @@ func ProcessShowMyProposeProblem(bot *telego.Bot, update telego.Update) {
}

if userSuggestions != nil {
// формируем итоговое сообщение с ссылками.
var entityMessages []tu.MessageEntityCollection
entityMessages = append(entityMessages, tu.Entity("Задачи, которые вы предложили и они еще не были разобраны:\n"))

Expand All @@ -173,7 +187,7 @@ func ProcessShowMyProposeProblem(bot *telego.Bot, update telego.Update) {

for i := range userSuggestions {
problem = userSuggestions[i]
problemUrl = problem.GetUrl()
problemUrl = problem.GetOriginalUrl()
entityMessages = append(entityMessages, tu.Entity("\n- "))
if problemUrl != "" {
entityMessages = append(entityMessages, tu.Entity(string(problem.Name)).TextLink(problemUrl))
Expand All @@ -194,19 +208,22 @@ func ProcessShowMyProposeProblem(bot *telego.Bot, update telego.Update) {
_, _ = bot.SendMessage(botMessage)
}

// Обработчик команды "/suggest_problem". Формирует сообщение с просьюой предоставить ссылку на Problem
func ProcessProposeProblemFromMessage(bot *telego.Bot, update telego.Update) {
message := tu.Message(
tu.ID(update.Message.Chat.ID),
"Укажите ссылку на задачу.",
string(custompredicates.GetMeProblemLink),
).WithReplyMarkup(tu.ForceReply())

_, _ = bot.SendMessage(message)
}

func sendErrorMessage(bot *telego.Bot, update *telego.Update, err models.ModelError) {
// Обработчик ошибок и отправка стандартного сообщения об ошибке.
// Позволяет добавлять кастомные ошибки models.ModelError.
func sendErrorMessage(bot *telego.Bot, update *telego.Update, err customerrors.CustomError) {
logger := log.GetLogger()
logger.Error(err)
msg, ok := models.ModelErrors[err]
msg, ok := customerrors.CustomErrors[err]

if !ok {
msg = "Почему-то не получилось посмотреть информацию... Попробуйте позже."
Expand Down
5 changes: 5 additions & 0 deletions internal/pkg/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import (
"go.uber.org/zap/zapcore"
)

// Синглтон логгера
var logger *zap.SugaredLogger = nil

// Инициализатор логгера.
// Если указать LOG_PATH в переменных окружения, то путь будет использоваться из нее.
func newLogger() {
logPath := os.Getenv("LOG_PATH")
if logPath == "" {
Expand Down Expand Up @@ -50,6 +53,8 @@ func newLogger() {
logger = l.Sugar()
}

// Позволяет инициализировать один раз Sindleton логгера.
// После это возвращает инстанс синглтон логгера.
func GetLogger() *zap.SugaredLogger {
var once sync.Once
once.Do(newLogger)
Expand Down
20 changes: 0 additions & 20 deletions internal/pkg/models/errors.go

This file was deleted.

Loading

0 comments on commit bc8d1a1

Please sign in to comment.