func (bot *BotAPI) KickChatMember(config KickChatMemberConfig) (APIResponse, error) {
	v := url.Values{}
	if config.SuperGroupUsername == "" {
		v.Add("chat_id", strconv.FormatInt(config.ChatID, 10))
	} else {
		v.Add("chat_id", config.SuperGroupUsername)
	}
	v.Add("user_id", strconv.Itoa(config.UserID))
	if config.UntilDate != 0 {
		v.Add("until_date", strconv.FormatInt(config.UntilDate, 10))
	}
	bot.debugLog("kickChatMember", v, nil)
	return bot.MakeRequest("kickChatMember", v)
}
KickChatMember kicks a user from a chat. Note that this only will work
in supergroups, and requires the bot to be an admin. Also note they
will be unable to rejoin until they are unbanned.
package tgbotapi
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
	"github.com/technoweenie/multipartstreamer"
)
type BotAPI struct {
	Token  string `json:"token"`
	Debug  bool   `json:"debug"`
	Buffer int    `json:"buffer"`
	Self   User         `json:"-"`
	Client *http.Client `json:"-"`
}
func NewBotAPI(token string) (*BotAPI, error) {
	return NewBotAPIWithClient(token, &http.Client{})
}
func NewBotAPIWithClient(token string, client *http.Client) (*BotAPI, error) {
	bot := &BotAPI{
		Token:  token,
		Client: client,
		Buffer: 100,
	}
	self, err := bot.GetMe()
	if err != nil {
		return nil, err
	}
	bot.Self = self
	return bot, nil
}
func (bot *BotAPI) MakeRequest(endpoint string, params url.Values) (APIResponse, error) {
	method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
	resp, err := bot.Client.PostForm(method, params)
	if err != nil {
		return APIResponse{}, err
	}
	defer resp.Body.Close()
	var apiResp APIResponse
	bytes, err := bot.decodeAPIResponse(resp.Body, &apiResp)
	if err != nil {
		return apiResp, err
	}
	if bot.Debug {
		log.Printf("%s resp: %s", endpoint, bytes)
	}
	if !apiResp.Ok {
		return apiResp, errors.New(apiResp.Description)
	}
	return apiResp, nil
}
func (bot *BotAPI) decodeAPIResponse(responseBody io.Reader, resp *APIResponse) (_ []byte, err error) {
	if !bot.Debug {
		dec := json.NewDecoder(responseBody)
		err = dec.Decode(resp)
		return
	}
	data, err := ioutil.ReadAll(responseBody)
	if err != nil {
		return
	}
	err = json.Unmarshal(data, resp)
	if err != nil {
		return
	}
	return data, nil
}
func (bot *BotAPI) makeMessageRequest(endpoint string, params url.Values) (Message, error) {
	resp, err := bot.MakeRequest(endpoint, params)
	if err != nil {
		return Message{}, err
	}
	var message Message
	json.Unmarshal(resp.Result, &message)
	bot.debugLog(endpoint, params, message)
	return message, nil
}
func (bot *BotAPI) UploadFile(endpoint string, params map[string]string, fieldname string, file interface{}) (APIResponse, error) {
	ms := multipartstreamer.New()
	switch f := file.(type) {
	case string:
		ms.WriteFields(params)
		fileHandle, err := os.Open(f)
		if err != nil {
			return APIResponse{}, err
		}
		defer fileHandle.Close()
		fi, err := os.Stat(f)
		if err != nil {
			return APIResponse{}, err
		}
		ms.WriteReader(fieldname, fileHandle.Name(), fi.Size(), fileHandle)
	case FileBytes:
		ms.WriteFields(params)
		buf := bytes.NewBuffer(f.Bytes)
		ms.WriteReader(fieldname, f.Name, int64(len(f.Bytes)), buf)
	case FileReader:
		ms.WriteFields(params)
		if f.Size != -1 {
			ms.WriteReader(fieldname, f.Name, f.Size, f.Reader)
			break
		}
		data, err := ioutil.ReadAll(f.Reader)
		if err != nil {
			return APIResponse{}, err
		}
		buf := bytes.NewBuffer(data)
		ms.WriteReader(fieldname, f.Name, int64(len(data)), buf)
	case url.URL:
		params[fieldname] = f.String()
		ms.WriteFields(params)
	default:
		return APIResponse{}, errors.New(ErrBadFileType)
	}
	method := fmt.Sprintf(APIEndpoint, bot.Token, endpoint)
	req, err := http.NewRequest("POST", method, nil)
	if err != nil {
		return APIResponse{}, err
	}
	ms.SetupRequest(req)
	res, err := bot.Client.Do(req)
	if err != nil {
		return APIResponse{}, err
	}
	defer res.Body.Close()
	bytes, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return APIResponse{}, err
	}
	if bot.Debug {
		log.Println(string(bytes))
	}
	var apiResp APIResponse
	err = json.Unmarshal(bytes, &apiResp)
	if err != nil {
		return APIResponse{}, err
	}
	if !apiResp.Ok {
		return APIResponse{}, errors.New(apiResp.Description)
	}
	return apiResp, nil
}
func (bot *BotAPI) GetFileDirectURL(fileID string) (string, error) {
	file, err := bot.GetFile(FileConfig{fileID})
	if err != nil {
		return "", err
	}
	return file.Link(bot.Token), nil
}
func (bot *BotAPI) GetMe() (User, error) {
	resp, err := bot.MakeRequest("getMe", nil)
	if err != nil {
		return User{}, err
	}
	var user User
	json.Unmarshal(resp.Result, &user)
	bot.debugLog("getMe", nil, user)
	return user, nil
}
func (bot *BotAPI) IsMessageToMe(message Message) bool {
	return strings.Contains(message.Text, "@"+bot.Self.UserName)
}
func (bot *BotAPI) Send(c Chattable) (Message, error) {
	switch c.(type) {
	case Fileable:
		return bot.sendFile(c.(Fileable))
	default:
		return bot.sendChattable(c)
	}
}
func (bot *BotAPI) debugLog(context string, v url.Values, message interface{}) {
	if bot.Debug {
		log.Printf("%s req : %+v\n", context, v)
		log.Printf("%s resp: %+v\n", context, message)
	}
}
func (bot *BotAPI) sendExisting(method string, config Fileable) (Message, error) {
	v, err := config.values()
	if err != nil {
		return Message{}, err
	}
	message, err := bot.makeMessageRequest(method, v)
	if err != nil {
		return Message{}, err
	}
	return message, nil
}
func (bot *BotAPI) uploadAndSend(method string, config Fileable) (Message, error) {
	params, err := config.params()
	if err != nil {
		return Message{}, err
	}
	file := config.getFile()
	resp, err := bot.UploadFile(method, params, config.name(), file)
	if err != nil {
		return Message{}, err
	}
	var message Message
	json.Unmarshal(resp.Result, &message)
	bot.debugLog(method, nil, message)
	return message, nil
}
func (bot *BotAPI) sendFile(config Fileable) (Message, error) {
	if config.useExistingFile() {
		return bot.sendExisting(config.method(), config)
	}
	return bot.uploadAndSend(config.method(), config)
}
func (bot *BotAPI) sendChattable(config Chattable) (Message, error) {
	v, err := config.values()
	if err != nil {
		return Message{}, err
	}
	message, err := bot.makeMessageRequest(config.method(), v)
	if err != nil {
		return Message{}, err
	}
	return message, nil
}
func (bot *BotAPI) GetUserProfilePhotos(config UserProfilePhotosConfig) (UserProfilePhotos, error) {
	v := url.Values{}
	v.Add("user_id", strconv.Itoa(config.UserID))
	if config.Offset != 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit != 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}
	resp, err := bot.MakeRequest("getUserProfilePhotos", v)
	if err != nil {
		return UserProfilePhotos{}, err
	}
	var profilePhotos UserProfilePhotos
	json.Unmarshal(resp.Result, &profilePhotos)
	bot.debugLog("GetUserProfilePhoto", v, profilePhotos)
	return profilePhotos, nil
}
func (bot *BotAPI) GetFile(config FileConfig) (File, error) {
	v := url.Values{}
	v.Add("file_id", config.FileID)
	resp, err := bot.MakeRequest("getFile", v)
	if err != nil {
		return File{}, err
	}
	var file File
	json.Unmarshal(resp.Result, &file)
	bot.debugLog("GetFile", v, file)
	return file, nil
}
func (bot *BotAPI) GetUpdates(config UpdateConfig) ([]Update, error) {
	v := url.Values{}
	if config.Offset != 0 {
		v.Add("offset", strconv.Itoa(config.Offset))
	}
	if config.Limit > 0 {
		v.Add("limit", strconv.Itoa(config.Limit))
	}
	if config.Timeout > 0 {
		v.Add("timeout", strconv.Itoa(config.Timeout))
	}
	resp, err := bot.MakeRequest("getUpdates", v)
	if err != nil {
		return []Update{}, err
	}
	var updates []Update
	json.Unmarshal(resp.Result, &updates)
	bot.debugLog("getUpdates", v, updates)
	return updates, nil
}
func (bot *BotAPI) RemoveWebhook() (APIResponse, error) {
	return bot.MakeRequest("setWebhook", url.Values{})
}
func (bot *BotAPI) SetWebhook(config WebhookConfig) (APIResponse, error) {
	if config.Certificate == nil {
		v := url.Values{}
		v.Add("url", config.URL.String())
		if config.MaxConnections != 0 {
			v.Add("max_connections", strconv.Itoa(config.MaxConnections))
		}
		return bot.MakeRequest("setWebhook", v)
	}
	params := make(map[string]string)
	params["url"] = config.URL.String()
	if config.MaxConnections != 0 {
		params["max_connections"] = strconv.Itoa(config.MaxConnections)
	}
	resp, err := bot.UploadFile("setWebhook", params, "certificate", config.Certificate)
	if err != nil {
		return APIResponse{}, err
	}
	return resp, nil
}
func (bot *BotAPI) GetWebhookInfo() (WebhookInfo, error) {
	resp, err := bot.MakeRequest("getWebhookInfo", url.Values{})
	if err != nil {
		return WebhookInfo{}, err
	}
	var info WebhookInfo
	err = json.Unmarshal(resp.Result, &info)
	return info, err
}
func (bot *BotAPI) GetUpdatesChan(config UpdateConfig) (UpdatesChannel, error) {
	ch := make(chan Update, bot.Buffer)
	go func() {
		for {
			updates, err := bot.GetUpdates(config)
			if err != nil {
				log.Println(err)
				log.Println("Failed to get updates, retrying in 3 seconds...")
				time.Sleep(time.Second * 3)
				continue
			}
			for _, update := range updates {
				if update.UpdateID >= config.Offset {
					config.Offset = update.UpdateID + 1
					ch <- update
				}
			}
		}
	}()
	return ch, nil
}
func (bot *BotAPI) ListenForWebhook(pattern string) UpdatesChannel {
	ch := make(chan Update, bot.Buffer)
	http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
		bytes, _ := ioutil.ReadAll(r.Body)
		var update Update
		json.Unmarshal(bytes, &update)
		ch <- update
	})
	return ch
}
func (bot *BotAPI) AnswerInlineQuery(config InlineConfig) (APIResponse, error) {
	v := url.Values{}
	v.Add("inline_query_id", config.InlineQueryID)
	v.Add("cache_time", strconv.Itoa(config.CacheTime))
	v.Add("is_personal", strconv.FormatBool(config.IsPersonal))
	v.Add("next_offset", config.NextOffset)
	data, err := json.Marshal(config.Results)
	if err != nil {
		return APIResponse{}, err
	}
	v.Add("results", string(data))
	v.Add("switch_pm_text", config.SwitchPMText)
	v.Add("switch_pm_parameter", config.SwitchPMParameter)
	bot.debugLog("answerInlineQuery", v, nil)
	return bot.MakeRequest("answerInlineQuery", v)
}
func (bot *BotAPI) AnswerCallbackQuery(config CallbackConfig) (APIResponse, error) {
	v := url.Values{}
	v.Add("callback_query_id", config.CallbackQueryID)
	if config.Text != "" {
		v.Add("text", config.Text)
	}
	v.Add("show_alert", strconv.FormatBool(config.ShowAlert))
	if config.URL != "" {
		v.Add("url", config.URL)
	}
	v.Add("cache_time", strconv.Itoa(config.CacheTime))
	bot.debugLog("answerCallbackQuery", v, nil)
	return bot.MakeRequest("answerCallbackQuery", v)
}