all repos — telegram-bot-api @ 18031c85e5bbe0781261fcbb2f20e11ca07583e6

Golang bindings for the Telegram Bot API

bot.go (view raw)

   1// Package tgbotapi has functions and types used for interacting with
   2// the Telegram Bot API.
   3package tgbotapi
   4
   5import (
   6	"bytes"
   7	"encoding/json"
   8	"errors"
   9	"fmt"
  10	"io"
  11	"io/ioutil"
  12	"net/http"
  13	"net/url"
  14	"os"
  15	"strconv"
  16	"strings"
  17	"time"
  18
  19	"github.com/technoweenie/multipartstreamer"
  20)
  21
  22type HttpClient interface {
  23	Do(req *http.Request) (*http.Response, error)
  24}
  25
  26// BotAPI allows you to interact with the Telegram Bot API.
  27type BotAPI struct {
  28	Token  string `json:"token"`
  29	Debug  bool   `json:"debug"`
  30	Buffer int    `json:"buffer"`
  31
  32	Self            User       `json:"-"`
  33	Client          HttpClient `json:"-"`
  34	shutdownChannel chan interface{}
  35
  36	apiEndpoint string
  37}
  38
  39// NewBotAPI creates a new BotAPI instance.
  40//
  41// It requires a token, provided by @BotFather on Telegram.
  42func NewBotAPI(token string) (*BotAPI, error) {
  43	return NewBotAPIWithClient(token, APIEndpoint, &http.Client{})
  44}
  45
  46// NewBotAPIWithAPIEndpoint creates a new BotAPI instance
  47// and allows you to pass API endpoint.
  48//
  49// It requires a token, provided by @BotFather on Telegram and API endpoint.
  50func NewBotAPIWithAPIEndpoint(token, apiEndpoint string) (*BotAPI, error) {
  51	return NewBotAPIWithClient(token, apiEndpoint, &http.Client{})
  52}
  53
  54// NewBotAPIWithClient creates a new BotAPI instance
  55// and allows you to pass a http.Client.
  56//
  57// It requires a token, provided by @BotFather on Telegram and API endpoint.
  58func NewBotAPIWithClient(token, apiEndpoint string, client HttpClient) (*BotAPI, error) {
  59	bot := &BotAPI{
  60		Token:           token,
  61		Client:          client,
  62		Buffer:          100,
  63		shutdownChannel: make(chan interface{}),
  64
  65		apiEndpoint: apiEndpoint,
  66	}
  67
  68	self, err := bot.GetMe()
  69	if err != nil {
  70		return nil, err
  71	}
  72
  73	bot.Self = self
  74
  75	return bot, nil
  76}
  77
  78// SetAPIEndpoint add telegram apiEndpont to Bot
  79func (bot *BotAPI) SetAPIEndpoint(apiEndpoint string) {
  80	bot.apiEndpoint = apiEndpoint
  81}
  82
  83// MakeRequest makes a request to a specific endpoint with our token.
  84func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
  85	method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
  86
  87	req, err := http.NewRequest("POST", method, strings.NewReader(params.Encode()))
  88	if err != nil {
  89		return APIResponse{}, err
  90	}
  91	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  92
  93	resp, err := bot.Client.Do(req)
  94	if err != nil {
  95		return APIResponse{}, err
  96	}
  97	defer resp.Body.Close()
  98
  99	var apiResp APIResponse
 100	bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
 101	if err != nil {
 102		return apiResp, err
 103	}
 104
 105	if bot.Debug {
 106		log.Printf("%s resp: %s", endpoint, bytes)
 107	}
 108
 109	if !apiResp.Ok {
 110		parameters := ResponseParameters{}
 111		if apiResp.Parameters != nil {
 112			parameters = *apiResp.Parameters
 113		}
 114		return apiResp, Error{Code: apiResp.ErrorCode, Message: apiResp.Description, ResponseParameters: parameters}
 115	}
 116
 117	return apiResp, nil
 118}
 119
 120// decodeAPIResponse decode response and return slice of bytes if debug enabled.
 121// If debug disabled, just decode http.Response.Body stream to APIResponse struct
 122// for efficient memory usage
 123func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) (_ []byte, err error) {
 124	if !bot.Debug {
 125		dec := json.NewDecoder(responseBody)
 126		err = dec.Decode(resp)
 127		return
 128	}
 129
 130	// if debug, read reponse body
 131	data, err := ioutil.ReadAll(responseBody)
 132	if err != nil {
 133		return
 134	}
 135
 136	err = json.Unmarshal(data, resp)
 137	if err != nil {
 138		return
 139	}
 140
 141	return data, nil
 142}
 143
 144// makeMessageRequest makes a request to a method that returns a Message.
 145func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
 146	resp, err := bot.MakeRequest(endpoint, params)
 147	if err != nil {
 148		return Message{}, err
 149	}
 150
 151	var message Message
 152	json.Unmarshal(resp.Result, &message)
 153
 154	bot.debugLog(endpoint, params, message)
 155
 156	return message, nil
 157}
 158
 159// UploadFile makes a request to the API with a file.
 160//
 161// Requires the parameter to hold the file not be in the params.
 162// File should be a string to a file path, a FileBytes struct,
 163// a FileReader struct, or a url.URL.
 164//
 165// Note that if your FileReader has a size set to -1, it will read
 166// the file into memory to calculate a size.
 167func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
 168	ms := multipartstreamer.New()
 169
 170	switch f := file.(type) {
 171	case string:
 172		ms.WriteFields(params)
 173
 174		fileHandle, err := os.Open(f)
 175		if err != nil {
 176			return APIResponse{}, err
 177		}
 178		defer fileHandle.Close()
 179
 180		fi, err := os.Stat(f)
 181		if err != nil {
 182			return APIResponse{}, err
 183		}
 184
 185		ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
 186	case FileBytes:
 187		ms.WriteFields(params)
 188
 189		buf := bytes.NewBuffer(f.Bytes)
 190		ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
 191	case FileReader:
 192		ms.WriteFields(params)
 193
 194		if f.Size != -1 {
 195			ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
 196
 197			break
 198		}
 199
 200		data, err := ioutil.ReadAll(f.Reader)
 201		if err != nil {
 202			return APIResponse{}, err
 203		}
 204
 205		buf := bytes.NewBuffer(data)
 206
 207		ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
 208	case url.URL:
 209		params[fieldname] = f.String()
 210
 211		ms.WriteFields(params)
 212	default:
 213		return APIResponse{}, errors.New(ErrBadFileType)
 214	}
 215
 216	method := fmt.Sprintf(bot.apiEndpoint, bot.Token, endpoint)
 217
 218	req, err := http.NewRequest("POST", method, nil)
 219	if err != nil {
 220		return APIResponse{}, err
 221	}
 222
 223	ms.SetupRequest(req)
 224
 225	res, err := bot.Client.Do(req)
 226	if err != nil {
 227		return APIResponse{}, err
 228	}
 229	defer res.Body.Close()
 230
 231	bytes, err := ioutil.ReadAll(res.Body)
 232	if err != nil {
 233		return APIResponse{}, err
 234	}
 235
 236	if bot.Debug {
 237		log.Println(string(bytes))
 238	}
 239
 240	var apiResp APIResponse
 241
 242	err = json.Unmarshal(bytes, &apiResp)
 243	if err != nil {
 244		return APIResponse{}, err
 245	}
 246
 247	if !apiResp.Ok {
 248		parameters := ResponseParameters{}
 249		if apiResp.Parameters != nil {
 250			parameters = *apiResp.Parameters
 251		}
 252		return apiResp, Error{Code: apiResp.ErrorCode, Message: apiResp.Description, ResponseParameters: parameters}
 253	}
 254
 255	return apiResp, nil
 256}
 257
 258// GetFileDirectURL returns direct URL to file
 259//
 260// It requires the FileID.
 261func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
 262	file, err := bot.GetFile(FileConfig{fileID})
 263
 264	if err != nil {
 265		return "", err
 266	}
 267
 268	return file.Link(bot.Token), nil
 269}
 270
 271// GetMe fetches the currently authenticated bot.
 272//
 273// This method is called upon creation to validate the token,
 274// and so you may get this data from BotAPI.Self without the need for
 275// another request.
 276func (bot *BotAPI) GetMe() (User, error) {
 277	resp, err := bot.MakeRequest("getMe", nil)
 278	if err != nil {
 279		return User{}, err
 280	}
 281
 282	var user User
 283	json.Unmarshal(resp.Result, &user)
 284
 285	bot.debugLog("getMe", nil, user)
 286
 287	return user, nil
 288}
 289
 290// IsMessageToMe returns true if message directed to this bot.
 291//
 292// It requires the Message.
 293func (bot *BotAPI) IsMessageToMe(message Message) bool {
 294	return strings.Contains(message.Text, "@"+bot.Self.UserName)
 295}
 296
 297// Send will send a Chattable item to Telegram.
 298//
 299// It requires the Chattable to send.
 300func (bot *BotAPI) Send(c Chattable) (Message, error) {
 301	switch c.(type) {
 302	case Fileable:
 303		return bot.sendFile(c.(Fileable))
 304	default:
 305		return bot.sendChattable(c)
 306	}
 307}
 308
 309// debugLog checks if the bot is currently running in debug mode, and if
 310// so will display information about the request and response in the
 311// debug log.
 312func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
 313	if bot.Debug {
 314		log.Printf("%s req : %+v\n", context, v)
 315		log.Printf("%s resp: %+v\n", context, message)
 316	}
 317}
 318
 319// sendExisting will send a Message with an existing file to Telegram.
 320func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
 321	v, err := config.values()
 322
 323	if err != nil {
 324		return Message{}, err
 325	}
 326
 327	message, err := bot.makeMessageRequest(method, v)
 328	if err != nil {
 329		return Message{}, err
 330	}
 331
 332	return message, nil
 333}
 334
 335// uploadAndSend will send a Message with a new file to Telegram.
 336func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
 337	params, err := config.params()
 338	if err != nil {
 339		return Message{}, err
 340	}
 341
 342	file := config.getFile()
 343
 344	resp, err := bot.UploadFile(method, params, config.name(), file)
 345	if err != nil {
 346		return Message{}, err
 347	}
 348
 349	var message Message
 350	json.Unmarshal(resp.Result, &message)
 351
 352	bot.debugLog(method, nil, message)
 353
 354	return message, nil
 355}
 356
 357// sendFile determines if the file is using an existing file or uploading
 358// a new file, then sends it as needed.
 359func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
 360	if config.useExistingFile() {
 361		return bot.sendExisting(config.method(), config)
 362	}
 363
 364	return bot.uploadAndSend(config.method(), config)
 365}
 366
 367// sendChattable sends a Chattable.
 368func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
 369	v, err := config.values()
 370	if err != nil {
 371		return Message{}, err
 372	}
 373
 374	message, err := bot.makeMessageRequest(config.method(), v)
 375
 376	if err != nil {
 377		return Message{}, err
 378	}
 379
 380	return message, nil
 381}
 382
 383// GetUserProfilePhotos gets a user's profile photos.
 384//
 385// It requires UserID.
 386// Offset and Limit are optional.
 387func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
 388	v := url.Values{}
 389	v.Add("user_id", strconv.Itoa(config.UserID))
 390	if config.Offset != 0 {
 391		v.Add("offset", strconv.Itoa(config.Offset))
 392	}
 393	if config.Limit != 0 {
 394		v.Add("limit", strconv.Itoa(config.Limit))
 395	}
 396
 397	resp, err := bot.MakeRequest("getUserProfilePhotos", v)
 398	if err != nil {
 399		return UserProfilePhotos{}, err
 400	}
 401
 402	var profilePhotos UserProfilePhotos
 403	json.Unmarshal(resp.Result, &profilePhotos)
 404
 405	bot.debugLog("GetUserProfilePhoto", v, profilePhotos)
 406
 407	return profilePhotos, nil
 408}
 409
 410// GetFile returns a File which can download a file from Telegram.
 411//
 412// Requires FileID.
 413func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
 414	v := url.Values{}
 415	v.Add("file_id", config.FileID)
 416
 417	resp, err := bot.MakeRequest("getFile", v)
 418	if err != nil {
 419		return File{}, err
 420	}
 421
 422	var file File
 423	json.Unmarshal(resp.Result, &file)
 424
 425	bot.debugLog("GetFile", v, file)
 426
 427	return file, nil
 428}
 429
 430// GetUpdates fetches updates.
 431// If a WebHook is set, this will not return any data!
 432//
 433// Offset, Limit, and Timeout are optional.
 434// To avoid stale items, set Offset to one higher than the previous item.
 435// Set Timeout to a large number to reduce requests so you can get updates
 436// instantly instead of having to wait between requests.
 437func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
 438	v := url.Values{}
 439	if config.Offset != 0 {
 440		v.Add("offset", strconv.Itoa(config.Offset))
 441	}
 442	if config.Limit > 0 {
 443		v.Add("limit", strconv.Itoa(config.Limit))
 444	}
 445	if config.Timeout > 0 {
 446		v.Add("timeout", strconv.Itoa(config.Timeout))
 447	}
 448
 449	resp, err := bot.MakeRequest("getUpdates", v)
 450	if err != nil {
 451		return []Update{}, err
 452	}
 453
 454	var updates []Update
 455	json.Unmarshal(resp.Result, &updates)
 456
 457	bot.debugLog("getUpdates", v, updates)
 458
 459	return updates, nil
 460}
 461
 462// RemoveWebhook unsets the webhook.
 463func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
 464	return bot.MakeRequest("deleteWebhook", url.Values{})
 465}
 466
 467// SetWebhook sets a webhook.
 468//
 469// If this is set, GetUpdates will not get any data!
 470//
 471// If you do not have a legitimate TLS certificate, you need to include
 472// your self signed certificate with the config.
 473func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
 474
 475	if config.Certificate == nil {
 476		v := url.Values{}
 477		v.Add("url", config.URL.String())
 478		if config.MaxConnections != 0 {
 479			v.Add("max_connections", strconv.Itoa(config.MaxConnections))
 480		}
 481
 482		return bot.MakeRequest("setWebhook", v)
 483	}
 484
 485	params := make(map[string]string)
 486	params["url"] = config.URL.String()
 487	if config.MaxConnections != 0 {
 488		params["max_connections"] = strconv.Itoa(config.MaxConnections)
 489	}
 490
 491	resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
 492	if err != nil {
 493		return APIResponse{}, err
 494	}
 495
 496	return resp, nil
 497}
 498
 499// GetWebhookInfo allows you to fetch information about a webhook and if
 500// one currently is set, along with pending update count and error messages.
 501func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
 502	resp, err := bot.MakeRequest("getWebhookInfo", url.Values{})
 503	if err != nil {
 504		return WebhookInfo{}, err
 505	}
 506
 507	var info WebhookInfo
 508	err = json.Unmarshal(resp.Result, &info)
 509
 510	return info, err
 511}
 512
 513// GetUpdatesChan starts and returns a channel for getting updates.
 514func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
 515	ch := make(chan Update, bot.Buffer)
 516
 517	go func() {
 518		for {
 519			select {
 520			case <-bot.shutdownChannel:
 521				close(ch)
 522				return
 523			default:
 524			}
 525
 526			updates, err := bot.GetUpdates(config)
 527			if err != nil {
 528				log.Println(err)
 529				log.Println("Failed to get updates, retrying in 3 seconds...")
 530				time.Sleep(time.Second * 3)
 531
 532				continue
 533			}
 534
 535			for _, update := range updates {
 536				if update.UpdateID >= config.Offset {
 537					config.Offset = update.UpdateID + 1
 538					ch <- update
 539				}
 540			}
 541		}
 542	}()
 543
 544	return ch, nil
 545}
 546
 547// StopReceivingUpdates stops the go routine which receives updates
 548func (bot *BotAPI) StopReceivingUpdates() {
 549	if bot.Debug {
 550		log.Println("Stopping the update receiver routine...")
 551	}
 552	close(bot.shutdownChannel)
 553}
 554
 555// ListenForWebhook registers a http handler for a webhook.
 556func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
 557	ch := make(chan Update, bot.Buffer)
 558
 559	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
 560		update, err := bot.HandleUpdate(r)
 561		if err != nil {
 562			errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
 563			w.WriteHeader(http.StatusBadRequest)
 564			w.Header().Set("Content-Type", "application/json")
 565			_, _ = w.Write(errMsg)
 566			return
 567		}
 568
 569		ch <- *update
 570		close(ch)
 571	})
 572
 573	return ch
 574}
 575
 576// ListenForWebhookRespReqFormat registers a http handler for a webhook.
 577func (bot *BotAPI) ListenForWebhookRespReqFormat(w http.ResponseWriter, r *http.Request) UpdatesChannel {
 578	ch := make(chan Update, bot.Buffer)
 579
 580	func(w http.ResponseWriter, r *http.Request) {
 581		update, err := bot.HandleUpdate(r)
 582		if err != nil {
 583			errMsg, _ := json.Marshal(map[string]string{"error": err.Error()})
 584			w.WriteHeader(http.StatusBadRequest)
 585			w.Header().Set("Content-Type", "application/json")
 586			_, _ = w.Write(errMsg)
 587			return
 588		}
 589
 590		ch <- *update
 591		close(ch)
 592	}(w, r)
 593
 594	return ch
 595}
 596
 597// HandleUpdate parses and returns update received via webhook
 598func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) {
 599	if r.Method != http.MethodPost {
 600		err := errors.New("wrong HTTP method required POST")
 601		return nil, err
 602	}
 603
 604	var update Update
 605	err := json.NewDecoder(r.Body).Decode(&update)
 606	if err != nil {
 607		return nil, err
 608	}
 609
 610	return &update, nil
 611}
 612
 613// AnswerInlineQuery sends a response to an inline query.
 614//
 615// Note that you must respond to an inline query within 30 seconds.
 616func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
 617	v := url.Values{}
 618
 619	v.Add("inline_query_id", config.InlineQueryID)
 620	v.Add("cache_time", strconv.Itoa(config.CacheTime))
 621	v.Add("is_personal", strconv.FormatBool(config.IsPersonal))
 622	v.Add("next_offset", config.NextOffset)
 623	data, err := json.Marshal(config.Results)
 624	if err != nil {
 625		return APIResponse{}, err
 626	}
 627	v.Add("results", string(data))
 628	v.Add("switch_pm_text", config.SwitchPMText)
 629	v.Add("switch_pm_parameter", config.SwitchPMParameter)
 630
 631	bot.debugLog("answerInlineQuery", v, nil)
 632
 633	return bot.MakeRequest("answerInlineQuery", v)
 634}
 635
 636// AnswerCallbackQuery sends a response to an inline query callback.
 637func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) {
 638	v := url.Values{}
 639
 640	v.Add("callback_query_id", config.CallbackQueryID)
 641	if config.Text != "" {
 642		v.Add("text", config.Text)
 643	}
 644	v.Add("show_alert", strconv.FormatBool(config.ShowAlert))
 645	if config.URL != "" {
 646		v.Add("url", config.URL)
 647	}
 648	v.Add("cache_time", strconv.Itoa(config.CacheTime))
 649
 650	bot.debugLog("answerCallbackQuery", v, nil)
 651
 652	return bot.MakeRequest("answerCallbackQuery", v)
 653}
 654
 655// KickChatMember kicks a user from a chat. Note that this only will work
 656// in supergroups, and requires the bot to be an admin. Also note they
 657// will be unable to rejoin until they are unbanned.
 658func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) {
 659	v := url.Values{}
 660
 661	if config.SuperGroupUsername == "" {
 662		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 663	} else {
 664		v.Add("chat_id", config.SuperGroupUsername)
 665	}
 666	v.Add("user_id", strconv.Itoa(config.UserID))
 667
 668	if config.UntilDate != 0 {
 669		v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
 670	}
 671
 672	bot.debugLog("kickChatMember", v, nil)
 673
 674	return bot.MakeRequest("kickChatMember", v)
 675}
 676
 677// LeaveChat makes the bot leave the chat.
 678func (bot *BotAPI) LeaveChat(config ChatConfig) (APIResponse, error) {
 679	v := url.Values{}
 680
 681	if config.SuperGroupUsername == "" {
 682		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 683	} else {
 684		v.Add("chat_id", config.SuperGroupUsername)
 685	}
 686
 687	bot.debugLog("leaveChat", v, nil)
 688
 689	return bot.MakeRequest("leaveChat", v)
 690}
 691
 692// GetChat gets information about a chat.
 693func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
 694	v := url.Values{}
 695
 696	if config.SuperGroupUsername == "" {
 697		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 698	} else {
 699		v.Add("chat_id", config.SuperGroupUsername)
 700	}
 701
 702	resp, err := bot.MakeRequest("getChat", v)
 703	if err != nil {
 704		return Chat{}, err
 705	}
 706
 707	var chat Chat
 708	err = json.Unmarshal(resp.Result, &chat)
 709
 710	bot.debugLog("getChat", v, chat)
 711
 712	return chat, err
 713}
 714
 715// GetChatAdministrators gets a list of administrators in the chat.
 716//
 717// If none have been appointed, only the creator will be returned.
 718// Bots are not shown, even if they are an administrator.
 719func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) {
 720	v := url.Values{}
 721
 722	if config.SuperGroupUsername == "" {
 723		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 724	} else {
 725		v.Add("chat_id", config.SuperGroupUsername)
 726	}
 727
 728	resp, err := bot.MakeRequest("getChatAdministrators", v)
 729	if err != nil {
 730		return []ChatMember{}, err
 731	}
 732
 733	var members []ChatMember
 734	err = json.Unmarshal(resp.Result, &members)
 735
 736	bot.debugLog("getChatAdministrators", v, members)
 737
 738	return members, err
 739}
 740
 741// GetChatMembersCount gets the number of users in a chat.
 742func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
 743	v := url.Values{}
 744
 745	if config.SuperGroupUsername == "" {
 746		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 747	} else {
 748		v.Add("chat_id", config.SuperGroupUsername)
 749	}
 750
 751	resp, err := bot.MakeRequest("getChatMembersCount", v)
 752	if err != nil {
 753		return -1, err
 754	}
 755
 756	var count int
 757	err = json.Unmarshal(resp.Result, &count)
 758
 759	bot.debugLog("getChatMembersCount", v, count)
 760
 761	return count, err
 762}
 763
 764// GetChatMember gets a specific chat member.
 765func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) {
 766	v := url.Values{}
 767
 768	if config.SuperGroupUsername == "" {
 769		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 770	} else {
 771		v.Add("chat_id", config.SuperGroupUsername)
 772	}
 773	v.Add("user_id", strconv.Itoa(config.UserID))
 774
 775	resp, err := bot.MakeRequest("getChatMember", v)
 776	if err != nil {
 777		return ChatMember{}, err
 778	}
 779
 780	var member ChatMember
 781	err = json.Unmarshal(resp.Result, &member)
 782
 783	bot.debugLog("getChatMember", v, member)
 784
 785	return member, err
 786}
 787
 788// UnbanChatMember unbans a user from a chat. Note that this only will work
 789// in supergroups and channels, and requires the bot to be an admin.
 790func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) {
 791	v := url.Values{}
 792
 793	if config.SuperGroupUsername != "" {
 794		v.Add("chat_id", config.SuperGroupUsername)
 795	} else if config.ChannelUsername != "" {
 796		v.Add("chat_id", config.ChannelUsername)
 797	} else {
 798		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 799	}
 800	v.Add("user_id", strconv.Itoa(config.UserID))
 801
 802	bot.debugLog("unbanChatMember", v, nil)
 803
 804	return bot.MakeRequest("unbanChatMember", v)
 805}
 806
 807// RestrictChatMember to restrict a user in a supergroup. The bot must be an
 808// administrator in the supergroup for this to work and must have the
 809// appropriate admin rights. Pass True for all boolean parameters to lift
 810// restrictions from a user. Returns True on success.
 811func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) {
 812	v := url.Values{}
 813
 814	if config.SuperGroupUsername != "" {
 815		v.Add("chat_id", config.SuperGroupUsername)
 816	} else if config.ChannelUsername != "" {
 817		v.Add("chat_id", config.ChannelUsername)
 818	} else {
 819		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 820	}
 821	v.Add("user_id", strconv.Itoa(config.UserID))
 822
 823	if config.CanSendMessages != nil {
 824		v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages))
 825	}
 826	if config.CanSendMediaMessages != nil {
 827		v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages))
 828	}
 829	if config.CanSendOtherMessages != nil {
 830		v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages))
 831	}
 832	if config.CanAddWebPagePreviews != nil {
 833		v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews))
 834	}
 835	if config.UntilDate != 0 {
 836		v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
 837	}
 838
 839	bot.debugLog("restrictChatMember", v, nil)
 840
 841	return bot.MakeRequest("restrictChatMember", v)
 842}
 843
 844// PromoteChatMember add admin rights to user
 845func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) {
 846	v := url.Values{}
 847
 848	if config.SuperGroupUsername != "" {
 849		v.Add("chat_id", config.SuperGroupUsername)
 850	} else if config.ChannelUsername != "" {
 851		v.Add("chat_id", config.ChannelUsername)
 852	} else {
 853		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 854	}
 855	v.Add("user_id", strconv.Itoa(config.UserID))
 856
 857	if config.CanChangeInfo != nil {
 858		v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo))
 859	}
 860	if config.CanPostMessages != nil {
 861		v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages))
 862	}
 863	if config.CanEditMessages != nil {
 864		v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages))
 865	}
 866	if config.CanDeleteMessages != nil {
 867		v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages))
 868	}
 869	if config.CanInviteUsers != nil {
 870		v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers))
 871	}
 872	if config.CanRestrictMembers != nil {
 873		v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers))
 874	}
 875	if config.CanPinMessages != nil {
 876		v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages))
 877	}
 878	if config.CanPromoteMembers != nil {
 879		v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers))
 880	}
 881
 882	bot.debugLog("promoteChatMember", v, nil)
 883
 884	return bot.MakeRequest("promoteChatMember", v)
 885}
 886
 887// GetGameHighScores allows you to get the high scores for a game.
 888func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
 889	v, _ := config.values()
 890
 891	resp, err := bot.MakeRequest(config.method(), v)
 892	if err != nil {
 893		return []GameHighScore{}, err
 894	}
 895
 896	var highScores []GameHighScore
 897	err = json.Unmarshal(resp.Result, &highScores)
 898
 899	return highScores, err
 900}
 901
 902// AnswerShippingQuery allows you to reply to Update with shipping_query parameter.
 903func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) {
 904	v := url.Values{}
 905
 906	v.Add("shipping_query_id", config.ShippingQueryID)
 907	v.Add("ok", strconv.FormatBool(config.OK))
 908	if config.OK == true {
 909		data, err := json.Marshal(config.ShippingOptions)
 910		if err != nil {
 911			return APIResponse{}, err
 912		}
 913		v.Add("shipping_options", string(data))
 914	} else {
 915		v.Add("error_message", config.ErrorMessage)
 916	}
 917
 918	bot.debugLog("answerShippingQuery", v, nil)
 919
 920	return bot.MakeRequest("answerShippingQuery", v)
 921}
 922
 923// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query.
 924func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) {
 925	v := url.Values{}
 926
 927	v.Add("pre_checkout_query_id", config.PreCheckoutQueryID)
 928	v.Add("ok", strconv.FormatBool(config.OK))
 929	if config.OK != true {
 930		v.Add("error_message", config.ErrorMessage)
 931	}
 932
 933	bot.debugLog("answerPreCheckoutQuery", v, nil)
 934
 935	return bot.MakeRequest("answerPreCheckoutQuery", v)
 936}
 937
 938// DeleteMessage deletes a message in a chat
 939func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) {
 940	v, err := config.values()
 941	if err != nil {
 942		return APIResponse{}, err
 943	}
 944
 945	bot.debugLog(config.method(), v, nil)
 946
 947	return bot.MakeRequest(config.method(), v)
 948}
 949
 950// GetInviteLink get InviteLink for a chat
 951func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
 952	v := url.Values{}
 953
 954	if config.SuperGroupUsername == "" {
 955		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 956	} else {
 957		v.Add("chat_id", config.SuperGroupUsername)
 958	}
 959
 960	resp, err := bot.MakeRequest("exportChatInviteLink", v)
 961	if err != nil {
 962		return "", err
 963	}
 964
 965	var inviteLink string
 966	err = json.Unmarshal(resp.Result, &inviteLink)
 967
 968	return inviteLink, err
 969}
 970
 971// PinChatMessage pin message in supergroup
 972func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) {
 973	v, err := config.values()
 974	if err != nil {
 975		return APIResponse{}, err
 976	}
 977
 978	bot.debugLog(config.method(), v, nil)
 979
 980	return bot.MakeRequest(config.method(), v)
 981}
 982
 983// UnpinChatMessage unpin message in supergroup
 984func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) {
 985	v, err := config.values()
 986	if err != nil {
 987		return APIResponse{}, err
 988	}
 989
 990	bot.debugLog(config.method(), v, nil)
 991
 992	return bot.MakeRequest(config.method(), v)
 993}
 994
 995// SetChatTitle change title of chat.
 996func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) {
 997	v, err := config.values()
 998	if err != nil {
 999		return APIResponse{}, err
1000	}
1001
1002	bot.debugLog(config.method(), v, nil)
1003
1004	return bot.MakeRequest(config.method(), v)
1005}
1006
1007// SetChatDescription change description of chat.
1008func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) {
1009	v, err := config.values()
1010	if err != nil {
1011		return APIResponse{}, err
1012	}
1013
1014	bot.debugLog(config.method(), v, nil)
1015
1016	return bot.MakeRequest(config.method(), v)
1017}
1018
1019// SetChatPhoto change photo of chat.
1020func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) {
1021	params, err := config.params()
1022	if err != nil {
1023		return APIResponse{}, err
1024	}
1025
1026	file := config.getFile()
1027
1028	return bot.UploadFile(config.method(), params, config.name(), file)
1029}
1030
1031// DeleteChatPhoto delete photo of chat.
1032func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, error) {
1033	v, err := config.values()
1034	if err != nil {
1035		return APIResponse{}, err
1036	}
1037
1038	bot.debugLog(config.method(), v, nil)
1039
1040	return bot.MakeRequest(config.method(), v)
1041}
1042
1043// GetStickerSet get a sticker set.
1044func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
1045	v, err := config.values()
1046	if err != nil {
1047		return StickerSet{}, err
1048	}
1049	bot.debugLog(config.method(), v, nil)
1050	res, err := bot.MakeRequest(config.method(), v)
1051	if err != nil {
1052		return StickerSet{}, err
1053	}
1054	stickerSet := StickerSet{}
1055	err = json.Unmarshal(res.Result, &stickerSet)
1056	if err != nil {
1057		return StickerSet{}, err
1058	}
1059	return stickerSet, nil
1060}
1061
1062// GetMyCommands gets the current list of the bot's commands.
1063func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) {
1064	res, err := bot.MakeRequest("getMyCommands", nil)
1065	if err != nil {
1066		return nil, err
1067	}
1068	var commands []BotCommand
1069	err = json.Unmarshal(res.Result, &commands)
1070	if err != nil {
1071		return nil, err
1072	}
1073	return commands, nil
1074}
1075
1076// SetMyCommands changes the list of the bot's commands.
1077func (bot *BotAPI) SetMyCommands(commands []BotCommand) error {
1078	v := url.Values{}
1079	data, err := json.Marshal(commands)
1080	if err != nil {
1081		return err
1082	}
1083	v.Add("commands", string(data))
1084	_, err = bot.MakeRequest("setMyCommands", v)
1085	if err != nil {
1086		return err
1087	}
1088	return nil
1089}
1090
1091// EscapeText takes an input text and escape Telegram markup symbols.
1092// In this way we can send a text without being afraid of having to escape the characters manually.
1093// Note that you don't have to include the formatting style in the input text, or it will be escaped too.
1094// If there is an error, an empty string will be returned.
1095//
1096// parseMode is the text formatting mode (ModeMarkdown, ModeMarkdownV2 or ModeHTML)
1097// text is the input string that will be escaped
1098func EscapeText(parseMode string, text string) string {
1099	var replacer *strings.Replacer
1100
1101	if parseMode == ModeHTML {
1102		replacer = strings.NewReplacer("<", "&lt;", ">", "&gt;", "&", "&amp;")
1103	} else if parseMode == ModeMarkdown {
1104		replacer = strings.NewReplacer("_", "\\_", "*", "\\*", "`", "\\`", "[", "\\[")
1105	} else if parseMode == ModeMarkdownV2 {
1106		replacer = strings.NewReplacer(
1107			"_", "\\_", "*", "\\*", "[", "\\[", "]", "\\]", "(",
1108			"\\(", ")", "\\)", "~", "\\~", "`", "\\`", ">", "\\>",
1109			"#", "\\#", "+", "\\+", "-", "\\-", "=", "\\=", "|",
1110			"\\|", "{", "\\{", "}", "\\}", ".", "\\.", "!", "\\!",
1111		)
1112	} else {
1113		return ""
1114	}
1115
1116	return replacer.Replace(text)
1117}