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.

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
*.exe
.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.
- [x] Ability to fetch latest quotes from bash.im
- [ ] Ability to send quote to dialog via inline mode
- [ ] Integrated search and autocomplete for inline mode
- [x] Ability to send quote to dialog via 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)
}
}