all repos — telegram-bot-api @ 3b0c0317300ecb967352a7ca2aa7a42a8822ab50

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