Compare commits

...

12 Commits

Author SHA1 Message Date
229572be10 Couchpotato Search 2021-12-04 06:04:04 +00:00
9af1dda45d Updating with more code. 2020-05-29 15:43:12 -05:00
5d4f13827c Adding the start to couchpotato and some middleware 2020-05-24 22:29:08 -05:00
69de0397a3 Adding Sonarr support into the code base. 2020-05-17 21:12:54 -05:00
4e846885dd Adding Sonarr support into the code base. 2020-05-17 21:06:52 -05:00
d6ed947bb9 Bigger and better rewrite. 2020-05-17 01:31:17 -05:00
dc7febd2ad Bigger and better rewrite. 2020-05-17 01:28:21 -05:00
7c6b9c4a74 Bigger and better rewrite. 2020-05-17 01:25:29 -05:00
Matt Burchett
aeac9e4e26 More updates. 2020-01-24 14:12:45 -06:00
Matt Burchett
c9706bd48a Rewriting in different framework 2020-01-22 14:51:26 -06:00
Matt Burchett
c4a1519d77 More thangs 2020-01-21 14:51:19 -06:00
Matt Burchett
8306698f9a Adding rewrite 2020-01-21 13:38:02 -06:00
16 changed files with 728 additions and 0 deletions

19
cmd/main.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"log"
"github.com/mattburchett/go_telegram/pkg/core/config"
"github.com/mattburchett/go_telegram/pkg/service/telegram"
)
func main() {
conf, err := config.GetConfig("config.json")
if err != nil {
log.Fatal("Failed to read JSON.")
}
tgBot := telegram.Bot{}
tgBot.Config = conf
tgBot.New(conf.Telegram.Token)
}

17
go.mod Normal file
View File

@ -0,0 +1,17 @@
module github.com/mattburchett/go_telegram
go 1.17
replace gopkg.in/tucnak/telebot.v2 => github.com/tucnak/telebot v0.0.0-20171121031619-29bd3707020c
require (
github.com/yanzay/tbot v1.0.0
github.com/yanzay/tbot/v2 v2.1.0
)
require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible // indirect
github.com/smartystreets/goconvey v1.7.2 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/yanzay/log v0.0.0-20160419144809-87352bb23506 // indirect
)

23
go.sum Normal file
View File

@ -0,0 +1,23 @@
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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/yanzay/log v0.0.0-20160419144809-87352bb23506 h1:RPrQRi67MUoUPg/3wwL9K9vKVWAB180e2WJgxC02CJE=
github.com/yanzay/log v0.0.0-20160419144809-87352bb23506/go.mod h1:x2hAcqPbZzQz79Fzr9xEe7BM73+UsZ3nBucx2u30oSk=
github.com/yanzay/tbot v1.0.0 h1:pKLHIvdiHRgU5iTfpWlgMTCWyD4vIQtAsOy7LCYMsAo=
github.com/yanzay/tbot v1.0.0/go.mod h1:rS36e4e2P56jkI0bUuZtzjBBIwMIdBBTUHfI4tgG5BM=
github.com/yanzay/tbot/v2 v2.1.0 h1:mppieSOIbzaCjp2en66Fz4unIJ57+aalQfdGbtYmaKg=
github.com/yanzay/tbot/v2 v2.1.0/go.mod h1:q0+8JblBq9tLAnKHdBIZsHwDvMS9TfO6mNfaAk1VrHg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

56
pkg/core/config/config.go Normal file
View File

@ -0,0 +1,56 @@
package config
import (
"encoding/json"
"fmt"
"log"
"os"
)
// Config - This struct will hold configuration components.
type Config struct {
Telegram struct {
Token string `json:"token"`
ChatID string `json:"chatId"`
Admins []int `json:"admins"`
AuthorizedChats []string `json:"authorizedChats"`
} `json:"telegram"`
Sonarr struct {
URL string `json:"url"`
APIKey string `json:"apiKey"`
SeasonLimit int `json:"seasonLimit"`
ProfileID int `json:"profileId"`
} `json:"sonarr"`
CouchPotato struct {
URL string `json:"url"`
APIKey string `json:"apiKey"`
ProfileID string `json:"profileId`
} `json:"couchpotato"`
}
//GetConfig gets the configuration values for the api using the file in the supplied configPath.
func GetConfig(configPath string) (Config, error) {
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return Config{}, fmt.Errorf("could not find the config file at path %s", configPath)
}
log.Println("Loading Configuration File: " + configPath)
return loadConfigFromFile(configPath)
}
//if the config loaded from the file errors, no defaults will be loaded and the app will exit.
func loadConfigFromFile(configPath string) (conf Config, err error) {
file, err := os.Open(configPath)
if err != nil {
log.Printf("Error opening config file: %v", err)
} else {
defer file.Close()
err = json.NewDecoder(file).Decode(&conf)
if err != nil {
log.Printf("Error decoding config file: %v", err)
}
}
return conf, err
}

