all repos — telegram-bot-api @ 483f21c48bebae648c4464138be96b1344151035

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