304 lines
8.3 KiB
Go
304 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"time"
|
|
|
|
"fergalla.com/dockerbot/internal/auth"
|
|
"fergalla.com/dockerbot/internal/cache"
|
|
"fergalla.com/dockerbot/internal/config"
|
|
"fergalla.com/dockerbot/internal/docker"
|
|
"fergalla.com/dockerbot/internal/i18n"
|
|
"fergalla.com/dockerbot/internal/logger"
|
|
"fergalla.com/dockerbot/internal/ui"
|
|
"github.com/go-telegram/bot"
|
|
"github.com/go-telegram/bot/models"
|
|
)
|
|
|
|
type App struct {
|
|
logger *logger.Logger
|
|
store *auth.Store
|
|
i18n *i18n.I18n
|
|
dockerInfo *docker.DockerInfo
|
|
renderer *ui.Renderer
|
|
config *config.Config
|
|
containerCache *cache.ContainerCache
|
|
}
|
|
|
|
func main() {
|
|
log := logger.New("DEBUG", "Bot Telegram")
|
|
cfg := config.Load()
|
|
i, err := i18n.New("./conf/locales", "en")
|
|
if err != nil {
|
|
log.Error(
|
|
"Failed to load locale file",
|
|
"module", "Bot",
|
|
"error", err)
|
|
os.Exit(1)
|
|
}
|
|
i.SetLocale(cfg.Locale)
|
|
|
|
store, err := auth.New("./conf/users.json")
|
|
if err != nil {
|
|
log.Error(
|
|
"Failed to load users file",
|
|
"module", "Auth",
|
|
"error", err,
|
|
)
|
|
}
|
|
d, err := docker.New()
|
|
if err != nil {
|
|
log.Error(
|
|
"Failed to create client docker",
|
|
"module", "Bot",
|
|
"error", err)
|
|
os.Exit(1)
|
|
}
|
|
ttl := time.Duration(cfg.CacheTTL) * time.Second
|
|
containerCache := cache.NewContainerCache(ttl, func() ([]docker.ContainerBasicInfo, error) {
|
|
return d.ListContainers()
|
|
})
|
|
appCtx := App{
|
|
logger: log,
|
|
store: store,
|
|
i18n: i,
|
|
dockerInfo: d,
|
|
config: cfg,
|
|
containerCache: containerCache,
|
|
}
|
|
renderer := &ui.Renderer{
|
|
T: appCtx.T,
|
|
}
|
|
appCtx.renderer = renderer
|
|
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
defer cancel()
|
|
|
|
if appCtx.config.Token == "" {
|
|
appCtx.logger.Error(
|
|
"Bot token not found",
|
|
"module", "Bot",
|
|
)
|
|
os.Exit(1)
|
|
}
|
|
opts := []bot.Option{
|
|
bot.WithMiddlewares(
|
|
appCtx.AuthMiddleware(),
|
|
appCtx.RecoveryMiddleware(),
|
|
appCtx.LoggingMiddleware(),
|
|
),
|
|
bot.WithDefaultHandler(appCtx.defaultHandler),
|
|
}
|
|
|
|
b, err := bot.New(appCtx.config.Token, opts...)
|
|
if err != nil {
|
|
appCtx.logger.Error(
|
|
"Bot created failed",
|
|
"module", "Bot",
|
|
"error", err,
|
|
)
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err := appCtx.setConfigBot(ctx, b); err != nil {
|
|
appCtx.logger.Error("Bot setup failed", "error", err)
|
|
}
|
|
|
|
b.RegisterHandler(bot.HandlerTypeMessageText, "/start", bot.MatchTypeExact, appCtx.startHandler)
|
|
b.RegisterHandler(bot.HandlerTypeMessageText, "/help", bot.MatchTypeExact, appCtx.helpHandler)
|
|
b.RegisterHandler(bot.HandlerTypeMessageText, "/docker", bot.MatchTypePrefix, appCtx.dockerHandler)
|
|
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "dockerList_page", bot.MatchTypePrefix, appCtx.callbackDockerPagination)
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "dockerList_refresh", bot.MatchTypePrefix, appCtx.callbackDockerListRefresh)
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "dockerList_infoDocker", bot.MatchTypePrefix, appCtx.callbackDockerInfo)
|
|
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "docker_action", bot.MatchTypePrefix, appCtx.callbackHandlerContainer)
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "docker_back_list", bot.MatchTypePrefix, appCtx.callbackDockerBack)
|
|
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "logs_back_detail", bot.MatchTypePrefix, appCtx.callbackDockerBackDetail)
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "logs_refresh", bot.MatchTypePrefix, appCtx.callbackDockerLogsRefresh)
|
|
b.RegisterHandler(bot.HandlerTypeCallbackQueryData, "logs", bot.MatchTypePrefix, appCtx.callbackDockerLogs)
|
|
|
|
if appCtx.config.Webhookurl != "" {
|
|
_, err := b.SetWebhook(ctx, &bot.SetWebhookParams{
|
|
URL: appCtx.config.Webhookurl,
|
|
SecretToken: appCtx.config.Webhooktoken,
|
|
})
|
|
if err != nil {
|
|
appCtx.logger.Error(
|
|
"Failed to set webhook",
|
|
"module", "Bot",
|
|
"url", appCtx.config.Webhookurl,
|
|
"error", err,
|
|
)
|
|
os.Exit(1)
|
|
}
|
|
|
|
go b.StartWebhook(ctx)
|
|
appCtx.logger.Info(
|
|
"Starting app",
|
|
"module", "Bot",
|
|
"mode", "webhook",
|
|
"url", appCtx.config.Webhookurl,
|
|
)
|
|
err = http.ListenAndServe(":8080", b.WebhookHandler())
|
|
if err != nil {
|
|
appCtx.logger.Error(
|
|
"HTTP server failed",
|
|
"module", "Bot",
|
|
"error", err,
|
|
)
|
|
os.Exit(1)
|
|
}
|
|
} else {
|
|
b.DeleteWebhook(ctx, &bot.DeleteWebhookParams{DropPendingUpdates: true})
|
|
appCtx.logger.Info(
|
|
"Starting app",
|
|
"module", "Bot",
|
|
"mode", "polling",
|
|
)
|
|
b.Start(ctx)
|
|
}
|
|
|
|
}
|
|
|
|
func (a *App) T(key string, vars ...map[string]string) string {
|
|
var values map[string]string
|
|
if len(vars) > 0 {
|
|
values = vars[0]
|
|
} else {
|
|
values = nil
|
|
}
|
|
return a.i18n.T(key, values)
|
|
}
|
|
|
|
func (appCtx *App) setConfigBot(ctx context.Context, b *bot.Bot) error {
|
|
var errs []error
|
|
|
|
commands := []models.BotCommand{
|
|
{
|
|
Command: "start",
|
|
Description: appCtx.T("i.cmd.start"),
|
|
},
|
|
{
|
|
Command: "docker",
|
|
Description: appCtx.T("i.cmd.docker.detailed"),
|
|
},
|
|
{
|
|
Command: "help",
|
|
Description: appCtx.T("i.cmd.help"),
|
|
},
|
|
}
|
|
|
|
if _, err := b.SetMyCommands(ctx, &bot.SetMyCommandsParams{
|
|
Commands: commands,
|
|
}); err != nil {
|
|
errs = append(errs, fmt.Errorf("set commands: %w", err))
|
|
}
|
|
|
|
/* if _, err := b.SetMyName(ctx, &bot.SetMyNameParams{
|
|
Name: "DockerBot 🤖",
|
|
}); err != nil {
|
|
errs = append(errs, fmt.Errorf("set name: %w", err))
|
|
}
|
|
|
|
if _, err := b.SetMyShortDescription(ctx, &bot.SetMyShortDescriptionParams{
|
|
ShortDescription: appCtx.T("app.short"),
|
|
}); err != nil {
|
|
errs = append(errs, fmt.Errorf("set short description: %w", err))
|
|
}
|
|
|
|
if _, err := b.SetMyDescription(ctx, &bot.SetMyDescriptionParams{
|
|
Description: appCtx.T("app.about"),
|
|
}); err != nil {
|
|
errs = append(errs, fmt.Errorf("set description: %w", err))
|
|
}
|
|
|
|
if err := setProfileImage(ctx, b); err != nil {
|
|
errs = append(errs, fmt.Errorf("set profile image: %w", err))
|
|
} */
|
|
|
|
if len(errs) > 0 {
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (appCtx *App) defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
|
|
chatID := update.Message.Chat.ID
|
|
appCtx.sendMessage(ctx, b, chatID, appCtx.T("e.unknown_command"), false, nil)
|
|
}
|
|
func (appCtx *App) startHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
|
|
appCtx.sendMessage(ctx, b, update.Message.Chat.ID, appCtx.T("app.welcome", map[string]string{"name": update.Message.From.FirstName}), true, nil)
|
|
}
|
|
func (appCtx *App) helpHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
|
|
appCtx.sendMessage(ctx, b, update.Message.Chat.ID, appCtx.T("app.help", nil), true, nil)
|
|
}
|
|
|
|
func (appCtx *App) dockerHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
|
|
chatID := update.Message.Chat.ID
|
|
args := strings.Fields(strings.TrimSpace(update.Message.Text[len("/docker"):]))
|
|
|
|
if len(args) == 0 || args[0] == "list" {
|
|
appCtx.handleDockerList(ctx, b, chatID)
|
|
return
|
|
}
|
|
|
|
subcommand := strings.ToLower(args[0])
|
|
params := args[1:]
|
|
|
|
appCtx.handleDockerSubcommand(ctx, b, chatID, subcommand, params)
|
|
}
|
|
func (appCtx *App) handleDockerSubcommand(ctx context.Context, b *bot.Bot, chatID int64, subcommand string, params []string) {
|
|
switch subcommand {
|
|
case "info":
|
|
appCtx.handleDockerInfo(ctx, b, chatID, params)
|
|
case "start", "stop", "pause", "unpause", "restart":
|
|
appCtx.handleDockerAction(ctx, b, chatID, subcommand, params)
|
|
case "logs":
|
|
appCtx.handleDockerLogs(ctx, b, chatID, params)
|
|
case "help":
|
|
appCtx.sendDockerHelp(ctx, b, chatID)
|
|
default:
|
|
appCtx.sendMessage(ctx, b, chatID, appCtx.T("e.unknown_command"), true, nil)
|
|
}
|
|
}
|
|
func (appCtx *App) sendDockerHelp(ctx context.Context, b *bot.Bot, chatID int64) {
|
|
appCtx.sendMessage(ctx, b, chatID, appCtx.T("app.help", nil), true, nil)
|
|
}
|
|
|
|
func (appCtx *App) sendMessage(ctx context.Context, b *bot.Bot, chatID int64, text string, parseHTML bool, keyboard *models.InlineKeyboardMarkup) {
|
|
params := &bot.SendMessageParams{
|
|
ChatID: chatID,
|
|
Text: text,
|
|
}
|
|
|
|
if parseHTML {
|
|
params.ParseMode = "html"
|
|
params.LinkPreviewOptions = &models.LinkPreviewOptions{IsDisabled: bot.True()}
|
|
} else {
|
|
params.ParseMode = "markdown"
|
|
}
|
|
if keyboard != nil {
|
|
params.ReplyMarkup = keyboard
|
|
}
|
|
|
|
_, err := b.SendMessage(ctx, params)
|
|
if err != nil {
|
|
appCtx.logger.Error(
|
|
"Failed to send message",
|
|
"module", "Bot",
|
|
"chat_id", chatID,
|
|
"error", err,
|
|
)
|
|
return
|
|
}
|
|
}
|