Archived
1

Initial commit

This commit is contained in:
2018-11-29 20:32:51 +04:00
commit ca1c52fc39
365 changed files with 81160 additions and 0 deletions

12
internal/config/proxy.go Normal file
View 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"`
}

10
internal/config/struct.go Normal file
View File

@@ -0,0 +1,10 @@
// 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"`
}

View 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"`
}

View 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"`
}

View File

@@ -0,0 +1,45 @@
// 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
// Requests is a pointer to initialized Router object
Requests *Router
)
// 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))
log.Info().Msg("Initialized requests router")
Requests = r
}

146
internal/router/router.go Normal file
View 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 (r *Router) 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 (r *Router) handleInlineQuery(update *telegram.Update) {
rxpMatched := false
for rxp, function := range r.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 (r *Router) 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 (r *Router) handleGroupRequest(update *telegram.Update) {
r.handleRequest(update, r.groupCommands, r.groupRegulars)
}
func (r *Router) handlePrivateRequest(update *telegram.Update) {
r.handleRequest(update, r.privateCommands, r.privateRegulars)
}
// RegisterPrivateCommand adds function to private commands list
func (r *Router) RegisterPrivateCommand(command string, handleFunc func(update *telegram.Update)) {
log.Debug().Msgf("Registering handler for private command /%s", command)
r.privateCommands[command] = handleFunc
}
// RegisterPrivateRegexp adds function to private regexp list
func (r *Router) 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
}
// RegisterGroupCommand adds function to group commands list
func (r *Router) RegisterGroupCommand(command string, handleFunc func(update *telegram.Update)) {
log.Debug().Msgf("Registering handler for group command /%s", command)
r.groupCommands[command] = handleFunc
}
// RegisterGroupRegexp adds function to group regexp list
func (r *Router) 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
}
// RegisterInlineQueryResult adds function to list of inline queries
func (r *Router) 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
}
// 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) {
switch {
case update.Message != nil:
if update.Message.Text != "" {
if update.Message.ForwardFrom != nil {
err := r.checkForward(&update)
if err != nil {
log.Warn().Err(err)
return
}
}
if update.Message.Chat.IsPrivate() {
r.handlePrivateRequest(&update)
} else if update.Message.Chat.IsGroup() || update.Message.Chat.IsSuperGroup() {
r.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 != "" {
r.handleInlineQuery(&update)
}
default:
log.Debug().Msg("Can't handle empty Message for now")
}
}

View File

@@ -0,0 +1,31 @@
// 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
)
// 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()
}

View File

@@ -0,0 +1,97 @@
// Fantasy World Zookeeper Bot
// Copyright (c) 2018 Vladimir "fat0troll" Hodakov
package telegram
import (
"net"
http "github.com/valyala/fasthttp"
"gitlab.com/toby3d/telegram"
"golang.org/x/net/proxy"
"lab.wtfteam.pro/fat0troll/fw_zookeeper/internal/router"
)
func (t *Telegram) 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 (t *Telegram) Bot() *telegram.Bot {
return t.bot
}
// StartBot starts connection with Telegram
func (t *Telegram) 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)
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()
} else {
t.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(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() + t.bot.AccessToken)
webhook := telegram.NewWebhook(url.String()+t.bot.AccessToken, nil)
webhook.MaxConnections = 40
updates = t.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()
if err != nil {
log.Fatal().Err(err)
}
if info != nil && info.URL != "" {
log.Info().Msg("Deleting old webhook...")
_, err := t.bot.DeleteWebhook()
if err != nil {
log.Fatal().Err(err)
}
}
updatesParams := telegram.GetUpdatesParameters{
Offset: 0,
Limit: 100,
Timeout: 60,
}
updates = t.bot.NewLongPollingChannel(&updatesParams)
}
log.Info().Msg("Connection with Telegram established")
for update := range updates {
log.Debug().Msgf("%+v", update)
go router.Requests.Respond(update)
}
}