func (config StickerConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add(config.name(), config.FileID)
	return v, nil
}
values returns a url.Values representation of StickerConfig.
package telegram
import (
	"encoding/json"
	"io"
	"net/url"
	"strconv"
)
const (
	APIEndpoint = "https://api.telegram.org/bot%s/%s"
	FileEndpoint = "https://api.telegram.org/file/bot%s/%s"
)
const (
	ChatTyping         = "typing"
	ChatUploadPhoto    = "upload_photo"
	ChatRecordVideo    = "record_video"
	ChatUploadVideo    = "upload_video"
	ChatRecordAudio    = "record_audio"
	ChatUploadAudio    = "upload_audio"
	ChatUploadDocument = "upload_document"
	ChatFindLocation   = "find_location"
)
const (
	ErrAPIForbidden = "forbidden"
)
const (
	ModeMarkdown = "Markdown"
	ModeHTML     = "HTML"
)
const (
	ErrBadFileType = "bad file type"
	ErrBadURL      = "bad or empty url"
)
type Chattable interface {
	values() (url.Values, error)
	method() string
}
type Fileable interface {
	Chattable
	params() (map[string]string, error)
	name() string
	getFile() interface{}
	useExistingFile() bool
}
type BaseChat struct {
	ChatID              int64       `json:"chat_id"`
	UserName            string      `json:"username"`
	ReplyToMessageID    int         `json:"reply_to_message_id,omitempty"`
	ReplyMarkup         interface{} `json:"reply_markup,omitempty"`
	DisableNotification bool        `json:"disable_notification"`
}
func (chat *BaseChat) values() (url.Values, error) {
	v := url.Values{}
	if chat.UserName != `` {
		v.Add(`chat_id`, chat.UserName)
	} else {
		v.Add(`chat_id`, strconv.FormatInt(chat.ChatID, 10))
	}
	if chat.ReplyToMessageID != 0 {
		v.Add("reply_to_message_id", strconv.Itoa(chat.ReplyToMessageID))
	}
	if chat.ReplyMarkup != nil {
		data, err := json.Marshal(chat.ReplyMarkup)
		if err != nil {
			return v, err
		}
		v.Add("reply_markup", string(data))
	}
	v.Add("disable_notification", strconv.FormatBool(chat.DisableNotification))
	return v, nil
}
type BaseFile struct {
	BaseChat
	File        interface{} `json:"photo,omitempty"`
	FileID      string      `json:"file_id,omitempty"`
	UseExisting bool
	MimeType    string `json:"mime_type,omitempty"`
	FileSize    int    `json:"file_size,omitempty"`
}
func (file BaseFile) params() (map[string]string, error) {
	params := make(map[string]string)
	if file.UserName != `` {
		params["chat_id"] = file.UserName
	} else {
		params["chat_id"] = strconv.FormatInt(file.ChatID, 10)
	}
	if file.ReplyToMessageID != 0 {
		params["reply_to_message_id"] = strconv.Itoa(file.ReplyToMessageID)
	}
	if file.ReplyMarkup != nil {
		data, err := json.Marshal(file.ReplyMarkup)
		if err != nil {
			return params, err
		}
		params["reply_markup"] = string(data)
	}
	if file.MimeType != "" {
		params["mime_type"] = file.MimeType
	}
	if file.FileSize > 0 {
		params["file_size"] = strconv.Itoa(file.FileSize)
	}
	params["disable_notification"] = strconv.FormatBool(file.DisableNotification)
	return params, nil
}
func (file BaseFile) getFile() interface{} {
	return file.File
}
func (file BaseFile) useExistingFile() bool {
	return file.UseExisting
}
type BaseEdit struct {
	ChatID          int64                 `json:"chat_id"`
	ChannelUsername string                `json:"username"`
	MessageID       int                   `json:"message_id"`
	InlineMessageID string                `json:"inline_message_id"`
	ReplyMarkup     *InlineKeyboardMarkup `json:"reply_markup"`
}
func (edit BaseEdit) values() (url.Values, error) {
	v := url.Values{}
	if edit.InlineMessageID == "" {
		if edit.ChannelUsername != "" {
			v.Add("chat_id", edit.ChannelUsername)
		} else {
			v.Add("chat_id", strconv.FormatInt(edit.ChatID, 10))
		}
		v.Add("message_id", strconv.Itoa(edit.MessageID))
	} else {
		v.Add("inline_message_id", edit.InlineMessageID)
	}
	if edit.ReplyMarkup != nil {
		data, err := json.Marshal(edit.ReplyMarkup)
		if err != nil {
			return v, err
		}
		v.Add("reply_markup", string(data))
	}
	return v, nil
}
type MessageConfig struct {
	BaseChat
	Text                  string `json:"text"`
	ParseMode             string `json:"parse_mode"`
	DisableWebPagePreview bool   `json:"disable_web_page_preview"`
}
func (config MessageConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add("text", config.Text)
	v.Add("disable_web_page_preview", strconv.FormatBool(config.DisableWebPagePreview))
	if config.ParseMode != "" {
		v.Add("parse_mode", config.ParseMode)
	}
	return v, nil
}
func (config MessageConfig) method() string {
	return "sendMessage"
}
type ForwardConfig struct {
	BaseChat
	FromChatID          int64 `json:"from_chat_id"`  
	FromChannelUsername string
	MessageID           int `json:"message_id"`  
}
func (config ForwardConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add("from_chat_id", strconv.FormatInt(config.FromChatID, 10))
	v.Add("message_id", strconv.Itoa(config.MessageID))
	return v, nil
}
func (config ForwardConfig) method() string {
	return "forwardMessage"
}
type PhotoConfig struct {
	BaseFile
	Caption   string `json:"caption"`
	ParseMode string `json:"parse_mode"`
}
func (config PhotoConfig) params() (map[string]string, error) {
	params, _ := config.BaseFile.params()
	if config.Caption != "" {
		params["caption"] = config.Caption
		if config.ParseMode != "" {
			params["parse_mode"] = config.ParseMode
		}
	}
	return params, nil
}
func (config PhotoConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add(config.name(), config.FileID)
	if config.Caption != "" {
		v.Add("caption", config.Caption)
		if config.ParseMode != "" {
			v.Add("parse_mode", config.ParseMode)
		}
	}
	return v, nil
}
func (config PhotoConfig) name() string {
	return "photo"
}
func (config PhotoConfig) method() string {
	return "sendPhoto"
}
type AudioConfig struct {
	BaseFile
	Caption   string `json:"caption"`
	ParseMode string `json:"parse_mode"`
	Duration  int    `json:"duration"`
	Performer string `json:"performer"`
	Title     string `json:"title"`
}
func (config AudioConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add(config.name(), config.FileID)
	if config.Duration != 0 {
		v.Add("duration", strconv.Itoa(config.Duration))
	}
	if config.Performer != "" {
		v.Add("performer", config.Performer)
	}
	if config.Title != "" {
		v.Add("title", config.Title)
	}
	if config.Caption != "" {
		v.Add("caption", config.Caption)
		if config.ParseMode != "" {
			v.Add("parse_mode", config.ParseMode)
		}
	}
	return v, nil
}
func (config AudioConfig) params() (map[string]string, error) {
	params, _ := config.BaseFile.params()
	if config.Duration != 0 {
		params["duration"] = strconv.Itoa(config.Duration)
	}
	if config.Performer != "" {
		params["performer"] = config.Performer
	}
	if config.Title != "" {
		params["title"] = config.Title
	}
	if config.Caption != "" {
		params["caption"] = config.Caption
		if config.ParseMode != "" {
			params["parse_mode"] = config.ParseMode
		}
	}
	return params, nil
}
func (config AudioConfig) name() string {
	return "audio"
}
func (config AudioConfig) method() string {
	return "sendAudio"
}
type DocumentConfig struct {
	BaseFile
	Caption   string `json:"caption"`
	ParseMode string `json:"parse_mode"`
}
func (config DocumentConfig) values() (url.Values, error) {
	v, err := config.BaseChat.values()
	if err != nil {
		return v, err
	}
	v.Add(config.name(), config.FileID)
	if config.Caption != "" {
		v.Add("caption", config.Caption)
		if config.ParseMode != "" {
			v.Add("parse_mode", config.ParseMode)
		}
	}
	return v, nil
}
func (config DocumentConfig) params() (map[string]string, error) {
	params, _ := config.BaseFile.params()
	if config.Caption != "" {
		params["caption"] = config.Caption
		if config.ParseMode != "" {
			params["parse_mode"] = config.ParseMode
		}
	}
	return params, nil
}
func (config DocumentConfig) name() string {
	return "document"
}
func (config DocumentConfig) method() string {
	return "sendDocument"
}
type StickerConfig struct {
	BaseFile
}