2015-07-06 13:46:19 +00:00
|
|
|
|
package telebot
|
|
|
|
|
|
2020-05-13 01:33:12 +00:00
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
2020-05-21 10:38:06 +00:00
|
|
|
|
"strings"
|
2020-05-13 01:33:12 +00:00
|
|
|
|
)
|
2020-04-24 14:52:15 +00:00
|
|
|
|
|
2020-05-21 09:02:35 +00:00
|
|
|
|
// Option is a shortcut flag type for certain message features
|
2017-11-18 13:06:20 +00:00
|
|
|
|
// (so-called options). It means that instead of passing
|
|
|
|
|
// fully-fledged SendOptions* to Send(), you can use these
|
|
|
|
|
// flags instead.
|
|
|
|
|
//
|
|
|
|
|
// Supported options are defined as iota-constants.
|
2020-06-19 20:34:37 +00:00
|
|
|
|
//
|
2017-11-17 06:20:36 +00:00
|
|
|
|
type Option int
|
|
|
|
|
|
|
|
|
|
const (
|
2017-11-19 15:16:39 +00:00
|
|
|
|
// NoPreview = SendOptions.DisableWebPagePreview
|
2017-11-17 06:20:36 +00:00
|
|
|
|
NoPreview Option = iota
|
|
|
|
|
|
2017-11-19 15:16:39 +00:00
|
|
|
|
// Silent = SendOptions.DisableNotification
|
2017-11-17 06:20:36 +00:00
|
|
|
|
Silent
|
|
|
|
|
|
2017-11-19 15:16:39 +00:00
|
|
|
|
// ForceReply = ReplyMarkup.ForceReply
|
2017-11-17 06:20:36 +00:00
|
|
|
|
ForceReply
|
|
|
|
|
|
2017-11-19 15:16:39 +00:00
|
|
|
|
// OneTimeKeyboard = ReplyMarkup.OneTimeKeyboard
|
2017-11-17 06:20:36 +00:00
|
|
|
|
OneTimeKeyboard
|
2020-11-07 23:26:54 +00:00
|
|
|
|
|
|
|
|
|
// RemoveKeyboard = ReplyMarkup.RemoveKeyboard
|
|
|
|
|
RemoveKeyboard
|
2017-11-17 06:20:36 +00:00
|
|
|
|
)
|
|
|
|
|
|
2021-07-23 17:12:27 +00:00
|
|
|
|
// Placeholder is used to set input field placeholder as a send option.
|
|
|
|
|
func Placeholder(text string) *SendOptions {
|
|
|
|
|
return &SendOptions{
|
2021-07-23 17:19:12 +00:00
|
|
|
|
ReplyMarkup: &ReplyMarkup{
|
|
|
|
|
ForceReply: true,
|
|
|
|
|
Placeholder: text,
|
|
|
|
|
},
|
2021-07-23 17:12:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-18 13:06:20 +00:00
|
|
|
|
// SendOptions has most complete control over in what way the message
|
|
|
|
|
// must be sent, providing an API-complete set of custom properties
|
|
|
|
|
// and options.
|
|
|
|
|
//
|
|
|
|
|
// Despite its power, SendOptions is rather inconvenient to use all
|
|
|
|
|
// the way through bot logic, so you might want to consider storing
|
|
|
|
|
// and re-using it somewhere or be using Option flags instead.
|
2020-06-19 20:34:37 +00:00
|
|
|
|
//
|
2015-07-06 13:46:19 +00:00
|
|
|
|
type SendOptions struct {
|
|
|
|
|
// If the message is a reply, original message.
|
2017-11-18 10:31:13 +00:00
|
|
|
|
ReplyTo *Message
|
2015-07-06 13:46:19 +00:00
|
|
|
|
|
2015-07-31 07:56:02 +00:00
|
|
|
|
// See ReplyMarkup struct definition.
|
2017-11-18 10:31:13 +00:00
|
|
|
|
ReplyMarkup *ReplyMarkup
|
2015-07-06 13:46:19 +00:00
|
|
|
|
|
|
|
|
|
// For text messages, disables previews for links in this message.
|
2017-11-18 10:31:13 +00:00
|
|
|
|
DisableWebPagePreview bool
|
2015-09-14 03:15:16 +00:00
|
|
|
|
|
2016-03-12 21:26:43 +00:00
|
|
|
|
// Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound.
|
2017-11-18 10:31:13 +00:00
|
|
|
|
DisableNotification bool
|
2016-03-12 21:26:43 +00:00
|
|
|
|
|
2015-09-14 03:15:16 +00:00
|
|
|
|
// ParseMode controls how client apps render your message.
|
2017-11-18 10:31:13 +00:00
|
|
|
|
ParseMode ParseMode
|
2021-05-23 16:55:49 +00:00
|
|
|
|
|
|
|
|
|
// DisableContentDetection abilities to disable server-side file content type detection.
|
|
|
|
|
DisableContentDetection bool
|
|
|
|
|
|
|
|
|
|
// AllowWithoutReply allows sending messages not a as reply if the replied-to message has already been deleted.
|
|
|
|
|
AllowWithoutReply bool
|
2015-07-06 13:46:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-10 23:21:51 +00:00
|
|
|
|
func (og *SendOptions) copy() *SendOptions {
|
|
|
|
|
cp := *og
|
|
|
|
|
if cp.ReplyMarkup != nil {
|
|
|
|
|
cp.ReplyMarkup = cp.ReplyMarkup.copy()
|
|
|
|
|
}
|
|
|
|
|
return &cp
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-18 13:06:20 +00:00
|
|
|
|
// ReplyMarkup controls two convenient options for bot-user communications
|
|
|
|
|
// such as reply keyboard and inline "keyboard" (a grid of buttons as a part
|
|
|
|
|
// of the message).
|
2015-07-31 07:56:02 +00:00
|
|
|
|
type ReplyMarkup struct {
|
2017-11-17 13:55:18 +00:00
|
|
|
|
// InlineKeyboard is a grid of InlineButtons displayed in the message.
|
|
|
|
|
//
|
|
|
|
|
// Note: DO NOT confuse with ReplyKeyboard and other keyboard properties!
|
|
|
|
|
InlineKeyboard [][]InlineButton `json:"inline_keyboard,omitempty"`
|
|
|
|
|
|
|
|
|
|
// ReplyKeyboard is a grid, consisting of keyboard buttons.
|
2015-07-31 07:56:02 +00:00
|
|
|
|
//
|
|
|
|
|
// Note: you don't need to set HideCustomKeyboard field to show custom keyboard.
|
2017-11-28 22:15:50 +00:00
|
|
|
|
ReplyKeyboard [][]ReplyButton `json:"keyboard,omitempty"`
|
2016-04-21 20:31:30 +00:00
|
|
|
|
|
2017-11-19 15:16:39 +00:00
|
|
|
|
// ForceReply forces Telegram clients to display
|
|
|
|
|
// a reply interface to the user (act as if the user
|
|
|
|
|
// has selected the bot‘s message and tapped "Reply").
|
|
|
|
|
ForceReply bool `json:"force_reply,omitempty"`
|
|
|
|
|
|
2015-07-31 07:56:02 +00:00
|
|
|
|
// Requests clients to resize the keyboard vertically for optimal fit
|
2017-11-17 13:55:18 +00:00
|
|
|
|
// (e.g. make the keyboard smaller if there are just two rows of buttons).
|
|
|
|
|
//
|
2015-07-31 07:56:02 +00:00
|
|
|
|
// Defaults to false, in which case the custom keyboard is always of the
|
|
|
|
|
// same height as the app's standard keyboard.
|
2020-09-26 15:49:24 +00:00
|
|
|
|
ResizeKeyboard bool `json:"resize_keyboard,omitempty"`
|
2015-07-31 07:56:02 +00:00
|
|
|
|
|
2017-11-17 13:55:18 +00:00
|
|
|
|
// Requests clients to hide the reply keyboard as soon as it's been used.
|
|
|
|
|
//
|
2017-11-17 06:20:36 +00:00
|
|
|
|
// Defaults to false.
|
|
|
|
|
OneTimeKeyboard bool `json:"one_time_keyboard,omitempty"`
|
2015-07-31 07:56:02 +00:00
|
|
|
|
|
2017-12-30 15:42:02 +00:00
|
|
|
|
// Requests clients to remove the reply keyboard.
|
|
|
|
|
//
|
2020-05-21 09:02:35 +00:00
|
|
|
|
// Defaults to false.
|
2020-09-26 15:49:24 +00:00
|
|
|
|
RemoveKeyboard bool `json:"remove_keyboard,omitempty"`
|
2017-12-30 15:42:02 +00:00
|
|
|
|
|
2015-07-06 13:46:19 +00:00
|
|
|
|
// Use this param if you want to force reply from
|
|
|
|
|
// specific users only.
|
|
|
|
|
//
|
|
|
|
|
// Targets:
|
|
|
|
|
// 1) Users that are @mentioned in the text of the Message object;
|
|
|
|
|
// 2) If the bot's message is a reply (has SendOptions.ReplyTo),
|
|
|
|
|
// sender of the original message.
|
2015-07-31 07:56:02 +00:00
|
|
|
|
Selective bool `json:"selective,omitempty"`
|
2021-07-23 17:12:27 +00:00
|
|
|
|
|
|
|
|
|
// Placeholder will be shown in the input field when the reply is active.
|
|
|
|
|
Placeholder string `json:"input_field_placeholder,omitempty"`
|
2015-07-06 13:46:19 +00:00
|
|
|
|
}
|
2017-11-18 13:06:20 +00:00
|
|
|
|
|
2020-05-21 09:02:35 +00:00
|
|
|
|
func (r *ReplyMarkup) copy() *ReplyMarkup {
|
|
|
|
|
cp := *r
|
2017-12-10 23:21:51 +00:00
|
|
|
|
|
2020-06-09 20:41:23 +00:00
|
|
|
|
if len(r.ReplyKeyboard) > 0 {
|
|
|
|
|
cp.ReplyKeyboard = make([][]ReplyButton, len(r.ReplyKeyboard))
|
|
|
|
|
for i, row := range r.ReplyKeyboard {
|
|
|
|
|
cp.ReplyKeyboard[i] = make([]ReplyButton, len(row))
|
|
|
|
|
copy(cp.ReplyKeyboard[i], row)
|
|
|
|
|
}
|
2017-12-10 23:21:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-09 20:41:23 +00:00
|
|
|
|
if len(r.InlineKeyboard) > 0 {
|
|
|
|
|
cp.InlineKeyboard = make([][]InlineButton, len(r.InlineKeyboard))
|
|
|
|
|
for i, row := range r.InlineKeyboard {
|
|
|
|
|
cp.InlineKeyboard[i] = make([]InlineButton, len(row))
|
|
|
|
|
copy(cp.InlineKeyboard[i], row)
|
|
|
|
|
}
|
2017-12-10 23:21:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &cp
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-28 22:15:50 +00:00
|
|
|
|
// ReplyButton represents a button displayed in reply-keyboard.
|
2017-11-18 14:41:23 +00:00
|
|
|
|
//
|
|
|
|
|
// Set either Contact or Location to true in order to request
|
|
|
|
|
// sensitive info, such as user's phone number or current location.
|
2020-06-19 20:34:37 +00:00
|
|
|
|
//
|
2017-11-28 22:15:50 +00:00
|
|
|
|
type ReplyButton struct {
|
2017-11-18 13:06:20 +00:00
|
|
|
|
Text string `json:"text"`
|
|
|
|
|
|
2020-04-24 14:52:15 +00:00
|
|
|
|
Contact bool `json:"request_contact,omitempty"`
|
|
|
|
|
Location bool `json:"request_location,omitempty"`
|
|
|
|
|
Poll PollType `json:"request_poll,omitempty"`
|
2017-11-18 13:06:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InlineKeyboardMarkup represents an inline keyboard that appears
|
|
|
|
|
// right next to the message it belongs to.
|
|
|
|
|
type InlineKeyboardMarkup struct {
|
|
|
|
|
// Array of button rows, each represented by
|
|
|
|
|
// an Array of KeyboardButton objects.
|
|
|
|
|
InlineKeyboard [][]InlineButton `json:"inline_keyboard,omitempty"`
|
|
|
|
|
}
|
2020-03-30 16:21:06 +00:00
|
|
|
|
|
2020-04-26 15:19:49 +00:00
|
|
|
|
// MarshalJSON implements json.Marshaler. It allows to pass
|
|
|
|
|
// PollType as keyboard's poll type instead of KeyboardButtonPollType object.
|
2020-04-24 14:52:15 +00:00
|
|
|
|
func (pt PollType) MarshalJSON() ([]byte, error) {
|
|
|
|
|
var aux = struct {
|
|
|
|
|
Type string `json:"type"`
|
|
|
|
|
}{
|
|
|
|
|
Type: string(pt),
|
|
|
|
|
}
|
|
|
|
|
return json.Marshal(&aux)
|
2020-03-30 16:21:06 +00:00
|
|
|
|
}
|
2020-05-13 01:33:12 +00:00
|
|
|
|
|
2020-11-15 23:42:55 +00:00
|
|
|
|
// Row represents an array of buttons, a row.
|
2020-07-11 18:26:47 +00:00
|
|
|
|
type Row []Btn
|
2020-05-13 16:55:06 +00:00
|
|
|
|
|
2020-11-15 23:42:55 +00:00
|
|
|
|
// Row creates a row of buttons.
|
2020-07-11 18:26:47 +00:00
|
|
|
|
func (r *ReplyMarkup) Row(many ...Btn) Row {
|
2020-05-13 01:33:12 +00:00
|
|
|
|
return many
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-15 23:42:55 +00:00
|
|
|
|
// Split splits the keyboard into the rows with N maximum number of buttons.
|
|
|
|
|
// For example, if you pass six buttons and 3 as the max, you get two rows with
|
|
|
|
|
// three buttons in each.
|
|
|
|
|
//
|
2021-07-26 15:08:10 +00:00
|
|
|
|
// Split(3, []Btn{six buttons...}) -> [[1, 2, 3], [4, 5, 6]]
|
|
|
|
|
// Split(2, []Btn{six buttons...}) -> [[1, 2],[3, 4],[5, 6]]
|
2020-11-15 23:42:55 +00:00
|
|
|
|
//
|
2021-07-26 15:08:10 +00:00
|
|
|
|
func (r *ReplyMarkup) Split(max int, btns []Btn) []Row {
|
|
|
|
|
rows := make([]Row, (1+len(btns))/max)
|
|
|
|
|
for i, b := range btns {
|
2020-11-15 23:42:55 +00:00
|
|
|
|
i /= max
|
|
|
|
|
rows[i] = append(rows[i], b)
|
|
|
|
|
}
|
|
|
|
|
return rows
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-11 18:26:47 +00:00
|
|
|
|
func (r *ReplyMarkup) Inline(rows ...Row) {
|
2020-05-13 01:33:12 +00:00
|
|
|
|
inlineKeys := make([][]InlineButton, 0, len(rows))
|
|
|
|
|
for i, row := range rows {
|
|
|
|
|
keys := make([]InlineButton, 0, len(row))
|
|
|
|
|
for j, btn := range row {
|
|
|
|
|
btn := btn.Inline()
|
|
|
|
|
if btn == nil {
|
|
|
|
|
panic(fmt.Sprintf(
|
|
|
|
|
"telebot: button row %d column %d is not an inline button",
|
|
|
|
|
i, j))
|
|
|
|
|
}
|
|
|
|
|
keys = append(keys, *btn)
|
|
|
|
|
}
|
|
|
|
|
inlineKeys = append(inlineKeys, keys)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.InlineKeyboard = inlineKeys
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-11 18:26:47 +00:00
|
|
|
|
func (r *ReplyMarkup) Reply(rows ...Row) {
|
2020-05-13 01:33:12 +00:00
|
|
|
|
replyKeys := make([][]ReplyButton, 0, len(rows))
|
|
|
|
|
for i, row := range rows {
|
|
|
|
|
keys := make([]ReplyButton, 0, len(row))
|
|
|
|
|
for j, btn := range row {
|
|
|
|
|
btn := btn.Reply()
|
|
|
|
|
if btn == nil {
|
|
|
|
|
panic(fmt.Sprintf(
|
|
|
|
|
"telebot: button row %d column %d is not a reply button",
|
|
|
|
|
i, j))
|
|
|
|
|
}
|
|
|
|
|
keys = append(keys, *btn)
|
|
|
|
|
}
|
|
|
|
|
replyKeys = append(replyKeys, keys)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r.ReplyKeyboard = replyKeys
|
|
|
|
|
}
|
2020-05-21 10:38:06 +00:00
|
|
|
|
|
|
|
|
|
func (r *ReplyMarkup) Text(text string) Btn {
|
|
|
|
|
return Btn{Text: text}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Contact(text string) Btn {
|
|
|
|
|
return Btn{Contact: true, Text: text}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Location(text string) Btn {
|
|
|
|
|
return Btn{Location: true, Text: text}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Poll(text string, poll PollType) Btn {
|
|
|
|
|
return Btn{Poll: poll, Text: text}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Data(text, unique string, data ...string) Btn {
|
|
|
|
|
return Btn{
|
|
|
|
|
Unique: unique,
|
|
|
|
|
Text: text,
|
|
|
|
|
Data: strings.Join(data, "|"),
|
|
|
|
|
}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) URL(text, url string) Btn {
|
|
|
|
|
return Btn{Text: text, URL: url}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Query(text, query string) Btn {
|
|
|
|
|
return Btn{Text: text, InlineQuery: query}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (r *ReplyMarkup) QueryChat(text, query string) Btn {
|
|
|
|
|
return Btn{Text: text, InlineQueryChat: query}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-21 10:38:06 +00:00
|
|
|
|
func (r *ReplyMarkup) Login(text string, login *Login) Btn {
|
|
|
|
|
return Btn{Login: login, Text: text}
|
2020-05-13 15:21:07 +00:00
|
|
|
|
}
|
2020-05-13 01:33:12 +00:00
|
|
|
|
|
|
|
|
|
// Btn is a constructor button, which will later become either a reply, or an inline button.
|
|
|
|
|
type Btn struct {
|
2020-09-26 15:49:24 +00:00
|
|
|
|
Unique string `json:"unique,omitempty"`
|
|
|
|
|
Text string `json:"text,omitempty"`
|
|
|
|
|
URL string `json:"url,omitempty"`
|
|
|
|
|
Data string `json:"callback_data,omitempty"`
|
|
|
|
|
InlineQuery string `json:"switch_inline_query,omitempty"`
|
|
|
|
|
InlineQueryChat string `json:"switch_inline_query_current_chat,omitempty"`
|
|
|
|
|
Contact bool `json:"request_contact,omitempty"`
|
|
|
|
|
Location bool `json:"request_location,omitempty"`
|
|
|
|
|
Poll PollType `json:"request_poll,omitempty"`
|
|
|
|
|
Login *Login `json:"login_url,omitempty"`
|
2020-05-13 01:33:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b Btn) Inline() *InlineButton {
|
|
|
|
|
return &InlineButton{
|
|
|
|
|
Unique: b.Unique,
|
|
|
|
|
Text: b.Text,
|
|
|
|
|
URL: b.URL,
|
|
|
|
|
Data: b.Data,
|
|
|
|
|
InlineQuery: b.InlineQuery,
|
|
|
|
|
InlineQueryChat: b.InlineQueryChat,
|
2020-05-21 10:38:06 +00:00
|
|
|
|
Login: b.Login,
|
2020-05-13 01:33:12 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b Btn) Reply() *ReplyButton {
|
|
|
|
|
if b.Unique != "" {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &ReplyButton{
|
|
|
|
|
Text: b.Text,
|
|
|
|
|
Contact: b.Contact,
|
|
|
|
|
Location: b.Location,
|
|
|
|
|
Poll: b.Poll,
|
|
|
|
|
}
|
|
|
|
|
}
|