View File

@ -0,0 +1 @@
package couchpotato

View File

@ -0,0 +1,87 @@
package couchpotato
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/mattburchett/go_telegram/pkg/core/config"
"github.com/yanzay/tbot/v2"
)
type couchpotatoSearch struct {
Movies []struct {
Title string `json:"original_title"`
Imdb string `json:"imdb"`
Year int `json:"year"`
InLibrary bool `json:"in_library"`
InWanted bool `json:"in_wanted"`
} `json:"movies"`
Success bool `json:"success"`
}
type request struct {
ImdbID string `json:"imdbid"`
Title string `json:"title"`
Year int `json:"year"`
Requested bool `json:"requested"`
Downloaded bool `json:"downloaded"`
}
func (search couchpotatoSearch) Convert() []request {
requests := []request{}
for _, result := range search.Movies {
requests = append(requests, request{
ImdbID: result.Imdb,
Title: result.Title,
Year: result.Year,
Requested: result.InWanted,
Downloaded: result.InLibrary,
})
}
return requests
}
type response struct {
Button string `json:"button"`
Callback string `json:"callback"`
}
// Search performs the lookup actions within CouchPotato
func Search(m *tbot.Message, config config.Config) ([]response, error) {
searchLookup, err := http.Get(config.CouchPotato.URL + config.CouchPotato.APIKey + "/movie.search?q=" + url.QueryEscape(strings.TrimPrefix(strings.TrimPrefix(m.Text, "/m"), " ")))
if err != nil {
return nil, err
}
search := couchpotatoSearch{}
searchData, err := ioutil.ReadAll(searchLookup.Body)
if err != nil {
return nil, err
}
requestJSON := json.Unmarshal(searchData, &search)
if requestJSON != nil {
return nil, err
}
requests := search.Convert()
responseData := []response{}
for _, r := range requests {
responseData = append(responseData,
response{
fmt.Sprintf("%v (%v)", r.Title, r.Year),
fmt.Sprintf("%v", r.ImdbID),
})
}
return responseData, err
}

View File

@ -0,0 +1,24 @@
package sonarr
import (
"io/ioutil"
"net/http"
"github.com/mattburchett/go_telegram/pkg/core/config"
"github.com/yanzay/tbot/v2"
)
// Status contains the Sonarr request for system status.
func Status(m *tbot.Message, config config.Config) (string, error) {
r, err := http.Get(config.Sonarr.URL + "system/status?apikey=" + config.Sonarr.APIKey)
if err != nil {
return "Failed to contact Sonarr for data", err
}
rd, err := ioutil.ReadAll(r.Body)
if err != nil {
return "Failed to read Sonarr status data.", err
}
return string(rd), err
}

View File

