Update dependencies
This commit is contained in:
10
internal/config/announces.go
Normal file
10
internal/config/announces.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package config
|
||||
|
||||
// Announces is a struct which handles announces configuration
|
||||
type Announces struct {
|
||||
ChannelID int64 `yaml:"channel_id"`
|
||||
EnableLocal bool `yaml:"enable_local"`
|
||||
}
|
12
internal/config/proxy.go
Normal file
12
internal/config/proxy.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package config
|
||||
|
||||
// Proxy handles settings for Telegram SOCKS5 proxy
|
||||
type Proxy struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Address string `yaml:"address,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
Password string `yaml:"password,omitempty"`
|
||||
}
|
11
internal/config/struct.go
Normal file
11
internal/config/struct.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package config
|
||||
|
||||
// Struct is a main configuration structure that holds all other
|
||||
// structs within.
|
||||
type Struct struct {
|
||||
Telegram Telegram `yaml:"telegram"`
|
||||
Announces Announces `yaml:"announces"`
|
||||
}
|
11
internal/config/telegram.go
Normal file
11
internal/config/telegram.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package config
|
||||
|
||||
// Telegram represents bot's Telegram configuration
|
||||
type Telegram struct {
|
||||
Token string `yaml:"token"`
|
||||
Webhook Webhook `yaml:"webhook"`
|
||||
Proxy Proxy `yaml:"proxy"`
|
||||
}
|
11
internal/config/webhook.go
Normal file
11
internal/config/webhook.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package config
|
||||
|
||||
// Webhook handles settings for Telegram webhook
|
||||
type Webhook struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Domain string `yaml:"domain,omitempty"`
|
||||
Listen string `yaml:"listen,omitempty"`
|
||||
}
|
39
internal/router/exported.go
Normal file
39
internal/router/exported.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"gitlab.com/toby3d/telegram"
|
||||
"lab.wtfteam.pro/fat0troll/fw_zookeeper/context"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var (
|
||||
c *context.Context
|
||||
log zerolog.Logger
|
||||
|
||||
// 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)
|
||||
}
|
||||
)
|
||||
|
||||
// New initializes package
|
||||
func New(cc *context.Context) {
|
||||
c = cc
|
||||
log = c.Logger.With().Str("domain", "router").Int("version", 1).Logger()
|
||||
|
||||
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")
|
||||
}
|
146
internal/router/router.go
Normal file
146
internal/router/router.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package router
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
var (
|
||||
// acceptingForwardsFrom defines list of users which messages can be used as
|
||||
// forwarded
|
||||
// In case of this bot, this is @FWorldBot
|
||||
acceptingForwardsFrom = []int{}
|
||||
)
|
||||
|
||||
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 {
|
||||
if acceptingForwardsFrom[i] == update.Message.ForwardFrom.ID {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("Can't handle forward from Telegram user with ID =" + strconv.Itoa(update.Message.ForwardFrom.ID))
|
||||
}
|
||||
|
||||
func handleInlineQuery(update *telegram.Update) {
|
||||
rxpMatched := false
|
||||
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))
|
||||
} else {
|
||||
rxpMatched = true
|
||||
function(update)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !rxpMatched {
|
||||
log.Debug().Msgf("There is no handler for inline: %s", update.InlineQuery.Query)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
commands[update.Message.Command()](update)
|
||||
} else {
|
||||
log.Warn().Msgf("There is no handler for command /%s", update.Message.Command())
|
||||
}
|
||||
default:
|
||||
rxpMatched := false
|
||||
for rxp, function := range rxps {
|
||||
if rxp.MatchString(update.Message.Text) {
|
||||
if rxpMatched {
|
||||
log.Warn().Msgf("The message handled more than once: %s, %s", update.Message.Text, strings.Replace(rxp.String(), "\n", "\\n", -1))
|
||||
} else {
|
||||
rxpMatched = true
|
||||
function(update)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !rxpMatched {
|
||||
log.Debug().Msgf("There is no handler for message: %s", update.Message.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleGroupRequest(update *telegram.Update) {
|
||||
handleRequest(update, router.groupCommands, router.groupRegulars)
|
||||
}
|
||||
|
||||
func handlePrivateRequest(update *telegram.Update) {
|
||||
handleRequest(update, router.privateCommands, router.privateRegulars)
|
||||
}
|
||||
|
||||
// RegisterPrivateCommand adds function to private commands list
|
||||
func RegisterPrivateCommand(command string, handleFunc func(update *telegram.Update)) {
|
||||
log.Debug().Msgf("Registering handler for private command /%s", command)
|
||||
router.privateCommands[command] = handleFunc
|
||||
}
|
||||
|
||||
// RegisterPrivateRegexp adds function to private regexp list
|
||||
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))
|
||||
router.privateRegulars[rxp] = handleFunc
|
||||
}
|
||||
|
||||
// RegisterGroupCommand adds function to group commands list
|
||||
func RegisterGroupCommand(command string, handleFunc func(update *telegram.Update)) {
|
||||
log.Debug().Msgf("Registering handler for group command /%s", command)
|
||||
router.groupCommands[command] = handleFunc
|
||||
}
|
||||
|
||||
// RegisterGroupRegexp adds function to group regexp list
|
||||
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))
|
||||
router.groupRegulars[rxp] = handleFunc
|
||||
}
|
||||
|
||||
// RegisterInlineQueryResult adds function to list of inline queries
|
||||
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))
|
||||
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 Respond(update telegram.Update) {
|
||||
switch {
|
||||
case update.Message != nil:
|
||||
if update.Message.Text != "" {
|
||||
if update.Message.ForwardFrom != nil {
|
||||
err := checkForward(&update)
|
||||
if err != nil {
|
||||
log.Warn().Err(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
if update.Message.Chat.IsPrivate() {
|
||||
handlePrivateRequest(&update)
|
||||
} else if update.Message.Chat.IsGroup() || update.Message.Chat.IsSuperGroup() {
|
||||
handleGroupRequest(&update)
|
||||
} else {
|
||||
log.Debug().Msg("Can't handle update")
|
||||
}
|
||||
} else {
|
||||
log.Debug().Msg("Can't handle empty Message for now")
|
||||
}
|
||||
case update.InlineQuery != nil:
|
||||
if update.InlineQuery.Query != "" {
|
||||
handleInlineQuery(&update)
|
||||
}
|
||||
default:
|
||||
log.Debug().Msg("Can't handle empty Message for now")
|
||||
}
|
||||
}
|
29
internal/telegram/exported.go
Normal file
29
internal/telegram/exported.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"gitlab.com/toby3d/telegram"
|
||||
"lab.wtfteam.pro/fat0troll/fw_zookeeper/context"
|
||||
)
|
||||
|
||||
var (
|
||||
c *context.Context
|
||||
log zerolog.Logger
|
||||
|
||||
bot *telegram.Bot
|
||||
)
|
||||
|
||||
// New initializes package
|
||||
func New(cc *context.Context) {
|
||||
c = cc
|
||||
log = c.Logger.With().Str("domain", "telegram").Int("version", 1).Logger()
|
||||
|
||||
log.Info().Msg("Starting Telegram instance")
|
||||
|
||||
go func() {
|
||||
StartBot()
|
||||
}()
|
||||
}
|
49
internal/telegram/respond.go
Normal file
49
internal/telegram/respond.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// RespondWithoutMarkdown will send message to given chat without parse mode
|
||||
func RespondWithoutMarkdown(chatID int64, message string) {
|
||||
messageParams := getMessageParams(chatID, message, false)
|
||||
messageParams.ParseMode = "HTML"
|
||||
|
||||
_, err := bot.SendMessage(&messageParams)
|
||||
if err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
// RespondWithInlineKeyboard will send message to given chat with Markdown parse
|
||||
// mode and keyboard attached
|
||||
func RespondWithInlineKeyboard(chatID int64, message string, keyboard *telegram.InlineKeyboardMarkup) {
|
||||
messageParams := getMessageParams(chatID, message, false)
|
||||
messageParams.ReplyMarkup = keyboard
|
||||
|
||||
_, err := bot.SendMessage(&messageParams)
|
||||
if err != nil {
|
||||
log.Error().Err(err)
|
||||
}
|
||||
}
|
97
internal/telegram/telegram.go
Normal file
97
internal/telegram/telegram.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Fantasy World Zookeeper Bot
|
||||
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
|
||||
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
http "github.com/valyala/fasthttp"
|
||||
"gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/net/proxy"
|
||||
"lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/router"
|
||||
"net"
|
||||
)
|
||||
|
||||
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 != "" {
|
||||
proxyAuth.User = c.Config.Telegram.Proxy.Username
|
||||
proxyAuth.Password = c.Config.Telegram.Proxy.Password
|
||||
}
|
||||
var dialProxy proxy.Dialer
|
||||
var err error
|
||||
dialProxy, err = proxy.SOCKS5("tcp", c.Config.Telegram.Proxy.Address, &proxyAuth, proxy.Direct)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to dial proxy")
|
||||
}
|
||||
|
||||
return dialProxy.Dial("tcp", addr)
|
||||
}
|
||||
|
||||
// Bot returns Telegram instance
|
||||
func Bot() *telegram.Bot {
|
||||
return bot
|
||||
}
|
||||
|
||||
// StartBot starts connection with Telegram
|
||||
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 {
|
||||
bot = new(telegram.Bot)
|
||||
client := new(http.Client)
|
||||
client.Dial = proxyDialer
|
||||
bot.SetClient(client)
|
||||
bot.AccessToken = c.Config.Telegram.Token
|
||||
bot.User, err = bot.GetMe()
|
||||
} else {
|
||||
bot, err = telegram.New(c.Config.Telegram.Token)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal().Err(err)
|
||||
}
|
||||
|
||||
if c.Config.Telegram.Webhook.Enabled {
|
||||
var url http.URI
|
||||
url.Parse(nil, []byte("https://"+c.Config.Telegram.Webhook.Domain))
|
||||
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() + bot.AccessToken)
|
||||
|
||||
webhook := telegram.NewWebhook(url.String()+bot.AccessToken, nil)
|
||||
webhook.MaxConnections = 40
|
||||
|
||||
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 = bot.GetWebhookInfo()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err)
|
||||
}
|
||||
if info != nil && info.URL != "" {
|
||||
log.Info().Msg("Deleting old webhook...")
|
||||
_, err := bot.DeleteWebhook()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err)
|
||||
}
|
||||
}
|
||||
updatesParams := telegram.GetUpdatesParameters{
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
Timeout: 60,
|
||||
}
|
||||
updates = bot.NewLongPollingChannel(&updatesParams)
|
||||
}
|
||||
|
||||
log.Info().Msg("Connection with Telegram established")
|
||||
|
||||
for update := range updates {
|
||||
updateText, _ := json.Marshal(update)
|
||||
log.Debug().Msgf("%s", string(updateText))
|
||||
go router.Respond(update)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user