all repos — telegram-bot-api @ 1bbe8c7bd299305b818da90bd2fd6ce0be5f5c07

Golang bindings for the Telegram Bot API

methods.go (view raw)

   1package tgbotapi
   2
   3import (
   4	"bytes"
   5	"encoding/json"
   6	"errors"
   7	"fmt"
   8	"github.com/technoweenie/multipartstreamer"
   9	"io"
  10	"io/ioutil"
  11	"log"
  12	"net/http"
  13	"net/url"
  14	"os"
  15	"strconv"
  16)
  17
  18// Telegram constants
  19const (
  20	// APIEndpoint is the endpoint for all API methods, with formatting for Sprintf
  21	APIEndpoint = "https://api.telegram.org/bot%s/%s"
  22)
  23
  24// Constant values for ChatActions
  25const (
  26	ChatTyping         = "typing"
  27	ChatUploadPhoto    = "upload_photo"
  28	ChatRecordVideo    = "record_video"
  29	ChatUploadVideo    = "upload_video"
  30	ChatRecordAudio    = "record_audio"
  31	ChatUploadAudio    = "upload_audio"
  32	ChatUploadDocument = "upload_document"
  33	ChatFindLocation   = "find_location"
  34)
  35
  36// API errors
  37const (
  38	// APIForbidden happens when a token is bad
  39	APIForbidden = "forbidden"
  40)
  41
  42// Constant values for ParseMode in MessageConfig
  43const (
  44	ModeMarkdown = "Markdown"
  45)
  46
  47// MessageConfig contains information about a SendMessage request.
  48type MessageConfig struct {
  49	ChatID                int
  50	Text                  string
  51	ParseMode             string
  52	DisableWebPagePreview bool
  53	ReplyToMessageID      int
  54	ReplyMarkup           interface{}
  55}
  56
  57// ForwardConfig contains information about a ForwardMessage request.
  58type ForwardConfig struct {
  59	ChatID     int
  60	FromChatID int
  61	MessageID  int
  62}
  63
  64// PhotoConfig contains information about a SendPhoto request.
  65type PhotoConfig struct {
  66	ChatID           int
  67	Caption          string
  68	ReplyToMessageID int
  69	ReplyMarkup      interface{}
  70	UseExistingPhoto bool
  71	FilePath         string
  72	File             interface{}
  73	FileID           string
  74}
  75
  76// AudioConfig contains information about a SendAudio request.
  77type AudioConfig struct {
  78	ChatID           int
  79	Duration         int
  80	Performer        string
  81	Title            string
  82	ReplyToMessageID int
  83	ReplyMarkup      interface{}
  84	UseExistingAudio bool
  85	FilePath         string
  86	File             interface{}
  87	FileID           string
  88}
  89
  90// DocumentConfig contains information about a SendDocument request.
  91type DocumentConfig struct {
  92	ChatID              int
  93	ReplyToMessageID    int
  94	ReplyMarkup         interface{}
  95	UseExistingDocument bool
  96	FilePath            string
  97	File                interface{}
  98	FileID              string
  99}
 100
 101// StickerConfig contains information about a SendSticker request.
 102type StickerConfig struct {
 103	ChatID             int
 104	ReplyToMessageID   int
 105	ReplyMarkup        interface{}
 106	UseExistingSticker bool
 107	FilePath           string
 108	File               interface{}
 109	FileID             string
 110}
 111
 112// VideoConfig contains information about a SendVideo request.
 113type VideoConfig struct {
 114	ChatID           int
 115	Duration         int
 116	Caption          string
 117	ReplyToMessageID int
 118	ReplyMarkup      interface{}
 119	UseExistingVideo bool
 120	FilePath         string
 121	File             interface{}
 122	FileID           string
 123}
 124
 125// VoiceConfig contains information about a SendVoice request.
 126type VoiceConfig struct {
 127	ChatID           int
 128	Duration         int
 129	ReplyToMessageID int
 130	ReplyMarkup      interface{}
 131	UseExistingVoice bool
 132	FilePath         string
 133	File             interface{}
 134	FileID           string
 135}
 136
 137// LocationConfig contains information about a SendLocation request.
 138type LocationConfig struct {
 139	ChatID           int
 140	Latitude         float64
 141	Longitude        float64
 142	ReplyToMessageID int
 143	ReplyMarkup      interface{}
 144}
 145
 146// ChatActionConfig contains information about a SendChatAction request.
 147type ChatActionConfig struct {
 148	ChatID int
 149	Action string
 150}
 151
 152// UserProfilePhotosConfig contains information about a GetUserProfilePhotos request.
 153type UserProfilePhotosConfig struct {
 154	UserID int
 155	Offset int
 156	Limit  int
 157}
 158
 159// FileConfig has information about a file hosted on Telegram
 160type FileConfig struct {
 161	FileID string
 162}
 163
 164// UpdateConfig contains information about a GetUpdates request.
 165type UpdateConfig struct {
 166	Offset  int
 167	Limit   int
 168	Timeout int
 169}
 170
 171// WebhookConfig contains information about a SetWebhook request.
 172type WebhookConfig struct {
 173	Clear       bool
 174	URL         *url.URL
 175	Certificate interface{}
 176}
 177
 178// FileBytes contains information about a set of bytes to upload as a File.
 179type FileBytes struct {
 180	Name  string
 181	Bytes []byte
 182}
 183
 184// FileReader contains information about a reader to upload as a File.
 185// If Size is -1, it will read the entire Reader into memory to calculate a Size.
 186type FileReader struct {
 187	Name   string
 188	Reader io.Reader
 189	Size   int64
 190}
 191
 192// MakeRequest makes a request to a specific endpoint with our token.
 193// All requests are POSTs because Telegram doesn't care, and it's easier.
 194func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
 195	resp, err := bot.Client.PostForm(fmt.Sprintf(APIEndpoint, bot.Token, endpoint), params)
 196	if err != nil {
 197		return APIResponse{}, err
 198	}
 199	defer resp.Body.Close()
 200
 201	if resp.StatusCode == http.StatusForbidden {
 202		return APIResponse{}, errors.New(APIForbidden)
 203	}
 204
 205	bytes, err := ioutil.ReadAll(resp.Body)
 206	if err != nil {
 207		return APIResponse{}, err
 208	}
 209
 210	if bot.Debug {
 211		log.Println(endpoint, string(bytes))
 212	}
 213
 214	var apiResp APIResponse
 215	json.Unmarshal(bytes, &apiResp)
 216
 217	if !apiResp.Ok {
 218		return APIResponse{}, errors.New(apiResp.Description)
 219	}
 220
 221	return apiResp, nil
 222}
 223
 224// UploadFile makes a request to the API with a file.
 225//
 226// Requires the parameter to hold the file not be in the params.
 227// File should be a string to a file path, a FileBytes struct, or a FileReader struct.
 228func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
 229	ms := multipartstreamer.New()
 230	ms.WriteFields(params)
 231
 232	switch f := file.(type) {
 233	case string:
 234		fileHandle, err := os.Open(f)
 235		if err != nil {
 236			return APIResponse{}, err
 237		}
 238		defer fileHandle.Close()
 239
 240		fi, err := os.Stat(f)
 241		if err != nil {
 242			return APIResponse{}, err
 243		}
 244
 245		ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
 246	case FileBytes:
 247		buf := bytes.NewBuffer(f.Bytes)
 248		ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
 249	case FileReader:
 250		if f.Size == -1 {
 251			data, err := ioutil.ReadAll(f.Reader)
 252			if err != nil {
 253				return APIResponse{}, err
 254			}
 255			buf := bytes.NewBuffer(data)
 256
 257			ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
 258
 259			break
 260		}
 261
 262		ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
 263	default:
 264		return APIResponse{}, errors.New("bad file type")
 265	}
 266
 267	req, err := http.NewRequest("POST", fmt.Sprintf(APIEndpoint, bot.Token, endpoint), nil)
 268	ms.SetupRequest(req)
 269	if err != nil {
 270		return APIResponse{}, err
 271	}
 272
 273	res, err := bot.Client.Do(req)
 274	if err != nil {
 275		return APIResponse{}, err
 276	}
 277	defer res.Body.Close()
 278
 279	bytes, err := ioutil.ReadAll(res.Body)
 280	if err != nil {
 281		return APIResponse{}, err
 282	}
 283
 284	if bot.Debug {
 285		log.Println(string(bytes[:]))
 286	}
 287
 288	var apiResp APIResponse
 289	json.Unmarshal(bytes, &apiResp)
 290
 291	return apiResp, nil
 292}
 293
 294// GetMe fetches the currently authenticated bot.
 295//
 296// There are no parameters for this method.
 297func (bot *BotAPI) GetMe() (User, error) {
 298	resp, err := bot.MakeRequest("getMe", nil)
 299	if err != nil {
 300		return User{}, err
 301	}
 302
 303	var user User
 304	json.Unmarshal(resp.Result, &user)
 305
 306	if bot.Debug {
 307		log.Printf("getMe: %+v\n", user)
 308	}
 309
 310	return user, nil
 311}
 312
 313// SendMessage sends a Message to a chat.
 314//
 315// Requires ChatID and Text.
 316// DisableWebPagePreview, ReplyToMessageID, and ReplyMarkup are optional.
 317func (bot *BotAPI) SendMessage(config MessageConfig) (Message, error) {
 318	v := url.Values{}
 319	v.Add("chat_id", strconv.Itoa(config.ChatID))
 320	v.Add("text", config.Text)
 321	v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
 322	if config.ParseMode != "" {
 323		v.Add("parse_mode", config.ParseMode)
 324	}
 325	if config.ReplyToMessageID != 0 {
 326		v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 327	}
 328	if config.ReplyMarkup != nil {
 329		data, err := json.Marshal(config.ReplyMarkup)
 330		if err != nil {
 331			return Message{}, err
 332		}
 333
 334		v.Add("reply_markup", string(data))
 335	}
 336
 337	resp, err := bot.MakeRequest("SendMessage", v)
 338	if err != nil {
 339		return Message{}, err
 340	}
 341
 342	var message Message
 343	json.Unmarshal(resp.Result, &message)
 344
 345	if bot.Debug {
 346		log.Printf("SendMessage req : %+v\n", v)
 347		log.Printf("SendMessage resp: %+v\n", message)
 348	}
 349
 350	return message, nil
 351}
 352
 353// ForwardMessage forwards a message from one chat to another.
 354//
 355// Requires ChatID (destination), FromChatID (source), and MessageID.
 356func (bot *BotAPI) ForwardMessage(config ForwardConfig) (Message, error) {
 357	v := url.Values{}
 358	v.Add("chat_id", strconv.Itoa(config.ChatID))
 359	v.Add("from_chat_id", strconv.Itoa(config.FromChatID))
 360	v.Add("message_id", strconv.Itoa(config.MessageID))
 361
 362	resp, err := bot.MakeRequest("forwardMessage", v)
 363	if err != nil {
 364		return Message{}, err
 365	}
 366
 367	var message Message
 368	json.Unmarshal(resp.Result, &message)
 369
 370	if bot.Debug {
 371		log.Printf("forwardMessage req : %+v\n", v)
 372		log.Printf("forwardMessage resp: %+v\n", message)
 373	}
 374
 375	return message, nil
 376}
 377
 378// SendPhoto sends or uploads a photo to a chat.
 379//
 380// Requires ChatID and FileID OR File.
 381// Caption, ReplyToMessageID, and ReplyMarkup are optional.
 382// File should be either a string, FileBytes, or FileReader.
 383func (bot *BotAPI) SendPhoto(config PhotoConfig) (Message, error) {
 384	if config.UseExistingPhoto {
 385		v := url.Values{}
 386		v.Add("chat_id", strconv.Itoa(config.ChatID))
 387		v.Add("photo", config.FileID)
 388		if config.Caption != "" {
 389			v.Add("caption", config.Caption)
 390		}
 391		if config.ReplyToMessageID != 0 {
 392			v.Add("reply_to_message_id", strconv.Itoa(config.ChatID))
 393		}
 394		if config.ReplyMarkup != nil {
 395			data, err := json.Marshal(config.ReplyMarkup)
 396			if err != nil {
 397				return Message{}, err
 398			}
 399
 400			v.Add("reply_markup", string(data))
 401		}
 402
 403		resp, err := bot.MakeRequest("SendPhoto", v)
 404		if err != nil {
 405			return Message{}, err
 406		}
 407
 408		var message Message
 409		json.Unmarshal(resp.Result, &message)
 410
 411		if bot.Debug {
 412			log.Printf("SendPhoto req : %+v\n", v)
 413			log.Printf("SendPhoto resp: %+v\n", message)
 414		}
 415
 416		return message, nil
 417	}
 418
 419	params := make(map[string]string)
 420	params["chat_id"] = strconv.Itoa(config.ChatID)
 421	if config.Caption != "" {
 422		params["caption"] = config.Caption
 423	}
 424	if config.ReplyToMessageID != 0 {
 425		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 426	}
 427	if config.ReplyMarkup != nil {
 428		data, err := json.Marshal(config.ReplyMarkup)
 429		if err != nil {
 430			return Message{}, err
 431		}
 432
 433		params["reply_markup"] = string(data)
 434	}
 435
 436	var file interface{}
 437	if config.FilePath == "" {
 438		file = config.File
 439	} else {
 440		file = config.FilePath
 441	}
 442
 443	resp, err := bot.UploadFile("SendPhoto", params, "photo", file)
 444	if err != nil {
 445		return Message{}, err
 446	}
 447
 448	var message Message
 449	json.Unmarshal(resp.Result, &message)
 450
 451	if bot.Debug {
 452		log.Printf("SendPhoto resp: %+v\n", message)
 453	}
 454
 455	return message, nil
 456}
 457
 458// SendAudio sends or uploads an audio clip to a chat.
 459// If using a file, the file must be in the .mp3 format.
 460//
 461// When the fields title and performer are both empty and
 462// the mime-type of the file to be sent is not audio/mpeg,
 463// the file must be an .ogg file encoded with OPUS.
 464// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
 465//
 466// Requires ChatID and FileID OR File.
 467// ReplyToMessageID and ReplyMarkup are optional.
 468// File should be either a string, FileBytes, or FileReader.
 469func (bot *BotAPI) SendAudio(config AudioConfig) (Message, error) {
 470	if config.UseExistingAudio {
 471		v := url.Values{}
 472		v.Add("chat_id", strconv.Itoa(config.ChatID))
 473		v.Add("audio", config.FileID)
 474		if config.ReplyToMessageID != 0 {
 475			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 476		}
 477		if config.Duration != 0 {
 478			v.Add("duration", strconv.Itoa(config.Duration))
 479		}
 480		if config.ReplyMarkup != nil {
 481			data, err := json.Marshal(config.ReplyMarkup)
 482			if err != nil {
 483				return Message{}, err
 484			}
 485
 486			v.Add("reply_markup", string(data))
 487		}
 488		if config.Performer != "" {
 489			v.Add("performer", config.Performer)
 490		}
 491		if config.Title != "" {
 492			v.Add("title", config.Title)
 493		}
 494
 495		resp, err := bot.MakeRequest("sendAudio", v)
 496		if err != nil {
 497			return Message{}, err
 498		}
 499
 500		var message Message
 501		json.Unmarshal(resp.Result, &message)
 502
 503		if bot.Debug {
 504			log.Printf("sendAudio req : %+v\n", v)
 505			log.Printf("sendAudio resp: %+v\n", message)
 506		}
 507
 508		return message, nil
 509	}
 510
 511	params := make(map[string]string)
 512
 513	params["chat_id"] = strconv.Itoa(config.ChatID)
 514	if config.ReplyToMessageID != 0 {
 515		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 516	}
 517	if config.Duration != 0 {
 518		params["duration"] = strconv.Itoa(config.Duration)
 519	}
 520	if config.ReplyMarkup != nil {
 521		data, err := json.Marshal(config.ReplyMarkup)
 522		if err != nil {
 523			return Message{}, err
 524		}
 525
 526		params["reply_markup"] = string(data)
 527	}
 528	if config.Performer != "" {
 529		params["performer"] = config.Performer
 530	}
 531	if config.Title != "" {
 532		params["title"] = config.Title
 533	}
 534
 535	var file interface{}
 536	if config.FilePath == "" {
 537		file = config.File
 538	} else {
 539		file = config.FilePath
 540	}
 541
 542	resp, err := bot.UploadFile("sendAudio", params, "audio", file)
 543	if err != nil {
 544		return Message{}, err
 545	}
 546
 547	var message Message
 548	json.Unmarshal(resp.Result, &message)
 549
 550	if bot.Debug {
 551		log.Printf("sendAudio resp: %+v\n", message)
 552	}
 553
 554	return message, nil
 555}
 556
 557// SendDocument sends or uploads a document to a chat.
 558//
 559// Requires ChatID and FileID OR File.
 560// ReplyToMessageID and ReplyMarkup are optional.
 561// File should be either a string, FileBytes, or FileReader.
 562func (bot *BotAPI) SendDocument(config DocumentConfig) (Message, error) {
 563	if config.UseExistingDocument {
 564		v := url.Values{}
 565		v.Add("chat_id", strconv.Itoa(config.ChatID))
 566		v.Add("document", config.FileID)
 567		if config.ReplyToMessageID != 0 {
 568			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 569		}
 570		if config.ReplyMarkup != nil {
 571			data, err := json.Marshal(config.ReplyMarkup)
 572			if err != nil {
 573				return Message{}, err
 574			}
 575
 576			v.Add("reply_markup", string(data))
 577		}
 578
 579		resp, err := bot.MakeRequest("sendDocument", v)
 580		if err != nil {
 581			return Message{}, err
 582		}
 583
 584		var message Message
 585		json.Unmarshal(resp.Result, &message)
 586
 587		if bot.Debug {
 588			log.Printf("sendDocument req : %+v\n", v)
 589			log.Printf("sendDocument resp: %+v\n", message)
 590		}
 591
 592		return message, nil
 593	}
 594
 595	params := make(map[string]string)
 596
 597	params["chat_id"] = strconv.Itoa(config.ChatID)
 598	if config.ReplyToMessageID != 0 {
 599		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 600	}
 601	if config.ReplyMarkup != nil {
 602		data, err := json.Marshal(config.ReplyMarkup)
 603		if err != nil {
 604			return Message{}, err
 605		}
 606
 607		params["reply_markup"] = string(data)
 608	}
 609
 610	var file interface{}
 611	if config.FilePath == "" {
 612		file = config.File
 613	} else {
 614		file = config.FilePath
 615	}
 616
 617	resp, err := bot.UploadFile("sendDocument", params, "document", file)
 618	if err != nil {
 619		return Message{}, err
 620	}
 621
 622	var message Message
 623	json.Unmarshal(resp.Result, &message)
 624
 625	if bot.Debug {
 626		log.Printf("sendDocument resp: %+v\n", message)
 627	}
 628
 629	return message, nil
 630}
 631
 632// SendVoice sends or uploads a playable voice to a chat.
 633// If using a file, the file must be encoded as an .ogg with OPUS.
 634// You may use the tgutils.EncodeAudio func to assist you with this, if needed.
 635//
 636// Requires ChatID and FileID OR File.
 637// ReplyToMessageID and ReplyMarkup are optional.
 638// File should be either a string, FileBytes, or FileReader.
 639func (bot *BotAPI) SendVoice(config VoiceConfig) (Message, error) {
 640	if config.UseExistingVoice {
 641		v := url.Values{}
 642		v.Add("chat_id", strconv.Itoa(config.ChatID))
 643		v.Add("voice", config.FileID)
 644		if config.ReplyToMessageID != 0 {
 645			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 646		}
 647		if config.Duration != 0 {
 648			v.Add("duration", strconv.Itoa(config.Duration))
 649		}
 650		if config.ReplyMarkup != nil {
 651			data, err := json.Marshal(config.ReplyMarkup)
 652			if err != nil {
 653				return Message{}, err
 654			}
 655
 656			v.Add("reply_markup", string(data))
 657		}
 658
 659		resp, err := bot.MakeRequest("sendVoice", v)
 660		if err != nil {
 661			return Message{}, err
 662		}
 663
 664		var message Message
 665		json.Unmarshal(resp.Result, &message)
 666
 667		if bot.Debug {
 668			log.Printf("SendVoice req : %+v\n", v)
 669			log.Printf("SendVoice resp: %+v\n", message)
 670		}
 671
 672		return message, nil
 673	}
 674
 675	params := make(map[string]string)
 676
 677	params["chat_id"] = strconv.Itoa(config.ChatID)
 678	if config.ReplyToMessageID != 0 {
 679		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 680	}
 681	if config.Duration != 0 {
 682		params["duration"] = strconv.Itoa(config.Duration)
 683	}
 684	if config.ReplyMarkup != nil {
 685		data, err := json.Marshal(config.ReplyMarkup)
 686		if err != nil {
 687			return Message{}, err
 688		}
 689
 690		params["reply_markup"] = string(data)
 691	}
 692
 693	var file interface{}
 694	if config.FilePath == "" {
 695		file = config.File
 696	} else {
 697		file = config.FilePath
 698	}
 699
 700	resp, err := bot.UploadFile("SendVoice", params, "voice", file)
 701	if err != nil {
 702		return Message{}, err
 703	}
 704
 705	var message Message
 706	json.Unmarshal(resp.Result, &message)
 707
 708	if bot.Debug {
 709		log.Printf("SendVoice resp: %+v\n", message)
 710	}
 711
 712	return message, nil
 713}
 714
 715// SendSticker sends or uploads a sticker to a chat.
 716//
 717// Requires ChatID and FileID OR File.
 718// ReplyToMessageID and ReplyMarkup are optional.
 719// File should be either a string, FileBytes, or FileReader.
 720func (bot *BotAPI) SendSticker(config StickerConfig) (Message, error) {
 721	if config.UseExistingSticker {
 722		v := url.Values{}
 723		v.Add("chat_id", strconv.Itoa(config.ChatID))
 724		v.Add("sticker", config.FileID)
 725		if config.ReplyToMessageID != 0 {
 726			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 727		}
 728		if config.ReplyMarkup != nil {
 729			data, err := json.Marshal(config.ReplyMarkup)
 730			if err != nil {
 731				return Message{}, err
 732			}
 733
 734			v.Add("reply_markup", string(data))
 735		}
 736
 737		resp, err := bot.MakeRequest("sendSticker", v)
 738		if err != nil {
 739			return Message{}, err
 740		}
 741
 742		var message Message
 743		json.Unmarshal(resp.Result, &message)
 744
 745		if bot.Debug {
 746			log.Printf("sendSticker req : %+v\n", v)
 747			log.Printf("sendSticker resp: %+v\n", message)
 748		}
 749
 750		return message, nil
 751	}
 752
 753	params := make(map[string]string)
 754
 755	params["chat_id"] = strconv.Itoa(config.ChatID)
 756	if config.ReplyToMessageID != 0 {
 757		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 758	}
 759	if config.ReplyMarkup != nil {
 760		data, err := json.Marshal(config.ReplyMarkup)
 761		if err != nil {
 762			return Message{}, err
 763		}
 764
 765		params["reply_markup"] = string(data)
 766	}
 767
 768	var file interface{}
 769	if config.FilePath == "" {
 770		file = config.File
 771	} else {
 772		file = config.FilePath
 773	}
 774
 775	resp, err := bot.UploadFile("sendSticker", params, "sticker", file)
 776	if err != nil {
 777		return Message{}, err
 778	}
 779
 780	var message Message
 781	json.Unmarshal(resp.Result, &message)
 782
 783	if bot.Debug {
 784		log.Printf("sendSticker resp: %+v\n", message)
 785	}
 786
 787	return message, nil
 788}
 789
 790// SendVideo sends or uploads a video to a chat.
 791//
 792// Requires ChatID and FileID OR File.
 793// ReplyToMessageID and ReplyMarkup are optional.
 794// File should be either a string, FileBytes, or FileReader.
 795func (bot *BotAPI) SendVideo(config VideoConfig) (Message, error) {
 796	if config.UseExistingVideo {
 797		v := url.Values{}
 798		v.Add("chat_id", strconv.Itoa(config.ChatID))
 799		v.Add("video", config.FileID)
 800		if config.ReplyToMessageID != 0 {
 801			v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 802		}
 803		if config.Duration != 0 {
 804			v.Add("duration", strconv.Itoa(config.Duration))
 805		}
 806		if config.Caption != "" {
 807			v.Add("caption", config.Caption)
 808		}
 809		if config.ReplyMarkup != nil {
 810			data, err := json.Marshal(config.ReplyMarkup)
 811			if err != nil {
 812				return Message{}, err
 813			}
 814
 815			v.Add("reply_markup", string(data))
 816		}
 817
 818		resp, err := bot.MakeRequest("sendVideo", v)
 819		if err != nil {
 820			return Message{}, err
 821		}
 822
 823		var message Message
 824		json.Unmarshal(resp.Result, &message)
 825
 826		if bot.Debug {
 827			log.Printf("sendVideo req : %+v\n", v)
 828			log.Printf("sendVideo resp: %+v\n", message)
 829		}
 830
 831		return message, nil
 832	}
 833
 834	params := make(map[string]string)
 835
 836	params["chat_id"] = strconv.Itoa(config.ChatID)
 837	if config.ReplyToMessageID != 0 {
 838		params["reply_to_message_id"] = strconv.Itoa(config.ReplyToMessageID)
 839	}
 840	if config.ReplyMarkup != nil {
 841		data, err := json.Marshal(config.ReplyMarkup)
 842		if err != nil {
 843			return Message{}, err
 844		}
 845
 846		params["reply_markup"] = string(data)
 847	}
 848
 849	var file interface{}
 850	if config.FilePath == "" {
 851		file = config.File
 852	} else {
 853		file = config.FilePath
 854	}
 855
 856	resp, err := bot.UploadFile("sendVideo", params, "video", file)
 857	if err != nil {
 858		return Message{}, err
 859	}
 860
 861	var message Message
 862	json.Unmarshal(resp.Result, &message)
 863
 864	if bot.Debug {
 865		log.Printf("sendVideo resp: %+v\n", message)
 866	}
 867
 868	return message, nil
 869}
 870
 871// SendLocation sends a location to a chat.
 872//
 873// Requires ChatID, Latitude, and Longitude.
 874// ReplyToMessageID and ReplyMarkup are optional.
 875func (bot *BotAPI) SendLocation(config LocationConfig) (Message, error) {
 876	v := url.Values{}
 877	v.Add("chat_id", strconv.Itoa(config.ChatID))
 878	v.Add("latitude", strconv.FormatFloat(config.Latitude, 'f', 6, 64))
 879	v.Add("longitude", strconv.FormatFloat(config.Longitude, 'f', 6, 64))
 880	if config.ReplyToMessageID != 0 {
 881		v.Add("reply_to_message_id", strconv.Itoa(config.ReplyToMessageID))
 882	}
 883	if config.ReplyMarkup != nil {
 884		data, err := json.Marshal(config.ReplyMarkup)
 885		if err != nil {
 886			return Message{}, err
 887		}
 888
 889		v.Add("reply_markup", string(data))
 890	}
 891
 892	resp, err := bot.MakeRequest("sendLocation", v)
 893	if err != nil {
 894		return Message{}, err
 895	}
 896
 897	var message Message
 898	json.Unmarshal(resp.Result, &message)
 899
 900	if bot.Debug {
 901		log.Printf("sendLocation req : %+v\n", v)
 902		log.Printf("sendLocation resp: %+v\n", message)
 903	}
 904
 905	return message, nil
 906}
 907
 908// SendChatAction sets a current action in a chat.
 909//
 910// Requires ChatID and a valid Action (see Chat constants).
 911func (bot *BotAPI) SendChatAction(config ChatActionConfig) error {
 912	v := url.Values{}
 913	v.Add("chat_id", strconv.Itoa(config.ChatID))
 914	v.Add("action", config.Action)
 915
 916	_, err := bot.MakeRequest("sendChatAction", v)
 917	if err != nil {
 918		return err
 919	}
 920
 921	return nil
 922}
 923
 924// GetUserProfilePhotos gets a user's profile photos.
 925//
 926// Requires UserID.
 927// Offset and Limit are optional.
 928func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
 929	v := url.Values{}
 930	v.Add("user_id", strconv.Itoa(config.UserID))
 931	if config.Offset != 0 {
 932		v.Add("offset", strconv.Itoa(config.Offset))
 933	}
 934	if config.Limit != 0 {
 935		v.Add("limit", strconv.Itoa(config.Limit))
 936	}
 937
 938	resp, err := bot.MakeRequest("getUserProfilePhotos", v)
 939	if err != nil {
 940		return UserProfilePhotos{}, err
 941	}
 942
 943	var profilePhotos UserProfilePhotos
 944	json.Unmarshal(resp.Result, &profilePhotos)
 945
 946	if bot.Debug {
 947		log.Printf("getUserProfilePhotos req : %+v\n", v)
 948		log.Printf("getUserProfilePhotos resp: %+v\n", profilePhotos)
 949	}
 950
 951	return profilePhotos, nil
 952}
 953
 954// GetFile returns a file_id required to download a file.
 955//
 956// Requires FileID.
 957func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
 958	v := url.Values{}
 959	v.Add("file_id", config.FileID)
 960
 961	resp, err := bot.MakeRequest("getFile", v)
 962	if err != nil {
 963		return File{}, err
 964	}
 965
 966	var file File
 967	json.Unmarshal(resp.Result, &file)
 968
 969	if bot.Debug {
 970		log.Printf("getFile req : %+v\n", v)
 971		log.Printf("getFile resp: %+v\n", file)
 972	}
 973
 974	return file, nil
 975}
 976
 977// GetUpdates fetches updates.
 978// If a WebHook is set, this will not return any data!
 979//
 980// Offset, Limit, and Timeout are optional.
 981// To not get old items, set Offset to one higher than the previous item.
 982// Set Timeout to a large number to reduce requests and get responses instantly.
 983func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
 984	v := url.Values{}
 985	if config.Offset > 0 {
 986		v.Add("offset", strconv.Itoa(config.Offset))
 987	}
 988	if config.Limit > 0 {
 989		v.Add("limit", strconv.Itoa(config.Limit))
 990	}
 991	if config.Timeout > 0 {
 992		v.Add("timeout", strconv.Itoa(config.Timeout))
 993	}
 994
 995	resp, err := bot.MakeRequest("getUpdates", v)
 996	if err != nil {
 997		return []Update{}, err
 998	}
 999
1000	var updates []Update
1001	json.Unmarshal(resp.Result, &updates)
1002
1003	if bot.Debug {
1004		log.Printf("getUpdates: %+v\n", updates)
1005	}
1006
1007	return updates, nil
1008}
1009
1010// SetWebhook sets a webhook.
1011// If this is set, GetUpdates will not get any data!
1012//
1013// Requires Url OR to set Clear to true.
1014func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
1015	if config.Certificate == nil {
1016		v := url.Values{}
1017		if !config.Clear {
1018			v.Add("url", config.URL.String())
1019		}
1020
1021		return bot.MakeRequest("setWebhook", v)
1022	}
1023
1024	params := make(map[string]string)
1025	params["url"] = config.URL.String()
1026
1027	resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
1028	if err != nil {
1029		return APIResponse{}, err
1030	}
1031
1032	var apiResp APIResponse
1033	json.Unmarshal(resp.Result, &apiResp)
1034
1035	if bot.Debug {
1036		log.Printf("setWebhook resp: %+v\n", apiResp)
1037	}
1038
1039	return apiResp, nil
1040}