diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9f08f62 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Vladimir "fat0troll" Hodakov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/domains/commands/v1/exported.go b/domains/commands/v1/exported.go new file mode 100644 index 0000000..68ed697 --- /dev/null +++ b/domains/commands/v1/exported.go @@ -0,0 +1,25 @@ +// Fantasy World Zookeeper Bot +// Copyright (c) 2018 Vladimir "fat0troll" Hodakov + +package commandsv1 + +import ( + "github.com/rs/zerolog" + "lab.wtfteam.pro/fat0troll/fw_zookeeper/context" + "lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/router" +) + +var ( + c *context.Context + log zerolog.Logger +) + +// New initializes package +func New(cc *context.Context) { + c = cc + log = c.Logger.With().Str("domain", "commands").Int("version", 1).Logger() + + router.RegisterPrivateCommand("start", StartCommand) + + log.Info().Msg("Domain «commands» initialized") +} diff --git a/domains/commands/v1/start.go b/domains/commands/v1/start.go new file mode 100644 index 0000000..93e17de --- /dev/null +++ b/domains/commands/v1/start.go @@ -0,0 +1,16 @@ +// Fantasy World Zookeeper Bot +// Copyright (c) 2018 Vladimir "fat0troll" Hodakov + +package commandsv1 + +import ( + "gitlab.com/toby3d/telegram" + itelegram "lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/telegram" +) + +// StartCommand responds to /start message +func StartCommand(update *telegram.Update) { + message := "*Hello, hello, hello*" + + itelegram.RespondWithMarkdown(update.Message.Chat.ID, message) +} diff --git a/internal/router/exported.go b/internal/router/exported.go index 950cd6c..e6bb56c 100644 --- a/internal/router/exported.go +++ b/internal/router/exported.go @@ -14,32 +14,26 @@ var ( c *context.Context log zerolog.Logger - // Requests is a pointer to initialized Router object - Requests *Router + // Router is a struct which handles router functions + router struct { + privateCommands map[string]func(update *telegram.Update) + groupCommands map[string]func(update *telegram.Update) + privateRegulars map[*regexp.Regexp]func(update *telegram.Update) + groupRegulars map[*regexp.Regexp]func(update *telegram.Update) + inlineQueries map[*regexp.Regexp]func(update *telegram.Update) + } ) -// Router is a struct which handles router functions -type Router struct { - privateCommands map[string]func(update *telegram.Update) - groupCommands map[string]func(update *telegram.Update) - privateRegulars map[*regexp.Regexp]func(update *telegram.Update) - groupRegulars map[*regexp.Regexp]func(update *telegram.Update) - inlineQueries map[*regexp.Regexp]func(update *telegram.Update) -} - // New initializes package func New(cc *context.Context) { c = cc log = c.Logger.With().Str("domain", "router").Int("version", 1).Logger() - r := &Router{} - r.privateCommands = make(map[string]func(update *telegram.Update)) - r.groupCommands = make(map[string]func(update *telegram.Update)) - r.privateRegulars = make(map[*regexp.Regexp]func(update *telegram.Update)) - r.groupRegulars = make(map[*regexp.Regexp]func(update *telegram.Update)) - r.inlineQueries = make(map[*regexp.Regexp]func(update *telegram.Update)) + router.privateCommands = make(map[string]func(update *telegram.Update)) + router.groupCommands = make(map[string]func(update *telegram.Update)) + router.privateRegulars = make(map[*regexp.Regexp]func(update *telegram.Update)) + router.groupRegulars = make(map[*regexp.Regexp]func(update *telegram.Update)) + router.inlineQueries = make(map[*regexp.Regexp]func(update *telegram.Update)) log.Info().Msg("Initialized requests router") - - Requests = r } diff --git a/internal/router/router.go b/internal/router/router.go index 47e018c..48b53e6 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -19,7 +19,7 @@ var ( acceptingForwardsFrom = []int{} ) -func (r *Router) checkForward(update *telegram.Update) error { +func checkForward(update *telegram.Update) error { if update.Message.ForwardFrom != nil { log.Debug().Msgf("Processing forward from Telegram ID = %d", update.Message.ForwardFrom.ID) for i := range acceptingForwardsFrom { @@ -32,9 +32,9 @@ func (r *Router) checkForward(update *telegram.Update) error { return errors.New("Can't handle forward from Telegram user with ID =" + strconv.Itoa(update.Message.ForwardFrom.ID)) } -func (r *Router) handleInlineQuery(update *telegram.Update) { +func handleInlineQuery(update *telegram.Update) { rxpMatched := false - for rxp, function := range r.inlineQueries { + for rxp, function := range router.inlineQueries { if rxp.MatchString(update.InlineQuery.Query) { if rxpMatched { log.Warn().Msgf("The message handled more than once: %s, %s", update.InlineQuery.Query, strings.Replace(rxp.String(), "\n", "\\n", -1)) @@ -49,7 +49,7 @@ func (r *Router) handleInlineQuery(update *telegram.Update) { } } -func (r *Router) handleRequest(update *telegram.Update, commands map[string]func(*telegram.Update), rxps map[*regexp.Regexp]func(*telegram.Update)) { +func handleRequest(update *telegram.Update, commands map[string]func(*telegram.Update), rxps map[*regexp.Regexp]func(*telegram.Update)) { switch { case update.Message.IsCommand(): if commands[update.Message.Command()] != nil { @@ -75,61 +75,61 @@ func (r *Router) handleRequest(update *telegram.Update, commands map[string]func } } -func (r *Router) handleGroupRequest(update *telegram.Update) { - r.handleRequest(update, r.groupCommands, r.groupRegulars) +func handleGroupRequest(update *telegram.Update) { + handleRequest(update, router.groupCommands, router.groupRegulars) } -func (r *Router) handlePrivateRequest(update *telegram.Update) { - r.handleRequest(update, r.privateCommands, r.privateRegulars) +func handlePrivateRequest(update *telegram.Update) { + handleRequest(update, router.privateCommands, router.privateRegulars) } // RegisterPrivateCommand adds function to private commands list -func (r *Router) RegisterPrivateCommand(command string, handleFunc func(update *telegram.Update)) { +func RegisterPrivateCommand(command string, handleFunc func(update *telegram.Update)) { log.Debug().Msgf("Registering handler for private command /%s", command) - r.privateCommands[command] = handleFunc + router.privateCommands[command] = handleFunc } // RegisterPrivateRegexp adds function to private regexp list -func (r *Router) RegisterPrivateRegexp(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { +func RegisterPrivateRegexp(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { log.Debug().Msgf("Registering handler for regular expresson: %s", strings.Replace(rxp.String(), "\n", "\\n", -1)) - r.privateRegulars[rxp] = handleFunc + router.privateRegulars[rxp] = handleFunc } // RegisterGroupCommand adds function to group commands list -func (r *Router) RegisterGroupCommand(command string, handleFunc func(update *telegram.Update)) { +func RegisterGroupCommand(command string, handleFunc func(update *telegram.Update)) { log.Debug().Msgf("Registering handler for group command /%s", command) - r.groupCommands[command] = handleFunc + router.groupCommands[command] = handleFunc } // RegisterGroupRegexp adds function to group regexp list -func (r *Router) RegisterGroupRegexp(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { +func RegisterGroupRegexp(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { log.Debug().Msgf("Registering handler for regular expresson: %s", strings.Replace(rxp.String(), "\n", "\\n", -1)) - r.groupRegulars[rxp] = handleFunc + router.groupRegulars[rxp] = handleFunc } // RegisterInlineQueryResult adds function to list of inline queries -func (r *Router) RegisterInlineQueryResult(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { +func RegisterInlineQueryResult(rxp *regexp.Regexp, handleFunc func(update *telegram.Update)) { log.Debug().Msgf("Registering handler for inline regular expresson: %s", strings.Replace(rxp.String(), "\n", "\\n", -1)) - r.inlineQueries[rxp] = handleFunc + router.inlineQueries[rxp] = handleFunc } // Respond searches for appropriative answer to the request and passes request to found function // If none of the functions can handle this request, it will be warned in log file -func (r *Router) Respond(update telegram.Update) { +func Respond(update telegram.Update) { switch { case update.Message != nil: if update.Message.Text != "" { if update.Message.ForwardFrom != nil { - err := r.checkForward(&update) + err := checkForward(&update) if err != nil { log.Warn().Err(err) return } } if update.Message.Chat.IsPrivate() { - r.handlePrivateRequest(&update) + handlePrivateRequest(&update) } else if update.Message.Chat.IsGroup() || update.Message.Chat.IsSuperGroup() { - r.handleGroupRequest(&update) + handleGroupRequest(&update) } else { log.Debug().Msg("Can't handle update") } @@ -138,7 +138,7 @@ func (r *Router) Respond(update telegram.Update) { } case update.InlineQuery != nil: if update.InlineQuery.Query != "" { - r.handleInlineQuery(&update) + handleInlineQuery(&update) } default: log.Debug().Msg("Can't handle empty Message for now") diff --git a/internal/telegram/exported.go b/internal/telegram/exported.go index 295e4b6..1cc5cbf 100644 --- a/internal/telegram/exported.go +++ b/internal/telegram/exported.go @@ -12,20 +12,16 @@ import ( var ( c *context.Context log zerolog.Logger -) -// Telegram is a struch which handles Telegram instance handling functions -type Telegram struct { bot *telegram.Bot -} +) // New initializes package func New(cc *context.Context) { c = cc log = c.Logger.With().Str("domain", "telegram").Int("version", 1).Logger() - t := &Telegram{} log.Info().Msg("Starting Telegram instance") - t.StartBot() + StartBot() } diff --git a/internal/telegram/respond.go b/internal/telegram/respond.go new file mode 100644 index 0000000..688a7eb --- /dev/null +++ b/internal/telegram/respond.go @@ -0,0 +1,26 @@ +// Fantasy World Zookeeper Bot +// Copyright (c) 2018 Vladimir "fat0troll" Hodakov + +package telegram + +import ( + "gitlab.com/toby3d/telegram" +) + +func getMessageParams(chatID int64, message string, disableWebPagePreview bool) telegram.SendMessageParameters { + return telegram.SendMessageParameters{ + ChatID: chatID, + Text: message, + ParseMode: "Markdown", + DisableWebPagePreview: disableWebPagePreview} +} + +// RespondWithMarkdown will send message to given chat with Markdown parse mode +func RespondWithMarkdown(chatID int64, message string) { + messageParams := getMessageParams(chatID, message, false) + + _, err := bot.SendMessage(&messageParams) + if err != nil { + log.Error().Err(err) + } +} diff --git a/internal/telegram/telegram.go b/internal/telegram/telegram.go index 3f3ee79..a952017 100644 --- a/internal/telegram/telegram.go +++ b/internal/telegram/telegram.go @@ -12,7 +12,7 @@ import ( "lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/router" ) -func (t *Telegram) proxyDialer(addr string) (net.Conn, error) { +func proxyDialer(addr string) (net.Conn, error) { log.Debug().Msgf("Proxy used: %s", c.Config.Telegram.Proxy.Address) proxyAuth := proxy.Auth{} if c.Config.Telegram.Proxy.Username != "" { @@ -30,24 +30,24 @@ func (t *Telegram) proxyDialer(addr string) (net.Conn, error) { } // Bot returns Telegram instance -func (t *Telegram) Bot() *telegram.Bot { - return t.bot +func Bot() *telegram.Bot { + return bot } // StartBot starts connection with Telegram -func (t *Telegram) StartBot() { +func StartBot() { // Any errors here considered fatal, because main purpose of this app is Telegram interactions var err error var updates telegram.UpdatesChannel if c.Config.Telegram.Proxy.Enabled { - t.bot = new(telegram.Bot) + bot = new(telegram.Bot) client := new(http.Client) - client.Dial = t.proxyDialer - t.bot.SetClient(client) - t.bot.AccessToken = c.Config.Telegram.Token - t.bot.User, err = t.bot.GetMe() + client.Dial = proxyDialer + bot.SetClient(client) + bot.AccessToken = c.Config.Telegram.Token + bot.User, err = bot.GetMe() } else { - t.bot, err = telegram.New(c.Config.Telegram.Token) + bot, err = telegram.New(c.Config.Telegram.Token) } if err != nil { log.Fatal().Err(err) @@ -59,22 +59,22 @@ func (t *Telegram) StartBot() { if len(url.Host()) == 0 { log.Fatal().Msg("Can't parse webhook URL: got empty host") } - log.Info().Msg("Trying to set webhook: " + url.String() + t.bot.AccessToken) + log.Info().Msg("Trying to set webhook: " + url.String() + bot.AccessToken) - webhook := telegram.NewWebhook(url.String()+t.bot.AccessToken, nil) + webhook := telegram.NewWebhook(url.String()+bot.AccessToken, nil) webhook.MaxConnections = 40 - updates = t.bot.NewWebhookChannel(url, webhook, "", "", c.Config.Telegram.Webhook.Listen) + updates = bot.NewWebhookChannel(url, webhook, "", "", c.Config.Telegram.Webhook.Listen) } else { log.Warn().Msg("Using long-polling for updates (not recommended)") var info *telegram.WebhookInfo - info, err = t.bot.GetWebhookInfo() + info, err = bot.GetWebhookInfo() if err != nil { log.Fatal().Err(err) } if info != nil && info.URL != "" { log.Info().Msg("Deleting old webhook...") - _, err := t.bot.DeleteWebhook() + _, err := bot.DeleteWebhook() if err != nil { log.Fatal().Err(err) } @@ -84,14 +84,13 @@ func (t *Telegram) StartBot() { Limit: 100, Timeout: 60, } - updates = t.bot.NewLongPollingChannel(&updatesParams) + updates = bot.NewLongPollingChannel(&updatesParams) } log.Info().Msg("Connection with Telegram established") for update := range updates { log.Debug().Msgf("%+v", update) - go router.Requests.Respond(update) + go router.Respond(update) } - } diff --git a/main.go b/main.go index 3766d4c..e42cd22 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ package main import ( "lab.wtfteam.pro/fat0troll/fw_zookeeper/context" + "lab.wtfteam.pro/fat0troll/fw_zookeeper/domains/commands/v1" "lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/router" "lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/telegram" "os" @@ -25,6 +26,8 @@ func main() { c.InitConfiguration() router.New(c) + commandsv1.New(c) + telegram.New(c) // CTRL+C handler.