This commit is contained in:
Pavel 2019-06-09 12:42:12 +03:00
parent 6c234a8084
commit 37d6d7baf7
12 changed files with 556 additions and 355 deletions

7
.env.dist Normal file
View File

@ -0,0 +1,7 @@
TG_BOT_TOKEN=token # Telegram Bot API Token
POLL_TIMEOUT=30 # Poll timeout (in seconds). Default: 30
WEBHOOK="https://www.google.com:{PORT}/{TOKEN}" # Webhook URL (don't provide if you want to use polling). {PORT} and {TOKEN} will be replaced automatically.
WEBHOOK_PORT=8000 # Webhook port. Ignored if webhook URL is not provided. Default: 8000
CERT="cert.pem" # Certificate file name. Provide if you want to use SSL
CERT_KEY="key.pem" # Certificate key. Should be provided with CERT
DEBUG=false # Debug mode. This value will be passed to API library, which will log all requests in debug mode.

4
.gitignore vendored
View File

@ -1,2 +1,4 @@
*.exe *.exe
.env .env
bin/*
bin

23
Makefile Normal file
View File

@ -0,0 +1,23 @@
.DEFAULT_GOAL := build
GO=$(shell which go)
PROJECT_DIR=$(shell pwd)
GOPATH=$(PROJECT_DIR)
SRC=$(PROJECT_DIR)/src
BIN=$(PROJECT_DIR)/bin/bash_im_bot
build: fmt deps
@echo "- Building"
@cd $(SRC) && $(GO) build -o $(BIN)
@echo Built "$(BIN)"
run:
@$(BIN)
deps:
@echo "- Installing dependencies"
@$(GO) mod tidy
fmt:
@echo "- Running 'go fmt'"
@$(GO) fmt $(SRC)

View File

@ -1,4 +1,7 @@
This bot will show latest quotes from bash.im. Also it can (but not yet) work inline. This bot will show latest quotes from bash.im. Also it can (but not yet) work inline.
- [x] Ability to fetch latest quotes from bash.im - [x] Ability to fetch latest quotes from bash.im
- [ ] Ability to send quote to dialog via inline mode - [x] Ability to send quote to dialog via inline mode
- [ ] Integrated search and autocomplete for inline mode - [x] Integrated search and autocomplete for inline mode
- [ ] Use [goquery](https://github.com/PuerkitoBio/goquery) instead of regular expressions
- [ ] Automated version increment
- [ ] Setup CI/CD

123
bashim.go
View File

@ -1,123 +0,0 @@
package main
import (
"errors"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
)
const BASH_URL = "https://bash.im"
type BashQuote struct {
ID int
Created string
Rating string
Permalink string
Text string
}
func GetBashQuote(id int) (BashQuote, error) {
var quote BashQuote
if resp, err := http.Get(BASH_URL + "/quote/" + strconv.Itoa(id)); err == nil {
defer resp.Body.Close()
if resp.StatusCode == 200 {
if bodyData, err := ioutil.ReadAll(resp.Body); err == nil {
body := string(bodyData)
items := getQuotesList(body)
if len(items) == 1 {
return items[0], nil
} else {
return quote, errors.New("Can't find quote")
}
} else {
return quote, err
}
} else {
return quote, errors.New("Incorrect status code: " + strconv.Itoa(resp.StatusCode))
}
} else {
return quote, err
}
}
func GetLatestQuotes() ([]BashQuote, error) {
var quotes []BashQuote
if resp, err := http.Get(BASH_URL); err == nil {
defer resp.Body.Close()
if resp.StatusCode == 200 {
if bodyData, err := ioutil.ReadAll(resp.Body); err == nil {
body := string(bodyData)
items := getQuotesList(body)
if len(items) > 1 {
return items, nil
} else {
return quotes, errors.New("Error while trying to extract quotes")
}
} else {
return quotes, err
}
} else {
return quotes, errors.New("Incorrect status code: " + strconv.Itoa(resp.StatusCode))
}
} else {
return quotes, err
}
}
func getQuotesList(response string) []BashQuote {
re := regexp.MustCompile(`(?im)[.\s\w\W]+?\<article\sclass\="quote\"[.\s\w\W]+?<\/article\>`)
matches := re.FindAllString(response, -1)
items := make([]BashQuote, len(matches))
for index, match := range matches {
id, created, rating, permalink, text, err := getQuoteData(match)
if err != nil {
continue
}
items[index] = BashQuote{
ID: id,
Created: created,
Rating: rating,
Permalink: permalink,
Text: text,
}
}
return items
}
func getQuoteData(response string) (id int, created string, rating string, permalink string, text string, err error) {
re := regexp.MustCompile(`(?im)data\-quote\=\"(?P<id>\d+)\"[.\s\w\W]+?quote__header_permalink.+href\=\"(?P<permalink>\/.+\d)\"[.\s\w\W]+?quote__header_date\"\>[.\s\w\W]+?(?P<date>.+)[.\s\w\W]+?quote__body\"\>\s+?(?P<text>.+)[.\s\w\W]+?quote__total.+\>(?P<rating>\d+)`)
matches := re.FindStringSubmatch(response)
if len(matches) == 0 {
return 0, "", "", "", "", errors.New("No data found")
} else {
matches = matches[1:]
}
id, err = strconv.Atoi(matches[0])
if err != nil {
return 0, "", "", "", "", err
}
created = strings.TrimSpace(matches[2])
rating = strings.TrimSpace(matches[4])
permalink = BASH_URL + matches[1]
text = strings.ReplaceAll(strings.TrimSpace(matches[3]), "<br>", "\n")
err = nil
return
}

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module github.com/Neur0toxine/bash.im-telegram-bot
go 1.12
require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/joho/godotenv v1.3.0
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
)

6
go.sum Normal file
View File

@ -0,0 +1,6 @@
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=

229
main.go
View File

@ -1,229 +0,0 @@
package main
import (
"fmt"
"log"
"net/http"
"os"
"strconv"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/joho/godotenv"
)
type updateFunc func(tgbotapi.Update, *tgbotapi.BotAPI)
const envToken = "TG_BOT_TOKEN"
const envPollTimeout = "POLL_TIMEOUT"
const envWebhook = "WEBHOOK"
const envWebhookPort = "WEBHOOK_PORT"
const envCert = "CERT"
const envKey = "CERT_KEY"
const envDebug = "DEBUG"
const defaultPollTimeout = 30
const defaultWebhookPort = 8000
func main() {
var (
pollTimeout, webhookPort int
err error
)
err = godotenv.Load()
if err != nil {
log.Printf("WARNING: Error while loading `.env` file `%s`", err.Error())
}
token := os.Getenv(envToken)
webhookUrl := os.Getenv(envWebhook)
webhookPortStr := os.Getenv(envWebhookPort)
certFile := os.Getenv(envCert)
certKey := os.Getenv(envKey)
pollTimeoutStr := os.Getenv(envPollTimeout)
debug, err := strconv.ParseBool(os.Getenv(envDebug))
if err != nil {
debug = false
}
if pollTimeout, err = strconv.Atoi(pollTimeoutStr); err != nil {
log.Printf("Using default poll timeout - %d seconds...", defaultPollTimeout)
pollTimeout = defaultPollTimeout
}
if webhookPort, err = strconv.Atoi(webhookPortStr); err != nil && webhookUrl != "" {
log.Printf("Using default webhook port %d ...", defaultWebhookPort)
webhookPort = defaultWebhookPort
}
if token == "" {
log.Fatalf(
"`%s` is not found in environment - specify it in `.env` file or pass it while launching",
envToken,
)
}
bot, err := tgbotapi.NewBotAPI(token)
if err != nil {
log.Fatal(err)
}
bot.Debug = debug
log.Printf("Authorized on account: @%s (id: %d)", bot.Self.UserName, bot.Self.ID)
log.Printf("Debug mode: %t", debug)
if webhookUrl == "" {
initWithPolling(bot, pollTimeout, processUpdate)
} else {
initWithWebhook(bot, webhookUrl, webhookPort, certFile, certKey, processUpdate)
}
}
func processUpdate(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
if update.InlineQuery != nil {
results := make([]interface{}, 1)
results[0] = tgbotapi.NewInlineQueryResultArticleMarkdown(
update.InlineQuery.ID,
"",
"",
)
bot.AnswerInlineQuery(
tgbotapi.InlineConfig{
InlineQueryID: update.InlineQuery.ID,
Results: results,
CacheTime: 30,
IsPersonal: false,
NextOffset: "",
SwitchPMText: "",
SwitchPMParameter: "",
},
)
}
msgs := make([]tgbotapi.MessageConfig, 1)
msgs[0] = tgbotapi.NewMessage(update.Message.Chat.ID, "")
msgs[0].ParseMode = "markdown"
if update.Message.IsCommand() {
switch update.Message.Command() {
case "latest":
items, err := GetLatestQuotes()
if err != nil {
msgs[len(msgs)-1].Text = "Не удалось получить последние цитаты :("
} else {
for _, item := range items {
text := fmt.Sprintf(
"*Цитата:* [#%d](%s) \n"+
"*Создано:* %s \n"+
"*Рейтинг:* %s \n"+
"*Текст:*\n %s \n\n",
item.ID,
item.Permalink,
item.Created,
item.Rating,
item.Text,
)
if len(msgs[len(msgs)-1].Text+text) > 4096 {
msgs = append(msgs, tgbotapi.NewMessage(update.Message.Chat.ID, text))
msgs[len(msgs)-1].ParseMode = "markdown"
} else {
msgs[len(msgs)-1].Text += text
}
}
}
default:
msgs[len(msgs)-1].Text = "Как насчёт последних цитат? Используйте /latest"
}
} else {
text := fmt.Sprintf("Что вы пытаетесь здесь найти? Тут ничего нет...\n"+
"Бот работает не так. Зайдите в любой чат, вызовите бота вот так: @%s <id>, где ID - "+
"это идентификатор цитаты на bash.im. И бот перешлёт её!", bot.Self.UserName)
msgs[len(msgs)-1] = tgbotapi.NewMessage(update.Message.Chat.ID, text)
msgs[len(msgs)-1].ReplyToMessageID = update.Message.MessageID
}
for _, msg := range msgs {
if _, err := bot.Send(msg); err != nil {
log.Printf("Error while trying to send message to chat `%d`: %s", update.Message.Chat.ID, err.Error())
}
}
}
func initWithPolling(bot *tgbotapi.BotAPI, updateTimeout int, updateCallback updateFunc) {
var (
updates tgbotapi.UpdatesChannel
err error
)
u := tgbotapi.NewUpdate(0)
u.Timeout = updateTimeout
if updates, err = bot.GetUpdatesChan(u); err != nil {
log.Fatalf("Error while trying to get updates: %s", err.Error())
}
for update := range updates {
updateCallback(update, bot)
}
}
func initWithWebhook(
bot *tgbotapi.BotAPI,
webhookUrl string,
webhookPort int,
certFile string,
certKey string,
updateCallback updateFunc,
) {
var err error
if webhookPort == 0 {
webhookPort = 80
}
if webhookUrl == "" {
log.Fatalf("Empty webhook URL provided (env %s)", envWebhook)
}
webhookLink := fmt.Sprintf("%s:%d/%s", webhookUrl, webhookPort, bot.Token)
if certFile != "" && certKey != "" {
_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert(webhookLink, "cert.pem"))
} else {
_, err = bot.SetWebhook(tgbotapi.NewWebhook(webhookLink))
}
if err != nil {
log.Fatal(err)
}
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("Telegram callback failed: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token)
serverUrl := fmt.Sprintf("0.0.0.0:%d", webhookPort)
if certFile != "" && certKey != "" {
go http.ListenAndServeTLS(serverUrl, certFile, certKey, nil)
} else {
go http.ListenAndServe(serverUrl, nil)
}
for update := range updates {
updateCallback(update, bot)
}
}

130
src/bashim.go Normal file
View File

@ -0,0 +1,130 @@
package main
import (
"errors"
"fmt"
"html"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
)
const BASH_URL = "https://bash.im"
type BashQuote struct {
ID int
Created string
Rating string
Permalink string
Text string
}
var (
replaceBrRe = regexp.MustCompile(`(?im)\<[\s+]?br[\s+\/]{0,2}?\>`)
getQuotesListRe = regexp.MustCompile(`(?im)[.\s\w\W]+?\<article\sclass\="quote\"[.\s\w\W]+?<\/article\>`)
getQuoteDataRe = regexp.MustCompile(`(?im)data\-quote\=\"(?P<id>\d+)\"[.\s\w\W]+?quote__header_permalink.+href\=\"(?P<permalink>\/.+\d)\"[.\s\w\W]+?quote__header_date\"\>[.\s\w\W]+?(?P<date>.+)[.\s\w\W]+?quote__body\"\>\s+?(?P<text>.+)[.\s\w\W]+?quote__total.+\>(?P<rating>\d+)`)
)
func getQuotesList(response string, maxItems int) []BashQuote {
var items []BashQuote
matches := getQuotesListRe.FindAllString(response, -1)
if maxItems != 0 && len(matches) > maxItems {
matches = matches[:maxItems]
}
for _, match := range matches {
id, created, rating, permalink, text, err := getQuoteData(match)
if err != nil {
continue
}
if id == 0 {
continue
}
items = append(items, BashQuote{
ID: id,
Created: created,
Rating: rating,
Permalink: permalink,
Text: text,
})
}
return items
}
func getQuoteData(response string) (id int, created string, rating string, permalink string, text string, err error) {
matches := getQuoteDataRe.FindStringSubmatch(response)
if len(matches) == 0 {
return 0, "", "", "", "", errors.New("No data found")
} else {
matches = matches[1:]
}
id, err = strconv.Atoi(matches[0])
if err != nil {
return 0, "", "", "", "", err
}
created = strings.ReplaceAll(strings.TrimSpace(matches[2]), " ", " ")
rating = strings.TrimSpace(matches[4])
permalink = BASH_URL + matches[1]
text = html.UnescapeString(replaceBrRe.ReplaceAllString(strings.TrimSpace(matches[3]), "\n"))
err = nil
return
}
func GetLatestQuotes() ([]BashQuote, error) {
return extractQuotes("/", 25)
}
func GetQuote(id int) (BashQuote, error) {
quotes, err := extractQuotes(fmt.Sprintf("/quote/%d", id), 1)
if err != nil {
return BashQuote{}, err
} else if len(quotes) == 0 {
return BashQuote{}, errors.New("Error while trying to extract quote")
} else {
return quotes[0], nil
}
}
func SearchQuotes(search string, maxResults int) ([]BashQuote, error) {
return extractQuotes(fmt.Sprintf("/search?text=%s", url.QueryEscape(search)), maxResults)
}
func extractQuotes(url string, maxItems int) ([]BashQuote, error) {
var (
quotes []BashQuote
link = fmt.Sprintf("%s%s", BASH_URL, url)
)
if resp, err := http.Get(link); err == nil {
defer resp.Body.Close()
if resp.StatusCode == 200 {
if bodyData, err := ioutil.ReadAll(resp.Body); err == nil {
body := string(bodyData)
items := getQuotesList(body, maxItems)
return items, nil
} else {
return quotes, err
}
} else {
return quotes, errors.New("Incorrect status code: " + strconv.Itoa(resp.StatusCode))
}
} else {
return quotes, err
}
}

233
src/bot.go Normal file
View File

@ -0,0 +1,233 @@
package main
import (
"fmt"
"log"
"math/rand"
"net/http"
"strconv"
"unicode/utf16"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
type updateFunc func(tgbotapi.Update, *tgbotapi.BotAPI)
func processUpdate(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
if update.InlineQuery != nil {
var (
results []interface{}
bashQuotes []BashQuote
quote BashQuote
err error
)
if update.InlineQuery.Query == "" {
return
}
if quoteId, errConv := strconv.Atoi(update.InlineQuery.Query); errConv == nil {
quote, err = GetQuote(quoteId)
bashQuotes = append(bashQuotes, quote)
} else {
log.Print(errConv)
bashQuotes, err = SearchQuotes(update.InlineQuery.Query, 3)
}
if err == nil {
for _, quote := range bashQuotes {
utfEncodedString := utf16.Encode([]rune(quote.Text))
runeString := utf16.Decode(utfEncodedString[:50])
title := fmt.Sprintf(
"[#%d]: %s\n",
quote.ID,
string(runeString)+"...\n",
)
text := fmt.Sprintf(
"*Цитата:* [#%d](%s), %s \n"+
"*Рейтинг:* %s \n"+
"%s \n\n",
quote.ID,
quote.Permalink,
quote.Created,
quote.Rating,
quote.Text,
)
results = append(results, tgbotapi.NewInlineQueryResultArticleMarkdown(
strconv.Itoa(rand.Int()),
title,
text,
))
}
} else {
errMsg := "Не удалось произвести поиск"
results = append(results, tgbotapi.NewInlineQueryResultArticleMarkdown(
strconv.Itoa(rand.Int()),
errMsg,
errMsg,
))
}
if len(results) == 0 {
errMsg := "Ничего не найдено..."
results = append(results, tgbotapi.NewInlineQueryResultArticleMarkdown(
strconv.Itoa(rand.Int()),
errMsg,
errMsg,
))
}
response, err := bot.AnswerInlineQuery(
tgbotapi.InlineConfig{
InlineQueryID: update.InlineQuery.ID,
Results: results,
CacheTime: 30,
IsPersonal: false,
NextOffset: "",
SwitchPMText: "",
SwitchPMParameter: "",
},
)
if err != nil {
log.Print(err)
}
if !response.Ok {
log.Printf("Error %d while trying to send inline update", response.ErrorCode)
}
} else {
msgs := make([]tgbotapi.MessageConfig, 1)
msgs[0] = NewMessage(update.Message.Chat.ID, 0, "", "markdown")
if update.Message.IsCommand() {
switch update.Message.Command() {
case "latest":
SendMessages(bot, []tgbotapi.MessageConfig{
NewMessage(update.Message.Chat.ID, update.Message.MessageID, "_Получаю свежие цитаты..._", "markdown"),
})
items, err := GetLatestQuotes()
if err != nil {
msgs[len(msgs)-1].Text = "Не удалось получить последние цитаты :("
} else {
for _, item := range items {
text := fmt.Sprintf(
"*Цитата:* [#%d](%s), %s \n"+
"*Рейтинг:* %s \n"+
"%s \n\n",
item.ID,
item.Permalink,
item.Created,
item.Rating,
item.Text,
)
if len(msgs[len(msgs)-1].Text+text) > 4096 {
msgs = append(msgs, NewMessage(update.Message.Chat.ID, 0, text, "markdown"))
} else {
msgs[len(msgs)-1].Text += text
}
}
}
default:
msgs[len(msgs)-1].Text = "Как насчёт последних цитат? Используйте /latest"
}
} else {
text := fmt.Sprintf("Зайдите в любой чат, вызовите бота вот так:\n `@%s <id>`, где ID - "+
"это идентификатор цитаты на bash.im. И бот перешлёт её!\n"+
"Ещё вместо идентификатора можно указать текст, по которому бот попытается найти цитаты.", bot.Self.UserName)
msgs[len(msgs)-1] = NewMessage(update.Message.Chat.ID, update.Message.MessageID, text, "markdown")
}
SendMessages(bot, msgs)
}
}
func NewMessage(chatID int64, replyTo int, text string, parse string) tgbotapi.MessageConfig {
msg := tgbotapi.NewMessage(chatID, text)
msg.ParseMode = parse
if replyTo != 0 {
msg.ReplyToMessageID = replyTo
}
return msg
}
func SendMessages(bot *tgbotapi.BotAPI, msgs []tgbotapi.MessageConfig) {
for _, msg := range msgs {
if _, err := bot.Send(msg); err != nil {
log.Printf("Error while trying to send message to chat `%d`: %s", msg.BaseChat.ChatID, err.Error())
}
}
}
func initWithPolling(bot *tgbotapi.BotAPI, updateTimeout int, updateCallback updateFunc) {
var (
updates tgbotapi.UpdatesChannel
err error
)
u := tgbotapi.NewUpdate(0)
u.Timeout = updateTimeout
if updates, err = bot.GetUpdatesChan(u); err != nil {
log.Fatalf("Error while trying to get updates: %s", err.Error())
}
for update := range updates {
go updateCallback(update, bot)
}
}
func initWithWebhook(
bot *tgbotapi.BotAPI,
webhookUrl string,
listenAddr string,
certFile string,
certKey string,
updateCallback updateFunc,
) {
var err error
if webhookUrl == "" {
log.Fatalf("Empty webhook URL provided (env %s)", envWebhook)
}
if certFile != "" && certKey != "" {
_, err = bot.SetWebhook(tgbotapi.NewWebhookWithCert(webhookUrl, certFile))
} else {
_, err = bot.SetWebhook(tgbotapi.NewWebhook(webhookUrl))
}
if err != nil {
log.Fatal(err)
}
info, err := bot.GetWebhookInfo()
if err != nil {
log.Fatal(err)
}
if info.LastErrorDate != 0 {
log.Printf("Telegram callback failed: %s", info.LastErrorMessage)
}
updates := bot.ListenForWebhook("/" + bot.Token)
if certFile != "" && certKey != "" {
go http.ListenAndServeTLS(listenAddr, certFile, certKey, nil)
} else {
go http.ListenAndServe(listenAddr, nil)
}
for update := range updates {
go updateCallback(update, bot)
}
}

105
src/config.go Normal file
View File

@ -0,0 +1,105 @@
package main
import (
"log"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/joho/godotenv"
)
const envToken = "TG_BOT_TOKEN"
const envPollTimeout = "POLL_TIMEOUT"
const envListen = "LISTEN_IP"
const envWebhook = "WEBHOOK"
const envWebhookPort = "WEBHOOK_PORT"
const envCert = "CERT"
const envKey = "CERT_KEY"
const envDebug = "DEBUG"
const defaultPollTimeout = 30
const defaultWebhookPort = 8000
const ModePolling = "polling"
const ModeWebhook = "webhook"
type BotConfig struct {
Token string
Mode string
Debug bool
PollingTimeout int
WebhookURL string
ListenAddr string
CertificateFile string
CertificateKey string
}
func LoadConfig() (BotConfig, error) {
var (
cfg BotConfig
pollTimeout, webhookPort int
)
if err := godotenv.Load(); err != nil {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
log.Fatal(err)
}
if err := godotenv.Load(filepath.Join(filepath.Dir(dir), ".env")); err != nil {
return cfg, err
}
}
token := os.Getenv(envToken)
webhookUrl := os.Getenv(envWebhook)
listenAddr := os.Getenv(envListen)
webhookPortStr := os.Getenv(envWebhookPort)
certFile := os.Getenv(envCert)
certKey := os.Getenv(envKey)
pollTimeoutStr := os.Getenv(envPollTimeout)
debug, err := strconv.ParseBool(os.Getenv(envDebug))
if err != nil {
debug = false
}
if pollTimeout, err = strconv.Atoi(pollTimeoutStr); err != nil {
pollTimeout = defaultPollTimeout
}
if webhookPort, err = strconv.Atoi(webhookPortStr); err != nil && webhookUrl != "" {
webhookPort = defaultWebhookPort
}
webhookLink := strings.ReplaceAll(webhookUrl, "{PORT}", strconv.Itoa(webhookPort))
webhookLink = strings.ReplaceAll(webhookLink, "{TOKEN}", token)
if token == "" {
log.Fatalf(
"`%s` is not found in environment - specify it in `.env` file or pass it while launching",
envToken,
)
}
cfg = BotConfig{
Token: token,
Debug: debug,
PollingTimeout: pollTimeout,
WebhookURL: webhookLink,
ListenAddr: listenAddr,
CertificateFile: certFile,
CertificateKey: certKey,
}
if webhookUrl == "" {
cfg.Mode = ModePolling
} else {
cfg.Mode = ModeWebhook
}
return cfg, nil
}

35
src/main.go Normal file
View File

@ -0,0 +1,35 @@
package main
import (
"log"
"math/rand"
"time"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)
func main() {
cfg, err := LoadConfig()
if err != nil {
log.Printf("WARNING: Error while loading `.env` file `%s`", err.Error())
}
bot, err := tgbotapi.NewBotAPI(cfg.Token)
if err != nil {
log.Fatal(err)
}
bot.Debug = cfg.Debug
log.Printf("Authorized on account: @%s (id: %d)", bot.Self.UserName, bot.Self.ID)
log.Printf("Debug mode: %t", cfg.Debug)
rand.Seed(time.Now().UTC().UnixNano())
if cfg.WebhookURL == "" {
initWithPolling(bot, cfg.PollingTimeout, processUpdate)
} else {
initWithWebhook(bot, cfg.WebhookURL, cfg.ListenAddr, cfg.CertificateFile, cfg.CertificateKey, processUpdate)
}
}