all repos — telegram-bot-api @ 45369759f12a4d2a226df2deddda8e596dea1573

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	})
 571
 572	return ch
 573}
 574
 575// HandleUpdate parses and returns update received via webhook
 576func (bot *BotAPI) HandleUpdate(r *http.Request) (*Update, error) {
 577	if r.Method != http.MethodPost {
 578		err := errors.New("wrong HTTP method required POST")
 579		return nil, err
 580	}
 581
 582	var update Update
 583	err := json.NewDecoder(r.Body).Decode(&update)
 584	if err != nil {
 585		return nil, err
 586	}
 587
 588	return &update, nil
 589}
 590
 591// AnswerInlineQuery sends a response to an inline query.
 592//
 593// Note that you must respond to an inline query within 30 seconds.
 594func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
 595	v := url.Values{}
 596
 597	v.Add("inline_query_id", config.InlineQueryID)
 598	v.Add("cache_time", strconv.Itoa(config.CacheTime))
 599	v.Add("is_personal", strconv.FormatBool(config.IsPersonal))
 600	v.Add("next_offset", config.NextOffset)
 601	data, err := json.Marshal(config.Results)
 602	if err != nil {
 603		return APIResponse{}, err
 604	}
 605	v.Add("results", string(data))
 606	v.Add("switch_pm_text", config.SwitchPMText)
 607	v.Add("switch_pm_parameter", config.SwitchPMParameter)
 608
 609	bot.debugLog("answerInlineQuery", v, nil)
 610
 611	return bot.MakeRequest("answerInlineQuery", v)
 612}
 613
 614// AnswerCallbackQuery sends a response to an inline query callback.
 615func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) {
 616	v := url.Values{}
 617
 618	v.Add("callback_query_id", config.CallbackQueryID)
 619	if config.Text != "" {
 620		v.Add("text", config.Text)
 621	}
 622	v.Add("show_alert", strconv.FormatBool(config.ShowAlert))
 623	if config.URL != "" {
 624		v.Add("url", config.URL)
 625	}
 626	v.Add("cache_time", strconv.Itoa(config.CacheTime))
 627
 628	bot.debugLog("answerCallbackQuery", v, nil)
 629
 630	return bot.MakeRequest("answerCallbackQuery", v)
 631}
 632
 633// KickChatMember kicks a user from a chat. Note that this only will work
 634// in supergroups, and requires the bot to be an admin. Also note they
 635// will be unable to rejoin until they are unbanned.
 636func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) {
 637	v := url.Values{}
 638
 639	if config.SuperGroupUsername == "" {
 640		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 641	} else {
 642		v.Add("chat_id", config.SuperGroupUsername)
 643	}
 644	v.Add("user_id", strconv.Itoa(config.UserID))
 645
 646	if config.UntilDate != 0 {
 647		v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
 648	}
 649
 650	bot.debugLog("kickChatMember", v, nil)
 651
 652	return bot.MakeRequest("kickChatMember", v)
 653}
 654
 655// LeaveChat makes the bot leave the chat.
 656func (bot *BotAPI) LeaveChat(config ChatConfig) (APIResponse, error) {
 657	v := url.Values{}
 658
 659	if config.SuperGroupUsername == "" {
 660		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 661	} else {
 662		v.Add("chat_id", config.SuperGroupUsername)
 663	}
 664
 665	bot.debugLog("leaveChat", v, nil)
 666
 667	return bot.MakeRequest("leaveChat", v)
 668}
 669
 670// GetChat gets information about a chat.
 671func (bot *BotAPI) GetChat(config ChatConfig) (Chat, error) {
 672	v := url.Values{}
 673
 674	if config.SuperGroupUsername == "" {
 675		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 676	} else {
 677		v.Add("chat_id", config.SuperGroupUsername)
 678	}
 679
 680	resp, err := bot.MakeRequest("getChat", v)
 681	if err != nil {
 682		return Chat{}, err
 683	}
 684
 685	var chat Chat
 686	err = json.Unmarshal(resp.Result, &chat)
 687
 688	bot.debugLog("getChat", v, chat)
 689
 690	return chat, err
 691}
 692
 693// GetChatAdministrators gets a list of administrators in the chat.
 694//
 695// If none have been appointed, only the creator will be returned.
 696// Bots are not shown, even if they are an administrator.
 697func (bot *BotAPI) GetChatAdministrators(config ChatConfig) ([]ChatMember, error) {
 698	v := url.Values{}
 699
 700	if config.SuperGroupUsername == "" {
 701		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 702	} else {
 703		v.Add("chat_id", config.SuperGroupUsername)
 704	}
 705
 706	resp, err := bot.MakeRequest("getChatAdministrators", v)
 707	if err != nil {
 708		return []ChatMember{}, err
 709	}
 710
 711	var members []ChatMember
 712	err = json.Unmarshal(resp.Result, &members)
 713
 714	bot.debugLog("getChatAdministrators", v, members)
 715
 716	return members, err
 717}
 718
 719// GetChatMembersCount gets the number of users in a chat.
 720func (bot *BotAPI) GetChatMembersCount(config ChatConfig) (int, error) {
 721	v := url.Values{}
 722
 723	if config.SuperGroupUsername == "" {
 724		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 725	} else {
 726		v.Add("chat_id", config.SuperGroupUsername)
 727	}
 728
 729	resp, err := bot.MakeRequest("getChatMembersCount", v)
 730	if err != nil {
 731		return -1, err
 732	}
 733
 734	var count int
 735	err = json.Unmarshal(resp.Result, &count)
 736
 737	bot.debugLog("getChatMembersCount", v, count)
 738
 739	return count, err
 740}
 741
 742// GetChatMember gets a specific chat member.
 743func (bot *BotAPI) GetChatMember(config ChatConfigWithUser) (ChatMember, error) {
 744	v := url.Values{}
 745
 746	if config.SuperGroupUsername == "" {
 747		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 748	} else {
 749		v.Add("chat_id", config.SuperGroupUsername)
 750	}
 751	v.Add("user_id", strconv.Itoa(config.UserID))
 752
 753	resp, err := bot.MakeRequest("getChatMember", v)
 754	if err != nil {
 755		return ChatMember{}, err
 756	}
 757
 758	var member ChatMember
 759	err = json.Unmarshal(resp.Result, &member)
 760
 761	bot.debugLog("getChatMember", v, member)
 762
 763	return member, err
 764}
 765
 766// UnbanChatMember unbans a user from a chat. Note that this only will work
 767// in supergroups and channels, and requires the bot to be an admin.
 768func (bot *BotAPI) UnbanChatMember(config ChatMemberConfig) (APIResponse, error) {
 769	v := url.Values{}
 770
 771	if config.SuperGroupUsername != "" {
 772		v.Add("chat_id", config.SuperGroupUsername)
 773	} else if config.ChannelUsername != "" {
 774		v.Add("chat_id", config.ChannelUsername)
 775	} else {
 776		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 777	}
 778	v.Add("user_id", strconv.Itoa(config.UserID))
 779
 780	bot.debugLog("unbanChatMember", v, nil)
 781
 782	return bot.MakeRequest("unbanChatMember", v)
 783}
 784
 785// RestrictChatMember to restrict a user in a supergroup. The bot must be an
 786// administrator in the supergroup for this to work and must have the
 787// appropriate admin rights. Pass True for all boolean parameters to lift
 788// restrictions from a user. Returns True on success.
 789func (bot *BotAPI) RestrictChatMember(config RestrictChatMemberConfig) (APIResponse, error) {
 790	v := url.Values{}
 791
 792	if config.SuperGroupUsername != "" {
 793		v.Add("chat_id", config.SuperGroupUsername)
 794	} else if config.ChannelUsername != "" {
 795		v.Add("chat_id", config.ChannelUsername)
 796	} else {
 797		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 798	}
 799	v.Add("user_id", strconv.Itoa(config.UserID))
 800
 801	if config.CanSendMessages != nil {
 802		v.Add("can_send_messages", strconv.FormatBool(*config.CanSendMessages))
 803	}
 804	if config.CanSendMediaMessages != nil {
 805		v.Add("can_send_media_messages", strconv.FormatBool(*config.CanSendMediaMessages))
 806	}
 807	if config.CanSendOtherMessages != nil {
 808		v.Add("can_send_other_messages", strconv.FormatBool(*config.CanSendOtherMessages))
 809	}
 810	if config.CanAddWebPagePreviews != nil {
 811		v.Add("can_add_web_page_previews", strconv.FormatBool(*config.CanAddWebPagePreviews))
 812	}
 813	if config.UntilDate != 0 {
 814		v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
 815	}
 816
 817	bot.debugLog("restrictChatMember", v, nil)
 818
 819	return bot.MakeRequest("restrictChatMember", v)
 820}
 821
 822// PromoteChatMember add admin rights to user
 823func (bot *BotAPI) PromoteChatMember(config PromoteChatMemberConfig) (APIResponse, error) {
 824	v := url.Values{}
 825
 826	if config.SuperGroupUsername != "" {
 827		v.Add("chat_id", config.SuperGroupUsername)
 828	} else if config.ChannelUsername != "" {
 829		v.Add("chat_id", config.ChannelUsername)
 830	} else {
 831		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 832	}
 833	v.Add("user_id", strconv.Itoa(config.UserID))
 834
 835	if config.CanChangeInfo != nil {
 836		v.Add("can_change_info", strconv.FormatBool(*config.CanChangeInfo))
 837	}
 838	if config.CanPostMessages != nil {
 839		v.Add("can_post_messages", strconv.FormatBool(*config.CanPostMessages))
 840	}
 841	if config.CanEditMessages != nil {
 842		v.Add("can_edit_messages", strconv.FormatBool(*config.CanEditMessages))
 843	}
 844	if config.CanDeleteMessages != nil {
 845		v.Add("can_delete_messages", strconv.FormatBool(*config.CanDeleteMessages))
 846	}
 847	if config.CanInviteUsers != nil {
 848		v.Add("can_invite_users", strconv.FormatBool(*config.CanInviteUsers))
 849	}
 850	if config.CanRestrictMembers != nil {
 851		v.Add("can_restrict_members", strconv.FormatBool(*config.CanRestrictMembers))
 852	}
 853	if config.CanPinMessages != nil {
 854		v.Add("can_pin_messages", strconv.FormatBool(*config.CanPinMessages))
 855	}
 856	if config.CanPromoteMembers != nil {
 857		v.Add("can_promote_members", strconv.FormatBool(*config.CanPromoteMembers))
 858	}
 859
 860	bot.debugLog("promoteChatMember", v, nil)
 861
 862	return bot.MakeRequest("promoteChatMember", v)
 863}
 864
 865// GetGameHighScores allows you to get the high scores for a game.
 866func (bot *BotAPI) GetGameHighScores(config GetGameHighScoresConfig) ([]GameHighScore, error) {
 867	v, _ := config.values()
 868
 869	resp, err := bot.MakeRequest(config.method(), v)
 870	if err != nil {
 871		return []GameHighScore{}, err
 872	}
 873
 874	var highScores []GameHighScore
 875	err = json.Unmarshal(resp.Result, &highScores)
 876
 877	return highScores, err
 878}
 879
 880// AnswerShippingQuery allows you to reply to Update with shipping_query parameter.
 881func (bot *BotAPI) AnswerShippingQuery(config ShippingConfig) (APIResponse, error) {
 882	v := url.Values{}
 883
 884	v.Add("shipping_query_id", config.ShippingQueryID)
 885	v.Add("ok", strconv.FormatBool(config.OK))
 886	if config.OK == true {
 887		data, err := json.Marshal(config.ShippingOptions)
 888		if err != nil {
 889			return APIResponse{}, err
 890		}
 891		v.Add("shipping_options", string(data))
 892	} else {
 893		v.Add("error_message", config.ErrorMessage)
 894	}
 895
 896	bot.debugLog("answerShippingQuery", v, nil)
 897
 898	return bot.MakeRequest("answerShippingQuery", v)
 899}
 900
 901// AnswerPreCheckoutQuery allows you to reply to Update with pre_checkout_query.
 902func (bot *BotAPI) AnswerPreCheckoutQuery(config PreCheckoutConfig) (APIResponse, error) {
 903	v := url.Values{}
 904
 905	v.Add("pre_checkout_query_id", config.PreCheckoutQueryID)
 906	v.Add("ok", strconv.FormatBool(config.OK))
 907	if config.OK != true {
 908		v.Add("error_message", config.ErrorMessage)
 909	}
 910
 911	bot.debugLog("answerPreCheckoutQuery", v, nil)
 912
 913	return bot.MakeRequest("answerPreCheckoutQuery", v)
 914}
 915
 916// DeleteMessage deletes a message in a chat
 917func (bot *BotAPI) DeleteMessage(config DeleteMessageConfig) (APIResponse, error) {
 918	v, err := config.values()
 919	if err != nil {
 920		return APIResponse{}, err
 921	}
 922
 923	bot.debugLog(config.method(), v, nil)
 924
 925	return bot.MakeRequest(config.method(), v)
 926}
 927
 928// GetInviteLink get InviteLink for a chat
 929func (bot *BotAPI) GetInviteLink(config ChatConfig) (string, error) {
 930	v := url.Values{}
 931
 932	if config.SuperGroupUsername == "" {
 933		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
 934	} else {
 935		v.Add("chat_id", config.SuperGroupUsername)
 936	}
 937
 938	resp, err := bot.MakeRequest("exportChatInviteLink", v)
 939	if err != nil {
 940		return "", err
 941	}
 942
 943	var inviteLink string
 944	err = json.Unmarshal(resp.Result, &inviteLink)
 945
 946	return inviteLink, err
 947}
 948
 949// PinChatMessage pin message in supergroup
 950func (bot *BotAPI) PinChatMessage(config PinChatMessageConfig) (APIResponse, error) {
 951	v, err := config.values()
 952	if err != nil {
 953		return APIResponse{}, err
 954	}
 955
 956	bot.debugLog(config.method(), v, nil)
 957
 958	return bot.MakeRequest(config.method(), v)
 959}
 960
 961// UnpinChatMessage unpin message in supergroup
 962func (bot *BotAPI) UnpinChatMessage(config UnpinChatMessageConfig) (APIResponse, error) {
 963	v, err := config.values()
 964	if err != nil {
 965		return APIResponse{}, err
 966	}
 967
 968	bot.debugLog(config.method(), v, nil)
 969
 970	return bot.MakeRequest(config.method(), v)
 971}
 972
 973// SetChatTitle change title of chat.
 974func (bot *BotAPI) SetChatTitle(config SetChatTitleConfig) (APIResponse, error) {
 975	v, err := config.values()
 976	if err != nil {
 977		return APIResponse{}, err
 978	}
 979
 980	bot.debugLog(config.method(), v, nil)
 981
 982	return bot.MakeRequest(config.method(), v)
 983}
 984
 985// SetChatDescription change description of chat.
 986func (bot *BotAPI) SetChatDescription(config SetChatDescriptionConfig) (APIResponse, error) {
 987	v, err := config.values()
 988	if err != nil {
 989		return APIResponse{}, err
 990	}
 991
 992	bot.debugLog(config.method(), v, nil)
 993
 994	return bot.MakeRequest(config.method(), v)
 995}
 996
 997// SetChatPhoto change photo of chat.
 998func (bot *BotAPI) SetChatPhoto(config SetChatPhotoConfig) (APIResponse, error) {
 999	params, err := config.params()
1000	if err != nil {
1001		return APIResponse{}, err
1002	}
1003
1004	file := config.getFile()
1005
1006	return bot.UploadFile(config.method(), params, config.name(), file)
1007}
1008
1009// DeleteChatPhoto delete photo of chat.
1010func (bot *BotAPI) DeleteChatPhoto(config DeleteChatPhotoConfig) (APIResponse, error) {
1011	v, err := config.values()
1012	if err != nil {
1013		return APIResponse{}, err
1014	}
1015
1016	bot.debugLog(config.method(), v, nil)
1017
1018	return bot.MakeRequest(config.method(), v)
1019}
1020
1021// GetStickerSet get a sticker set.
1022func (bot *BotAPI) GetStickerSet(config GetStickerSetConfig) (StickerSet, error) {
1023	v, err := config.values()
1024	if err != nil {
1025		return StickerSet{}, err
1026	}
1027	bot.debugLog(config.method(), v, nil)
1028	res, err := bot.MakeRequest(config.method(), v)
1029	if err != nil {
1030		return StickerSet{}, err
1031	}
1032	stickerSet := StickerSet{}
1033	err = json.Unmarshal(res.Result, &stickerSet)
1034	if err != nil {
1035		return StickerSet{}, err
1036	}
1037	return stickerSet, nil
1038}
1039
1040// GetMyCommands gets the current list of the bot's commands.
1041func (bot *BotAPI) GetMyCommands() ([]BotCommand, error) {
1042	res, err := bot.MakeRequest("getMyCommands", nil)
1043	if err != nil {
1044		return nil, err
1045	}
1046	var commands []BotCommand
1047	err = json.Unmarshal(res.Result, &commands)
1048	if err != nil {
1049		return nil, err
1050	}
1051	return commands, nil
1052}
1053
1054// SetMyCommands changes the list of the bot's commands.
1055func (bot *BotAPI) SetMyCommands(commands []BotCommand) error {
1056	v := url.Values{}
1057	data, err := json.Marshal(commands)
1058	if err != nil {
1059		return err
1060	}
1061	v.Add("commands", string(data))
1062	_, err = bot.MakeRequest("setMyCommands", v)
1063	if err != nil {
1064		return err
1065	}
1066	return nil
1067}