@ -0,0 +1,245 @@
package sonarr
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/mattburchett/go_telegram/pkg/core/config"
"github.com/yanzay/tbot/v2"
)
// Response holds the information needed for Telegram callback.
type response struct {
Button string `json:"button"`
Callback string `json:"callback"`
}
type sonarrSearch []struct {
Title string `json:"title"`
SeasonCount int `json:"seasonCount"`
Year int `json:"year"`
TvdbID int `json:"tvdbId"`
Downloaded bool `json:"downloaded"`
QualityProfileID int `json:"qualityProfileId"`
TitleSlug string `json:"titleSlug"`
Images []struct {
CoverType string `json:"coverType"`
URL string `json:"url"`
} `json:"images"`
Seasons []struct {
SeasonNumber int `json:"seasonNumber"`
Monitored bool `json:"monitored"`
} `json:"seasons"`
ProfileID int `json:"profileId"`
}
type sonarrAdd struct {
Title string `json:"title"`
TvdbID int `json:"tvdbId"`
QualityProfileID int `json:"qualityProfileId"`
TitleSlug string `json:"titleSlug"`
Images []struct {
CoverType string `json:"coverType"`
URL string `json:"url"`
} `json:"images"`
Seasons []struct {
SeasonNumber int `json:"seasonNumber"`
Monitored bool `json:"monitored"`
} `json:"seasons"`
ProfileID int `json:"profileId"`
RootFolderPath string `json:"rootFolderPath"`
AddOptions struct {
IgnoreEpisodesWithFiles bool `json:"ignoreEpisodesWithFiles"`
IgnoreEpisodesWithoutFiles bool `json:"ignoreEpisodesWithoutFiles"`
SearchForMIssingEpisodes bool `json:"searchForMissingEpisodes"`
} `json:"addOptions"`
}
type rootFolderLookup []struct {
Path string `json:"path"`
FreeSpace int64 `json:"freeSpace"`
TotalSpace int64 `json:"totalSpace"`
UnmappedFolders []struct {
Name string `json:"name"`
Path string `json:"path"`
} `json:"unmappedFolders"`
ID int `json:"id"`
}
// Search performs the lookup actions within Sonarr.
func Search(m *tbot.Message, config config.Config) ([]response, error) {
var remote sonarrSearch
var local sonarrSearch
// Perform series lookup
remoteLookup, err := http.Get(config.Sonarr.URL +
"series/lookup?apikey=" +
config.Sonarr.APIKey +
"&term=" +
url.QueryEscape(strings.TrimPrefix(strings.TrimPrefix(m.Text, "/s"), " ")))
if err != nil {
return nil, err
}
// Perform series local database lookup.
localLookup, err := http.Get(config.Sonarr.URL + "series?apikey=" + config.Sonarr.APIKey)
if err != nil {
return nil, err
}
// Read remote and local Data
remoteData, err := ioutil.ReadAll(remoteLookup.Body)
if err != nil {
return nil, err
}
localData, err := ioutil.ReadAll(localLookup.Body)
if err != nil {
return nil, err
}
// Unmarshal remote and local JSON
remoteJSON := json.Unmarshal(remoteData, &remote)
if remoteJSON != nil {
return nil, err
}
localJSON := json.Unmarshal(localData, &local)
if localJSON != nil {
return nil, err
}
// Check for downloaded items.
for r := range remote {
for l := range local {
if remote[r].TvdbID == local[l].TvdbID {
remote[r].Downloaded = true
}
}
}
// Form URLs and return to Telegram
responseData := []response{}
for k := range remote {
responseData = append(responseData,
response{
fmt.Sprintf("%v%v%v (%v) - %v Seasons",
seasonHandler(remote[k].SeasonCount, config.Sonarr.SeasonLimit),
downloadedHandler(remote[k].Downloaded),
remote[k].Title,
remote[k].Year,
remote[k].SeasonCount),
fmt.Sprintf("%d%s%s", remote[k].TvdbID,
strings.TrimSuffix(downloadedHandler(remote[k].Downloaded), " "),
strings.TrimSuffix(seasonHandler(remote[k].SeasonCount, config.Sonarr.SeasonLimit), " ")),
})
}
return responseData, err
}
// Add will take the callback data and add the show to Sonarr.
func Add(callback string, config config.Config) string {
// Separate the TVDB ID
tvdbid := strings.TrimPrefix(strings.TrimSuffix(strings.TrimSuffix(callback, "+"), "*"), "tv_")
// Look it up, to gather the information needed and the title.
seriesLookup, err := http.Get(config.Sonarr.URL + "/series/lookup?apikey=" + config.Sonarr.APIKey + "&term=tvdb:" + tvdbid)
if err != nil {
return err.Error()
}
seriesData, err := ioutil.ReadAll(seriesLookup.Body)
if err != nil {
return err.Error()
}
series := sonarrSearch{}
seriesJSON := json.Unmarshal(seriesData, &series)
if seriesJSON != nil {
return err.Error()
}
// If the "downloaded" asterisk is already in place, go ahead and return.
if strings.Contains(callback, "*") {
return fmt.Sprintf("%v has already been requested for download.", series[0].Title)
}
// Gather the root folder location.
rootFolderLookupQuery, err := http.Get(config.Sonarr.URL + "/rootfolder?apikey=" + config.Sonarr.APIKey)
if err != nil {
return err.Error()
}
rootFolderData, err := ioutil.ReadAll(rootFolderLookupQuery.Body)
if err != nil {
return err.Error()
}
rootFolder := rootFolderLookup{}
rootFolderJSON := json.Unmarshal(rootFolderData, &rootFolder)
if rootFolderJSON != nil {
return err.Error()
}
// Form the JSON needed for adding to Sonarr.
seriesAdd := sonarrAdd{
TvdbID: series[0].TvdbID,
Title: series[0].Title,
// QualityProfileID: series[0].QualityProfileID,
TitleSlug: series[0].TitleSlug,
Images: series[0].Images,
Seasons: series[0].Seasons,
RootFolderPath: rootFolder[0].Path,
ProfileID: config.Sonarr.ProfileID,
}
seriesAdd.AddOptions.IgnoreEpisodesWithFiles = false
seriesAdd.AddOptions.IgnoreEpisodesWithoutFiles = false
seriesAdd.AddOptions.SearchForMIssingEpisodes = true
// Post it to Sonarr to be added.
seriesAddJSON, err := json.Marshal(seriesAdd)
if err != nil {
return err.Error()
}
seriesAddReq, err := http.Post(config.Sonarr.URL+"/series?apikey="+config.Sonarr.APIKey, "application/json", bytes.NewBuffer(seriesAddJSON))
if err != nil {
return err.Error()
}
if seriesAddReq.StatusCode != 201 {
return "There was an error processing this request."
}
return fmt.Sprintf("%s has been queued for download.", series[0].Title)
}
// downloadHandler returns the proper string that should be shown in the Telegram response.
func downloadedHandler(downloaded bool) string {
if downloaded {
return "* "
}
return ""
}
// seasonHandler returns the proper string that should be shown in the Telegram response.
// it is also used to prevent non-admins from downloading shows with a high number of seasons.
func seasonHandler(seasonCount int, seasonLimit int) string {
if seasonLimit == 0 {
return ""
}
if seasonCount >= seasonLimit {
return "+ "
}
return ""
}

