NewBot, Settings, Poller, LongPoller - new bot creation API.

pull/108/head
Ian Byrd 7 years ago
parent 0f1d521167
commit 43be06e850
No known key found for this signature in database
GPG Key ID: 598F598CA3B8055F

@ -117,52 +117,42 @@ func (b *Bot) sendObject(f *File, what string, params map[string]string) (*Messa
return extractMsgResponse(respJSON)
}
func (b *Bot) getMe() (User, error) {
func (b *Bot) getMe() (*User, error) {
meJSON, err := b.sendCommand("getMe", nil)
if err != nil {
return User{}, err
return nil, err
}
var botInfo struct {
Ok bool
Result User
Result *User
Description string
}
err = json.Unmarshal(meJSON, &botInfo)
if err != nil {
return User{}, errors.Wrap(err, "bad response json")
return nil, errors.Wrap(err, "bad response json")
}
if !botInfo.Ok {
return User{}, errors.Errorf("api error: %s", botInfo.Description)
return nil, errors.Errorf("api error: %s", botInfo.Description)
}
return botInfo.Result, nil
}
// Update object represents an incoming update.
type Update struct {
ID int64 `json:"update_id"`
Payload *Message `json:"message"`
// optional
Callback *Callback `json:"callback_query"`
Query *Query `json:"inline_query"`
}
func (b *Bot) getUpdates(offset int64, timeout time.Duration) (upd []Update, err error) {
func (b *Bot) getUpdates(offset int, timeout time.Duration) (upd []Update, err error) {
params := map[string]string{
"offset": strconv.FormatInt(offset, 10),
"timeout": strconv.FormatInt(int64(timeout/time.Second), 10),
"offset": strconv.Itoa(offset),
"timeout": strconv.Itoa(int(timeout / time.Second)),
}
updatesJSON, errCommand := b.sendCommand("getUpdates", params)
if errCommand != nil {
err = errCommand
return
}
}
var updatesReceived struct {
Ok bool
Result []Update

127
bot.go

@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"strconv"
"time"
"github.com/armon/go-radix"
"github.com/pkg/errors"
@ -12,8 +11,10 @@ import (
// Bot represents a separate Telegram bot instance.
type Bot struct {
Token string
Identity User
Token string
Identity *User
Updates chan Update
Messages chan Message
Queries chan Query
Callbacks chan Callback
@ -27,96 +28,56 @@ type Bot struct {
// NewBot does try to build a Bot with token `token`, which
// is a secret API key assigned to particular bot.
func NewBot(token string) (*Bot, error) {
bot := &Bot{
Token: token,
tree: radix.New(),
func NewBot(pref Settings) (*Bot, error) {
if pref.Updates == 0 {
pref.Updates = 100
}
user, err := bot.getMe()
if err != nil {
return nil, err
bot := &Bot{
Token: pref.Token,
Updates: make(chan Update, pref.Updates),
tree: radix.New(),
}
bot.Identity = user
return bot, nil
}
// Listen starts a new polling goroutine, one that periodically looks for
// updates and delivers new messages to the subscription channel.
func (b *Bot) Listen(subscription chan Message, timeout time.Duration) {
go b.poll(subscription, nil, nil, timeout)
}
// Start periodically polls messages, updates and callbacks into their
// corresponding channels of the bot object.
//
// NOTE: It's a blocking method!
func (b *Bot) Start(timeout time.Duration) {
b.poll(b.Messages, b.Queries, b.Callbacks, timeout)
}
func (b *Bot) debug(err error) {
if b.Errors != nil {
b.Errors <- errors.WithStack(err)
if pref.Messages != 0 {
bot.Messages = make(chan Message, pref.Messages)
}
}
func (b *Bot) poll(
messages chan Message,
queries chan Query,
callbacks chan Callback,
timeout time.Duration,
) {
var latestUpdate int64
for {
updates, err := b.getUpdates(latestUpdate+1, timeout)
if err != nil {
b.debug(errors.Wrap(err, "getUpdates() failed"))
continue
}
for _, update := range updates {
if update.Payload != nil /* if message */ {
if messages == nil {
continue
}
messages <- *update.Payload
} else if update.Query != nil /* if query */ {
if queries == nil {
continue
}
queries <- *update.Query
} else if update.Callback != nil {
if callbacks == nil {
continue
}
callbacks <- *update.Callback
}
latestUpdate = update.ID
}
if pref.Queries != 0 {
bot.Queries = make(chan Query, pref.Queries)
}
}
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
params := map[string]string{
"chat_id": to.Recipient(),
"text": text,
if pref.Callbacks != 0 {
bot.Callbacks = make(chan Callback, pref.Callbacks)
}
embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendMessage", params)
user, err := bot.getMe()
if err != nil {
return nil, err
}
return extractMsgResponse(respJSON)
bot.Identity = user
return bot, nil
}
// Settings represents a utility struct for passing certain
// properties of a bot around and is required to make bots.
type Settings struct {
// Telegram token
Token string
// Telegram serves three types of updates: messages,
// inline queries and callbacks.
//
// The following three variables set the capacity of
// each of the receiving channels.
Updates int // Default: 100
Messages int
Queries int
Callbacks int
// Poller is the provider of Updates.
Poller Poller
}
// Send accepts 2+ arguments, starting with destination chat, followed by
@ -148,8 +109,8 @@ func (b *Bot) Send(to Recipient, what interface{}, options ...interface{}) (*Mes
// Reply behaves just like Send() with an exception of "reply-to" indicator.
//
// This function will panic upon unsupported payloads and options!
func (b *Bot) Reply(to *Message, what interface{}, options ...interface{}) (*Message, error) {
// This function will panic upon unsupported payloads and options!
sendOpts := extractOptions(options)
if sendOpts == nil {
sendOpts = &SendOptions{}
@ -496,10 +457,10 @@ func (b *Bot) ProfilePhotosOf(user *User) ([]Photo, error) {
return resp.Result.Photos, nil
}
// GetChatMember return information about a member of a chat.
// ChatMemberOf return information about a member of a chat.
//
// Returns a ChatMember object on success.
func (b *Bot) GetChatMember(chat *Chat, user *User) (ChatMember, error) {
func (b *Bot) ChatMemberOf(chat *Chat, user *User) (ChatMember, error) {
params := map[string]string{
"chat_id": chat.Recipient(),
"user_id": user.Recipient(),
@ -529,7 +490,7 @@ func (b *Bot) GetChatMember(chat *Chat, user *User) (ChatMember, error) {
}
// GetFileDirectURL returns direct url for files using FileId which you can get from File object
func (b *Bot) GetFileDirectURL(fileID string) (string, error) {
func (b *Bot) FileURLByID(fileID string) (string, error) {
f, err := b.FileByID(fileID)
if err != nil {
return "", err

@ -0,0 +1,53 @@
package telebot
import (
"time"
"github.com/pkg/errors"
)
// Update object represents an incoming update.
type Update struct {
ID int `json:"update_id"`
// Either.
Message *Message `json:"message"`
Callback *Callback `json:"callback_query"`
Query *Query `json:"inline_query"`
}
// Poller is a provider of Updates.
//
// All pollers must implement Poll(), which accepts bot
// pointer and subscription channel and start polling
// asynchronously straight away.
type Poller interface {
// Poll is supposed to take the bot object
// subscription channel and start polling
// for Updates immediately.
Poll(b *Bot, dest chan Update)
}
// LongPoller is a classic LongPoller with timeout.
type LongPoller struct {
Timeout time.Duration
}
// Poll does long polling.
func (p *LongPoller) Poll(b *Bot, dest chan Update) {
var latestUpd int
for {
updates, err := b.getUpdates(latestUpd+1, p.Timeout)
if err != nil {
b.debug(errors.Wrap(err, "getUpdates() failed"))
continue
}
for _, update := range updates {
latestUpd = update.ID
dest <- update
}
}
}

@ -12,6 +12,27 @@ func wrapSystem(err error) error {
return errors.Wrap(err, "system error")
}
func (b *Bot) debug(err error) {
if b.Errors != nil {
b.Errors <- errors.WithStack(err)
}
}
func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
params := map[string]string{
"chat_id": to.Recipient(),
"text": text,
}
embedSendOptions(params, opt)
respJSON, err := b.sendCommand("sendMessage", params)
if err != nil {
return nil, err
}
return extractMsgResponse(respJSON)
}
func extractMsgResponse(respJSON []byte) (*Message, error) {
var resp struct {
Ok bool

Loading…
Cancel
Save