View File

@ -0,0 +1,36 @@
package telegram
import (
"strconv"
"github.com/yanzay/tbot/v2"
)
func (tb *Bot) myID(m *tbot.Message) {
if tb.adminCheck(m.From.ID, false) {
tb.Client.SendMessage(m.Chat.ID, strconv.Itoa(m.From.ID))
return
}
tb.Client.SendMessage(m.Chat.ID, "You are not an authorized admin.")
}
func (tb *Bot) chatID(m *tbot.Message) {
if tb.adminCheck(m.From.ID, false) {
tb.Client.SendMessage(m.Chat.ID, m.Chat.ID)
return
}
tb.Client.SendMessage(m.Chat.ID, "You are not an authorized admin.")
}
// adminCheck checks for valid bot admins.
func (tb *Bot) adminCheck(id int, callback bool) bool {
for _, admin := range tb.Config.Telegram.Admins {
if id == admin {
return true
}
}
return false
}

View File

@ -0,0 +1,73 @@
package telegram
import (
"strings"
"github.com/yanzay/tbot/v2"
)
// Handler creates the active Telegram handlers.
func (tb *Bot) Handler() {
// Bot Healthcheck
tb.Bot.HandleMessage("/ping", func(m *tbot.Message) {
tb.Client.SendMessage(m.Chat.ID, "pong")
})
// telegram/couchpotato.go
tb.Bot.HandleMessage("/m", tb.couchpotatoSearch)
// telegram/sonar.go
tb.Bot.HandleMessage("/s", tb.sonarrSearch)
tb.Bot.HandleMessage("/admin sonarrStatus", tb.sonarrStatus)
// telegram/testhandler.go
tb.Bot.HandleMessage("/test", tb.testHandler)
// telegram/admin.go
tb.Bot.HandleMessage("/admin myID", tb.myID)
tb.Bot.HandleMessage("/admin chatID", tb.chatID)
// Help
tb.Bot.HandleMessage("/help$", tb.helpHandler)
tb.Bot.HandleMessage("/h$", tb.helpHandler)
// Callback Handler
tb.Bot.HandleCallback(tb.callbackHandler)
}
// callbackHandler handles callbacks.
func (tb *Bot) callbackHandler(cq *tbot.CallbackQuery) {
go func() {
tb.Client.AnswerCallbackQuery(cq.ID, tbot.OptText("Request received."))
tb.Client.DeleteMessage(tb.CallbackChatID, tb.CallbackMessageID)
}()
if strings.Contains(cq.Data, "tv_") {
tb.sonarrAdd(cq)
return
}
tb.Client.SendMessage(tb.CallbackChatID, cq.Data)
}
func (tb *Bot) helpHandler(m *tbot.Message) {
if !tb.whitelistHandler(m) {
return
}
tb.Client.SendMessage(m.Chat.ID, "USAGE:\n\n/movie <Movie Name> or /m <Movie Name>\n/show <TV Show Name> or /s <TV Show Name>\n\nEXAMPLES:\n\n/s The Walking Dead\n/m Avatar")
}
func (tb *Bot) whitelistHandler(m *tbot.Message) bool {
for _, id := range tb.Config.Telegram.AuthorizedChats {
if id == m.Chat.ID {
return true
}
}
tb.Client.SendMessage(m.Chat.ID, "This bot is not authorized for use in this chat.")
return false
}

View File

@ -0,0 +1,85 @@
package telegram
import (
"fmt"
"strings"
"github.com/mattburchett/go_telegram/pkg/service/sonarr"
"github.com/yanzay/tbot/v2"
)
// Sonarr Search
func (tb *Bot) sonarrSearch(m *tbot.Message) {
if !tb.whitelistHandler(m) {
return
}
text := strings.TrimPrefix(strings.TrimPrefix(m.Text, "/s"), " ")
if len(text) == 0 {
tb.Client.SendMessage(m.Chat.ID, "You must specify a show. Type /help for help.")
return
}
request, err := sonarr.Search(m, tb.Config)
if err != nil {
tb.Client.SendMessage(m.Chat.ID, err.Error())
return
}
inlineResponse := make([][]tbot.InlineKeyboardButton, 0)
for _, i := range request {
inlineResponse = append(inlineResponse, []tbot.InlineKeyboardButton{{
Text: i.Button,
CallbackData: "tv_" + i.Callback,
}})
}
if len(request) == 0 {
tb.Client.SendMessage(m.Chat.ID, "No results found, try harder.")
return
}
response, _ := tb.Client.SendMessage(m.Chat.ID, "Please select the show you would like to download.", tbot.OptInlineKeyboardMarkup(&tbot.InlineKeyboardMarkup{InlineKeyboard: inlineResponse}))
tb.CallbackMessageID = response.MessageID
tb.CallbackChatID = m.Chat.ID
}
// sonarrAdd will perform the add requests to Sonarr.
func (tb *Bot) sonarrAdd(cq *tbot.CallbackQuery) {
if strings.Contains(cq.Data, "+") {
if tb.adminCheck(cq.From.ID, true) {
tb.Client.SendMessage(tb.CallbackChatID, sonarr.Add(cq.Data, tb.Config))
} else {
tb.Client.AnswerCallbackQuery(cq.ID, tbot.OptText("This request is over the season limit."))
}
return
}
tb.Client.SendMessage(tb.CallbackChatID, sonarr.Add(cq.Data, tb.Config))
}
// Admin Functions
// sonarrStatus queries Sonarr for it's system status information.
func (tb *Bot) sonarrStatus(m *tbot.Message) {
if !tb.whitelistHandler(m) {
return
}
if tb.adminCheck(m.From.ID, false) {
request, err := sonarr.Status(m, tb.Config)
if err != nil {
tb.Client.SendMessage(m.Chat.ID, fmt.Sprintf("%v: \n %v", request, err))
return
}
tb.Client.SendMessage(m.Chat.ID, "Sonarr Status:")
tb.Client.SendMessage(m.Chat.ID, request)
}
}

View File

@ -0,0 +1,37 @@
package telegram
import (
"log"
"time"
"github.com/mattburchett/go_telegram/pkg/core/config"
"github.com/yanzay/tbot/v2"
)
// Bot contains all the necessary bot and callback information.
type Bot struct {
Client *tbot.Client
Config config.Config
Bot *tbot.Server
CallbackChatID string
CallbackMessageID int
}
// Stat middleware.
func stat(h tbot.UpdateHandler) tbot.UpdateHandler {
return func(u *tbot.Update) {
start := time.Now()
h(u)
log.Printf("Handle time: %v", time.Now().Sub(start))
}
}
// New creates an active telegram bot and loads the handlers.
func (tb *Bot) New(token string) {
tb.Bot = tbot.New(token)
tb.Bot.Use(stat)
tb.Client = tb.Bot.Client()
tb.Handler()
tb.Bot.Start()
}

View File

@ -0,0 +1,25 @@
package telegram
import (
"strings"
"github.com/yanzay/tbot/v2"
)
func (tb *Bot) testHandler(m *tbot.Message) {
buttons := make([]string, 0)
buttons = append(buttons, "ping", "is", "stupid")
inline2 := make([][]tbot.InlineKeyboardButton, 0)
for _, i := range buttons {
inline2 = append(inline2, []tbot.InlineKeyboardButton{{
Text: i,
CallbackData: i,
}})
}
msg, _ := tb.Client.SendMessage(m.Chat.ID, "Inline test. "+strings.TrimPrefix(m.Text, "/test "), tbot.OptInlineKeyboardMarkup(&tbot.InlineKeyboardMarkup{InlineKeyboard: inline2}))
tb.CallbackMessageID = msg.MessageID
tb.CallbackChatID = m.Chat.ID
}